vault.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package vault
  13. import (
  14. "context"
  15. "crypto/x509"
  16. b64 "encoding/base64"
  17. "errors"
  18. "fmt"
  19. "io/ioutil"
  20. "net/http"
  21. "os"
  22. "strings"
  23. "github.com/go-logr/logr"
  24. vault "github.com/hashicorp/vault/api"
  25. corev1 "k8s.io/api/core/v1"
  26. "k8s.io/apimachinery/pkg/types"
  27. ctrl "sigs.k8s.io/controller-runtime"
  28. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  29. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  30. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  31. "github.com/external-secrets/external-secrets/pkg/provider"
  32. "github.com/external-secrets/external-secrets/pkg/provider/schema"
  33. )
  34. var (
  35. _ provider.Provider = &connector{}
  36. _ provider.SecretsClient = &client{}
  37. )
  38. const (
  39. serviceAccTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
  40. clientCertsBasePath = "/auth-certs/"
  41. errVaultStore = "received invalid Vault SecretStore resource: %w"
  42. errVaultClient = "cannot setup new vault client: %w"
  43. errVaultTLSClient = "cannot setup new TLS vault client: %w"
  44. errVaultCert = "cannot set Vault CA certificate: %w"
  45. errReadSecret = "cannot read secret data from Vault: %w"
  46. errAuthFormat = "cannot initialize Vault client: no valid auth method specified: %w"
  47. errVaultData = "cannot parse Vault response data: %w"
  48. errVaultToken = "cannot parse Vault authentication token: %w"
  49. errVaultReqParams = "cannot set Vault request parameters: %w"
  50. errVaultRequest = "error from Vault request: %w"
  51. errVaultResponse = "cannot parse Vault response: %w"
  52. errServiceAccount = "cannot read Kubernetes service account token from file system: %w"
  53. errGetKubeSA = "cannot get Kubernetes service account %q: %w"
  54. errGetKubeSASecrets = "cannot find secrets bound to service account: %q"
  55. errGetKubeSecret = "cannot get Kubernetes secret %q: %w"
  56. errSecretKeyFmt = "cannot find secret data for key: %q"
  57. errGetCertPath = "cannot get certificates path: %w"
  58. errOsCreateFile = "cannot create file to store certificate: %w"
  59. errCertDecode = "error decoding certificate: %w"
  60. errWriteCertToFile = "cannot write certificate to file: %w"
  61. errCertMkdir = "cannot create path to store certificate: %w"
  62. )
  63. type Client interface {
  64. NewRequest(method, requestPath string) *vault.Request
  65. RawRequestWithContext(ctx context.Context, r *vault.Request) (*vault.Response, error)
  66. SetToken(v string)
  67. SetNamespace(namespace string)
  68. }
  69. type client struct {
  70. kube kclient.Client
  71. store *esv1alpha1.VaultProvider
  72. log logr.Logger
  73. client Client
  74. namespace string
  75. storeKind string
  76. }
  77. func init() {
  78. schema.Register(&connector{
  79. newVaultClient: newVaultClient,
  80. }, &esv1alpha1.SecretStoreProvider{
  81. Vault: &esv1alpha1.VaultProvider{},
  82. })
  83. }
  84. func newVaultClient(c *vault.Config) (Client, error) {
  85. return vault.NewClient(c)
  86. }
  87. type connector struct {
  88. newVaultClient func(c *vault.Config) (Client, error)
  89. }
  90. func (c *connector) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
  91. storeSpec := store.GetSpec()
  92. if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Vault == nil {
  93. return nil, errors.New(errVaultStore)
  94. }
  95. vaultSpec := storeSpec.Provider.Vault
  96. vStore := &client{
  97. kube: kube,
  98. store: vaultSpec,
  99. log: ctrl.Log.WithName("provider").WithName("vault"),
  100. namespace: namespace,
  101. storeKind: store.GetObjectKind().GroupVersionKind().Kind,
  102. }
  103. cfg, err := vStore.newConfig(ctx)
  104. if err != nil {
  105. return nil, err
  106. }
  107. client, err := c.newVaultClient(cfg)
  108. if err != nil {
  109. return nil, fmt.Errorf(errVaultClient, err)
  110. }
  111. if vaultSpec.Namespace != nil {
  112. client.SetNamespace(*vaultSpec.Namespace)
  113. }
  114. if err := vStore.setAuth(ctx, client, cfg); err != nil {
  115. return nil, err
  116. }
  117. vStore.client = client
  118. return vStore, nil
  119. }
  120. func (v *client) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
  121. data, err := v.readSecret(ctx, ref.Key, ref.Version)
  122. if err != nil {
  123. return nil, err
  124. }
  125. value, exists := data[ref.Property]
  126. if !exists {
  127. return nil, fmt.Errorf(errSecretKeyFmt, ref.Property)
  128. }
  129. return value, nil
  130. }
  131. func (v *client) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  132. return v.readSecret(ctx, ref.Key, ref.Version)
  133. }
  134. func (v *client) Close() error {
  135. return nil
  136. }
  137. func (v *client) readSecret(ctx context.Context, path, version string) (map[string][]byte, error) {
  138. kvPath := v.store.Path
  139. if v.store.Version == esv1alpha1.VaultKVStoreV2 {
  140. if !strings.HasSuffix(kvPath, "/data") {
  141. kvPath = fmt.Sprintf("%s/data", kvPath)
  142. }
  143. }
  144. // path formated according to vault docs for v1 and v2 API
  145. // v1: https://www.vaultproject.io/api-docs/secret/kv/kv-v1#read-secret
  146. // v2: https://www.vaultproject.io/api/secret/kv/kv-v2#read-secret-version
  147. req := v.client.NewRequest(http.MethodGet, fmt.Sprintf("/v1/%s/%s", kvPath, path))
  148. if version != "" {
  149. req.Params.Set("version", version)
  150. }
  151. resp, err := v.client.RawRequestWithContext(ctx, req)
  152. if err != nil {
  153. return nil, fmt.Errorf(errReadSecret, err)
  154. }
  155. vaultSecret, err := vault.ParseSecret(resp.Body)
  156. if err != nil {
  157. return nil, err
  158. }
  159. secretData := vaultSecret.Data
  160. if v.store.Version == esv1alpha1.VaultKVStoreV2 {
  161. // Vault KV2 has data embedded within sub-field
  162. // reference - https://www.vaultproject.io/api/secret/kv/kv-v2#read-secret-version
  163. dataInt, ok := vaultSecret.Data["data"]
  164. if !ok {
  165. return nil, errors.New(errVaultData)
  166. }
  167. secretData, ok = dataInt.(map[string]interface{})
  168. if !ok {
  169. return nil, errors.New(errVaultData)
  170. }
  171. }
  172. byteMap := make(map[string][]byte, len(secretData))
  173. for k, v := range secretData {
  174. switch t := v.(type) {
  175. case string:
  176. byteMap[k] = []byte(t)
  177. case []byte:
  178. byteMap[k] = t
  179. default:
  180. return nil, errors.New(errVaultData)
  181. }
  182. }
  183. return byteMap, nil
  184. }
  185. func (v *client) newConfig(ctx context.Context) (*vault.Config, error) {
  186. cfg := vault.DefaultConfig()
  187. cfg.Address = v.store.Server
  188. if len(v.store.CABundle) == 0 {
  189. return cfg, nil
  190. }
  191. caCertPool := x509.NewCertPool()
  192. ok := caCertPool.AppendCertsFromPEM(v.store.CABundle)
  193. if !ok {
  194. return nil, errors.New(errVaultCert)
  195. }
  196. if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
  197. transport.TLSClientConfig.RootCAs = caCertPool
  198. }
  199. return cfg, nil
  200. }
  201. func (v *client) setAuth(ctx context.Context, client Client, cfg *vault.Config) error {
  202. tokenRef := v.store.Auth.TokenSecretRef
  203. if tokenRef != nil {
  204. token, err := v.secretKeyRef(ctx, tokenRef)
  205. if err != nil {
  206. return err
  207. }
  208. client.SetToken(token)
  209. return nil
  210. }
  211. appRole := v.store.Auth.AppRole
  212. if appRole != nil {
  213. token, err := v.requestTokenWithAppRoleRef(ctx, client, appRole)
  214. if err != nil {
  215. return err
  216. }
  217. client.SetToken(token)
  218. return nil
  219. }
  220. kubernetesAuth := v.store.Auth.Kubernetes
  221. if kubernetesAuth != nil {
  222. token, err := v.requestTokenWithKubernetesAuth(ctx, client, kubernetesAuth)
  223. if err != nil {
  224. return err
  225. }
  226. client.SetToken(token)
  227. return nil
  228. }
  229. ldapAuth := v.store.Auth.Ldap
  230. if ldapAuth != nil {
  231. token, err := v.requestTokenWithLdapAuth(ctx, client, ldapAuth)
  232. if err != nil {
  233. return err
  234. }
  235. client.SetToken(token)
  236. return nil
  237. }
  238. jwtAuth := v.store.Auth.Jwt
  239. if jwtAuth != nil {
  240. token, err := v.requestTokenWithJwtAuth(ctx, client, jwtAuth)
  241. if err != nil {
  242. return err
  243. }
  244. client.SetToken(token)
  245. return nil
  246. }
  247. certAuth := v.store.Auth.Cert
  248. if certAuth != nil {
  249. token, err := v.requestTokenWithCertAuth(ctx, client, certAuth, cfg)
  250. if err != nil {
  251. return err
  252. }
  253. client.SetToken(token)
  254. return nil
  255. }
  256. return errors.New(errAuthFormat)
  257. }
  258. func (v *client) secretKeyRefForServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) (string, error) {
  259. serviceAccount := &corev1.ServiceAccount{}
  260. ref := types.NamespacedName{
  261. Namespace: v.namespace,
  262. Name: serviceAccountRef.Name,
  263. }
  264. if (v.storeKind == esv1alpha1.ClusterSecretStoreKind) &&
  265. (serviceAccountRef.Namespace != nil) {
  266. ref.Namespace = *serviceAccountRef.Namespace
  267. }
  268. err := v.kube.Get(ctx, ref, serviceAccount)
  269. if err != nil {
  270. return "", fmt.Errorf(errGetKubeSA, ref.Name, err)
  271. }
  272. if len(serviceAccount.Secrets) == 0 {
  273. return "", fmt.Errorf(errGetKubeSASecrets, ref.Name)
  274. }
  275. tokenRef := serviceAccount.Secrets[0]
  276. return v.secretKeyRef(ctx, &esmeta.SecretKeySelector{
  277. Name: tokenRef.Name,
  278. Namespace: &ref.Namespace,
  279. Key: "token",
  280. })
  281. }
  282. func (v *client) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySelector) (string, error) {
  283. secret := &corev1.Secret{}
  284. ref := types.NamespacedName{
  285. Namespace: v.namespace,
  286. Name: secretRef.Name,
  287. }
  288. if (v.storeKind == esv1alpha1.ClusterSecretStoreKind) &&
  289. (secretRef.Namespace != nil) {
  290. ref.Namespace = *secretRef.Namespace
  291. }
  292. err := v.kube.Get(ctx, ref, secret)
  293. if err != nil {
  294. return "", fmt.Errorf(errGetKubeSecret, ref.Name, err)
  295. }
  296. keyBytes, ok := secret.Data[secretRef.Key]
  297. if !ok {
  298. return "", fmt.Errorf(errSecretKeyFmt, secretRef.Key)
  299. }
  300. value := string(keyBytes)
  301. valueStr := strings.TrimSpace(value)
  302. return valueStr, nil
  303. }
  304. // appRoleParameters creates the required body for Vault AppRole Auth.
  305. // Reference - https://www.vaultproject.io/api-docs/auth/approle#login-with-approle
  306. func appRoleParameters(role, secret string) map[string]string {
  307. return map[string]string{
  308. "role_id": role,
  309. "secret_id": secret,
  310. }
  311. }
  312. func (v *client) requestTokenWithAppRoleRef(ctx context.Context, client Client, appRole *esv1alpha1.VaultAppRole) (string, error) {
  313. roleID := strings.TrimSpace(appRole.RoleID)
  314. secretID, err := v.secretKeyRef(ctx, &appRole.SecretRef)
  315. if err != nil {
  316. return "", err
  317. }
  318. parameters := appRoleParameters(roleID, secretID)
  319. url := strings.Join([]string{"/v1", "auth", appRole.Path, "login"}, "/")
  320. request := client.NewRequest("POST", url)
  321. err = request.SetJSONBody(parameters)
  322. if err != nil {
  323. return "", fmt.Errorf(errVaultReqParams, err)
  324. }
  325. resp, err := client.RawRequestWithContext(ctx, request)
  326. if err != nil {
  327. return "", fmt.Errorf(errVaultRequest, err)
  328. }
  329. defer resp.Body.Close()
  330. vaultResult := vault.Secret{}
  331. if err = resp.DecodeJSON(&vaultResult); err != nil {
  332. return "", fmt.Errorf(errVaultResponse, err)
  333. }
  334. token, err := vaultResult.TokenID()
  335. if err != nil {
  336. return "", fmt.Errorf(errVaultToken, err)
  337. }
  338. return token, nil
  339. }
  340. // kubeParameters creates the required body for Vault Kubernetes auth.
  341. // Reference - https://www.vaultproject.io/api/auth/kubernetes#login
  342. func kubeParameters(role, jwt string) map[string]string {
  343. return map[string]string{
  344. "role": role,
  345. "jwt": jwt,
  346. }
  347. }
  348. func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, client Client, kubernetesAuth *esv1alpha1.VaultKubernetesAuth) (string, error) {
  349. jwtString := ""
  350. if kubernetesAuth.ServiceAccountRef != nil {
  351. jwt, err := v.secretKeyRefForServiceAccount(ctx, kubernetesAuth.ServiceAccountRef)
  352. if err != nil {
  353. return "", err
  354. }
  355. jwtString = jwt
  356. } else if kubernetesAuth.SecretRef != nil {
  357. tokenRef := kubernetesAuth.SecretRef
  358. if tokenRef.Key == "" {
  359. tokenRef = kubernetesAuth.SecretRef.DeepCopy()
  360. tokenRef.Key = "token"
  361. }
  362. jwt, err := v.secretKeyRef(ctx, tokenRef)
  363. if err != nil {
  364. return "", err
  365. }
  366. jwtString = jwt
  367. } else {
  368. // Kubernetes authentication is specified, but without a referenced
  369. // Kubernetes secret. We check if the file path for in-cluster service account
  370. // exists and attempt to use the token for Vault Kubernetes auth.
  371. if _, err := os.Stat(serviceAccTokenPath); err != nil {
  372. return "", fmt.Errorf(errServiceAccount, err)
  373. }
  374. jwtByte, err := ioutil.ReadFile(serviceAccTokenPath)
  375. if err != nil {
  376. return "", fmt.Errorf(errServiceAccount, err)
  377. }
  378. jwtString = string(jwtByte)
  379. }
  380. parameters := kubeParameters(kubernetesAuth.Role, jwtString)
  381. url := strings.Join([]string{"/v1", "auth", kubernetesAuth.Path, "login"}, "/")
  382. request := client.NewRequest("POST", url)
  383. err := request.SetJSONBody(parameters)
  384. if err != nil {
  385. return "", fmt.Errorf(errVaultReqParams, err)
  386. }
  387. resp, err := client.RawRequestWithContext(ctx, request)
  388. if err != nil {
  389. return "", fmt.Errorf(errVaultRequest, err)
  390. }
  391. defer resp.Body.Close()
  392. vaultResult := vault.Secret{}
  393. err = resp.DecodeJSON(&vaultResult)
  394. if err != nil {
  395. return "", fmt.Errorf(errVaultResponse, err)
  396. }
  397. token, err := vaultResult.TokenID()
  398. if err != nil {
  399. return "", fmt.Errorf(errVaultToken, err)
  400. }
  401. return token, nil
  402. }
  403. func (v *client) requestTokenWithLdapAuth(ctx context.Context, client Client, ldapAuth *esv1alpha1.VaultLdapAuth) (string, error) {
  404. username := strings.TrimSpace(ldapAuth.Username)
  405. password, err := v.secretKeyRef(ctx, &ldapAuth.SecretRef)
  406. if err != nil {
  407. return "", err
  408. }
  409. parameters := map[string]string{
  410. "password": password,
  411. }
  412. url := strings.Join([]string{"/v1", "auth", "ldap", "login", username}, "/")
  413. request := client.NewRequest("POST", url)
  414. err = request.SetJSONBody(parameters)
  415. if err != nil {
  416. return "", fmt.Errorf(errVaultReqParams, err)
  417. }
  418. resp, err := client.RawRequestWithContext(ctx, request)
  419. if err != nil {
  420. return "", fmt.Errorf(errVaultRequest, err)
  421. }
  422. defer resp.Body.Close()
  423. vaultResult := vault.Secret{}
  424. if err = resp.DecodeJSON(&vaultResult); err != nil {
  425. return "", fmt.Errorf(errVaultResponse, err)
  426. }
  427. token, err := vaultResult.TokenID()
  428. if err != nil {
  429. return "", fmt.Errorf(errVaultToken, err)
  430. }
  431. return token, nil
  432. }
  433. func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwtAuth *esv1alpha1.VaultJwtAuth) (string, error) {
  434. role := strings.TrimSpace(jwtAuth.Role)
  435. jwt, err := v.secretKeyRef(ctx, &jwtAuth.SecretRef)
  436. if err != nil {
  437. return "", err
  438. }
  439. parameters := map[string]string{
  440. "role": role,
  441. "jwt": jwt,
  442. }
  443. url := strings.Join([]string{"/v1", "auth", "cert", "login"}, "/")
  444. request := client.NewRequest("POST", url)
  445. err = request.SetJSONBody(parameters)
  446. if err != nil {
  447. return "", fmt.Errorf(errVaultReqParams, err)
  448. }
  449. resp, err := client.RawRequestWithContext(ctx, request)
  450. if err != nil {
  451. return "", fmt.Errorf(errVaultRequest, err)
  452. }
  453. defer resp.Body.Close()
  454. vaultResult := vault.Secret{}
  455. if err = resp.DecodeJSON(&vaultResult); err != nil {
  456. return "", fmt.Errorf(errVaultResponse, err)
  457. }
  458. token, err := vaultResult.TokenID()
  459. if err != nil {
  460. return "", fmt.Errorf(errVaultToken, err)
  461. }
  462. return token, nil
  463. }
  464. func (v *client) requestTokenWithCertAuth(ctx context.Context, client Client, certAuth *esv1alpha1.VaultCertAuth, cfg *vault.Config) (string, error) {
  465. clientKey, err := v.secretKeyRef(ctx, &certAuth.SecretRef)
  466. if err != nil {
  467. return "", err
  468. }
  469. clientKeyPath, err := getCertPath(clientKey, "client.key")
  470. if err != nil {
  471. return "", fmt.Errorf(errGetCertPath, err)
  472. }
  473. clientCertPath, err := getCertPath(certAuth.ClientCert, "client.crt")
  474. if err != nil {
  475. return "", fmt.Errorf(errGetCertPath, err)
  476. }
  477. caCertPath, err := getCertPath(certAuth.CACert, "ca.crt")
  478. if err != nil {
  479. return "", fmt.Errorf(errGetCertPath, err)
  480. }
  481. tlscfg := vault.TLSConfig{
  482. ClientCert: clientCertPath,
  483. ClientKey: clientKeyPath,
  484. CACert: caCertPath,
  485. }
  486. err = cfg.ConfigureTLS(&tlscfg)
  487. if err != nil {
  488. return "", fmt.Errorf(errVaultCert, err)
  489. }
  490. url := strings.Join([]string{"/v1", "auth", "cert", "login"}, "/")
  491. request := client.NewRequest("POST", url)
  492. resp, err := client.RawRequestWithContext(ctx, request)
  493. if err != nil {
  494. return "", fmt.Errorf(errVaultRequest, err)
  495. }
  496. defer resp.Body.Close()
  497. vaultResult := vault.Secret{}
  498. if err = resp.DecodeJSON(&vaultResult); err != nil {
  499. return "", fmt.Errorf(errVaultResponse, err)
  500. }
  501. token, err := vaultResult.TokenID()
  502. if err != nil {
  503. return "", fmt.Errorf(errVaultToken, err)
  504. }
  505. return token, nil
  506. }
  507. func getCertPath(cert, filename string) (string, error) {
  508. certPath := clientCertsBasePath + filename
  509. if _, err := os.Stat(clientCertsBasePath); os.IsNotExist(err) {
  510. _, err := MkdirToStoreCertificate(clientCertsBasePath)
  511. return "", err
  512. }
  513. f, err := os.Create(certPath)
  514. if err != nil {
  515. return "", fmt.Errorf(errOsCreateFile, err)
  516. }
  517. defer f.Close()
  518. if filename == "client.crt" || filename == "ca.crt" {
  519. decodedCert, err := b64.StdEncoding.DecodeString(cert)
  520. if err != nil {
  521. return "", fmt.Errorf(errCertDecode, err)
  522. }
  523. cert = string(decodedCert)
  524. }
  525. _, err = f.WriteString(cert)
  526. if err != nil {
  527. return "", fmt.Errorf(errWriteCertToFile, err)
  528. }
  529. return certPath, nil
  530. }
  531. func MkdirToStoreCertificate(clientCertsBasePath string) (string, error) {
  532. if err := os.MkdirAll(clientCertsBasePath, 0700); err != nil {
  533. return "", fmt.Errorf(errCertMkdir, err)
  534. }
  535. return "", nil
  536. }