Installing and Configuring HashiCorp Vault on Kubernetes

A step-by-step guide to deploying Vault on Kind cluster

Featured image



Overview

Following our previous post about Vault concepts, we’ll now explore how to install and configure Vault on a Kubernetes cluster using Kind. We’ll cover the complete setup process, including initialization, unsealing, and basic configuration.


Prerequisites

Setting up Kind Cluster

Create a Kind cluster with port forwarding for Vault (30000):

# cluster-3nodes.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
    listenAddress: "0.0.0.0"
    protocol: TCP
- role: worker
- role: worker
# Create cluster
kind create cluster --name somaz-3nodes-kubernetes --config ./cluster-3nodes.yaml

# Verify nodes
kubectl get nodes


🔐 Vault Initialization Concepts

Before installation, it’s important to understand Vault’s initialization process.

In an HA environment with Raft storage backend, initialization generates two critical pieces:

1. Unseal Keys

{
  "unseal_keys_b64": [
    "xxxxx"
  ],
  "unseal_shares": 1,
  "unseal_threshold": 1
}

2. Root Token

{
  "root_token": "hvs.xxxx"
}


📦 Installing Vault

1. Add Helm Repository

# Add HashiCorp helm repo
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update

# Verify repository
helm repo list

2. Install Vault

# Install with HA configuration
helm install vault hashicorp/vault \
    --set='server.ha.enabled=true' \
    --set='server.ha.raft.enabled=true' \
    --namespace vault \
    --create-namespace

3. Verify Installation

kubectl get pods -n vault
NAME                                    READY   STATUS    RESTARTS   AGE
vault-0                                 0/1     Running   0          82s
vault-1                                 0/1     Running   0          82s
vault-2                                 0/1     Running   0          82s
vault-agent-injector-7f7f68d457-kqlsh   1/1     Running   0          83s


Initializing and Unsealing Vault

1. Initialize Vault

# Initialize with single key share
kubectl exec vault-0 -n vault -- vault operator init \
    -key-shares=1 \
    -key-threshold=1 \
    -format=json > cluster-keys.json

2. Capture Keys

# Store unseal key and root token
VAULT_UNSEAL_KEY=$(cat cluster-keys.json | jq -r ".unseal_keys_b64[]")
CLUSTER_ROOT_TOKEN=$(cat cluster-keys.json | jq -r ".root_token")

3. Unseal Vault Nodes

# Unseal vault-0
kubectl exec vault-0 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY

# Join and unseal vault-1
kubectl exec vault-1 -n vault -- vault operator raft join http://vault-0.vault-internal:8200
kubectl exec vault-1 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY

# Repeat for vault-2
kubectl exec vault-2 -n vault -- vault operator raft join http://vault-0.vault-internal:8200
kubectl exec vault-2 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY


Accessing Vault UI

1. Configure NodePort Service

kubectl patch svc vault -n vault -p '{"spec": {"type": "NodePort", "ports": [{"nodePort": 30000, "port": 8200, "protocol": "TCP"}]}}'

2. Access UI

CLUSTER_ROOT_TOKEN=$(cat cluster-keys.json | jq -r ".root_token")

echo $CLUSTER_ROOT_TOKEN
hvs.bOXica9b3vWqfBaiZWfASOC1

create a user and access it with that user.

kubectl exec --stdin=true --tty=true vault-0 -n vault -- /bin/sh

# activate userpass
vault auth enable userpass

# create user example
vault write auth/userpass/users/<username> password=<password> policies=<policies>

# create user
vault write auth/userpass/users/somaz password=<password>

# login with user
vault login -method=userpass username=somaz password=<password>


Basic Secret Management

1. Enable KV Secrets Engine

# Enable KV version 2
kubectl exec -it vault-0 -n vault -- vault secrets enable -path=secret kv-v2

# Create a secret
kubectl exec -it vault-0 -n vault -- vault kv put secret/devdb/config \
    username='somaz' \
    password='somazpassword'

2. Create Policies

# Create read-only policy
kubectl exec -it vault-0 -n vault -- vault policy write devdb - <<EOF
path "secret/data/devdb/config" {
  capabilities = ["read"]
}
EOF

3. Configure Kubernetes Authentication

The initial root token is a user who is authorized to perform all tasks on all paths.

Web applications only need the ability to read secrets defined in a single path.

This application must authenticate and receive tokens with limited access.

# Enable Kubernetes auth
kubectl exec -it vault-0 -n vault -- vault auth enable kubernetes

# Configure Kubernetes auth
kubectl exec -it vault-0 -n vault -- vault write auth/kubernetes/config \
    kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"

4. Create a policy that enables Vault devdb (example)

# create policy
vault policy write devdb - <<EOF
path "secret/data/devdb/config" {
  capabilities = ["read"]
}
EOF

# list policy
vault policy list
default
devdb
root

# read policy
vault policy read devdb
path "secret/data/devdb/config" {
  capabilities = ["read"]
}

5. Creates a Kubernetes authentication role.

vault write auth/kubernetes/role/db-app \\
        bound_service_account_names=somaz-app \\
        bound_service_account_namespaces=somaz \\
        policies=devdb \\
        ttl=24h

# read role
vault read auth/kubernetes/role/db-app


Deploy Web Application

1. Create a Kubernetes service account and Namespace

# create namespace
kubectl create namespace somaz

# create service account
kubectl create serviceaccount somaz-app -n somaz

2. Deploy the Web Application

# create yaml file
cat > devsomazapp.yaml <<EOF
---
apiVersion: v1
kind: Pod
metadata:
  name: devsomazapp
  labels:
    app: devsomazapp
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "db-app"
    vault.hashicorp.com/agent-inject-secret-credentials.txt: "secret/data/devdb/config"
spec:
  serviceAccountName: somaz-app
  containers:
    - name: devsomazapp
      image: jweissig/app:0.0.1
EOF

# create pod
k apply -f devsomazapp.yaml -n somaz

# read secret
k exec --stdin=true --tty=true devsomazapp -c devsomazapp -n somaz -- cat /vault/secrets/credentials.txt

3. Form of an Annotation

vault.hashicorp.com/agent-inject-secret-<unique-name>: /path/to/secret


If more than one Secret is designated as a policy, it should be set as follows.

vault.hashicorp.com/agent-inject-secret-devdb: secret/data/devdb/config
vault.hashicorp.com/agent-inject-secret-qadb: secret/data/qadb/config
vault.hashicorp.com/role: 'devdb-app'


Test Right Now.

# connect pod
kubectl exec --stdin=true --tty=true vault-0 -n vault -- /bin/sh

# create secret
vault kv put secret/qadb/config username='qasomaz' password='<password>'

# create policy
vault policy write qadb - <<EOF
path "secret/data/qadb/config" {
  capabilities = ["read"]
}
EOF

# create role
vault write auth/kubernetes/role/db-app \\
        bound_service_account_names=somaz-app \\
        bound_service_account_namespaces=somaz \\
        policies=devdb,qadb \\
        ttl=24h

# read role
vault read auth/kubernetes/role/db-app

4. Change the Web Application yaml file and verify

# create yaml file
cat > devsomazapp.yaml <<EOF
---
apiVersion: v1
kind: Pod
metadata:
  name: devsomazapp
  labels:
    app: devsomazapp
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "db-app"
    vault.hashicorp.com/agent-inject-secret-devdb.txt: "secret/data/devdb/config"
    vault.hashicorp.com/agent-inject-secret-qadb.txt: "secret/data/qadb/config"
spec:
  serviceAccountName: somaz-app
  containers:
    - name: devsomazapp
      image: jweissig/app:0.0.1
EOF

# create pod
k apply -f devsomazapp.yaml -n somaz

# restart pod
k delete po -n somaz devsomazapp
k apply -f devsomazapp.yaml -n somaz

# read secret
k exec --stdin=true --tty=true devsomazapp -c devsomazapp -n somaz -- ls /vault/secrets/
devdb.txt  qadb.txt

# read secret devdb
k exec --stdin=true --tty=true devsomazapp -c devsomazapp -n somaz -- cat /vault/secrets/devdb.txt

# read secret qadb
k exec --stdin=true --tty=true devsomazapp -c devsomazapp -n somaz -- cat /vault/secrets/qadb.txt


Check the connection log.

k logs devsomazapp -c devsomazapp -n somaz


Setting User Policy

If you check the policy using Root Token on the web, you can see it all. Vault Web Root


I can’t see anything when I connect to the user called Somaz. Vault Web Somaz


Log in with the CLI to check.

vault login -method=userpass username=somaz password=<password>

vault token lookup


Create and assign policies.

# root로 로그인
vault login hvs.bOXica9b3vWqfBaiZWfASOC1

# 정책 생성
vault policy write admin - <<EOF
path "*" {
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
EOF

vault write auth/userpass/users/somaz policies=devdb policies=admin

vault read auth/userpass/users/somaz


I can see it well. I gave you all the authority. Vault Web Somaz



References