Posted in

HashiCorp Vault HA Cluster Installation on Kubernetes

Intoduction

HashiCorp Vault adalah sistem manajemen rahasia (secret management) yang digunakan untuk menyimpan, mengelola, dan mengontrol akses terhadap data sensitif seperti password, token, dan sertifikat secara aman. Dalam lingkungan produksi, keandalan dan ketersediaan tinggi (High Availability/HA) menjadi hal yang sangat penting agar layanan tetap dapat diakses meskipun terjadi kegagalan pada salah satu node.

Configuration TLS for Vault HA

1. Create Self-signed certificate

Kita akan membuat self-signed certificate untuk ssl pada vault yang akan kita install. Pada tutorial disini kita akan membuat certificate menggunakan openssl. Kita akan menggunakan referensi dari konfiguarsi resmi vault https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-minikube-tls

Change working directory

mkdir -p ~/vault/cert
cd ~/vault/cert

Generate private key

openssl genrsa -out vault.key 2048

Create CSR configuration

cat > vault-csr.conf << EOF
[req]
default_bits = 2048
prompt = no
encrypt_key = yes
default_md = sha256
distinguished_name = kubelet_serving
req_extensions = v3_req
[ kubelet_serving ]
O = system:nodes
CN = system:node:vault.vault.svc
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = vault.vault.svc
DNS.2 = vault.vault.svc.cluster.local
DNS.3 = *.vault-internal
IP.1 = 127.0.0.1
EOF

Generate file CSR

openssl req -new -key vault.key -out vault.csr -config vault-csr.conf

2. Issue Certificate

Kita sudah selesai generate certificate, sekarang kita akan melakukan issue certificate yang sudah kita generate ke kubernetes

Create CSR yaml

cat > csr.yaml << EOF
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
   name: vault.svc
spec:
   signerName: kubernetes.io/kubelet-serving
   expirationSeconds: 8640000
   request: $(cat vault.csr|base64|tr -d '\n')
   usages:
   - digital signature
   - key encipherment
   - server auth
EOF

Apply file CSR ke kubernetes dan approve certificate tersebut

kubectl create -f csr.yaml
kubectl certificate approve vault.svc

3. Store the certificates and Key in the Kubernetes secrets store

Ambil certificate yang sudah kita approve

kubectl get csr vault.svc -o jsonpath='{.status.certificate}' | openssl base64 -d -A -out vault.crt

Ambil certificate CA

kubectl config view \
--raw \
--minify \
--flatten \
-o jsonpath='{.clusters[].cluster.certificate-authority-data}' \
| base64 -d > vault.ca

Create namespace

kubectl create ns vault

Buat TLS Secret

kubectl create secret generic vault-ha-tls \
   -n vault \
   --from-file=vault.key=vault.key \
   --from-file=vault.crt=vault.crt \
   --from-file=vault.ca=vault.ca

Installation & Configuration Vault

1. Install Vault using helm

Sekarang kita akan deploy vault menggunakan helm v3. Tambahkan repo hashicorp via helm

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

create files values.yaml

global:
  enabled: true
  tlsDisable: false
  resources:
    requests:
      memory: 256Mi
      cpu: 250m
    limits:
      memory: 256Mi
      cpu: 250m

server:
  resources:
    requests:
      memory: 2Gi
      cpu: 2000m
    limits:
      memory: 4Gi
      cpu: 4000m

  extraEnvironmentVars:
    VAULT_CACERT: /vault/userconfig/vault-ha-tls/vault.ca
    VAULT_TLSCERT: /vault/userconfig/vault-ha-tls/vault.crt
    VAULT_TLSKEY: /vault/userconfig/vault-ha-tls/vault.key

  volumes:
    - name: userconfig-vault-ha-tls
      secret:
        defaultMode: 420
        secretName: vault-ha-tls
  volumeMounts:
    - mountPath: /vault/userconfig/vault-ha-tls
      name: userconfig-vault-ha-tls
      readOnly: true

  auditStorage:
    enabled: false

  standalone:
    enabled: false
    
  # Run Vault in "HA" mode.
  ha:
    enabled: true
    replicas: 3
    raft:
      enabled: true
      setNodeId: true
      config: |
        cluster_name = "vault-integrated-storage"
        ui = true
        listener "tcp" {
           tls_disable = 0
           address = "[::]:8200"
           cluster_address = "[::]:8201"
           tls_cert_file = "/vault/userconfig/vault-ha-tls/vault.crt"
           tls_key_file  = "/vault/userconfig/vault-ha-tls/vault.key"
           tls_client_ca_file = "/vault/userconfig/vault-ha-tls/vault.ca"
        }
        storage "raft" {
          path = "/vault/data"
        }
        disable_mlock = true
        service_registration "kubernetes" {}

# Vault UI
ui:
  enabled: true
  serviceType: "LoadBalancer"
  loadBalancerIP: "10.100.19.93"
  serviceNodePort: null
  externalPort: 8200

Install vault menggunakan values diatas menggunakan version 0.31.0 (terbaru pada saat blog ini ditulus)

helm install vault -n vault -f values.yaml --version 0.31.0 hashicorp/vault 

Check instalasi vault

kubectl get all -n vault

2. Init & unseal vault

Ketika kita sudah install vault, status pod masih belum ready karna kita harus unseal masing-masing pods.

Init vault disalah satu pod (vault-0)

kubectl exec -it -n vault vault-0 -- vault operator init

Kita akan mendapatkan 5 key, dan kita akan unseal vault-0 yang sudah kita init menggunakan 3 dari 5 key. Simpan key tersebut ketempat yang aman, karena kita akan membutuhkan key tersebut.

kubectl exec -it -n vault vault-0 -- vault operator unseal

Kita sudah init cluster dan unseal vault-0. Sekarang kita harus join raft-cluster dari vault-1 dan vault-2 ke raft-cluster

Exec terlebih dahulu vault-1

kubectl exec -it -n vault vault-1 -- sh

Kemudian kita join cluster

vault operator raft join -address=https://vault-1.vault-internal:8200 -leader-ca-cert="$(cat /vault/userconfig/vault-ha-tls/vault.ca)" -leader-client-cert="$(cat /vault/userconfig/vault-ha-tls/vault.crt)" -leader-client-key="$(cat /vault/userconfig/vault-ha-tls/vault.key)" https://vault-0.vault-internal:8200

Unseal vault-1

vault operator unseal

Sekarang untuk vault-2

kubectl exec -it -n vault vault-2 -- sh

Kemudian kita join cluster

vault operator raft join -address=https://vault-2.vault-internal:8200 -leader-ca-cert="$(cat /vault/userconfig/vault-ha-tls/vault.ca)" -leader-client-cert="$(cat /vault/userconfig/vault-ha-tls/vault.crt)" -leader-client-key="$(cat /vault/userconfig/vault-ha-tls/vault.key)" https://vault-0.vault-internal:8200

Kemudian kita unseal vault-2

vault operator unseal

check status vault

kubectl exec -it -n vault vault-1 -- vault status
kubectl exec -it -n vault vault-2 -- vault status

3.1 Configuration secret, policy, kubernetes auth (via CLI)

Ada 2 cara untuk konfiguarsi pada vault, bisa menggunakan CLI dan GUI. Pada tahap ini kita akan membuat secret, policy, dan kubernetes auth menggunakan CLI yang diexec dari vault-0.

Kita masuk exec vault-0

kubectl exec -it -n vault vault-0 -- sh

Secret Data

Kita membuat secret data menggunakan kv-v2

vault secrets enable -path=secret kv-v2
vault kv put secret/myapps/config username="usersaya" password="passwordsaya"

Check apakah data sudah kesave

vault kv get secret/myapps/config
vault kv get -field=username secret/myapps/config
vault kv get -field=password secret/myapps/config

Policies

Kita membuat policy dengan nama myapps-policy

vault policy write myapps-policy - << EOF
path "secret/data/myapps/*" {
    capabilities = ["read"]
}
EOF

Check policy

vault read sys/policy/myapps-policy

Kubernetes auth

Enable auto kubernetes

vault auth enable kubernetes
vault write auth/kubernetes/config \
        kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" 

Buat role baru

vault write auth/kubernetes/role/myapps-role \
      bound_service_account_names=myapps-sa \
      bound_service_account_namespaces=myapps \
      policies=myapps-policy \
      ttl=24h

Chek role yang sudah dibuat

vault read auth/kubernetes/role/myapps-role

3.2 Configuration secret, policy, kubernetes auth (via GUI)

Pada tutorial ini kita akan menggunakan dashboard vault yang sudah kita install menggunakan LoadBalancerIP. Jika sudah disetup menggunakan CLI, kita bisa skip tahap ini.

Akses vault via browser

Untuk login kita bisa menggunakan token yang terdapat pada saat kita init vault (Initial root token)

Secret Data

Buat engine kv-v2 dengan cara, Secret Engine -> Enable new engine -> Pilih KV -> Isi path dengan nama secret

Setelah kita membuat engine, kita membuat secret baru, Create Secret -> kemudian isi path myapps/config dan dengan secret data sesuai kebutuhan kita

Policies

Sekarang kita akan membuat policy hanya bisa akses read ke secret tersebut, Policies -> Create ACL Policy -> kemudian kita beri nama policy myapps-policy dan acl

path "secret/data/myapps/*" {
    capabilities = ["read"]
}

Kubernetes Auth

Sekarang kita akan konfiguarsi agar vault kita bisa terhubung ke kubernetes cluster, Access -> Enable new method -> Kubernetes -> Enable method (pathnya menggunakan default yaitu kubernetes)

Setelah kita enable kubernetes method, kita harus isi Kubernetes Host, untuk mengetahui IP Address kubernetes host kita bisa menggunakan kubectl

kubectl get all

Setelah itu kita membuat role baru

4. Testing vault to pods using vault-agent-injector

Sekarang kita akan menggunakan pods dan menggunakan annotation vault untuk melakukan inject ke pods. Pada tahap sebelumnya kita menggunakan service account myapps-sa.

Kita buat file manifest yang berisi service account dan pods

apiVersion: v1
kind: ServiceAccount
metadata:
  name: myapps-sa
  namespace: myapps
---
apiVersion: v1
kind: Pod
metadata:
  name: demo
  namespace: myapps
  # We will use annotation for inject the vault into pods
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/ca-cert: "/run/secrets/kubernetes.io/serviceaccount/ca.crt"
    vault.hashicorp.com/role: "myapps-role"
    vault.hashicorp.com/agent-inject-secret-testwithraw: "secret/myapps/config"
    vault.hashicorp.com/agent-inject-template-testwithtemplate: |
      {{- with secret "secret/data/myapps/config" -}}
      username={{ .Data.data.username }}
      password={{ .Data.data.password }}
      {{- end }}

spec:
  serviceAccountName: myapps-sa
  containers:
  - name: app
    image: nginx

Sekarang kita apply file manifest tersebut kemudian check pods dan service-account tersebut

kubectl get all,sa -n myapps

Kemudian kita check vault secret didalam pods demo

kubectl exec -it -n myapps demo -c app -- ls /vault/secrets/
kubectl exec -it -n myapps demo -c app -- cat /vault/secrets/testwithraw
kubectl exec -it -n myapps demo -c app -- cat /vault/secrets/testwithtemplate

Secara default, letak secret vault ada di /vault/secrets/. Secret sudah bisa dibaca oleh pod dan sudah bisa digunakan

Troubleshooting

All pod is terminating

Ketika semua pods vault terminating, dan pada saat autostart, maka kita harus unseal pods tersebut menggunakan key (pada saat init) yang sudah disimpan.

Leave a Reply

Your email address will not be published. Required fields are marked *