vault.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  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) readSecret(ctx context.Context, path, version string) (map[string][]byte, error) {
  127. kvPath := v.store.Path
  128. if v.store.Version == esv1alpha1.VaultKVStoreV2 {
  129. if !strings.HasSuffix(kvPath, "/data") {
  130. kvPath = fmt.Sprintf("%s/data", kvPath)
  131. }
  132. }
  133. // path formated according to vault docs for v1 and v2 API
  134. // v1: https://www.vaultproject.io/api-docs/secret/kv/kv-v1#read-secret
  135. // v2: https://www.vaultproject.io/api/secret/kv/kv-v2#read-secret-version
  136. req := v.client.NewRequest(http.MethodGet, fmt.Sprintf("/v1/%s/%s", kvPath, path))
  137. if version != "" {
  138. req.Params.Set("version", version)
  139. }
  140. resp, err := v.client.RawRequestWithContext(ctx, req)
  141. if err != nil {
  142. return nil, fmt.Errorf(errReadSecret, err)
  143. }
  144. vaultSecret, err := vault.ParseSecret(resp.Body)
  145. if err != nil {
  146. return nil, err
  147. }
  148. secretData := vaultSecret.Data
  149. if v.store.Version == esv1alpha1.VaultKVStoreV2 {
  150. // Vault KV2 has data embedded within sub-field
  151. // reference - https://www.vaultproject.io/api/secret/kv/kv-v2#read-secret-version
  152. dataInt, ok := vaultSecret.Data["data"]
  153. if !ok {
  154. return nil, errors.New(errVaultData)
  155. }
  156. secretData, ok = dataInt.(map[string]interface{})
  157. if !ok {
  158. return nil, errors.New(errVaultData)
  159. }
  160. }
  161. byteMap := make(map[string][]byte, len(secretData))
  162. for k, v := range secretData {
  163. switch t := v.(type) {
  164. case string:
  165. byteMap[k] = []byte(t)
  166. case []byte:
  167. byteMap[k] = t
  168. default:
  169. return nil, errors.New(errVaultData)
  170. }
  171. }
  172. return byteMap, nil
  173. }
  174. func (v *client) newConfig() (*vault.Config, error) {
  175. cfg := vault.DefaultConfig()
  176. cfg.Address = v.store.Server
  177. if len(v.store.CABundle) == 0 {
  178. return cfg, nil
  179. }
  180. caCertPool := x509.NewCertPool()
  181. ok := caCertPool.AppendCertsFromPEM(v.store.CABundle)
  182. if !ok {
  183. return nil, errors.New(errVaultCert)
  184. }
  185. if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
  186. transport.TLSClientConfig.RootCAs = caCertPool
  187. }
  188. return cfg, nil
  189. }
  190. func (v *client) setAuth(ctx context.Context, client Client) error {
  191. tokenRef := v.store.Auth.TokenSecretRef
  192. if tokenRef != nil {
  193. token, err := v.secretKeyRef(ctx, tokenRef)
  194. if err != nil {
  195. return err
  196. }
  197. client.SetToken(token)
  198. return nil
  199. }
  200. appRole := v.store.Auth.AppRole
  201. if appRole != nil {
  202. token, err := v.requestTokenWithAppRoleRef(ctx, client, appRole)
  203. if err != nil {
  204. return err
  205. }
  206. client.SetToken(token)
  207. return nil
  208. }
  209. kubernetesAuth := v.store.Auth.Kubernetes
  210. if kubernetesAuth != nil {
  211. token, err := v.requestTokenWithKubernetesAuth(ctx, client, kubernetesAuth)
  212. if err != nil {
  213. return err
  214. }
  215. client.SetToken(token)
  216. return nil
  217. }
  218. ldapAuth := v.store.Auth.Ldap
  219. if ldapAuth != nil {
  220. token, err := v.requestTokenWithLdapAuth(ctx, client, ldapAuth)
  221. if err != nil {
  222. return err
  223. }
  224. client.SetToken(token)
  225. return nil
  226. }
  227. jwtAuth := v.store.Auth.Jwt
  228. if jwtAuth != nil {
  229. token, err := v.requestTokenWithJwtAuth(ctx, client, jwtAuth)
  230. if err != nil {
  231. return err
  232. }
  233. client.SetToken(token)
  234. return nil
  235. }
  236. return errors.New(errAuthFormat)
  237. }
  238. func (v *client) secretKeyRefForServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) (string, error) {
  239. serviceAccount := &corev1.ServiceAccount{}
  240. ref := types.NamespacedName{
  241. Namespace: v.namespace,
  242. Name: serviceAccountRef.Name,
  243. }
  244. if (v.storeKind == esv1alpha1.ClusterSecretStoreKind) &&
  245. (serviceAccountRef.Namespace != nil) {
  246. ref.Namespace = *serviceAccountRef.Namespace
  247. }
  248. err := v.kube.Get(ctx, ref, serviceAccount)
  249. if err != nil {
  250. return "", fmt.Errorf(errGetKubeSA, ref.Name, err)
  251. }
  252. if len(serviceAccount.Secrets) == 0 {
  253. return "", fmt.Errorf(errGetKubeSASecrets, ref.Name)
  254. }
  255. tokenRef := serviceAccount.Secrets[0]
  256. return v.secretKeyRef(ctx, &esmeta.SecretKeySelector{
  257. Name: tokenRef.Name,
  258. Namespace: &ref.Namespace,
  259. Key: "token",
  260. })
  261. }
  262. func (v *client) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySelector) (string, error) {
  263. secret := &corev1.Secret{}
  264. ref := types.NamespacedName{
  265. Namespace: v.namespace,
  266. Name: secretRef.Name,
  267. }
  268. if (v.storeKind == esv1alpha1.ClusterSecretStoreKind) &&
  269. (secretRef.Namespace != nil) {
  270. ref.Namespace = *secretRef.Namespace
  271. }
  272. err := v.kube.Get(ctx, ref, secret)
  273. if err != nil {
  274. return "", fmt.Errorf(errGetKubeSecret, ref.Name, err)
  275. }
  276. keyBytes, ok := secret.Data[secretRef.Key]
  277. if !ok {
  278. return "", fmt.Errorf(errSecretKeyFmt, secretRef.Key)
  279. }
  280. value := string(keyBytes)
  281. valueStr := strings.TrimSpace(value)
  282. return valueStr, nil
  283. }
  284. // appRoleParameters creates the required body for Vault AppRole Auth.
  285. // Reference - https://www.vaultproject.io/api-docs/auth/approle#login-with-approle
  286. func appRoleParameters(role, secret string) map[string]string {
  287. return map[string]string{
  288. "role_id": role,
  289. "secret_id": secret,
  290. }
  291. }
  292. func (v *client) requestTokenWithAppRoleRef(ctx context.Context, client Client, appRole *esv1alpha1.VaultAppRole) (string, error) {
  293. roleID := strings.TrimSpace(appRole.RoleID)
  294. secretID, err := v.secretKeyRef(ctx, &appRole.SecretRef)
  295. if err != nil {
  296. return "", err
  297. }
  298. parameters := appRoleParameters(roleID, secretID)
  299. url := strings.Join([]string{"/v1", "auth", appRole.Path, "login"}, "/")
  300. request := client.NewRequest("POST", url)
  301. err = request.SetJSONBody(parameters)
  302. if err != nil {
  303. return "", fmt.Errorf(errVaultReqParams, err)
  304. }
  305. resp, err := client.RawRequestWithContext(ctx, request)
  306. if err != nil {
  307. return "", fmt.Errorf(errVaultRequest, err)
  308. }
  309. defer resp.Body.Close()
  310. vaultResult := vault.Secret{}
  311. if err = resp.DecodeJSON(&vaultResult); err != nil {
  312. return "", fmt.Errorf(errVaultResponse, err)
  313. }
  314. token, err := vaultResult.TokenID()
  315. if err != nil {
  316. return "", fmt.Errorf(errVaultToken, err)
  317. }
  318. return token, nil
  319. }
  320. // kubeParameters creates the required body for Vault Kubernetes auth.
  321. // Reference - https://www.vaultproject.io/api/auth/kubernetes#login
  322. func kubeParameters(role, jwt string) map[string]string {
  323. return map[string]string{
  324. "role": role,
  325. "jwt": jwt,
  326. }
  327. }
  328. func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, client Client, kubernetesAuth *esv1alpha1.VaultKubernetesAuth) (string, error) {
  329. jwtString := ""
  330. if kubernetesAuth.ServiceAccountRef != nil {
  331. jwt, err := v.secretKeyRefForServiceAccount(ctx, kubernetesAuth.ServiceAccountRef)
  332. if err != nil {
  333. return "", err
  334. }
  335. jwtString = jwt
  336. } else if kubernetesAuth.SecretRef != nil {
  337. tokenRef := kubernetesAuth.SecretRef
  338. if tokenRef.Key == "" {
  339. tokenRef = kubernetesAuth.SecretRef.DeepCopy()
  340. tokenRef.Key = "token"
  341. }
  342. jwt, err := v.secretKeyRef(ctx, tokenRef)
  343. if err != nil {
  344. return "", err
  345. }
  346. jwtString = jwt
  347. } else {
  348. // Kubernetes authentication is specified, but without a referenced
  349. // Kubernetes secret. We check if the file path for in-cluster service account
  350. // exists and attempt to use the token for Vault Kubernetes auth.
  351. if _, err := os.Stat(serviceAccTokenPath); err != nil {
  352. return "", fmt.Errorf(errServiceAccount, err)
  353. }
  354. jwtByte, err := ioutil.ReadFile(serviceAccTokenPath)
  355. if err != nil {
  356. return "", fmt.Errorf(errServiceAccount, err)
  357. }
  358. jwtString = string(jwtByte)
  359. }
  360. parameters := kubeParameters(kubernetesAuth.Role, jwtString)
  361. url := strings.Join([]string{"/v1", "auth", kubernetesAuth.Path, "login"}, "/")
  362. request := client.NewRequest("POST", url)
  363. err := request.SetJSONBody(parameters)
  364. if err != nil {
  365. return "", fmt.Errorf(errVaultReqParams, err)
  366. }
  367. resp, err := client.RawRequestWithContext(ctx, request)
  368. if err != nil {
  369. return "", fmt.Errorf(errVaultRequest, err)
  370. }
  371. defer resp.Body.Close()
  372. vaultResult := vault.Secret{}
  373. err = resp.DecodeJSON(&vaultResult)
  374. if err != nil {
  375. return "", fmt.Errorf(errVaultResponse, err)
  376. }
  377. token, err := vaultResult.TokenID()
  378. if err != nil {
  379. return "", fmt.Errorf(errVaultToken, err)
  380. }
  381. return token, nil
  382. }
  383. func (v *client) requestTokenWithLdapAuth(ctx context.Context, client Client, ldapAuth *esv1alpha1.VaultLdapAuth) (string, error) {
  384. username := strings.TrimSpace(ldapAuth.Username)
  385. password, err := v.secretKeyRef(ctx, &ldapAuth.SecretRef)
  386. if err != nil {
  387. return "", err
  388. }
  389. parameters := map[string]string{
  390. "password": password,
  391. }
  392. url := strings.Join([]string{"/v1", "auth", "ldap", "login", username}, "/")
  393. request := client.NewRequest("POST", url)
  394. err = request.SetJSONBody(parameters)
  395. if err != nil {
  396. return "", fmt.Errorf(errVaultReqParams, err)
  397. }
  398. resp, err := client.RawRequestWithContext(ctx, request)
  399. if err != nil {
  400. return "", fmt.Errorf(errVaultRequest, err)
  401. }
  402. defer resp.Body.Close()
  403. vaultResult := vault.Secret{}
  404. if err = resp.DecodeJSON(&vaultResult); err != nil {
  405. return "", fmt.Errorf(errVaultResponse, err)
  406. }
  407. token, err := vaultResult.TokenID()
  408. if err != nil {
  409. return "", fmt.Errorf(errVaultToken, err)
  410. }
  411. return token, nil
  412. }
  413. func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwtAuth *esv1alpha1.VaultJwtAuth) (string, error) {
  414. role := strings.TrimSpace(jwtAuth.Role)
  415. jwt, err := v.secretKeyRef(ctx, &jwtAuth.SecretRef)
  416. if err != nil {
  417. return "", err
  418. }
  419. parameters := map[string]string{
  420. "role": role,
  421. "jwt": jwt,
  422. }
  423. url := strings.Join([]string{"/v1", "auth", "jwt", "login"}, "/")
  424. request := client.NewRequest("POST", url)
  425. err = request.SetJSONBody(parameters)
  426. if err != nil {
  427. return "", fmt.Errorf(errVaultReqParams, err)
  428. }
  429. resp, err := client.RawRequestWithContext(ctx, request)
  430. if err != nil {
  431. return "", fmt.Errorf(errVaultRequest, err)
  432. }
  433. defer resp.Body.Close()
  434. vaultResult := vault.Secret{}
  435. if err = resp.DecodeJSON(&vaultResult); err != nil {
  436. return "", fmt.Errorf(errVaultResponse, err)
  437. }
  438. token, err := vaultResult.TokenID()
  439. if err != nil {
  440. return "", fmt.Errorf(errVaultToken, err)
  441. }
  442. return token, nil
  443. }