vault.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  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/tls"
  16. "crypto/x509"
  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. errVaultStore = "received invalid Vault SecretStore resource: %w"
  41. errVaultClient = "cannot setup new vault client: %w"
  42. errVaultCert = "cannot set Vault CA certificate: %w"
  43. errReadSecret = "cannot read secret data from Vault: %w"
  44. errAuthFormat = "cannot initialize Vault client: no valid auth method specified: %w"
  45. errDataField = "failed to find data field"
  46. errJSONUnmarshall = "failed to unmarshall JSON"
  47. errSecretFormat = "secret data not in expected format"
  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. errGetKubeSANoToken = "cannot find token in secrets bound to service account: %q"
  56. errGetKubeSecret = "cannot get Kubernetes secret %q: %w"
  57. errSecretKeyFmt = "cannot find secret data for key: %q"
  58. errClientTLSAuth = "error from Client TLS Auth: %q"
  59. errVaultRevokeToken = "error while revoking token: %w"
  60. )
  61. type Client interface {
  62. NewRequest(method, requestPath string) *vault.Request
  63. RawRequestWithContext(ctx context.Context, r *vault.Request) (*vault.Response, error)
  64. SetToken(v string)
  65. Token() string
  66. ClearToken()
  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()
  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(ctx context.Context) error {
  135. // Revoke the token if we have one set and it wasn't sourced from a TokenSecretRef
  136. if v.client.Token() != "" && v.store.Auth.TokenSecretRef == nil {
  137. req := v.client.NewRequest(http.MethodPost, "/v1/auth/token/revoke-self")
  138. _, err := v.client.RawRequestWithContext(ctx, req)
  139. if err != nil {
  140. return fmt.Errorf(errVaultRevokeToken, err)
  141. }
  142. v.client.ClearToken()
  143. }
  144. return nil
  145. }
  146. func (v *client) readSecret(ctx context.Context, path, version string) (map[string][]byte, error) {
  147. kvPath := v.store.Path
  148. if v.store.Version == esv1alpha1.VaultKVStoreV2 {
  149. if !strings.HasSuffix(kvPath, "/data") {
  150. kvPath = fmt.Sprintf("%s/data", kvPath)
  151. }
  152. }
  153. // path formated according to vault docs for v1 and v2 API
  154. // v1: https://www.vaultproject.io/api-docs/secret/kv/kv-v1#read-secret
  155. // v2: https://www.vaultproject.io/api/secret/kv/kv-v2#read-secret-version
  156. req := v.client.NewRequest(http.MethodGet, fmt.Sprintf("/v1/%s/%s", kvPath, path))
  157. if version != "" {
  158. req.Params.Set("version", version)
  159. }
  160. resp, err := v.client.RawRequestWithContext(ctx, req)
  161. if err != nil {
  162. return nil, fmt.Errorf(errReadSecret, err)
  163. }
  164. vaultSecret, err := vault.ParseSecret(resp.Body)
  165. if err != nil {
  166. return nil, err
  167. }
  168. secretData := vaultSecret.Data
  169. if v.store.Version == esv1alpha1.VaultKVStoreV2 {
  170. // Vault KV2 has data embedded within sub-field
  171. // reference - https://www.vaultproject.io/api/secret/kv/kv-v2#read-secret-version
  172. dataInt, ok := vaultSecret.Data["data"]
  173. if !ok {
  174. return nil, errors.New(errDataField)
  175. }
  176. secretData, ok = dataInt.(map[string]interface{})
  177. if !ok {
  178. return nil, errors.New(errJSONUnmarshall)
  179. }
  180. }
  181. byteMap := make(map[string][]byte, len(secretData))
  182. for k, v := range secretData {
  183. switch t := v.(type) {
  184. case string:
  185. byteMap[k] = []byte(t)
  186. case []byte:
  187. byteMap[k] = t
  188. default:
  189. return nil, errors.New(errSecretFormat)
  190. }
  191. }
  192. return byteMap, nil
  193. }
  194. func (v *client) newConfig() (*vault.Config, error) {
  195. cfg := vault.DefaultConfig()
  196. cfg.Address = v.store.Server
  197. if len(v.store.CABundle) == 0 && v.store.CAProvider == nil {
  198. return cfg, nil
  199. }
  200. caCertPool := x509.NewCertPool()
  201. if len(v.store.CABundle) > 0 {
  202. ok := caCertPool.AppendCertsFromPEM(v.store.CABundle)
  203. if !ok {
  204. return nil, errors.New(errVaultCert)
  205. }
  206. }
  207. if v.store.CAProvider != nil {
  208. if v.store.CAProvider.Type == esv1alpha1.CAProviderTypeSecret {
  209. secretRef := esmeta.SecretKeySelector{
  210. Name: v.store.CAProvider.Name,
  211. Namespace: &v.store.CAProvider.Namespace,
  212. Key: v.store.CAProvider.Key,
  213. }
  214. ctx := context.Background()
  215. res, err := v.secretKeyRef(ctx, &secretRef)
  216. if err != nil {
  217. return nil, errors.New(fmt.Sprintf(errVaultCert, err))
  218. }
  219. ok := caCertPool.AppendCertsFromPEM([]byte(res))
  220. if !ok {
  221. return nil, errors.New(errVaultCert)
  222. }
  223. }
  224. }
  225. if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
  226. transport.TLSClientConfig.RootCAs = caCertPool
  227. }
  228. return cfg, nil
  229. }
  230. func (v *client) setAuth(ctx context.Context, client Client, cfg *vault.Config) error {
  231. tokenRef := v.store.Auth.TokenSecretRef
  232. if tokenRef != nil {
  233. token, err := v.secretKeyRef(ctx, tokenRef)
  234. if err != nil {
  235. return err
  236. }
  237. client.SetToken(token)
  238. return nil
  239. }
  240. appRole := v.store.Auth.AppRole
  241. if appRole != nil {
  242. token, err := v.requestTokenWithAppRoleRef(ctx, client, appRole)
  243. if err != nil {
  244. return err
  245. }
  246. client.SetToken(token)
  247. return nil
  248. }
  249. kubernetesAuth := v.store.Auth.Kubernetes
  250. if kubernetesAuth != nil {
  251. token, err := v.requestTokenWithKubernetesAuth(ctx, client, kubernetesAuth)
  252. if err != nil {
  253. return err
  254. }
  255. client.SetToken(token)
  256. return nil
  257. }
  258. ldapAuth := v.store.Auth.Ldap
  259. if ldapAuth != nil {
  260. token, err := v.requestTokenWithLdapAuth(ctx, client, ldapAuth)
  261. if err != nil {
  262. return err
  263. }
  264. client.SetToken(token)
  265. return nil
  266. }
  267. jwtAuth := v.store.Auth.Jwt
  268. if jwtAuth != nil {
  269. token, err := v.requestTokenWithJwtAuth(ctx, client, jwtAuth)
  270. if err != nil {
  271. return err
  272. }
  273. client.SetToken(token)
  274. return nil
  275. }
  276. certAuth := v.store.Auth.Cert
  277. if certAuth != nil {
  278. token, err := v.requestTokenWithCertAuth(ctx, client, certAuth, cfg)
  279. if err != nil {
  280. return err
  281. }
  282. client.SetToken(token)
  283. return nil
  284. }
  285. return errors.New(errAuthFormat)
  286. }
  287. func (v *client) secretKeyRefForServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) (string, error) {
  288. serviceAccount := &corev1.ServiceAccount{}
  289. ref := types.NamespacedName{
  290. Namespace: v.namespace,
  291. Name: serviceAccountRef.Name,
  292. }
  293. if (v.storeKind == esv1alpha1.ClusterSecretStoreKind) &&
  294. (serviceAccountRef.Namespace != nil) {
  295. ref.Namespace = *serviceAccountRef.Namespace
  296. }
  297. err := v.kube.Get(ctx, ref, serviceAccount)
  298. if err != nil {
  299. return "", fmt.Errorf(errGetKubeSA, ref.Name, err)
  300. }
  301. if len(serviceAccount.Secrets) == 0 {
  302. return "", fmt.Errorf(errGetKubeSASecrets, ref.Name)
  303. }
  304. for _, tokenRef := range serviceAccount.Secrets {
  305. retval, err := v.secretKeyRef(ctx, &esmeta.SecretKeySelector{
  306. Name: tokenRef.Name,
  307. Namespace: &ref.Namespace,
  308. Key: "token",
  309. })
  310. if err != nil {
  311. continue
  312. }
  313. return retval, nil
  314. }
  315. return "", fmt.Errorf(errGetKubeSANoToken, ref.Name)
  316. }
  317. func (v *client) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySelector) (string, error) {
  318. secret := &corev1.Secret{}
  319. ref := types.NamespacedName{
  320. Namespace: v.namespace,
  321. Name: secretRef.Name,
  322. }
  323. if (v.storeKind == esv1alpha1.ClusterSecretStoreKind) &&
  324. (secretRef.Namespace != nil) {
  325. ref.Namespace = *secretRef.Namespace
  326. }
  327. err := v.kube.Get(ctx, ref, secret)
  328. if err != nil {
  329. return "", fmt.Errorf(errGetKubeSecret, ref.Name, err)
  330. }
  331. keyBytes, ok := secret.Data[secretRef.Key]
  332. if !ok {
  333. return "", fmt.Errorf(errSecretKeyFmt, secretRef.Key)
  334. }
  335. value := string(keyBytes)
  336. valueStr := strings.TrimSpace(value)
  337. return valueStr, nil
  338. }
  339. // appRoleParameters creates the required body for Vault AppRole Auth.
  340. // Reference - https://www.vaultproject.io/api-docs/auth/approle#login-with-approle
  341. func appRoleParameters(role, secret string) map[string]string {
  342. return map[string]string{
  343. "role_id": role,
  344. "secret_id": secret,
  345. }
  346. }
  347. func (v *client) requestTokenWithAppRoleRef(ctx context.Context, client Client, appRole *esv1alpha1.VaultAppRole) (string, error) {
  348. roleID := strings.TrimSpace(appRole.RoleID)
  349. secretID, err := v.secretKeyRef(ctx, &appRole.SecretRef)
  350. if err != nil {
  351. return "", err
  352. }
  353. parameters := appRoleParameters(roleID, secretID)
  354. url := strings.Join([]string{"/v1", "auth", appRole.Path, "login"}, "/")
  355. request := client.NewRequest("POST", url)
  356. err = request.SetJSONBody(parameters)
  357. if err != nil {
  358. return "", fmt.Errorf(errVaultReqParams, err)
  359. }
  360. resp, err := client.RawRequestWithContext(ctx, request)
  361. if err != nil {
  362. return "", fmt.Errorf(errVaultRequest, err)
  363. }
  364. defer resp.Body.Close()
  365. vaultResult := vault.Secret{}
  366. if err = resp.DecodeJSON(&vaultResult); err != nil {
  367. return "", fmt.Errorf(errVaultResponse, err)
  368. }
  369. token, err := vaultResult.TokenID()
  370. if err != nil {
  371. return "", fmt.Errorf(errVaultToken, err)
  372. }
  373. return token, nil
  374. }
  375. // kubeParameters creates the required body for Vault Kubernetes auth.
  376. // Reference - https://www.vaultproject.io/api/auth/kubernetes#login
  377. func kubeParameters(role, jwt string) map[string]string {
  378. return map[string]string{
  379. "role": role,
  380. "jwt": jwt,
  381. }
  382. }
  383. func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, client Client, kubernetesAuth *esv1alpha1.VaultKubernetesAuth) (string, error) {
  384. jwtString := ""
  385. if kubernetesAuth.ServiceAccountRef != nil {
  386. jwt, err := v.secretKeyRefForServiceAccount(ctx, kubernetesAuth.ServiceAccountRef)
  387. if err != nil {
  388. return "", err
  389. }
  390. jwtString = jwt
  391. } else if kubernetesAuth.SecretRef != nil {
  392. tokenRef := kubernetesAuth.SecretRef
  393. if tokenRef.Key == "" {
  394. tokenRef = kubernetesAuth.SecretRef.DeepCopy()
  395. tokenRef.Key = "token"
  396. }
  397. jwt, err := v.secretKeyRef(ctx, tokenRef)
  398. if err != nil {
  399. return "", err
  400. }
  401. jwtString = jwt
  402. } else {
  403. // Kubernetes authentication is specified, but without a referenced
  404. // Kubernetes secret. We check if the file path for in-cluster service account
  405. // exists and attempt to use the token for Vault Kubernetes auth.
  406. if _, err := os.Stat(serviceAccTokenPath); err != nil {
  407. return "", fmt.Errorf(errServiceAccount, err)
  408. }
  409. jwtByte, err := ioutil.ReadFile(serviceAccTokenPath)
  410. if err != nil {
  411. return "", fmt.Errorf(errServiceAccount, err)
  412. }
  413. jwtString = string(jwtByte)
  414. }
  415. parameters := kubeParameters(kubernetesAuth.Role, jwtString)
  416. url := strings.Join([]string{"/v1", "auth", kubernetesAuth.Path, "login"}, "/")
  417. request := client.NewRequest("POST", url)
  418. err := request.SetJSONBody(parameters)
  419. if err != nil {
  420. return "", fmt.Errorf(errVaultReqParams, err)
  421. }
  422. resp, err := client.RawRequestWithContext(ctx, request)
  423. if err != nil {
  424. return "", fmt.Errorf(errVaultRequest, err)
  425. }
  426. defer resp.Body.Close()
  427. vaultResult := vault.Secret{}
  428. err = resp.DecodeJSON(&vaultResult)
  429. if err != nil {
  430. return "", fmt.Errorf(errVaultResponse, err)
  431. }
  432. token, err := vaultResult.TokenID()
  433. if err != nil {
  434. return "", fmt.Errorf(errVaultToken, err)
  435. }
  436. return token, nil
  437. }
  438. func (v *client) requestTokenWithLdapAuth(ctx context.Context, client Client, ldapAuth *esv1alpha1.VaultLdapAuth) (string, error) {
  439. username := strings.TrimSpace(ldapAuth.Username)
  440. password, err := v.secretKeyRef(ctx, &ldapAuth.SecretRef)
  441. if err != nil {
  442. return "", err
  443. }
  444. parameters := map[string]string{
  445. "password": password,
  446. }
  447. url := strings.Join([]string{"/v1", "auth", "ldap", "login", username}, "/")
  448. request := client.NewRequest("POST", url)
  449. err = request.SetJSONBody(parameters)
  450. if err != nil {
  451. return "", fmt.Errorf(errVaultReqParams, err)
  452. }
  453. resp, err := client.RawRequestWithContext(ctx, request)
  454. if err != nil {
  455. return "", fmt.Errorf(errVaultRequest, err)
  456. }
  457. defer resp.Body.Close()
  458. vaultResult := vault.Secret{}
  459. if err = resp.DecodeJSON(&vaultResult); err != nil {
  460. return "", fmt.Errorf(errVaultResponse, err)
  461. }
  462. token, err := vaultResult.TokenID()
  463. if err != nil {
  464. return "", fmt.Errorf(errVaultToken, err)
  465. }
  466. return token, nil
  467. }
  468. func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwtAuth *esv1alpha1.VaultJwtAuth) (string, error) {
  469. role := strings.TrimSpace(jwtAuth.Role)
  470. jwt, err := v.secretKeyRef(ctx, &jwtAuth.SecretRef)
  471. if err != nil {
  472. return "", err
  473. }
  474. parameters := map[string]string{
  475. "role": role,
  476. "jwt": jwt,
  477. }
  478. url := strings.Join([]string{"/v1", "auth", "jwt", "login"}, "/")
  479. request := client.NewRequest("POST", url)
  480. err = request.SetJSONBody(parameters)
  481. if err != nil {
  482. return "", fmt.Errorf(errVaultReqParams, err)
  483. }
  484. resp, err := client.RawRequestWithContext(ctx, request)
  485. if err != nil {
  486. return "", fmt.Errorf(errVaultRequest, err)
  487. }
  488. defer resp.Body.Close()
  489. vaultResult := vault.Secret{}
  490. if err = resp.DecodeJSON(&vaultResult); err != nil {
  491. return "", fmt.Errorf(errVaultResponse, err)
  492. }
  493. token, err := vaultResult.TokenID()
  494. if err != nil {
  495. return "", fmt.Errorf(errVaultToken, err)
  496. }
  497. return token, nil
  498. }
  499. func (v *client) requestTokenWithCertAuth(ctx context.Context, client Client, certAuth *esv1alpha1.VaultCertAuth, cfg *vault.Config) (string, error) {
  500. clientKey, err := v.secretKeyRef(ctx, &certAuth.SecretRef)
  501. if err != nil {
  502. return "", err
  503. }
  504. clientCert, err := v.secretKeyRef(ctx, &certAuth.ClientCert)
  505. if err != nil {
  506. return "", err
  507. }
  508. cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
  509. if err != nil {
  510. return "", fmt.Errorf(errClientTLSAuth, err)
  511. }
  512. if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
  513. transport.TLSClientConfig.Certificates = []tls.Certificate{cert}
  514. }
  515. url := strings.Join([]string{"/v1", "auth", "cert", "login"}, "/")
  516. request := client.NewRequest("POST", url)
  517. resp, err := client.RawRequestWithContext(ctx, request)
  518. if err != nil {
  519. return "", fmt.Errorf(errVaultRequest, err)
  520. }
  521. defer resp.Body.Close()
  522. vaultResult := vault.Secret{}
  523. if err = resp.DecodeJSON(&vaultResult); err != nil {
  524. return "", fmt.Errorf(errVaultResponse, err)
  525. }
  526. token, err := vaultResult.TokenID()
  527. if err != nil {
  528. return "", fmt.Errorf(errVaultToken, err)
  529. }
  530. return token, nil
  531. }