vault.go 16 KB

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