A Hands-On Guide to Vault in Kubernetes ⚙️

Written by teamember02
Updated 7 months ago

Manage k8s Secrets Using HashiCorp Vault: With Practical Examples

Manage Secrets Using HashiCorp Vault in Kubernetes by Anvesh Muppeda

Inthe world of Kubernetes, managing secrets such as API keys, passwords, and other sensitive information is a critical task. Kubernetes has its own built-in secrets management mechanism, but it has certain limitations that might not meet the security requirements of all organizations. For instance, Kubernetes secrets are stored in etcd, which, although encrypted at rest, might not provide the level of security and access control needed for highly sensitive information.

This is where HashiCorp Vault comes into play. Vault is a tool designed to securely store and manage sensitive information. It provides robust mechanisms for dynamic secrets, encryption as a service, and access control, making it an ideal solution for managing secrets in a Kubernetes environment.

In this tutorial, we will walk through the steps to install and configure Vault in a Kubernetes cluster using Helm and deploy a pod and access the secrets from Vault. By the end of this guide, you’ll have a working Vault setup in your Kubernetes cluster, ready to manage your application’s secrets securely.

Prerequisites

Before you begin, ensure you have the following:

  1. A Kubernetes cluster up and running.
  2. kubectl configured to interact with your cluster.
  3. Helm installed on your local machine

Manage secrets using hashicorp vault in kubernetes by Anvesh Muppeda

Create Vault Namespace

First, we need to create a separate namespace for Vault. This helps in managing resources specific to Vault independently.

$ kubectl create ns vault

Install Vault

We will install the latest version of Vault using the Helm chart provided by HashiCorp. There are two methods to do this: 1. directly running the Helm install command using the HashiCorp Helm repository or 2. downloading the Helm chart and installing it locally.

1. Add the HashiCorp Helm Repository

Add the HashiCorp Helm repository to your Helm configuration.

helm repo add hashicorp https://helm.releases.hashicorp.com

2. Installation Methods

1. Directly Running Helm Install

You can directly install Vault using the Helm chart from the HashiCorp repository with the following command:

helm install vault hashicorp/vault \
--set='server.dev.enabled=true' \
--set='ui.enabled=true' \
--set='ui.serviceType=LoadBalancer' \
--namespace vault

2. By Downloading the Helm Chart and Installing

Alternatively, you can download the Helm chart and install it locally:

# Download the Helm chart
helm pull hashicorp/vault --untar

# Install Vault using the downloaded chart
helm install vault \
--set='server.dev.enabled=true' \
--set='ui.enabled=true' \
--set='ui.serviceType=LoadBalancer' \
--namespace vault \
./vault-chart

Using these settings, we are installing Vault in development mode with the UI enabled and exposed via a LoadBalancer service to access it externally. This setup is ideal for testing and development purposes.

Running Vault in “dev” mode. This requires no further setup, no state management, and no initialization. This is useful for experimenting with Vault without needing to unseal, store keys, et. al. All data is lost on restart — do not use dev mode for anything other than experimenting. See https://developer.hashicorp.com/vault/docs/concepts/dev-server to know more

Output:

$ kubectl get all -n vault
NAME READY STATUS RESTARTS AGE
pod/vault-0 1/1 Running 0 2m39s
pod/vault-agent-injector-8497dd4457-8jgcm 1/1 Running 0 2m39s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/vault ClusterIP 10.245.225.169 <none> 8200/TCP,8201/TCP 2m40s
service/vault-agent-injector-svc ClusterIP 10.245.32.56 <none> 443/TCP 2m40s
service/vault-internal ClusterIP None <none> 8200/TCP,8201/TCP 2m40s
service/vault-ui LoadBalancer 10.245.103.246 24.132.59.59 8200:31764/TCP 2m40s

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/vault-agent-injector 1/1 1 1 2m40s

NAME DESIRED CURRENT READY AGE
replicaset.apps/vault-agent-injector-8497dd4457 1 1 1 2m40s

NAME READY AGE
statefulset.apps/vault 1/1 2m40s

Configure Vault

In this step, we will set up Vault policies and authentication methods to securely manage and access secrets within the Kubernetes cluster. This configuration ensures that only authorized applications can retrieve sensitive data from Vault.

1. Connect to the Vault Pod

After the installation, connect to the Vault pod to perform initial configuration:

kubectl exec -it vault-0 -- /bin/sh

2. Create and Apply a Policy

Next, we’ll create a policy that allows reading secrets. This policy will be attached to a role, which can be used to grant access to specific Kubernetes service accounts.

Create the policy file:

cat <<EOF > /home/vault/read-policy.hcl
path "secret*" {
capabilities = ["read"]
}
EOF

Apply the policy:

# SYNTAX
$ vault policy write <policy-name> /path/to/policy.hcl

# EXAMPLE
$ vault policy write read-policy /home/vault/read-policy.hcl

3. Enable Kubernetes Authentication

Enable the Kubernetes authentication method in Vault:

vault auth enable kubernetes

4. Configure Kubernetes Authentication

Configure Vault to communicate with the Kubernetes API server:

vault write auth/kubernetes/config \
token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
kubernetes_host=https://${KUBERNETES_PORT_443_TCP_ADDR}:443 \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

5. Create a Role

Create a role(vault-role) that binds the above policy to a Kubernetes service account(vault-serviceaccount) in a specific namespace. This allows the service account to access secrets stored in Vault:

vault write auth/kubernetes/role/vault-role \
bound_service_account_names=vault-serviceaccount \
bound_service_account_namespaces=vault \
policies=read-policy \
ttl=1h

Here we can pass multiple service accounts and namespaces:

vault write auth/kubernetes/role/<my-role> \
bound_service_account_names=sa1, sa2 \
bound_service_account_namespaces=namespace1, namespace2 \
policies=<policy-name> \
ttl=1h

Create Secrets

Now, let’s create some secrets in Vault:

We can create the secrets in two ways:

  1. Using the Vault CLI
  2. Using the Vault UI

1. Using the Vault CLI

Create secret using the below command

$ vault kv put secret/login pattoken=ytbuytbytbf765rb65u56rv

You can verify the secrets by listing them using the below command:

$ vault kv list secret
Keys
----

login

2. Using the Vault UI

List service in vault namespace to get the EXTERNAL-IP of the LoadBalancer.

$ kubectl get svc -n vault
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
vault ClusterIP 10.245.139.117 <none> 8200/TCP,8201/TCP 28h
vault-agent-injector-svc ClusterIP 10.245.58.140 <none> 443/TCP 28h
vault-internal ClusterIP None <none> 8200/TCP,8201/TCP 28h
vault-ui LoadBalancer 10.245.11.13 24.123.49.59 8200:32273/TCP 26h

Access the Vault UI using the above external IP of the loadbalancer.

Ex: <external-ip>:8200

In my case: 24.123.49.59:8200

Now you can login to vault using the Token method, initially use Token= root to login.

Now using the Secrets Dashboard from Vault UI, we can create the secrets.

Navigate to Secrets Engines > Secret

Then click on Create Secret from right top corner.

Now enter the desired fields for our secret to create our secret.

Finally secret is created.

Even you can access the above secrets from Vault CLI using the below command:

$ vault kv list secret
Keys
----

login
my-first-secret

You have successfully installed and configured Vault in your Kubernetes cluster. You can now use Vault to manage secrets for your applications running in Kubernetes.

Accessing Secrets in Kubernetes Pods

Using the above steps, we have installed Vault and configured a Vault role(vault-role) to allow the service account(vault-serviceaccount) to access secrets stored in Vault.

Additionally, we have created two secrets: login and my-first-secret with key-value pairs. Now, let's create a simple Kubernetes deployment and try to access those secrets.

First, let’s create a service account named vault-serviceaccount in the vault namespace. This service account is granted permissions for the Vault role as defined in the "Create a Role" step above.

Save the below manifest file as vault-sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-serviceaccount
labels:
app: read-vault-secret

Apply the above manifest using the below command

kubectl apply -f vault-sa.yaml

Now, let’s create a simple deployment(vault-secret-test-deploy.yaml) using the manifest file below.

This deployment manifest creates a single replica of an Nginx pod configured to securely fetch secrets from Vault. The Vault Agent injects the secrets login and my-first-secret into the pod according to the specified templates. The secrets are stored in the pod's filesystem and can be accessed by the application running in the container. The vault-serviceaccount service account, which has the necessary permissions, is used to authenticate with Vault.

Let’s delve deeper into the annotations section to understand their purpose and functionality:

annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-status: "update"
vault.hashicorp.com/agent-inject-secret-login: "secret/login"
vault.hashicorp.com/agent-inject-template-login: |
{{- with secret "secret/login" -}}
pattoken={{ .Data.data.pattoken }}
{{- end }}
vault.hashicorp.com/agent-inject-secret-my-first-secret: "secret/my-first-secret"
vault.hashicorp.com/agent-inject-template-my-first-secret: |
{{- with secret "secret/my-first-secret" -}}
username={{ .Data.data.username }}
password={{ .Data.data.password }}
{{- end }}
vault.hashicorp.com/role: "vault-role"

These annotations are used to configure the Vault Agent to inject secrets into the pod volume.

  • vault.hashicorp.com/agent-inject: “true”: Enables Vault Agent injection for this pod.
  • vault.hashicorp.com/agent-inject-status: “update”: Ensures the status of secret injection is updated.
  • vault.hashicorp.com/agent-inject-secret-login: “secret/login”: Specifies that the secret stored at secret/login in Vault should be injected.
  • vault.hashicorp.com/agent-inject-template-login: Defines the template for the injected login secret, specifying the format in which the secret will be written.
  • vault.hashicorp.com/agent-inject-secret-my-first-secret: “secret/my-first-secret”: Specifies that the secret stored at secret/my-first-secret in Vault should be injected.
  • vault.hashicorp.com/agent-inject-template-my-first-secret: Defines the template for the injected my-first-secret, specifying the format in which the secret will be written.
  • vault.hashicorp.com/role: “vault-role”: Specifies the Vault role to be used for authentication.

serviceAccountName: Uses the service account vault-serviceaccount which has permissions to access Vault.

Apply the above manifest using the below command:

kubectl apply -f vault-secret-test-deploy.yaml

Use the below command to check the vault secrets from the pod volume

$ kubectl exec -it vault-test-84d9dc9986-gcxfv -- sh -c "cat /vault/secrets/login && cat /vault/secrets/my-first-secret" -n vault
$ kubectl exec -it vault-test-84d9dc9986-gcxfv -- sh -c "cat /vault/secrets/login && cat /vault/secrets/my-first-secret" -n vault

Defaulted container "nginx" out of: nginx, vault-agent, vault-agent-init (init)
pattoken=ytbuytbytbf765rb65u56rv
username=anvesh
password=anveshpassword

That’s it! We have successfully created secrets in Vault and utilized them within our pod.

Source Code

You’re invited to explore our GitHub repository, which houses a comprehensive collection of source code for Kubernetes.

GitHub - anveshmuppeda/kubernetes: Kuberntes Complete Notes

Kuberntes Complete Notes. Contribute to anveshmuppeda/kubernetes development by creating an account on GitHub.

github.com

Also, if we welcome your feedback and suggestions! If you encounter any issues or have ideas for improvements, please open an issue on our GitHub repository. 🚀

Did this answer your question?