github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/creds/credhub/manager.go (about) 1 package credhub 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/url" 8 "sync" 9 10 "code.cloudfoundry.org/credhub-cli/credhub" 11 "code.cloudfoundry.org/credhub-cli/credhub/auth" 12 "code.cloudfoundry.org/lager" 13 "github.com/pf-qiu/concourse/v6/atc/creds" 14 ) 15 16 type CredHubManager struct { 17 URL string `long:"url" description:"CredHub server address used to access secrets."` 18 19 PathPrefix string `long:"path-prefix" default:"/concourse" description:"Path under which to namespace credential lookup."` 20 21 TLS TLS 22 UAA UAA 23 Client *LazyCredhub 24 } 25 26 type TLS struct { 27 CACerts []string `long:"ca-cert" description:"Paths to PEM-encoded CA cert files to use to verify the CredHub server SSL cert."` 28 ClientCert string `long:"client-cert" description:"Path to the client certificate for mutual TLS authorization."` 29 ClientKey string `long:"client-key" description:"Path to the client private key for mutual TLS authorization."` 30 Insecure bool `long:"insecure-skip-verify" description:"Enable insecure SSL verification."` 31 } 32 33 type UAA struct { 34 ClientId string `long:"client-id" description:"Client ID for CredHub authorization."` 35 ClientSecret string `long:"client-secret" description:"Client secret for CredHub authorization."` 36 } 37 38 func (manager *CredHubManager) MarshalJSON() ([]byte, error) { 39 health, err := manager.Health() 40 if err != nil { 41 return nil, err 42 } 43 44 response := map[string]interface{}{ 45 "url": manager.URL, 46 "path_prefix": manager.PathPrefix, 47 "ca_certs": manager.TLS.CACerts, 48 "uaa_client_id": manager.UAA.ClientId, 49 "health": health, 50 } 51 52 return json.Marshal(&response) 53 } 54 55 func (manager *CredHubManager) Init(log lager.Logger) error { 56 var options []credhub.Option 57 if manager.TLS.Insecure { 58 options = append(options, credhub.SkipTLSValidation(true)) 59 } 60 61 caCerts := []string{} 62 for _, cert := range manager.TLS.CACerts { 63 contents, err := ioutil.ReadFile(cert) 64 if err != nil { 65 return err 66 } 67 68 caCerts = append(caCerts, string(contents)) 69 } 70 71 if len(caCerts) > 0 { 72 options = append(options, credhub.CaCerts(caCerts...)) 73 } 74 75 if manager.UAA.ClientId != "" && manager.UAA.ClientSecret != "" { 76 options = append(options, credhub.Auth(auth.UaaClientCredentials( 77 manager.UAA.ClientId, 78 manager.UAA.ClientSecret, 79 ))) 80 } 81 82 if manager.TLS.ClientCert != "" && manager.TLS.ClientKey != "" { 83 options = append(options, credhub.ClientCert(manager.TLS.ClientCert, manager.TLS.ClientKey)) 84 } 85 86 manager.Client = newLazyCredhub(manager.URL, options) 87 88 return nil 89 } 90 91 func (manager CredHubManager) IsConfigured() bool { 92 return manager.URL != "" || 93 manager.UAA.ClientId != "" || 94 manager.UAA.ClientSecret != "" || 95 len(manager.TLS.CACerts) != 0 || 96 manager.TLS.ClientCert != "" || 97 manager.TLS.ClientKey != "" 98 } 99 100 func (manager CredHubManager) Validate() error { 101 parsedUrl, err := url.Parse(manager.URL) 102 if err != nil { 103 return fmt.Errorf("invalid URL: %s", err) 104 } 105 106 // "foo" will parse without error (as a Path, with an empty Host) 107 // so we'll do a few additional sanity checks that this is a valid URL 108 if parsedUrl.Host == "" || !(parsedUrl.Scheme == "http" || parsedUrl.Scheme == "https") { 109 return fmt.Errorf("invalid URL (must be http or https)") 110 } 111 112 return nil 113 } 114 115 func (manager CredHubManager) Health() (*creds.HealthResponse, error) { 116 healthResponse := &creds.HealthResponse{ 117 Method: "/health", 118 } 119 120 credhubObject, err := manager.Client.CredHub() 121 if err != nil { 122 return healthResponse, err 123 } 124 125 response, err := credhubObject.Client().Get(manager.URL + "/health") 126 if err != nil { 127 healthResponse.Error = err.Error() 128 return healthResponse, nil 129 } 130 131 if response.StatusCode < 200 || response.StatusCode > 299 { 132 healthResponse.Error = "not ok" 133 return healthResponse, nil 134 } 135 136 var credhubHealth struct { 137 Status string `json:"status"` 138 } 139 140 defer response.Body.Close() 141 err = json.NewDecoder(response.Body).Decode(&credhubHealth) 142 if err != nil { 143 return nil, err 144 } 145 146 healthResponse.Response = credhubHealth 147 148 return healthResponse, nil 149 } 150 151 func (manager CredHubManager) NewSecretsFactory(logger lager.Logger) (creds.SecretsFactory, error) { 152 return NewCredHubFactory(logger, manager.Client, manager.PathPrefix), nil 153 } 154 155 type LazyCredhub struct { 156 url string 157 options []credhub.Option 158 credhub *credhub.CredHub 159 mu sync.Mutex 160 } 161 162 func newLazyCredhub(url string, options []credhub.Option) *LazyCredhub { 163 return &LazyCredhub{ 164 url: url, 165 options: options, 166 } 167 } 168 169 func (lc *LazyCredhub) CredHub() (*credhub.CredHub, error) { 170 lc.mu.Lock() 171 defer lc.mu.Unlock() 172 if lc.credhub != nil { 173 return lc.credhub, nil 174 } 175 176 ch, err := credhub.New(lc.url, lc.options...) 177 if err != nil { 178 return nil, err 179 } 180 181 lc.credhub = ch 182 183 return lc.credhub, nil 184 } 185 186 func (manager CredHubManager) Close(logger lager.Logger) { 187 // TODO - to implement 188 }