Секреты в 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.
Динамические секреты для PostgreSQL — это реально меняет подход к безопасности. Когда каждый сервис получает свои уникальные временные credentials, компрометация одного не затрагивает остальные.