vault.go 15 KB

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