Chuyển đến nội dung chính

Bài 20: Vault trên Kubernetes - Helm, Operator và CSI

Deploy Vault trên Kubernetes bằng Helm chart, Vault Secrets Operator (VSO), Vault CSI Provider, Vault Agent Injector. So sánh VSO vs CSI vs Agent Injector.

🔒 DevSecOps — Bài 20 Bài 20: Vault trên Kubernetes - Helm, Operator và CSI

HashiCorp Vault từ Cơ bản đến Nâng cao

Phần 5: Vault Agent, Proxy và Kubernetes Integration

xdev.asia

1. Deploy Vault trên Kubernetes bằng Helm

Các mode deploy

ModeMô tảUse case
DevSingle pod, in-memory storageDevelopment, testing
StandaloneSingle pod, persistent storageSmall deployments
HAMulti-pod, Integrated Storage (Raft)Production
ExternalVault chạy ngoài K8s, chỉ deploy Agent InjectorExternal Vault server

Helm Install (HA Mode)

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

# Install Vault HA với Integrated Storage
helm install vault hashicorp/vault \
  --namespace vault \
  --create-namespace \
  --set server.ha.enabled=true \
  --set server.ha.replicas=3 \
  --set server.ha.raft.enabled=true \
  --set server.dataStorage.size=10Gi \
  --set server.resources.requests.memory=256Mi \
  --set server.resources.requests.cpu=250m \
  --set server.resources.limits.memory=512Mi \
  --set server.resources.limits.cpu=500m \
  --set ui.enabled=true \
  --set ui.serviceType=ClusterIP

Custom Values

# values-production.yaml
server:
  ha:
    enabled: true
    replicas: 3
    raft:
      enabled: true
      config: |
        ui = true

        listener "tcp" {
          tls_disable = 0
          address = "[::]:8200"
          cluster_address = "[::]:8201"
          tls_cert_file = "/vault/userconfig/vault-tls/tls.crt"
          tls_key_file  = "/vault/userconfig/vault-tls/tls.key"
        }

        storage "raft" {
          path = "/vault/data"
          retry_join {
            leader_api_addr = "https://vault-0.vault-internal:8200"
            leader_ca_cert_file = "/vault/userconfig/vault-tls/ca.crt"
          }
          retry_join {
            leader_api_addr = "https://vault-1.vault-internal:8200"
            leader_ca_cert_file = "/vault/userconfig/vault-tls/ca.crt"
          }
          retry_join {
            leader_api_addr = "https://vault-2.vault-internal:8200"
            leader_ca_cert_file = "/vault/userconfig/vault-tls/ca.crt"
          }
        }

        service_registration "kubernetes" {}

  dataStorage:
    size: 20Gi
    storageClass: gp3

  extraVolumes:
    - type: secret
      name: vault-tls

  resources:
    requests:
      memory: 512Mi
      cpu: 500m
    limits:
      memory: 1Gi
      cpu: 1000m

  ingress:
    enabled: true
    hosts:
      - host: vault.company.com

ui:
  enabled: true

injector:
  enabled: true
  replicas: 2
# Install với custom values
helm install vault hashicorp/vault \
  --namespace vault \
  --create-namespace \
  -f values-production.yaml

Init và Unseal

# Init trên vault-0
kubectl exec -n vault vault-0 -- vault operator init \
  -key-shares=5 \
  -key-threshold=3 \
  -format=json > vault-init.json

# Unseal vault-0
kubectl exec -n vault vault-0 -- vault operator unseal <key-1>
kubectl exec -n vault vault-0 -- vault operator unseal <key-2>
kubectl exec -n vault vault-0 -- vault operator unseal <key-3>

# Join vault-1 và vault-2 vào Raft cluster
kubectl exec -n vault vault-1 -- vault operator raft join \
  https://vault-0.vault-internal:8200

kubectl exec -n vault vault-2 -- vault operator raft join \
  https://vault-0.vault-internal:8200

# Unseal vault-1 và vault-2
kubectl exec -n vault vault-1 -- vault operator unseal <key-1>
# ... (repeat for each node)

2. Vault Secrets Operator (VSO)

VSO là Kubernetes operator chính thức, tự động sync secrets từ Vault vào Kubernetes Secrets. Đây là phương pháp recommended cho Kubernetes-native secret management.

Install VSO

helm install vault-secrets-operator hashicorp/vault-secrets-operator \
  --namespace vault-secrets-operator-system \
  --create-namespace

Custom Resources

# 1. VaultConnection — kết nối đến Vault server
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultConnection
metadata:
  name: vault-connection
  namespace: app
spec:
  address: https://vault.company.com:8200
  skipTLSVerify: false
  caCertSecretRef: vault-ca-cert

---
# 2. VaultAuth — xác thực với Vault
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
  name: vault-auth
  namespace: app
spec:
  vaultConnectionRef: vault-connection
  method: kubernetes
  mount: kubernetes
  kubernetes:
    role: webapp
    serviceAccount: webapp-sa

---
# 3. VaultStaticSecret — sync KV secret
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
  name: app-db-secret
  namespace: app
spec:
  vaultAuthRef: vault-auth
  mount: secret
  type: kv-v2
  path: production/db
  refreshAfter: 60s
  destination:
    name: app-db-credentials
    create: true
    labels:
      app: webapp
    transformation:
      excludeRaw: true
      templates:
        connection-string:
          text: "postgresql://{{ .Secrets.username }}:{{ .Secrets.password }}@{{ .Secrets.host }}:5432/{{ .Secrets.database }}"

---
# 4. VaultDynamicSecret — dynamic database credentials
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultDynamicSecret
metadata:
  name: app-dynamic-db
  namespace: app
spec:
  vaultAuthRef: vault-auth
  mount: database
  path: creds/app-role
  renewalPercent: 67
  destination:
    name: app-dynamic-db-credentials
    create: true

---
# 5. VaultPKISecret — auto-issue certificates
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultPKISecret
metadata:
  name: app-tls-cert
  namespace: app
spec:
  vaultAuthRef: vault-auth
  mount: pki
  role: app-cert
  commonName: app.company.com
  altNames:
    - app.internal.company.com
  ttl: 24h
  expiryOffset: 1h
  destination:
    name: app-tls
    create: true

Sử dụng trong Pod

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  namespace: app
spec:
  template:
    spec:
      serviceAccountName: webapp-sa
      containers:
        - name: webapp
          image: myapp:latest
          envFrom:
            - secretRef:
                name: app-db-credentials
          volumeMounts:
            - name: tls
              mountPath: /etc/tls
              readOnly: true
      volumes:
        - name: tls
          secret:
            secretName: app-tls

3. Vault CSI Provider

Vault CSI Provider sử dụng Secrets Store CSI Driver để mount secrets trực tiếp vào Pod volumes, không tạo Kubernetes Secrets.

# SecretProviderClass
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-db-creds
  namespace: app
spec:
  provider: vault
  parameters:
    vaultAddress: "https://vault.company.com:8200"
    roleName: "webapp"
    objects: |
      - objectName: "db-username"
        secretPath: "secret/data/production/db"
        secretKey: "username"
      - objectName: "db-password"
        secretPath: "secret/data/production/db"
        secretKey: "password"
  # Optionally sync to K8s Secret
  secretObjects:
    - secretName: app-db-synced
      type: Opaque
      data:
        - objectName: db-username
          key: username
        - objectName: db-password
          key: password

---
# Pod sử dụng CSI volume
apiVersion: v1
kind: Pod
metadata:
  name: webapp
spec:
  serviceAccountName: webapp-sa
  containers:
    - name: webapp
      image: myapp:latest
      volumeMounts:
        - name: secrets
          mountPath: /mnt/secrets
          readOnly: true
  volumes:
    - name: secrets
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: vault-db-creds

4. Vault Agent Injector

Agent Injector sử dụng Kubernetes Mutating Webhook để tự động inject Vault Agent sidecar vào Pods bằng annotations.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  template:
    metadata:
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "webapp"
        vault.hashicorp.com/agent-inject-secret-db.txt: "secret/data/production/db"
        vault.hashicorp.com/agent-inject-template-db.txt: |
          {{- with secret "secret/data/production/db" -}}
          DB_HOST={{ .Data.data.host }}
          DB_USER={{ .Data.data.username }}
          DB_PASS={{ .Data.data.password }}
          {{- end }}
    spec:
      serviceAccountName: webapp-sa
      containers:
        - name: webapp
          image: myapp:latest
          # Secrets available at /vault/secrets/db.txt

5. So sánh VSO vs CSI vs Agent Injector

Tiêu chíVSOCSI ProviderAgent Injector
MechanismOperator + CRDsCSI DriverMutating Webhook
K8s SecretsTạo K8s SecretsOptional syncKhông
Dynamic secrets✅ (VaultDynamicSecret)Hạn chế✅ (templates)
PKI certificates✅ (VaultPKISecret)Hạn chế✅ (templates)
Auto-rotation✅ (refreshAfter)Cấu hình riêng✅ (sidecar)
Sidecar requiredKhôngKhôngCó
Resource overheadThấp (operator chung)ThấpCao (1 sidecar/pod)
Recommendation✅ RecommendedOK cho simpleLegacy

6. Best Practices cho Kubernetes

  • Ưu tiên VSO cho deployments mới — native K8s experience, ít overhead

  • Sử dụng dedicated ServiceAccount cho mỗi workload

  • Enable Auto-unseal với Cloud KMS cho production

  • Cấu hình Pod Security cho Vault pods (non-root, read-only filesystem)

  • Sử dụng NetworkPolicy để restrict access đến Vault

  • Mount secrets vào emptyDir (memory-backed) thay vì persistent volume

7. Tổng kết

  • Helm chart — deploy Vault HA với Integrated Storage trên K8s

  • VSO — recommended, CRD-based, tự động sync secrets vào K8s Secrets

  • CSI Provider — mount secrets trực tiếp vào Pod volumes

  • Agent Injector — legacy, sidecar-based, annotation-driven

Phần tiếp theo sẽ tập trung vào tích hợp Vault với các ứng dụng thực tế — Spring Boot, Node.js, Terraform, Ansible và CI/CD pipelines.