vault.go 14 KB

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