Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи?

Написать
Войти
Дайджесты
Инжекция секретов Vault в под Kubernetes

Безопасное управление секретами в Kubernetes и GitLab CI с помощью HashiCorp Vault

Практическое руководство по интеграции HashiCorp Vault с Kubernetes с использованием Agent Injector для безопасного проброса секретов в поды, а также подключение к GitLab CI через JWT. Рассматривается переход на динамические пароли с ограничением TTL до 15 минут для повышения безопасности БД.

Безопасное управление секретами в Kubernetes и GitLab CI с помощью HashiCorp Vault

Хранение учетных данных в конфигурационных файлах проекта, репозиториях или в виде переменных CI/CD несет серьезные риски утечки секретов через логи сборщиков или при компрометации раннеров. Использование статических паролей усугубляет проблему: они редко ротируются из-за опасения нарушить работу систем. Интеграция с HashiCorp Vault (системой централизованного хранения секретов и управления доступом) позволяет решить эти проблемы за счет динамической генерации и инжекции учетных данных.

Архитектура интеграции с Kubernetes через Vault Agent Injector

Vault Agent Injector работает как Mutating Admission Webhook в Kubernetes. Он перехватывает запросы на создание подов, проверяет аннотации в манифесте и добавляет в под Init-контейнер и Sidecar-контейнер с Vault Agent. Агент авторизуется в Vault с помощью JWT-токена сервисного аккаунта Kubernetes, считывает секреты и монтирует их в виде файлов по пути /vault/secrets/ в память пода.

Ниже приведен пример Deployment с настроенной инжекцией секретов базы данных. Аннотации должны располагаться строго в блоке spec.template.metadata.annotations (шаблон пода):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: billing-service
  namespace: production
spec:
  replicas: 1
  selector:
    matchLabels:
      app: billing-service
  template:
    metadata:
      labels:
        app: billing-service
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "db-app"
        vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/db-app"
        vault.hashicorp.com/agent-inject-template-db-creds: |
          {{ with secret "database/creds/db-app" }}
          export DB_USER="{{ .Data.username }}"
          export DB_PASSWORD="{{ .Data.password }}"
          {{ end }}
    spec:
      serviceAccountName: app-example
      containers:
      - name: application
        image: registry.example.com/billing:v1.2.0
        command: ["/bin/sh", "-c", ". /vault/secrets/db-creds && exec billing-app"]

Интеграция с GitLab CI: OIDC и JWT

Для аутентификации пайплайнов GitLab CI в Vault используется механизм OIDC. GitLab генерирует JWT-токен сборки, а Vault проверяет его подпись через JWKS-эндпоинт GitLab и выдает временный токен авторизации.

1. Нативная интеграция (GitLab Premium / Ultimate)

В коммерческих версиях доступен синтаксис secrets, автоматически запрашивающий секреты из Vault без необходимости устанавливать клиент Vault в образ:

deploy_prod:
  stage: deploy
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  secrets:
    DATABASE_PASSWORD:
      vault: production/db/password@ops
      file: false
  script:
    - ansible-playbook deploy.yml --extra-vars "db_pass=$DATABASE_PASSWORD"
2. Интеграция через CLI (GitLab Community Edition - CE)

В бесплатной версии GitLab аналогичный функционал реализуется через вызовы CLI-клиента Vault:

deploy_staging:
  stage: deploy
  image: hashicorp/vault:1.15.0
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  script:
    - export VAULT_TOKEN=$(vault write -field=token auth/jwt/login role=billing-deploy jwt=$VAULT_ID_TOKEN)
    - export STAGING_DB_PASS=$(vault kv get -field=password secret/billing/staging/db)
    - ansible-playbook deploy.yml --extra-vars "db_pass=$STAGING_DB_PASS"

Критическое предупреждение по безопасности (security caveat): При настройке JWT-авторизации необходимо ограничивать доступ через проверку полей токена. В настройках роли Vault задавайте bound_claims (например, разрешая доступ только для конкретных project_id, namespace_id или веток ref: refs/heads/main), чтобы другие проекты вашего инстанса GitLab не могли получить доступ к секретам этой роли.

Переход к динамическим секретам PostgreSQL

Вместо постоянных паролей для базы данных движок Dynamic Secrets в Vault генерирует уникальных пользователей в СУБД со случайными паролями и коротким временем жизни (TTL).

Настройка PostgreSQL-движка в Vault выполняется администратором:

# Включение движка баз данных
vault secrets enable database

# Настройка подключения к базе данных
vault write database/config/my-postgres-database     plugin_name=postgresql-database-plugin     allowed_roles=my-db-role     connection_url="postgresql://{{username}}:{{password}}@postgres-server:5432/billing?sslmode=disable"     username="vault_admin_user"     password="super_secure_admin_password"

# Создание роли с TTL в 15 минут
vault write database/roles/my-db-role     db_name="my-postgres-database"     creation_statements="CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO "{{name}}";"     default_ttl="15m"     max_ttl="30m"

При обращении к роли vault read database/creds/my-db-role Vault выполняет SQL-запрос из creation_statements, генерируя пользователя вида v-token-my-db-ro-xxxx, и автоматически удаляет его по истечении TTL.

Пошаговый план миграции инфраструктуры

  1. Включение методов аутентификации: Активируйте поддержку токенов Kubernetes и JWT-токенов в Vault.
  2. Установка Vault Agent Injector: Установите Helm-чарт Vault в Kubernetes:
    helm repo add hashicorp https://helm.releases.hashicorp.com
    helm repo update
    helm install vault hashicorp/vault --namespace vault --create-namespace --set "injector.enabled=true"
    
  3. Настройка связки GitLab CI и Vault: Создайте роли с привязкой к OIDC GitLab и проверьте прохождение аутентификации.
  4. Перенос статических секретов: Перенесите секреты из переменных репозиториев в KV-хранилище Vault.
  5. Внедрение динамических секретов: Настройте интеграцию с PostgreSQL и переведите доступ на временных пользователей.

Риски и эксплуатационные ограничения

  • Утечка через переменные: При file: false секреты хранятся в памяти процесса. По возможности используйте временные файлы секретов для изоляции.
  • Ограничения короткого TTL: Время жизни пароля в 15 минут может прервать длительные миграции БД. Для таких задач создавайте роли с увеличенным max_ttl или используйте механизм продления аренды секрета (lease renewal).
  • Стратегия отката (Rollback): На время миграции сохраняйте старые статические учетные записи в режиме «только чтение» для быстрого переключения в случае сбоя.