Серверное оборудование для бизнеса — надёжные решения для любых задач Подробнее →
Статья

Секреты в DevOps: HashiCorp Vault на практике

Секреты в DevOps: HashiCorp Vault на практике

Пароли в переменных окружения, токены в репозиториях, ключи в конфиг-файлах на серверах — знакомая картина? Это технический долг, который рано или поздно превращается в инцидент безопасности. HashiCorp Vault решает проблему управления секретами системно: централизованное хранение, аудит, автоматическая ротация и тонкое управление доступом.

Почему не env vars

Переменные окружения кажутся удобными, но имеют серьёзные недостатки:

  • Видны всем процессам через /proc/PID/environ
  • Попадают в логи при ошибках
  • Нет аудита кто и когда читал секрет
  • Ротация требует перезапуска приложения
  • В Docker Compose часто оказываются в git-репозитории

Vault решает все эти проблемы: API-доступ, полный аудит лог, динамические секреты, TTL.

Dev и Prod режимы

Для локальной разработки и тестирования:

vault server -dev -dev-root-token-id="root"
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN="root"

Dev-режим: всё в памяти, unsealed автоматически, не для продакшена. Для продакшена нужен конфиг с persistent storage (Raft, Consul, S3).

Минимальный production конфиг /etc/vault.d/vault.hcl:

storage "raft" {
  path    = "/opt/vault/data"
  node_id = "vault-node-1"
}

listener "tcp" {
  address     = "0.0.0.0:8200"
  tls_cert_file = "/opt/vault/tls/vault.crt"
  tls_key_file  = "/opt/vault/tls/vault.key"
}

api_addr = "https://vault.example.com:8200"
cluster_addr = "https://vault.example.com:8201"

KV Engine: хранение секретов

Key-Value — самый простой secrets engine. Версия KV v2 поддерживает versioning:

# Включить KV v2
vault secrets enable -path=secret kv-v2

# Записать секрет
vault kv put secret/myapp/config 
    db_password="SuperSecretPass123" 
    api_key="sk-abc123xyz"

# Прочитать
vault kv get secret/myapp/config

# Получить конкретную версию
vault kv get -version=2 secret/myapp/config

KV v2 хранит до 10 версий по умолчанию — можно откатиться к предыдущему значению.

AppRole Auth: аутентификация приложений

AppRole — метод аутентификации для машин и сервисов (не людей):

# Включить AppRole
vault auth enable approle

# Создать role
vault write auth/approle/role/myapp 
    secret_id_ttl=10m 
    token_num_uses=10 
    token_ttl=20m 
    token_max_ttl=30m 
    secret_id_num_uses=40

# Получить Role ID (публичный, можно хранить в конфиге)
vault read auth/approle/role/myapp/role-id

# Получить Secret ID (одноразовый, передаётся через CI/CD)
vault write -f auth/approle/role/myapp/secret-id

Приложение логинится с парой RoleID + SecretID и получает временный токен.

Динамические секреты для PostgreSQL

Это killer feature Vault. Вместо хранения статического пароля к БД, Vault создаёт временных пользователей на лету:

# Включить database engine
vault secrets enable database

# Настроить подключение к PostgreSQL
vault write database/config/mydb 
    plugin_name=postgresql-database-plugin 
    connection_url="postgresql://{{username}}:{{password}}@postgres:5432/mydb" 
    allowed_roles="myapp-role" 
    username="vault" 
    password="VaultDBPass"

# Создать role с шаблоном SQL
vault write database/roles/myapp-role 
    db_name=mydb 
    creation_statements="CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO "{{name}}";" 
    default_ttl="1h" 
    max_ttl="24h"

# Приложение запрашивает credentials
vault read database/creds/myapp-role

Каждый запрос создаёт уникального пользователя с TTL 1 час. После истечения пользователь автоматически удаляется.

Vault Agent: автоматическое обновление секретов

Vault Agent — демон, который аутентифицируется в Vault и рендерит секреты в файлы:

# /etc/vault-agent/config.hcl
auto_auth {
  method "approle" {
    config = {
      role_id_file_path   = "/etc/vault-agent/role-id"
      secret_id_file_path = "/etc/vault-agent/secret-id"
    }
  }
}

template {
  source      = "/etc/vault-agent/templates/db.conf.tpl"
  destination = "/etc/myapp/db.conf"
  command     = "systemctl reload myapp"
}

При ротации секрета Agent автоматически обновляет файл и перезапускает приложение.

Интеграция с Kubernetes

В K8s используйте Vault Agent Injector — он добавляет init-контейнер и sidecar к подам через аннотации:

annotations:
  vault.hashicorp.com/agent-inject: "true"
  vault.hashicorp.com/role: "myapp"
  vault.hashicorp.com/agent-inject-secret-db: "secret/data/myapp/config"

Секреты появятся в /vault/secrets/db внутри контейнера. Альтернатива — Vault Secrets Operator для нативной интеграции через CRD.

3 Ответа

  1. Динамические секреты для PostgreSQL — это реально меняет подход к безопасности. Когда каждый сервис получает свои уникальные временные credentials, компрометация одного не затрагивает остальные.

  1. Vault Agent Injector в Kubernetes это очень удобно, но у нас были проблемы с производительностью когда много подов стартуют одновременно — Vault не справлялся с нагрузкой. Решили через горизонтальное масштабирование Vault с Raft.

  1. Не забудьте настроить audit log в Vault — без него весь смысл централизованного управления секретами теряется. vault audit enable file file_path=/var/log/vault/audit.log и вы видите каждое обращение к секрету.