HashiCorp Vault — управление секретами
Vault — инструмент для безопасного хранения и выдачи секретов: паролей, токенов и ключей. Он шифрует данные, раздаёт их по политикам доступа и умеет генерировать временные учётные данные для баз, облаков и сертификатов.
1. Что такое секрет-менеджер и Vault
Секреты — пароли, токены, ключи API и SSH, сертификаты. Когда они разбросаны по .env, конфигам и вики, ими невозможно управлять: никто не знает, где что лежит и кто его трогал.
HashiCorp Vault — централизованный секрет-менеджер: хранит секреты в зашифрованном виде, выдаёт их по правилам доступа, следит за сроком жизни и может генерировать временные учётные данные. Отсюда его три кита: шифрование, политики и ротация.
vault --version
# Vault v1.17.0 (build c3a537a)
# storage: file, raft, consul
# secrets engines: kv, database, aws, pki, ...| Подход | Где секрет | Риски |
|---|---|---|
| В коде | ветка репозитория | утечка в Git, нет ротации |
| .env / конфиг | сервер приложения | кто прочитал файл — владеет секретом |
| Переменные CI | панель CI | нет контроля сроков жизни |
| Vault | зашифрованное хранилище | минимум: политики, аудит, ротация |
.env, но делает их защищёнными и управляемыми: пароль можно выдать на 24 часа, а потом автоматически перевыпустить.2. Установка Vault в dev-режиме
Самый быстрый способ попробовать Vault — dev-режим: хранилище в памяти, автоматическая распечатка (unseal) и root-токен, напечатанный при старте. Он нужен только для экспериментов.
curl -fsSL https://releases.hashicorp.com/vault/1.17.0/vault_1.17.0_linux_amd64.zip -o vault.zip
unzip vault.zip
sudo mv vault /usr/local/bin/
vault server -dev
# Dev server started!
# Root Token: hvs.abcd1234...
# Unseal Key: в dev не нуженexport VAULT_ADDR='http://127.0.0.1:8200'
vault status
# Seal Type: shamir
# Initialized: true
# Sealed: falseVAULT_ADDR задаёт адрес сервера, VAULT_TOKEN — токен аутентификации. Многие CLI-команды и клиенты Vault читают эти переменные автоматически.3. Установка Vault в prod-режиме
Реальный сервер хранит данные на диске и запускается в запечатанном (sealed) состоянии: до распечатки ключами шифрования он не читает секреты. Конфигурация живёт в HCL-файле, например /etc/vault.d/vault.hcl:
storage "file" {
path = "/opt/vault/data"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = 1
}
api_addr = "http://127.0.0.1:8200"
ui = truevault server -config=/etc/vault.d/vault.hcl
vault operator init
# Unseal Key 1: d8Uy...
# Unseal Key 2: 7kPn...
# Root Token: hvs.9x2m...
vault operator unseal
vault operator unsealПроцесс vault operator init один раз создаёт ключи распечатки и root-токен. Каждый запуск unseal вводит один ключ; по умолчанию требуется 3 из 5.
| Термин | Что значит |
|---|---|
| Sealed | сервер запечатан, секреты недоступны |
| Unseal | распечатка: ввод ключей для чтения ключа расшифровки |
| Root token | полный доступ к конфигурации и секретам |
| Shamir | схема разделения секрета между n ключами |
raft (встроенное, простое) или consul (при уже имеющемся Consul).4. Первый секрет
Секреты в Vault хранятся в secrets engines — подсистемах, которые умеют хранить значения или генерировать их на лету. Самая используемая — kv (ключ-значение), по умолчанию подключённая по пути secret/.
vault login hvs.abcd1234...
vault kv put secret/web/db password=super-secret username=app
# Key Value
# created_time 2026-09-19...
# version 1
vault kv get secret/web/db
vault kv get -format=json secret/web/db
vault kv delete secret/web/dbРабота с kv построена на версиях: каждая запись имеет номер, изменить — добавить новую версию, откатиться — vault kv rollback.
| Команда | Что делает |
|---|---|
| vault kv put PATH key=value | записать секрет |
| vault kv get PATH | прочитать секрет |
| vault kv list SECRET-PATH | список ключей каталога |
| vault kv delete PATH | удалить секрет |
| vault kv metadata get PATH | история версий и метаданные |
secret/web/db — просто иерархия ключей. Правильное нейминг-соглашение (проект/окружение/роль) сильно упрощает политики доступа.vault login выполнен в правильную среду: случайно записать секрет не в тот инстанс — частая ошибка.5. KV secrets engine: версии и параметры
Двигатель kv существует в двух генерациях: v1 (просто ключ-значение, без версий) и v2 (версии, метаданные, откат). Начиная с Vault 1.10 путь secret/ по умолчанию работает как v2.
vault secrets list
# Path Type Accessor Options
# cubbyhole/ cubbyhole ...
# secret/ kv kv_8f2c... version=2
vault secrets enable -path=myapp kv-v2
vault kv put myapp/db url=postgres://db:5432
vault kv get -version=1 myapp/dbВ v2 путь к данным меняется: сама запись лежит по адресу secret/data/имя, метаданные — secret/metadata/имя. Клиентские библиотеки учитывают это автоматически.
| Параметр | kv v1 | kv v2 |
|---|---|---|
| Версии секрета | нет | есть, с откатом |
| Метаданные | нет | created_time, destroyed time |
| DELETE | удаление сразу | пометка «уничтожен» |
| Путь доступа | secret/имя | secret/data/имя |
kv-v2): возможность откатиться на рабочую версию секрета — страховка при ошибочном обновлении.myapp/db, кому-то нет.6. Политики доступа
Политики определяют, кто и к каким путям имеет доступ. Описываются на HCL c правилами вида «путь → список разрешённых операций». Файл myapp-policy.hcl:
path "secret/data/myapp/*" {
capabilities = ["read", "list"]
}
path "secret/metadata/myapp/*" {
capabilities = ["list"]
}vault policy write myapp myapp-policy.hcl
vault policy list
vault token create -policy=myapp| Capability | Что разрешает |
|---|---|
| create | создавать данные по пути |
| read | читать секреты |
| update | изменять существующие |
| delete | удалять |
| list | получить список ключей |
| deny | запрет (побеждает все остальные) |
* даёт доступ ко всем путям и операциям. Давай его только root-токену и администраторам; обычные сервисы получают точечные правила.reader, deployer, admin. Логин по роли проще менять при увольнении сотрудника — достаточно отозвать его токен.sentinel/vault policy fmt приводит файл политики к единому стилю и ловит опечатки в путях.6. Auth-методы: токен
Auth-методы решают вопрос «кто ты?». По умолчанию активен метод token: выписанный почти любым путём токен сразу валиден. Для человека это удобный третий фактор, для машины — промежуточное звено.
vault auth list
# Path Type ...
# token/ token ...
vault token create -policy=myapp -ttl=1h
# Key Value
# token hvs.CAES...
# token_accessor ...
# policies [myapp]
vault login hvs.CAES...Токен живёт ограниченное время (-ttl=1h) и умеет быть родительским: дочерние токены наследуют политики, но не больше прав.
| Параметр | Значение |
|---|---|
| -policy | привязать политики |
| -ttl | время жизни токена |
| -orphan | не удалять при удалении родительского |
| -period | срок, после которого при сбое службы продлевается |
vault token revoke <accessor> — все его сессии и дочерние токены перестанут работать.7. Auth-метод AppRole
Для машин и сервисов идеально подходит AppRole: приложение получает пару «роль + секрет» и обменивает их на токен. Пара обёрнута так, что сам секрет приложение читает одноразово.
vault auth enable approle
vault write auth/approle/role/myapp \
token_policies=myapp \
token_ttl=1h \
secret_id_ttl=24h \
secret_id_num_uses=0
vault read auth/approle/role/myapp/role-id
vault write -f auth/approle/role/myapp/secret-id
vault write auth/approle/login \
role_id="b0e9e8..." \
secret_id="7b7f11..."| Термин | Роль в схеме |
|---|---|
| role-id | идентификатор роли, «имя» приложения |
| secret-id | секретный ключ роли, «пароль» приложения |
| token | выданный после логина токен с политиками |
| secret_id_num_uses | сколько раз можно применить secret-id (0 — бессрочно) |
vault read -field=secret_id auth/approle/role/myapp/secret-id и передавать сервису при первом старте — секрет-айди не хранится в конфиге сервиса.token_bound_cidrs: роль сможет логиниться только с определённых IP — лишний барьер безопасности.8. Динамические секреты
Vault умеет не хранить, а генерировать секреты для подключений: пользователю в базе, ключу для облака, сертификату. Сгенерированный пароль валиден определённое время и автоматически перевыпускается — это и есть динамические секреты.
vault secrets enable database
vault write database/config/ordersdb \
plugin_name=postgresql-database-plugin \
allowed_roles=orders-reader \
connection_url="postgresql://{{username}}:{{password}}@10.0.0.5:5432/orders" \
username="vault_admin" \
password="hopefully-not-here"
vault write database/roles/orders-reader \
db_name=ordersdb \
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
vault read database/creds/orders-reader
# username: v-token-orders-reader-abc...
# password: A9dk... (живёт 1 час)| Secrets engine | Что генерирует |
|---|---|
| database | учётные записи в PostgreSQL, MySQL и др. |
| aws | временные IAM-ключи и STS-токены |
| pki | сертификаты с коротким сроком жизни |
| azure / gcp | роли и ключи облачных провайдеров |
password=hopefully-not-here — для демонстрации. В продакшене такие креденшелы передаются Vault через env или Аuth-метод, а не вписаны руками в конфиг.9. Интеграция с приложением
Приложение получает секреты через API Vault: логин (AppRole или токен) затем чтение пути. Минимальный пример на curl:
export VAULT_ADDR=http://127.0.0.1:8200
TOKEN=$(curl -s --request POST \
$VAULT_ADDR/v1/auth/approle/login \
-d '{"role_id":"...","secret_id":"..."}' \
| jq -r .auth.client_token)
curl -s \
--header "X-Vault-Token: $TOKEN" \
$VAULT_ADDR/v1/secret/data/myapp/db \
| jq -r .data.dataДля языков есть официальные SDK (hvac для Python, vault для Go), которые кэшируют токен и продлевают его до истечения TTL.
| Подход | Плюсы | Минусы |
|---|---|---|
| SDK в приложении | токен живёт в памяти, авто-ренуал | код + библиотека |
| Vault Agent Template | не меняет код, шаблон на месте | нужен sidecar-процесс |
| env чере crate-env | просто, привычно для 12-factor | секрет в переменной окружения |
10. Политики и namespace
Политики Vault — точечные права на действия с путями. Минимум прав — главное правило: приложение читает свой секрет и почти ничего больше.
# policy/myapp.hcl
path "secret/data/myapp/*" {
capabilities = ["read"]
}
path "auth/token/lookup-self" {
capabilities = ["read"]
}vault policy write myapp policy/myapp.hclNamespaces (в Vault Enterprise) делят секреты между командами, а в OSS роль «разделителя» играют mount-точки и префиксы путей.
| Принцип | Пример |
|---|---|
| least privilege | read только на свой путь |
| короткий TTL | токен живёт 10 минут, auto-renew |
| обязательная логика | каждый сервис — свой токен |
capabilities = ["*"] на * — равноценна root-доступу. Такие токены ищите и отзывайте первыми при аудите.vault token create -policy=myapp и пробный read — «а работает ли то, что разрешено?».11. Ротация и аудит секретов
Секрет — это обязательство постоянного обновления. Vault облегчает жизнь: версии KV, аудит-лог и ротация динамических учётных данных почти без ручной работы.
- KV v2 хранит версии:
vault kv get -version=2вернёт состояние на момент откачки; - динамические секреты (database/aws) истекают по TTL и пересоздаются автоматически;
- аудит-лог (
vault audit enable file file_path=...) пишет все операции для разбора инцидентов.
# Проверка, кто и когда работал с секретом
vault audit enable file file_path=/var/log/vault-audit.json
grep myapp/db /var/log/vault-audit.jsonvault token revoke) и ротируйте секрет. Историю смотрите в аудите, а не по памяти.12. Disaster Recovery и план на будущее
Vault — единая точка отказа, если упал он, упала выдача секретов всем сервисам. Готовность к катастрофе проверяется заблаговременно.
# Резервная копия и восстановление
vault operator raft snapshot save backup.snap
vault operator raft snapshot restore backup.snap- снимайте снэпшоты raft-кластера по расписанию и храните вне инфраструктуры;
- в HA-кластере держите нечётное число узлов (3 и 5) в разных зонах;
- для командных секретов продумайте seal/unseal: unseal-ключи разделены и не у одного человека;
- прогоняйте восстановление на стэнде: «восстановились за час» — лучше, чем «где-то так».
13. Vault в Kubernetes: Agent Injector и CSI
В Kubernetes секреты лучше брать из Vault, а не из манифестов: два распространённых пути — Vault Agent Injector и CSI-провайдер.
# Аннотации пода: инжектировать секрет из secret/myapp
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-appconfig: "secret/data/myapp"- Injector перехватывает поды по аннотациям и добавляет sidecar-агента, который записывает секрет в volume;
- CSI-провайдер монтирует секрет как файл и обновляет его по TTL на лету;
- для обоих механизмов нужны свои политики и роли (см. разделы 10–11).
helm install vault hashicorp/vault \
--set injector.enabled=true
kubectl apply -f secrets-store-csi-driver.yamlВыбор простой: Injector проще для контейнеров приложений, CSI — когда нужен файл-секрет и обновление без рестарта. Приложения при этом Vault напрямую не трогают.
14. Transit: шифрование данных на лету
Отдельно хранить секреты мало: сами данные тоже шифруются. Движок Transit делает это централизованно — приложения шифруют и расшифровывают по ключу, не получая его.
vault secrets enable transit
vault write -f transit/keys/customer_pii
# Приложение: шифрование без доступа к ключу
curl -XPOST -H "X-Vault-Token: $TOKEN" \
--data '{"plaintext":"base64-данные"}' \
vault:8200/v1/transit/encrypt/customer_pii- ключи живут в Vault — утечка одного сервиса не тянет чужие данные;
- ротация ключей создаёт версии: расшифровка старых данных продолжает работать;
- по умолчанию AES-GCM, подключаются и настраиваемые варианты для интеграций.
15. Мониторинг Vault: метрики и алерты
Vault молча перестаёт отвечать — и все сервисы вдруг теряют секреты. Следить за ним нужно, как за базой данных: метрики Prometheus и алерты на ключевые границы.
| Метрика | Что означает |
|---|---|
| seal status | Vault запечатан — секреты не выдаются, сервисы «встают» |
| vault.raft.leader | кто лидер кластера, есть ли раскол |
| token count | рост — потенциальная утечка или сбой ротации |
| request latency | задержка выдачи секрета для приложений |
# Снять метрики вручную
vault read sys/metrics | headОбязательные алерты: seal (критично, проверяется skаждый минуту), недоступность лидера, рост латенции и количества токенов. Готовые правила есть в официальном helm-чарте.
16. Vault в команде: структура путей и онбординг
Когда Vault обслуживает несколько команд, хаос приходит быстро: все пишут в общие mount-точки, роли раздаются из «дефолтных» токенов. Организуйте структуру заранее.
secret/app-team-1/ # KV для команды 1
secret/app-team-2/
transit/keys/ # общий transit
auth/approle/ # роли приложений- каждой команде — свой префикс и политика least-privilege (см. раздел 10);
- токены и AppRole-роли заводятся под конкретный сервис, а не «по привычке»;
- обязателен описанный онбординг: кто выдаёт роли, как проверяются политики, куда писать за помощью.
17. PKI engine: внутренний центр сертификации
PKI-engine превращает Vault в собственный центр сертификации: вы выпускаете TLS-сертификаты под кастомный CA, раздаёте их приложениям и отзываете при компрометации. Это избавляет от покупки сертификата на каждый сервис.
vault secrets enable pki
vault secrets tune -max-lease-ttl=87600h pki
# Создаём корневой CA
vault write pki/root/generate/internal \
common_name=internal.example.com \
ttl=87600h
# Выпуск сертификата для службы
vault write pki/issue/web common_name=web.internal.example.com \
ttl=720h- раздельные роли: один путь для подписи, другой — для выдачи под лимиты;
- автоматическая ротация через short-lived (TTL 24–48 часов);
- crl и ocsp-ответы для отзыва «на лету».
Классика: сервисы получают сертификаты под собственный внутренний домен, а шифрование mTLS между микросервисами настраивается без «ручного» импорта ключей.
18. Vault Agent: шаблоны и кэширование
Приложение не всегда умеет говорить с Vault API само. Vault Agent — агент, который живёт рядом с процессом, получает секреты, подставляет их в шаблоны и (опционально) кэширует для локального доступа.
# agent-config.hcl
vault { address = "https://vault.internal:8200" }
template {
destination = "/etc/app/config.json"
contents = `{ "db": "{{ with secret \"secret/weapp/db\" }}{{ .Data.data.password }}{{ end }}" }`
}- шаблоны пересобираются при изменении секрета — конфиг не устаревает;
- cache-agent обслуживает других запрашивающих без прямых паролей к Vault;
- агент берет на себя renew/auth — приложение просто читает файл.
Для Kubernetes это превращается в инжектор (раздел 13), а для виртуалок и systemd-служб агент — классика: никаких «секретов в git».
19. Обновление и миграция Vault
Vault развивается, и когда-то наступит момент обновления. В отличие от приложений, здесь есть ключевая особенность: во время миграции хранилище (storage backend) переживает переключение узла, а распечатывание (unseal) — критическая операция.
1. Бэкап storage (consul/s3 или файловый raft)
2. Обновление бинаря на ВСЕХ нодах (версии идут по шагам)
3. После включения — статус: seal/unseal и репликация
4. Проверка чтения секретов и «новых» функций
Совет: всегда в «тихой» зоне — во время окна обслуживания- не прыгайте через мажорные версии: читайте upgrade-гайд под каждую пару;
- ключи unseal держите в недоступном для логирования месте (например, split-key хранилища);
- изменения storage-бэкенда (например, файл → raft) требуют отдельной процедуры миграции.
Правило бэкапа здесь критично: если миграция сломала авторизацию, восстанавливайте из бэкапа, а не «переизобретайте политики с нуля».
20. Vault в CI/CD: секреты в пайплайнах
Секреты в переменных CI — плохая практика: их сложно ротировать и аудитить. Зрелый пайплайн берёт секреты у Vault по кратковременному токену, выдаваемому на конкретную задачу.
# GitHub Actions: вход по OIDC, без постоянного токена
- uses: hashicorp/vault-action@v2
with:
url: https://vault.internal:8200
role: ci-deploy
method: jwt
secrets: |
secret/data/ci/db password | DB_PASSWORD;
# В Jenkins аналог — шаг с authenticate + wrapping token- OIDC/JWT-авторизация: у каждой среды (prod, staging) свои роли и сроки действия;
- короткоживущие токены (десять минут) вместо «секретов навсегда»;
- аудит: какой запуск, какая роль, какой секрет — в лог Vault.
Результат: ротация базы паролей — правка в Vault, а не «пересборка джобов». Деплой и тесты работают со свежими значениями всегда.
Что дальше
Ты прошёл путь от dev-сервера до динамических секретов и интеграции: KV, политики, токен, AppRole, генерация учётных данных. Практикуйся: заведи секреты приложения и подключай его через AppRole, а затем оберни выпуск секретов в автоматический деплой GitHub Actions или Jenkins. Защиту инфраструктуры дополнительно закрепи уроком Основы кибербезопасности.