Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

See https://learn.hashicorp.com/vault/identity-access-management/vault-agent-k8s#step-4-leverage-vault-agent-auto-authTBD

Configuring the Agent

Create a file called vault-agent-config.hcl:

Code Block
languagetext
linenumberstrue
exit_after_auth = true
pid_file = "/home/vault/pidfile"

auto_auth {
    method "kubernetes" {
        mount_path = "auth/kubernetes"
        config = {
            role = "example"
        }
    }

    sink "file" {
        config = {
            path = "/home/vault/.vault-token"
        }
    }
}

Notice that the Vault Agent Auto-Auth is configured to use the kubernetes auth method enabled at the auth/kubernetes path on the Vault server. The Vault Agent will use the example role to authenticate.

The sink block specifies the location on disk where to write tokens. Vault Agent Auto-Auth sink can be configured multiple times if you want Vault Agent to place the token into multiple locations. In this example, the sink is set to /home/vault/.vault-token.

Create a Consul Template

Vault uses Consul under the hood to persist its key-value pairs. Consul is a service mesh for Kubernetes that provides convenient ways to plug data into running pods.

NOTE: Consul is outside of the scope of this document. For more about Consul, see https://medium.com/velotio-perspectives/a-practical-guide-to-hashicorp-consul-part-1-5ee778a7fcf4

We can leverage Consul to create a template called consul-template-config.hcl:

Code Block
languagetext
linenumberstrue
vault {
  renew_token = false
  vault_agent_token_file = "/home/vault/.vault-token"
  retry {
    backoff = "1s"
  }
}

template {
  destination = "/etc/secrets/index.html"
  contents = <<EOH
  <html>
  <body>
  <p>Some secrets:</p>
  {{- with secret "secret/myapp/config" }}
  <ul>
  <li><pre>username: {{ .Data.username }}</pre></li>
  <li><pre>password: {{ .Data.password }}</pre></li>
  </ul>
  {{ end }}
  </body>
  </html>
  EOH
}

This template reads secrets at the secret/myapp/config path and set the username and password values.

NOTE: If the secret/ path is enabled with key/value v2 secrets engine, the templatized expressions should be modified as follow (Line 15 through 18):

Code Block
languagebash
...
template {
  ...
  {{- with secret "secret/data/myapp/config?version=1" }}
  <ul>
  <li><pre>username: {{ .Data.data.username }}</pre></li>
  <li><pre>password: {{ .Data.data.password }}</pre></li>
  </ul>
  {{ end }}
  ...
}

Create a ConfigMap

Place the two files you just created into a new directory called ./config-k8s and run the following commands to create a ConfigMap from them:

Code Block
languagebash
# Create a ConfigMap called 'example-vault-agent-config'
$ kubectl create configmap example-vault-agent-config --from-file=./configs-k8s/

# View the created ConfigMap
$ kubectl get configmap example-vault-agent-config -o yaml

Now create a pod spec anmed example-k8s-spec.yml that mounts in the files from the examplevault-agent-config ConfigMap and using the vault-auth ServiceAccount:

Code Block
languageyml
---
apiVersion: v1
kind: Pod
metadata:
  name: vault-agent-example
spec:
  serviceAccountName: vault-auth

  restartPolicy: Never

  volumes:
    - name: vault-token
      emptyDir:
        medium: Memory

    - name: config
      configMap:
        name: example-vault-agent-config
        items:
          - key: vault-agent-config.hcl
            path: vault-agent-config.hcl

          - key: consul-template-config.hcl
            path: consul-template-config.hcl

    - name: shared-data
      emptyDir: {}

  initContainers:
    # Vault container
    - name: vault-agent-auth
      image: vault

      volumeMounts:
        - name: config
          mountPath: /etc/vault
        - name: vault-token
          mountPath: /home/vault

      # This assumes Vault running on local host and K8s running in Minikube using VirtualBox
      env:
        - name: VAULT_ADDR
          value: http://10.0.2.2:8200

      # Run the Vault agent
      args:
        [
          "agent",
          "-config=/etc/vault/vault-agent-config.hcl",
          #"-log-level=debug",
        ]

  containers:
    # Consul Template container
    - name: consul-template
      image: hashicorp/consul-template:alpine
      imagePullPolicy: Always

      volumeMounts:
        - name: vault-token
          mountPath: /home/vault

        - name: config
          mountPath: /etc/consul-template

        - name: shared-data
          mountPath: /etc/secrets

      env:
        - name: HOME
          value: /home/vault

        - name: VAULT_ADDR
          value: http://10.0.2.2:8200

      # Consul-Template looks in $HOME/.vault-token, $VAULT_TOKEN, or -vault-token (via CLI)
      args:
        [
          "-config=/etc/consul-template/consul-template-config.hcl",
          #"-log-level=debug",
        ]

    # Nginx container
    - name: nginx-container
      image: nginx

      ports:
        - containerPort: 80

      volumeMounts:
        - name: shared-data
          mountPath: /usr/share/nginx/html

Execute the following command to create the vault-agent-example Pod:

Code Block
languagebash
$ kubectl apply -f example-k8s-spec.yml --record

 It will take a minute or so for the Pod to become fully up and running.

Verification

Open a port-forward so you can connect to the client from browser:

Code Block
languagebash
$ kubectl port-forward pod/vault-agent-example 8080:80

In a web browser, go to localhost:8080

KubernetesImage Added

Notice that the username and password values were successfully read from secret/myapp/config.

This proves that we are able to capture Vault values and automatically render them at container startup using Consul.

Open a shell to the consul-template container:

Code Block
languagebash
$ kubectl exec -it vault-agent-example --container consul-template sh

Remember that we set the Vault Agent's sink to be /home/vault/.vault-token. To view the token stored in the sink:

Code Block
languagebash
/# echo $(cat /home/vault/.vault-token)
s.7MQZzFZxUTBQMrtfy98wTGkZ

This proves that our Vault token is being properly written to the sink.

Even without integrating directly with Consul, this pattern would allow us to use the Vault token within the container to access a Vault's contents!