Mange secrets by injecting a Vault Agent container
Deploying applications that act as secret consumers of Vault require the application to:
- Authenticate and acquire a client token.
- Manage the lifecycle of the token.
- Retrieve secrets from Vault.
- Manage the leases of any dynamic secrets.
Vault Agent takes responsibility for these tasks and enables your applications to remain unaware of Vault. However, this introduces a new requirement that deployments install and configure Vault Agent alongside the application as a sidecar.
The Vault Helm chart enables you to run Vault and the Vault Agent Sidecar Injector service. This injector service leverages the Sidecar container pattern and Kubernetes mutating admission webhook to intercept pods that define specific annotations and inject a Vault Agent container to manage these secrets.
This is beneficial because:
- Applications remain Vault unaware as the secrets are stored on the file-system in their container.
- Existing deployments require no change; as annotations can be patched.
- Access to secrets can be enforced via Kubernetes service accounts and namespaces
In this tutorial, you setup Vault and this injector service with the Vault Helm chart. Then you will deploy several applications to demonstrate how this new injector service retrieves and writes these secrets for the applications to use.
Prerequisites
This tutorial requires:
Install kubectl and helm CLIs
Install kubectl
with Homebrew.
$ brew install kubernetes-cli
Install helm
with Homebrew.
$ brew install helm
This tutorial was last tested 21 May 2023 on a macOS 13.3.1 using the following software versions.
$ docker versionClient:Cloud integration: v1.0.25Version: 20.10.16## ...
$ helm versionversion.BuildInfo{Version:"v3.12.0", GitCommit:"c9f554d75773799f72ceef38c51210f1842a1dea", GitTreeState:"clean", GoVersion:"go1.20.4"}
$ kubectl version --shortClient Version: v1.27.1Kustomize Version: v5.0.1Server Version: v1.26.3
$ minikube versionminikube version: v1.30.1commit: 08896fd1dc362c097c925146c4a0d0dac715ace0
Clone GitHub repositories
Retrieve the web application and additional configuration by cloning the hashicorp-education/learn-vault-kubernetes-sidecar repository from GitHub.
$ git clone https://github.com/hashicorp-education/learn-vault-kubernetes-sidecar.git
Move into the clones repository.
$ cd learn-vault-kubernetes-sidecar
Working directory
This tutorial assumes that the remainder of commandsare executed in this directory.
Start Minikube
Minikube is a CLI tool that provisions and manages the lifecycle of single-node Kubernetes clusters locally inside Virtual Machines (VM) on your system.
Start a Kubernetes cluster.
$ minikube start😄 minikube v1.30.1 on Darwin 13.3.1 (arm64)✨ Using the docker driver based on existing profile👍 Starting control plane node minikube in cluster minikube🚜 Pulling base image ...🔄 Restarting existing docker container for "minikube" ...🐳 Preparing Kubernetes v1.26.3 on Docker 23.0.2 ...🔗 Configuring bridge CNI (Container Networking Interface) ...🔎 Verifying Kubernetes components... ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5🌟 Enabled addons: storage-provisioner, default-storageclass🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
Note
If minikube detects a new version of kubernetes it might download that so the above text could look different as a new version is downloaded.The initialization process takes several minutes as it retrieves any necessary dependencies and executes various container images.
Verify the status of the Minikube cluster.
$ minikube statusminikubetype: Control Planehost: Runningkubelet: Runningapiserver: Runningkubeconfig: Configured
Additional waiting
Even if this last command completed successfully, you may have to wait for Minikube to be available. If an error is displayed, try again after a few minutes.
The host, kubelet, and apiserver report that they are running. The
kubectl
, a command line interface (CLI) for running commands against Kubernetes cluster, is also configured to communicate with this recently started cluster.Minikube provides a visual representation of the status in a web-based dashboard. This interface displays the cluster activity in a visual interface that can assist in delving into the issues affecting it.
In another terminal, launch the minikube dashboard.
$ minikube dashboard
The operating system's default browser opens and displays the dashboard.
Install the Vault Helm chart
The recommended way to run Vault on Kubernetes is via the Helm chart. Helm is a package manager that installs and configures all the necessary components to run Vault in several different modes. A Helm chart includes templates that enable conditional and parameterized execution. These parameters can be set through command-line arguments or defined in YAML.
Add the HashiCorp Helm repository.
$ helm repo add hashicorp https://helm.releases.hashicorp.com"hashicorp" has been added to your repositories
Update all the repositories to ensure
helm
is aware of the latest versions.$ helm repo updateHang tight while we grab the latest from your chart repositories......Successfully got an update from the "hashicorp" chart repositoryUpdate Complete. ⎈Happy Helming!⎈
Install the latest version of the Vault server running in development mode.
$ helm install vault hashicorp/vault --set "server.dev.enabled=true"NAME: vault## ...
The Vault pod and Vault Agent Injector pod are deployed in the default namespace.
Display all the pods in the default namespace.
$ kubectl get podsNAME READY STATUS RESTARTS AGEvault-0 1/1 Running 0 80svault-agent-injector-5945fb98b5-tpglz 1/1 Running 0 80s
The
vault-0
pod runs a Vault server in development mode. Thevault-agent-injector
pod performs the injection based on the annotations present or patched on a deployment.Development mode
Running a Vault server in development is automatically initialized and unsealed. This is ideal in a learning environment but NOT recommended for a production environment.
Wait until the
vault-0
pod andvault-agent-injector
pod are running and ready (1/1
).
Set a secret in Vault
The applications that you deploy in the Inject secrets into the
pod section expect Vault to store a username and
password stored at the path internal/database/config
. To create this secret
requires that a key-value secret
engine is enabled and a
username and password is put at the specified path.
Start an interactive shell session on the
vault-0
pod.$ kubectl exec -it vault-0 -- /bin/sh/ $
Your system prompt is replaced with a new prompt
/ $
. Commands issued at this prompt are executed on thevault-0
container.Enable kv-v2 secrets at the path
internal
.$ vault secrets enable -path=internal kv-v2Success! Enabled the kv-v2 secrets engine at: internal/
Learn more
This tutorial focuses on Vault's integration with Kubernetes and not interacting the key-value secrets engine. For more information refer to the Versioned Key/value secrets engine tutorial.
Create a secret at path
internal/database/config
with ausername
andpassword
.$ vault kv put internal/database/config username="db-readonly-username" password="db-secret-password"Key Value--- -----created_time 2020-03-25T19:03:57.127711644Zdeletion_time n/adestroyed falseversion 1
Verify that the secret is defined at the path
internal/database/config
.$ vault kv get internal/database/config====== Metadata ======Key Value--- -----created_time 2020-03-25T19:03:57.127711644Zdeletion_time n/adestroyed falseversion 1 ====== Data ======Key Value--- -----password db-secret-passwordusername db-readonly-username
The secret is ready for the application.
Lastly, exit the
vault-0
pod.$ exit
Configure Kubernetes authentication
Vault provides a Kubernetes authentication method that enables clients to authenticate with a Kubernetes Service Account Token. This token is provided to each pod when it is created.
Start an interactive shell session on the
vault-0
pod.$ kubectl exec -it vault-0 -- /bin/sh/ $
Your system prompt is replaced with a new prompt
/ $
. Commands issued at this prompt are executed on thevault-0
container.Enable the Kubernetes authentication method.
$ vault auth enable kubernetesSuccess! Enabled kubernetes auth method at: kubernetes/
Vault accepts a service token from any client in the Kubernetes cluster. During authentication, Vault verifies that the service account token is valid by querying a token review Kubernetes endpoint.
Configure the Kubernetes authentication method to use the location of the Kubernetes API.
Note
For the best compatibility with recent Kubernetes versions, ensure you are using Vault v1.13.3 or greater.$ vault write auth/kubernetes/config \ kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"
Successful output from the command resembles this example:
Success! Data written to: auth/kubernetes/config
The environment variable
KUBERNETES_PORT_443_TCP_ADDR
is defined and references the internal network address of the Kubernetes host.For a client to read the secret data defined at
internal/database/config
, requires that the read capability be granted for the pathinternal/data/database/config
. This is an example of a policy. A policy defines a set of capabilities.Write out the policy named
internal-app
that enables theread
capability for secrets at pathinternal/data/database/config
.$ vault policy write internal-app - <<EOFpath "internal/data/database/config" { capabilities = ["read"]}EOF
Create a Kubernetes authentication role named
internal-app
.$ vault write auth/kubernetes/role/internal-app \ bound_service_account_names=internal-app \ bound_service_account_namespaces=default \ policies=internal-app \ ttl=24h
Successful output from the command resembles this example:
Success! Data written to: auth/kubernetes/role/internal-app
The role connects the Kubernetes service account,
internal-app
, and namespace,default
, with the Vault policy,internal-app
. The tokens returned after authentication are valid for 24 hours.Lastly, exit the
vault-0
pod.$ exit
Define a Kubernetes service account
The Vault Kubernetes authentication role defined a Kubernetes service account named internal-app
.
A service account provides an identity for processes that run in a Pod. With this identity we will be able to run the application within the cluster.
Get all the service accounts in the default namespace.
$ kubectl get serviceaccountsNAME SECRETS AGEdefault 1 43mvault 1 34mvault-agent-injector 1 34m
Create a Kubernetes service account named
internal-app
in the default namespace.$ kubectl create sa internal-app
Verify that the service account has been created.
$ kubectl get serviceaccountsNAME SECRETS AGEdefault 1 52minternal-app 1 13svault 1 43mvault-agent-injector 1 43m
The name of the service account here aligns with the name assigned to the
bound_service_account_names
field when theinternal-app
role was created.
Launch an application
You have created a sample application, published it to DockerHub, and created a Kubernetes deployment that launches this application.
Display the deployment for the
orgchart
application.$ cat deployment-orgchart.yaml
deployment-orgchart.yaml
apiVersion: apps/v1kind: Deploymentmetadata: name: orgchart labels: app: orgchartspec: selector: matchLabels: app: orgchart replicas: 1 template: metadata: annotations: labels: app: orgchart spec: serviceAccountName: internal-app containers: - name: orgchart image: jweissig/app:0.0.1
The name of this deployment is
orgchart
. Thespec.template.spec.serviceAccountName
defines the service accountinternal-app
to run this container.Apply the deployment defined in
deployment-orgchart.yaml
.$ kubectl apply --filename deployment-orgchart.yamldeployment.apps/orgchart created
Get all the pods in the default namespace and note down the name of the pod with a name prefixed with "orgchart-".
$ kubectl get podsNAME READY STATUS RESTARTS AGEorgchart-69697d9598-l878s 1/1 Running 0 18svault-0 1/1 Running 0 58mvault-agent-injector-5945fb98b5-tpglz 1/1 Running 0 58m
The orgchart pod is displayed here as the pod prefixed with
orgchart
.Additional waiting
The deployment of the pod requires the retrieval of the application container from Docker Hub. This displays the STATUS of
ContainerCreating
. The pod reports that it is not ready (0/1
).The Vault-Agent injector looks for deployments that define specific annotations. None of these annotations exist in the current deployment. This means that no secrets are present on the
orgchart
container in theorgchart
pod.Note
Consider removing the rest of this section - the user should not purposely fail...Verify that no secrets are written to the
orgchart
container in theorgchart
pod.$ kubectl exec \ $(kubectl get pod -l app=orgchart -o jsonpath="{.items[0].metadata.name}") \ --container orgchart -- ls /vault/secrets
The output displays that there is no such file or directory named
/vault/secrets
:ls: /vault/secrets: No such file or directorycommand terminated with exit code 1
Inject secrets into the pod
The deployment is running the pod with the internal-app
Kubernetes service account in the default namespace. The Vault Agent Injector only modifies a deployment if it contains a specific set of annotations. An existing deployment may have its definition patched to include the necessary annotations.
Display the deployment patch
patch-inject-secrets.yaml
.$ cat patch-inject-secrets.yaml
patch-inject-secrets.yaml
spec: template: metadata: annotations: vault.hashicorp.com/agent-inject: 'true' vault.hashicorp.com/role: 'internal-app' vault.hashicorp.com/agent-inject-secret-database-config.txt: 'internal/data/database/config'
These annotations define a partial structure of the deployment schema and are prefixed with
vault.hashicorp.com
.agent-inject
enables the Vault Agent Injector servicerole
is the Vault Kubernetes authentication roleagent-inject-secret-FILEPATH
prefixes the path of the file,database-config.txt
written to the/vault/secrets
directory. The value is the path to the secret defined in Vault.
Patch the
orgchart
deployment defined inpatch-inject-secrets.yaml
.$ kubectl patch deployment orgchart --patch "$(cat patch-inject-secrets.yaml)"deployment.apps/orgchart patched
A new
orgchart
pod starts alongside the existing pod. When it is ready the original terminates and removes itself from the list of active pods.Get all the pods in the default namespace.
$ kubectl get podsNAME READY STATUS RESTARTS AGEorgchart-599cb74d9c-s8hhm 0/2 Init:0/1 0 23sorgchart-69697d9598-l878s 1/1 Running 0 20mvault-0 1/1 Running 0 78mvault-agent-injector-5945fb98b5-tpglz 1/1 Running 0 78m
Wait until the re-deployed
orgchart
pod reports that it isRunning
and ready (2/2
).This new pod now launches two containers. The application container, named
orgchart
, and the Vault Agent container, namedvault-agent
.Display the logs of the
vault-agent
container in the neworgchart
pod.$ kubectl logs \ $(kubectl get pod -l app=orgchart -o jsonpath="{.items[0].metadata.name}") \ --container vault-agent
Vault Agent manages the token lifecycle and the secret retrieval. The secret is rendered in the
orgchart
container at the path/vault/secrets/database-config.txt
.Display the secret written to the
orgchart
container.$ kubectl exec \ $(kubectl get pod -l app=orgchart -o jsonpath="{.items[0].metadata.name}") \ --container orgchart -- cat /vault/secrets/database-config.txt
The unformatted secret data is present on the container:
data: map[password:db-secret-password username:db-readonly-user]metadata: map[created_time:2019-12-20T18:17:50.930264759Z deletion_time: destroyed:false version:2]
Apply a template to the injected secrets
The structure of the injected secrets may need to be structured in a way for an application to use. Before writing the secrets to the file system a template can structure the data. To apply this template a new set of annotations need to be applied.
Display the annotations file that contains a template definition.
$ cat patch-inject-secrets-as-template.yaml
patch-inject-secrets-as-template.yaml
spec: template: metadata: annotations: vault.hashicorp.com/agent-inject: 'true' vault.hashicorp.com/agent-inject-status: 'update' vault.hashicorp.com/role: 'internal-app' vault.hashicorp.com/agent-inject-secret-database-config.txt: 'internal/data/database/config' vault.hashicorp.com/agent-inject-template-database-config.txt: | {{- with secret "internal/data/database/config" -}} postgresql://{{ .Data.data.username }}:{{ .Data.data.password }}@postgres:5432/wizard {{- end -}}
This patch contains two new annotations:
agent-inject-status
set toupdate
informs the injector reinject these values.agent-inject-template-FILEPATH
prefixes the file path. The value defines the Vault Agent template to apply to the secret's data.
The template formats the username and password as a PostgreSQL connection string.
Apply the updated annotations.
$ kubectl patch deployment orgchart --patch "$(cat patch-inject-secrets-as-template.yaml)"deployment.apps/exampleapp patched
Get all the pods in the default namespace.
$ kubectl get podsNAME READY STATUS RESTARTS AGEorgchart-554db4579d-w6565 2/2 Running 0 16svault-0 1/1 Running 0 126mvault-agent-injector-5945fb98b5-tpglz 1/1 Running 0 126m
Wait until the re-deployed
orgchart
pod reports that it isRunning
and ready (2/2
).Finally, display the secret written to the
orgchart
container in theorgchart
pod.$ kubectl exec \ $(kubectl get pod -l app=orgchart -o jsonpath="{.items[0].metadata.name}") \ -c orgchart -- cat /vault/secrets/database-config.txt
The secrets are rendered in a PostgreSQL connection string is present on the container:
postgresql://db-readonly-user:db-secret-password@postgres:5432/wizard
Pod with annotations
The annotations may patch these secrets into any deployment. Pods require that the annotations be included in their initial definition.
Display the pod definition for the
payroll
application.$ cat pod-payroll.yaml
pod-payroll.yaml
apiVersion: v1kind: Podmetadata:name: payrolllabels: app: payrollannotations: vault.hashicorp.com/agent-inject: 'true' vault.hashicorp.com/role: 'internal-app' vault.hashicorp.com/agent-inject-secret-database-config.txt: 'internal/data/database/config' vault.hashicorp.com/agent-inject-template-database-config.txt: | {{- with secret "internal/data/database/config" -}} postgresql://{{ .Data.data.username }}:{{ .Data.data.password }}@postgres:5432/wizard {{- end -}}spec:serviceAccountName: internal-appcontainers: - name: payroll image: jweissig/app:0.0.1
Apply the pod defined in
pod-payroll.yaml
.$ kubectl apply --filename pod-payroll.yamlpod/payroll created
Get all the pods in the default namespace.
$ kubectl get podsNAME READY STATUS RESTARTS AGEorgchart-554db4579d-w6565 2/2 Running 0 29mpayroll 2/2 Running 0 12svault-0 1/1 Running 0 155mvault-agent-injector-5945fb98b5-tpglz 1/1 Running 0 155m
Wait until the
payroll
pod reports that it isRunning
and ready (2/2
).Display the secret written to the
payroll
container in thepayroll
pod.$ kubectl exec \ payroll \ --container payroll -- cat /vault/secrets/database-config.txt
The secrets are rendered in a PostgreSQL connection string is present on the container:
postgresql://db-readonly-user:db-secret-password@postgres:5432/wizard
Secrets are bound to the service account
Pods run with a Kubernetes service account other than the ones defined in the Vault Kubernetes authentication role are NOT able to access the secrets defined at that path.
Display the deployment and service account for the
website
application.$ cat deployment-website.yaml
deployment-website.yaml
apiVersion: apps/v1kind: Deploymentmetadata:name: websitelabels: app: websitespec:selector: matchLabels: app: websitereplicas: 1template: metadata: annotations: vault.hashicorp.com/agent-inject: 'true' vault.hashicorp.com/role: 'internal-app' vault.hashicorp.com/agent-inject-secret-database-config.txt: 'internal/data/database/config' vault.hashicorp.com/agent-inject-template-database-config.txt: | {{- with secret "internal/data/database/config" -}} postgresql://{{ .Data.data.username }}:{{ .Data.data.password }}@postgres:5432/wizard {{- end -}} labels: app: website spec: # This service account does not have permission to request the secrets. serviceAccountName: website containers: - name: website image: jweissig/app:0.0.1---apiVersion: v1kind: ServiceAccountmetadata:name: website
Apply the deployment and service account defined in
deployment-website.yaml
.$ kubectl apply --filename deployment-website.yamldeployment.apps/website createdserviceaccount/website created
Get all the pods in the default namespace.
$ kubectl get podsNAME READY STATUS RESTARTS AGEorgchart-554db4579d-w6565 2/2 Running 0 29mpayroll 2/2 Running 0 12svault-0 1/1 Running 0 155mvault-agent-injector-5945fb98b5-tpglz 1/1 Running 0 155mwebsite-7fc8b69645-527rf 0/2 Init:0/1 0 76s
The
website
deployment creates a pod but it is NEVER ready.Display the logs of the
vault-agent-init
container in thewebsite
pod.$ kubectl logs \ $(kubectl get pod -l app=website -o jsonpath="{.items[0].metadata.name}") \ --container vault-agent-init
The initialization process failed because the service account name is not authorized:
...[INFO] auth.handler: authenticating[ERROR] auth.handler: error authenticating: error="Error making API request.URL: PUT http://vault.default.svc:8200/v1/auth/kubernetes/loginCode: 403. Errors:* service account name not authorized" backoff=1.562132589
The service account,
website
is not assigned to any Vault Kubernetes authentication role. This failure to authenticate causes the deployment to fail initialization.Display the deployment patch
patch-website.yaml
.$ cat patch-website.yaml
patch-website.yaml
spec: template: spec: serviceAccountName: internal-app
The patch modifies the deployment definition to use the service account
internal-app
. This Kubernetes service account is authorized by the Vault Kubernetes authentication role.Patch the
website
deployment defined inpatch-website.yaml
.$ kubectl patch deployment website --patch "$(cat patch-website.yaml)"
Get all the pods in the default namespace.
$ kubectl get podsNAME READY STATUS RESTARTS AGEorgchart-554db4579d-w6565 2/2 Running 0 29mpayroll 2/2 Running 0 12svault-0 1/1 Running 0 155mvault-agent-injector-5945fb98b5-tpglz 1/1 Running 0 155mwebsite-788d689b87-tll2r 2/2 Running 0 27s
Wait until the
website
pod reports that it isRunning
and ready (2/2
).Finally, display the secret written to the
website
container in thewebsite
pod.$ kubectl exec \ $(kubectl get pod -l app=website -o jsonpath="{.items[0].metadata.name}") \ --container website -- cat /vault/secrets/database-config.txt
The secrets are rendered in a PostgreSQL connection string is present on the container:
postgresql://db-readonly-user:db-secret-password@postgres:5432/wizard
Vault Kubernetes Roles
Alternatively, you can define a new Vault Kubernetes role, that enables the original service account access, and patch the deployment.
Secrets are bound to the namespace
Pods run in a namespace other than the ones defined in the Vault Kubernetes authentication role are NOT able to access the secrets defined at that path.
Create the
offsite
namespace.$ kubectl create namespace offsitenamespace/offsite created
Set the current context to the offsite namespace.
$ kubectl config set-context --current --namespace offsiteContext "minikube" modified.
Create a Kubernetes service account named
internal-app
in the offsite namespace.$ kubectl create sa internal-appserviceaccount/internal-app created
Display the deployment for the
issues
application.$ cat deployment-issues.yaml
deployment-issues.yaml
apiVersion: apps/v1kind: Deploymentmetadata:name: issueslabels: app: issuesspec:selector: matchLabels: app: issuesreplicas: 1template: metadata: annotations: vault.hashicorp.com/agent-inject: 'true' vault.hashicorp.com/role: 'internal-app' vault.hashicorp.com/agent-inject-secret-database-config.txt: 'internal/data/database/config' vault.hashicorp.com/agent-inject-template-database-config.txt: | {{- with secret "internal/data/database/config" -}} postgresql://{{ .Data.data.username }}:{{ .Data.data.password }}@postgres:5432/wizard {{- end -}} labels: app: issues spec: serviceAccountName: internal-app containers: - name: issues image: jweissig/app:0.0.1
Apply the deployment defined in
deployment-issues.yaml
.$ kubectl apply --filename deployment-issues.yamldeployment.apps/issues created
Get all the pods in the offsite namespace.
$ kubectl get podsNAME READY STATUS RESTARTS AGEissues-79d8bf7cdf-dkdlq 0/2 Init:0/1 0 3s
Current context
The same command is issued but the results are different because you are now in a different namespace.
The
issues
deployment creates a pod but it is NEVER ready.Display the logs of the
vault-agent-init
container in theissues
pod.$ kubectl logs \ $(kubectl get pod -l app=issues -o jsonpath="{.items[0].metadata.name}") \ --container vault-agent-init
The initialization process fails because the namespace is not authorized:
...[INFO] auth.handler: authenticating[ERROR] auth.handler: error authenticating: error="Error making API request.URL: PUT http://vault.default.svc:8200/v1/auth/kubernetes/loginCode: 500. Errors:* namespace not authorized" backoff=1.9882590740000001
The namespace,
offsite
is not assigned to any Vault Kubernetes authentication role. This failure to authenticate causes the deployment to fail initialization.Start an interactive shell session on the
vault-0
pod in the default namespace.$ kubectl exec --namespace default -it vault-0 -- /bin/sh/ $
Your system prompt is replaced with a new prompt
/ $
. Commands issued at this prompt are executed on thevault-0
container.Create a Kubernetes authentication role named
offsite-app
.$ vault write auth/kubernetes/role/offsite-app \ bound_service_account_names=internal-app \ bound_service_account_namespaces=offsite \ policies=internal-app \ ttl=24h
Successful output from the command resembles this example:
Success! Data written to: auth/kubernetes/role/offsite-app
Exit the
vault-0
pod.$ exit
Display the deployment patch
patch-issues.yaml
.$ cat patch-issues.yaml
patch-issues.yaml
spec:template: metadata: annotations: vault.hashicorp.com/agent-inject: 'true' vault.hashicorp.com/agent-inject-status: 'update' vault.hashicorp.com/role: 'offsite-app' vault.hashicorp.com/agent-inject-secret-database-config.txt: 'internal/data/database/config' vault.hashicorp.com/agent-inject-template-database-config.txt: | {{- with secret "internal/data/database/config" -}} postgresql://{{ .Data.data.username }}:{{ .Data.data.password }}@postgres:5432/wizard {{- end -}}
The patch performs an update to set the
vault.hashicorp.com/role
to the Vault Kubernetes roleoffsite-app
.Patch the
issues
deployment defined inpatch-issues.yaml
.$ kubectl patch deployment issues --patch "$(cat patch-issues.yaml)"deployment.apps/issues patched
A new
issues
pod starts alongside the existing pod. When it is ready the original terminates and removes itself from the list of active pods.Get all the pods in the offsite namespace.
$ kubectl get podsNAME READY STATUS RESTARTS AGEissues-7fd66f98f6-ffzh7 2/2 Running 0 94s
Wait until the re-deployed
issues
pod reports that it isRunning
and ready (2/2
).Finally, display the secret written to the
issues
container in theissues
pod.$ kubectl exec \ $(kubectl get pod -l app=issues -o jsonpath="{.items[0].metadata.name}") \ --container issues -- cat /vault/secrets/database-config.txt
The secrets are rendered in a PostgreSQL connection string is present on the container:
postgresql://db-readonly-user:db-secret-password@postgres:5432/wizard
Clean up
Clean up after this tutorial.
Stop the running local Kubernetes cluster.
$ minikube stop
This deactivates minikube, and all pods still exist at this point.
Delete the local Kubernetes cluster.
Be aware that
minikube delete
removes the minikube deployment including all pods. Be sure you want everything removed before continuing.$ minikube delete🔥 Deleting "minikube" in docker ...🔥 Deleting container "minikube" ...🔥 Removing /Users/mrken/.minikube/machines/minikube ...💀 Removed all traces of the "minikube" cluster.
Delete the supporting code repository copied locally.
$ cd .. && rm -rf learn-vault-kubernetes-sidecar
Next steps
You launched Vault and the injector service with the Vault Helm chart. Learn more about the Vault Helm chart by reading the documentation, exploring the project source code, reading the blog post announcing the "Injecting Vault Secrets into Kubernetes Pods via a Sidecar", or the documentation for Agent Sidecar Injector
Then you deployed several applications to demonstrate how this new injector service retrieves and writes these secrets for the applications to use. Explore how pods can retrieve them directly via network requests or secrets mounted on ephemeral volumes.
An alternative option to the agent is the is the Vault Secrets Operator. It is a Kubernetes operator specifically made to aid in management of Vault on Kubernetes. This is similar to the Vault Agent sidecar, but in a Kubernetes native fashion.