From f333e051c1f6934a297e30fd238acb87b660ac3f Mon Sep 17 00:00:00 2001 From: Sergio Rua <58211930+digiserg@users.noreply.github.com> Date: Sun, 19 Dec 2021 23:50:19 +0000 Subject: [PATCH] Adds support to Kubernetes authn for Vault (#64) Enables Kubernetes authentication for Vault to allow running `vals` from inside a container. --- README.md | 20 +++++++++++++----- pkg/providers/vault/vault.go | 39 ++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b517b06..c0aba09 100644 --- a/README.md +++ b/README.md @@ -182,18 +182,28 @@ Please see [pkg/providers](https://github.com/variantdev/vals/tree/master/pkg/pr - `ref+vault://PATH/TO/KVBACKEND[?address=VAULT_ADDR:PORT&token_file=PATH/TO/FILE&token_env=VAULT_TOKEN]#/fieldkey` - `ref+vault://PATH/TO/KVBACKEND[?address=VAULT_ADDR:PORT&auth_method=approle&role_id=ce5e571a-f7d4-4c73-93dd-fd6922119839&secret_id=5c9194b9-585e-4539-a865-f45604bd6f56]#/fieldkey` +- `ref+vault://PATH/TO/KVBACKEND[?address=VAULT_ADDR:PORT&auth_method=kubernetes&role_id=K8S-ROLE` -`address` defaults to the value of the `VAULT_ADDR` envvar. -`auth_method` default to `token` and can also be set to the value of the `VAULT_AUTH_METHOD` envar. -`role_id` defaults to the value of the `VAULT_ROLE_ID` envvar. -`secret_id` defaults to the value of the `VAULT_SECRET_ID` envvar. -`version` is the specific version of the secret to be obtained. Used when you want to get a previous content of the secret. +* `address` defaults to the value of the `VAULT_ADDR` envvar. +* `auth_method` default to `token` and can also be set to the value of the `VAULT_AUTH_METHOD` envar. +* `role_id` defaults to the value of the `VAULT_ROLE_ID` envvar. +* `secret_id` defaults to the value of the `VAULT_SECRET_ID` envvar. +* `version` is the specific version of the secret to be obtained. Used when you want to get a previous content of the secret. + +### Authentication + +The `auth_method` or `VAULT_AUTH_METHOD` envar configures how `vals` authenticates to HashiCorp Vault. Currently only these options are supported: + +* [approle](https://www.vaultproject.io/docs/auth/approle#via-the-api): it requires you pass on a `role_id` together with a `secret_id`. +* [token](https://www.vaultproject.io/docs/auth/token): you just need creating and passing on a `VAULT_TOKEN` +* [kubernetes](https://www.vaultproject.io/docs/auth/kubernetes): if you're running inside a Kubernetes cluster, you can use this option. It requires you [configure](https://www.vaultproject.io/docs/auth/kubernetes#configuration) a policy, a Kubernetes role, a service account and a JWT token. The login path can also be set using the environment variable `VAULT_KUBERNETES_MOUNT_POINT` (default is `/kubernetes`). You must also set `role_id` or `VAULT_ROLE_ID` envar to the Kubernetes role. Examples: - `ref+vault://mykv/foo#/bar?address=https://vault1.example.com:8200` reads the value for the field `bar` in the kv `foo` on Vault listening on `https://vault1.example.com` with the Vault token read from **the envvar `VAULT_TOKEN`, or the file `~/.vault_token` when the envvar is not set** - `ref+vault://mykv/foo#/bar?token_env=VAULT_TOKEN_VAULT1&address=https://vault1.example.com:8200` reads the value for the field `bar` in the kv `foo` on Vault listening on `https://vault1.example.com` with the Vault token read from **the envvar `VAULT_TOKEN_VAULT1`** - `ref+vault://mykv/foo#/bar?token_file=~/.vault_token_vault1&address=https://vault1.example.com:8200` reads the value for the field `bar` in the kv `foo` on Vault listening on `https://vault1.example.com` with the Vault token read from **the file `~/.vault_token_vault1`** +- `ref+vault://mykv/foo#/bar?role_id=my-kube-role` using the Kubernetes role to log in to Vault ### AWS diff --git a/pkg/providers/vault/vault.go b/pkg/providers/vault/vault.go index b1b42ae..3f73341 100644 --- a/pkg/providers/vault/vault.go +++ b/pkg/providers/vault/vault.go @@ -13,8 +13,9 @@ import ( ) const ( - FormatYAML = "yaml" - FormatRaw = "raw" + FormatYAML = "yaml" + FormatRaw = "raw" + kubernetesJwtTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token" ) // Test procedure: @@ -61,6 +62,8 @@ func New(cfg api.StaticConfig) *provider { if p.AuthMethod == "" { if os.Getenv("VAULT_AUTH_METHOD") == "approle" { p.AuthMethod = "approle" + } else if os.Getenv("VAULT_AUTH_METHOD") == "kubernetes" { + p.AuthMethod = "kubernetes" } else { p.AuthMethod = "token" } @@ -225,6 +228,38 @@ func (p *provider) ensureClient() (*vault.Client, error) { return nil, fmt.Errorf("no auth info returned") } + cli.SetToken(resp.Auth.ClientToken) + } else if p.AuthMethod == "kubernetes" { + fd, err := os.Open(kubernetesJwtTokenPath) + defer fd.Close() + if err != nil { + return nil, fmt.Errorf("unable to read file containing service account token: %w", err) + } + jwt, err := ioutil.ReadAll(fd) + if err != nil { + return nil, fmt.Errorf("unable to read file containing service account token: %w", err) + } + + data := map[string]interface{}{ + "jwt": string(jwt), + "role": p.RoleId, + } + mount_point, ok := os.LookupEnv("VAULT_KUBERNETES_MOUNT_POINT") + if !ok { + mount_point = "/kubernetes" + } + + auth_path := filepath.Join("auth", mount_point, "login") + + resp, err := cli.Logical().Write(auth_path, data) + if err != nil { + return nil, err + } + + if resp.Auth == nil { + return nil, fmt.Errorf("no auth info returned") + } + cli.SetToken(resp.Auth.ClientToken) } p.client = cli