HashiCorp Vault — управление секретами

Vault — инструмент для безопасного хранения и выдачи секретов: паролей, токенов и ключей. Он шифрует данные, раздаёт их по политикам доступа и умеет генерировать временные учётные данные для баз, облаков и сертификатов.

Уровень: средний 16 разделов 2–3 часа

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зашифрованное хранилищеминимум: политики, аудит, ротация
Vault не отменяет хранение секретов в CI и в .env, но делает их защищёнными и управляемыми: пароль можно выдать на 24 часа, а потом автоматически перевыпустить.
Секреты в Git-репозитории не удаляются даже после «исправления» коммита — они остаются в истории. Никогда не коммить пароли.

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: false
Dev-режим хранит все секреты в оперативной памяти: после перезапуска процесс они исчезнут. Для реальных данных dev-режим не используется.
Переменная VAULT_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 = true
vault 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 ключами
Потеря ключей unseal — потеря всех секретов, попадание 3 ключей чужим людям — потеря контроля. Храни ключи раздельно: банковская ячейка, сейф, у нескольких доверенных лиц.
Для продакшена выбирай HA-хранилище: 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 v1kv v2
Версии секретанетесть, с откатом
Метаданныенетcreated_time, destroyed time
DELETEудаление сразупометка «уничтожен»
Путь доступаsecret/имяsecret/data/имя
Для новых проектов всегда включай v2 (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. Логин по роли проще менять при увольнении сотрудника — достаточно отозвать его токен.
Сторонний linter 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срок, после которого при сбое службы продлевается
Не выдавай разработчикам root-токен «чтобы не мешать»: одна утёкшая вкладка браузера — и весь Vault скомпрометирован. Каждый пользователь получает свой токен с минимумом прав.
Для отзыва доступа сотруднику достаточно 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роли и ключи облачных провайдеров
Указанный в примере admin-пароль базы password=hopefully-not-here — для демонстрации. В продакшене такие креденшелы передаются Vault через env или Аuth-метод, а не вписаны руками в конфиг.
При истечении TTL Vault сам удаляет учётную запись в базе (REVOKE и DROP ROLE) — «мёртвые» аккаунты не копятся, и данные для взлома не живут месяцами.

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секрет в переменной окружения
Никогда не передавай токен приложения в код как константу и не логируй его. Правильный паттерн — login на каждом старте и чтение секрета из Vault.
В Kubernetes токен сервиса можно получить автоматически через k8s auth-метод: сд-аккаунт кластера сам проходит аутентификацию Vault без секретов в манифестах.

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.hcl

Namespaces (в Vault Enterprise) делят секреты между командами, а в OSS роль «разделителя» играют mount-точки и префиксы путей.

ПринципПример
least privilegeread только на свой путь
короткий TTLтокен живёт 10 минут, auto-renew
обязательная логикакаждый сервис — свой токен
Политика с capabilities = ["*"] на * — равноценна root-доступу. Такие токены ищите и отзывайте первыми при аудите.
Проверяйте политики через vault token create -policy=myapp и пробный read — «а работает ли то, что разрешено?».

11. Ротация и аудит секретов

Секрет — это обязательство постоянного обновления. Vault облегчает жизнь: версии KV, аудит-лог и ротация динамических учётных данных почти без ручной работы.

# Проверка, кто и когда работал с секретом
vault audit enable file file_path=/var/log/vault-audit.json
grep myapp/db /var/log/vault-audit.json
При утечке токена не удаляйте запись — отзовите токен (vault token revoke) и ротируйте секрет. Историю смотрите в аудите, а не по памяти.
Ротация нельзя откладывать «до лучших времён»: дольше живёт секрет — больше поверхность атаки. TTL в девелопере можно длиннее, в проде — короче.

12. Disaster Recovery и план на будущее

Vault — единая точка отказа, если упал он, упала выдача секретов всем сервисам. Готовность к катастрофе проверяется заблаговременно.

# Резервная копия и восстановление
vault operator raft snapshot save backup.snap
vault operator raft snapshot restore backup.snap
  1. снимайте снэпшоты raft-кластера по расписанию и храните вне инфраструктуры;
  2. в HA-кластере держите нечётное число узлов (3 и 5) в разных зонах;
  3. для командных секретов продумайте seal/unseal: unseal-ключи разделены и не у одного человека;
  4. прогоняйте восстановление на стэнде: «восстановились за час» — лучше, чем «где-то так».
Unseal-ключи и root-токен не храним в Vault и в репозитории: они нужны только в защищённом месте (сейф, банковская ячейка). Потеряли их — потеряли доступ навсегда.
Документируйте сам процесс: кто держит ключи, как проходит восстановление и когда тестировали последний раз.

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"
helm install vault hashicorp/vault \
  --set injector.enabled=true
kubectl apply -f secrets-store-csi-driver.yaml

Выбор простой: Injector проще для контейнеров приложений, CSI — когда нужен файл-секрет и обновление без рестарта. Приложения при этом Vault напрямую не трогают.

Не храните root-токен Vault в кубернетес-секрете рядом с приложением: bootstrap через AppRole с узкими политиками надёжнее.
Начните с Injector на одном тестовом поде, посмотрите логи sidecar и только потом масштабируйте на сервисы.

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
Ищите по зашифрованным полям через детерминированные ключи: одинаковый вход даёт одинаковый шифротекст, а расшифровка по индексу не нужна.
Transit шифрует «на лету», но не «заменяет» KV: эти два движка решают разные задачи — хранение секретов и защита данных приложений.

15. Мониторинг Vault: метрики и алерты

Vault молча перестаёт отвечать — и все сервисы вдруг теряют секреты. Следить за ним нужно, как за базой данных: метрики Prometheus и алерты на ключевые границы.

МетрикаЧто означает
seal statusVault запечатан — секреты не выдаются, сервисы «встают»
vault.raft.leaderкто лидер кластера, есть ли раскол
token countрост — потенциальная утечка или сбой ротации
request latencyзадержка выдачи секрета для приложений
# Снять метрики вручную
vault read sys/metrics | head

Обязательные алерты: seal (критично, проверяется skаждый минуту), недоступность лидера, рост латенции и количества токенов. Готовые правила есть в официальном helm-чарте.

Мониторинг самого Vault не живёт «внутри» Vault: выносите метрики в Prometheus, а алерты — в свою систему оповещения из Grafana.
Нагружайте alerta-политику не «перечётом», а 3–5 правилами: редкие, но важные события поднимают шум, а критичные тонут в нём.

16. Vault в команде: структура путей и онбординг

Когда Vault обслуживает несколько команд, хаос приходит быстро: все пишут в общие mount-точки, роли раздаются из «дефолтных» токенов. Организуйте структуру заранее.

secret/app-team-1/        # KV для команды 1
secret/app-team-2/
transit/keys/             # общий transit
auth/approle/             # роли приложений
Заведите двух «Vault-офицеров» с доступом к root, остальные работают через роли: власть и доступ — разные вещи.
Наводите порядок в путях до роста: миграция структуры в живом кластере с десятками потребителей — согласованный день, а не «в обед».

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

Классика: сервисы получают сертификаты под собственный внутренний домен, а шифрование mTLS между микросервисами настраивается без «ручного» импорта ключей.

Начните с CA под dev-домен и TTL в дни, а не годы: регулярное «получение сертификата» станет нормой.
Корневой ключ защищайте максимально: если CA скомпрометирован — «мир» всех сертификатов под ним лежит. Храните ключ в другой группе и не логируйте.
Для production ставьте секреты с минимальным TTL и авто-подписью схемы: перевыпуск дешевле, чем раздача статики.

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 }}" }`
}

Для Kubernetes это превращается в инжектор (раздел 13), а для виртуалок и systemd-служб агент — классика: никаких «секретов в git».

Ограничьте права файлов: шаблон с секретом может читать только сервисный пользователь («os_user» в настройках агента).
Кэш агента — это «секрет на диске»: настраивайте шифрование и права на каталог кэша, иначе пароли окажутся доступны соседним процессам.
Смена пароля в Vault перезапишет шаблон за секунды: проверьте, что приложение перечитывает файл конфигурации без рестарта.

19. Обновление и миграция Vault

Vault развивается, и когда-то наступит момент обновления. В отличие от приложений, здесь есть ключевая особенность: во время миграции хранилище (storage backend) переживает переключение узла, а распечатывание (unseal) — критическая операция.

1. Бэкап storage (consul/s3 или файловый raft)
2. Обновление бинаря на ВСЕХ нодах (версии идут по шагам)
3. После включения — статус: seal/unseal и репликация
4. Проверка чтения секретов и «новых» функций

Совет: всегда в «тихой» зоне — во время окна обслуживания

Правило бэкапа здесь критично: если миграция сломала авторизацию, восстанавливайте из бэкапа, а не «переизобретайте политики с нуля».

Автоматизируйте проверку после обновления: скрипт читает тестовый секрет и логирует версию. Локальный смоук-тест обязателен до апгрейда боевого кластера.
Не делайте апгрейд стоя на одной руке: во время окна обслуживания отключите автоматику (cron, deploy) — иначе «полуобновлённый» кластер поедет под нагрузкой.

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

Результат: ротация базы паролей — правка в Vault, а не «пересборка джобов». Деплой и тесты работают со свежими значениями всегда.

Держите культуры «минимум прав»: каждая ветка пипа получает только те секреты, что нужны её задаче.
Никогда не логируйте значение секрета: в CI выводите только имена и статусы обращения, реальные данные — только в рантайме приложения.
Приложение само берёт секреты у Vault на старте (см. раздел 9), а пайплайн лишь «представляет» ему микросервису.

Что дальше

Ты прошёл путь от dev-сервера до динамических секретов и интеграции: KV, политики, токен, AppRole, генерация учётных данных. Практикуйся: заведи секреты приложения и подключай его через AppRole, а затем оберни выпуск секретов в автоматический деплой GitHub Actions или Jenkins. Защиту инфраструктуры дополнительно закрепи уроком Основы кибербезопасности.

← Вернуться к курсам