Loading...
Усі статті
Security · 7 min read

Управління секретами з HashiCorp Vault у Kubernetes

Практичний посібник з розгортання HashiCorp Vault на Kubernetes, налаштування динамічних секретів, інтеграції з застосунками через Vault Agent Injector та впровадження найкращих практик для управління секретами продакшн-рівня.

Проблема секретів у Kubernetes

Кожному застосунку потрібні секрети — паролі баз даних, API-ключі, TLS-сертифікати, ключі шифрування. У Kubernetes підхід за замовчуванням — зберігати їх у Kubernetes Secrets, які закодовані в base64 (не зашифровані) і доступні кожному з RBAC-доступом до неймспейсу.

Це не безпека. Це театр безпеки.

Поширені анти-патерни, які ми бачимо в продакшн-середовищах:

  • Секрети, hardcoded в образах контейнерів — Експоновані в кожному реєстрі та логі збірки
  • Секрети у змінних середовища — Видимі у виводі kubectl describe pod
  • Секрети, закомічені в Git — Навіть "приватні" репо зламують
  • Спільні статичні облікові дані — Без ротації, без аудиту, без терміну дії

HashiCorp Vault вирішує ці проблеми, надаючи централізоване управління секретами з динамічною генерацією облікових даних, автоматичною ротацією, детальним audit logging та точним контролем доступу.

Крок 1: Розгорніть Vault на Kubernetes за допомогою Helm

Рекомендований підхід — запускати Vault у режимі високої доступності з інтегрованим Raft-сховищем:

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

# Create a dedicated namespace
kubectl create namespace vault

# Install Vault with HA configuration
helm install vault hashicorp/vault \
  --namespace vault \
  --set server.ha.enabled=true \
  --set server.ha.replicas=3 \
  --set server.ha.raft.enabled=true \
  --set server.dataStorage.size=10Gi \
  --set server.dataStorage.storageClass=gp3 \
  --set server.auditStorage.enabled=true \
  --set server.auditStorage.size=10Gi \
  --set injector.enabled=true \
  --set ui.enabled=true

Після розгортання ініціалізуйте та розпечатайте перший Vault pod:

# Initialize Vault with 5 key shares, 3 required to unseal
kubectl exec -n vault vault-0 -- vault operator init \
  -key-shares=5 \
  -key-threshold=3 \
  -format=json > vault-init.json

# Unseal the first pod (repeat with 3 different keys)
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 the other pods to the Raft cluster
kubectl exec -n vault vault-1 -- vault operator raft join \
  http://vault-0.vault-internal:8200

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

# Unseal the remaining pods
# (In production, use auto-unseal with AWS KMS, GCP KMS, or Azure Key Vault)

Для продакшн-розгортань завжди налаштовуйте auto-unseal, щоб уникнути ручного втручання після перезапуску podʼів:

# vault-config.hcl
seal "awskms" {
  region     = "us-east-1"
  kms_key_id = "alias/vault-unseal-key"
}

Крок 2: Налаштуйте автентифікацію Kubernetes

Vault повинен перевіряти, що podʼи, що запитують секрети, є тими, ким себе видають. Метод автентифікації Kubernetes використовує токени service account для автентифікації:

# Login to Vault
export VAULT_ADDR="http://127.0.0.1:8200"
vault login <root-token>

# Enable Kubernetes auth method
vault auth enable kubernetes

# Configure it to communicate with the Kubernetes API
vault write auth/kubernetes/config \
  kubernetes_host="https://kubernetes.default.svc:443"

Створіть політику, що визначає, до яких секретів сервіс може мати доступ:

# payment-service-policy.hcl
path "secret/data/payment-service/*" {
  capabilities = ["read"]
}

path "database/creds/payment-service-role" {
  capabilities = ["read"]
}

path "pki/issue/payment-service" {
  capabilities = ["create", "update"]
}

Застосуйте політику і прив'яжіть її до Kubernetes service account:

# Write the policy
vault policy write payment-service payment-service-policy.hcl

# Create a role that maps Kubernetes SA to Vault policy
vault write auth/kubernetes/role/payment-service \
  bound_service_account_names=payment-service \
  bound_service_account_namespaces=production \
  policies=payment-service \
  ttl=1h

Крок 3: Впровадьте динамічні облікові дані бази даних

Статичні паролі бази даних — це зобов'язання: вони ніколи не закінчуються, спільні для середовищ і неможливо ротувати без простою. Database secrets engine у Vault генерує унікальні, короткоживучі облікові дані на вимогу.

# Enable the database secrets engine
vault secrets enable database

# Configure the PostgreSQL connection
vault write database/config/payments-db \
  plugin_name=postgresql-database-plugin \
  allowed_roles="payment-service-role" \
  connection_url="postgresql://{{username}}:{{password}}@payments-db.production.svc:5432/payments?sslmode=require" \
  username="vault_admin" \
  password="initial-setup-password"

# Rotate the root password so only Vault knows it
vault write -force database/rotate-root/payments-db

# Create a role that generates credentials with a 1-hour TTL
vault write database/roles/payment-service-role \
  db_name=payments-db \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
  revocation_statements="REVOKE ALL ON ALL TABLES IN SCHEMA public FROM \"{{name}}\"; DROP ROLE IF EXISTS \"{{name}}\";" \
  default_ttl="1h" \
  max_ttl="24h"

Тепер кожного разу, коли pod стартує, він отримує унікальні облікові дані бази даних, що автоматично закінчуються. Якщо облікові дані скомпрометовано, blast radius обмежений одним pod і однією годиною.

Крок 4: Інжектуйте секрети в podʼи за допомогою Vault Agent

Vault Agent Injector використовує Kubernetes mutating webhooks, щоб автоматично інжектувати секрети в podʼи через анотації. Жодних змін коду застосунку не потрібно.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: payment-service
  template:
    metadata:
      labels:
        app: payment-service
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "payment-service"

        # Static secrets
        vault.hashicorp.com/agent-inject-secret-config: "secret/data/payment-service/config"
        vault.hashicorp.com/agent-inject-template-config: |
          {{- with secret "secret/data/payment-service/config" -}}
          export STRIPE_API_KEY="{{ .Data.data.stripe_api_key }}"
          export WEBHOOK_SECRET="{{ .Data.data.webhook_secret }}"
          {{- end }}

        # Dynamic database credentials
        vault.hashicorp.com/agent-inject-secret-db: "database/creds/payment-service-role"
        vault.hashicorp.com/agent-inject-template-db: |
          {{- with secret "database/creds/payment-service-role" -}}
          export DB_USERNAME="{{ .Data.username }}"
          export DB_PASSWORD="{{ .Data.password }}"
          {{- end }}

        # Auto-renew credentials before they expire
        vault.hashicorp.com/agent-cache-enable: "true"
        vault.hashicorp.com/agent-cache-listener-port: "8200"
    spec:
      serviceAccountName: payment-service
      containers:
        - name: payment-service
          image: myorg/payment-service:v1.2.3
          command: ["/bin/sh", "-c"]
          args:
            - source /vault/secrets/config &&
              source /vault/secrets/db &&
              /app/payment-service
          ports:
            - containerPort: 8080
          resources:
            limits:
              cpu: 500m
              memory: 256Mi
            requests:
              cpu: 100m
              memory: 128Mi

Vault Agent sidecar обробляє автентифікацію, отримання секретів та автоматичне продовження — ваш застосунок просто читає файли або змінні середовища.

Крок 5: Альтернативний підхід з External Secrets Operator

Для команд, що віддають перевагу Kubernetes-нативному workflow, External Secrets Operator (ESO) автоматично синхронізує секрети Vault у Kubernetes Secrets:

# secret-store.yaml
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: vault-backend
spec:
  provider:
    vault:
      server: "http://vault.vault.svc:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "external-secrets"
          serviceAccountRef:
            name: external-secrets
            namespace: external-secrets

---
# external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: payment-service-secrets
  namespace: production
spec:
  refreshInterval: 5m
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: payment-service-secrets
    creationPolicy: Owner
  data:
    - secretKey: stripe-api-key
      remoteRef:
        key: secret/data/payment-service/config
        property: stripe_api_key
    - secretKey: webhook-secret
      remoteRef:
        key: secret/data/payment-service/config
        property: webhook_secret

Цей підхід добре працює, коли ваші застосунки вже читають з Kubernetes Secrets і ви хочете додати Vault як джерело правди без зміни коду застосунку.

Крок 6: Увімкніть audit logging

У регульованих середовищах ви повинні демонструвати, хто отримував доступ до яких секретів і коли. Увімкніть audit log Vault:

# Enable file-based audit logging
vault audit enable file file_path=/vault/audit/audit.log

# For production, ship logs to your SIEM
# Example: enable syslog backend
vault audit enable syslog tag="vault" facility="AUTH"

Кожна операція Vault — читання, запис, спроби автентифікації, зміни політик — записується з повним запитом і відповіддю (чутливі значення HMAC'ed). Пересилайте ці логи у ваш SIEM (Splunk, Elastic або Datadog) для сповіщень на підозрілі патерни доступу.

Чеклист зміцнення для продакшну

Перед виходом у продакшн перевірте ці пункти:

  • Auto-unseal налаштовано з cloud KMS (ніколи не зберігайте unseal-ключі на диску)
  • TLS увімкнено для всієї комунікації Vault (бажано mTLS)
  • Audit logging увімкнено і перенаправляється до SIEM
  • Root token відкликано після початкового налаштування (використовуйте identity-based auth)
  • Namespaces налаштовано для ізоляції команд (Vault Enterprise)
  • Disaster recovery replication налаштовано між регіонами
  • Backup і restore протестовано зі snapshots Raft
  • Sentinel policies забезпечують організаційні правила (Vault Enterprise)
  • Ліміти ресурсів встановлено на podʼи Vault, щоб запобігти проблемам noisy-neighbor
  • Network policies обмежують, які podʼи можуть досягати Vault
# Take a Raft snapshot for backup
vault operator raft snapshot save /tmp/vault-backup.snap

# Verify the snapshot
vault operator raft snapshot inspect /tmp/vault-backup.snap

# Automate daily backups with a CronJob
# vault-backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: vault-backup
  namespace: vault
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: vault-backup
          containers:
            - name: backup
              image: hashicorp/vault:1.15
              command:
                - /bin/sh
                - -c
                - |
                  vault operator raft snapshot save /tmp/vault-$(date +%Y%m%d).snap
                  aws s3 cp /tmp/vault-$(date +%Y%m%d).snap \
                    s3://my-vault-backups/$(date +%Y%m%d).snap
              env:
                - name: VAULT_ADDR
                  value: "http://vault-active.vault.svc:8200"
                - name: VAULT_TOKEN
                  valueFrom:
                    secretKeyRef:
                      name: vault-backup-token
                      key: token
          restartPolicy: OnFailure

Висновок

Управління секретами не є опціональним — це фундаментальна вимога безпеки, яку лише складніше додати пізніше, коли ваша інфраструктура зростає. HashiCorp Vault, належно розгорнутий на Kubernetes, надає динамічні секрети, автоматичну ротацію, точний контроль доступу та вичерпний audit logging, з якими статичні секрети просто не можуть зрівнятися.

У DevOpsVibe ми розгортали і керували Vault у десятках Kubernetes-кластерів для організацій від стартапів до enterprise. Чи потрібна вам допомога з початковим розгортанням, міграцією зі статичних секретів чи інтеграцією з вашими існуючими CI/CD-конвеєрами — наша команда може довести вас до управління секретами продакшн-рівня за тижні, а не місяці. Захистіть свою інфраструктуру сьогодні.

у категорії
vaultvaultsecrets-managementkuberneteskubernetessecurityhashicorpdevops
працювати з нами

Хочете, щоб наша команда допомогла з вашою інфраструктурою?

talk to an engineerFree 30-min discovery callBook
close