ADG Оставить заявку
Блог Информационная безопасность 5 мин чтения

HashiCorp Vault 0.11 и dynamic secrets: как мы перестали хранить пароли к PostgreSQL в конфигах

Перевели ротацию credentials к PostgreSQL на Vault dynamic secrets. Пароль живёт 1 час, создаётся под каждый деплой - утечка из конфига перестала быть рабочей угрозой.

Контекст момента

HashiCorp Vault 0.11 - dynamic secrets для баз данных и PKI, улучшенный UI и performance standby nodes

Vault у нас в инфраструктуре появился года полтора назад, и всё это время мы использовали его в основном как защищённое хранилище статических секретов: положили пароль, достали пароль, зашифровали. Полезно, но принципиально не отличается от зашифрованного S3-бакета с правильными политиками. В сентябре вышел Vault 0.11, и мы наконец дошли до того, ради чего он изначально проектировался, - dynamic secrets для PostgreSQL.

Что не так со статическими паролями

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

Сценарий утечки простой: разработчик скопировал .env с продакшн-сервера на ноутбук «посмотреть», ноутбук угнали, или файл осел в git-репозитории в момент отладки, или попал в логи через --verbose в CI. Пароль валиден - значит атакующий имеет доступ к базе ровно столько, сколько пароль существует. Если его никто не ротирует - фактически бесконечно.

Инцидентов такого рода у наших клиентов напрямую не было, но в аудитах мы видели конфиги с паролями пятилетней давности в production-окружениях. Это не теоретический риск.

Как работают dynamic secrets в Vault

Идея прямолинейная. Vault подключается к PostgreSQL с правами администратора, и когда приложение запрашивает credentials, он:

  1. Создаёт нового пользователя в базе с уникальным именем вида v-app-xK3mP9q-1537952341
  2. Выдаёт этому пользователю нужные привилегии (которые описаны в role-шаблоне)
  3. Возвращает логин и пароль приложению
  4. Устанавливает TTL - после истечения автоматически отзывает пользователя

Приложение получает credentials на конкретный промежуток времени. Когда TTL истекает - Vault удаляет пользователя из PostgreSQL. Следующий деплой получит другой пароль.

Что это означает на практике: даже если credentials утекли, они валидны только до конца TTL. С нашим выбором в один час атакующий имеет час. Не год.

Как настраивали

Настройка через Vault CLI или Terraform-провайдер. Мы использовали Terraform - он у нас уже управляет инфраструктурой, логично держать конфигурацию Vault там же.

Включение database secrets engine и подключение PostgreSQL:

vault secrets enable database

vault write database/config/myapp-postgres \
  plugin_name=postgresql-database-plugin \
  allowed_roles="myapp-readonly,myapp-readwrite" \
  connection_url="postgresql://{{username}}:{{password}}@postgres.internal:5432/myapp?sslmode=require" \
  username="vault_admin" \
  password="<admin-password>"

Роль с SQL-шаблоном для создания пользователя:

vault write database/roles/myapp-readwrite \
  db_name=myapp-postgres \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
    GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\"; \
    GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO \"{{name}}\";" \
  default_ttl="1h" \
  max_ttl="4h"

Получение credentials приложением:

vault read database/creds/myapp-readwrite

Возвращает username и password, которые валидны 1 час. Максимальный TTL 4 часа - это для случаев когда деплой завис или нужно дать больше времени на graceful shutdown.

Что изменилось в Vault 0.11

Версия 0.11 добавила несколько вещей, которые повлияли на нашу конфигурацию:

Performance standby nodes - раньше все запросы шли через активный Vault-узел, standby только реплицировали данные. В 0.11 standby-узлы могут обслуживать read-запросы, включая чтение секретов. Для database credentials это значит что при высокой нагрузке на деплой (несколько параллельных запросов credentials) нагрузка распределяется.

Улучшенный веб-интерфейс - database backends теперь видны и настраиваемы через UI. Для нас это в основном инструмент диагностики: посмотреть какие leases активны, какие пользователи сейчас существуют в базе.

Batch tokens - новый тип токенов с меньшими накладными расходами. Для приложений которые запрашивают credentials часто это даёт заметное снижение нагрузки на Vault.

Интеграция с приложением

Самый неудобный вопрос при переходе на dynamic secrets - как приложение обновляет credentials до истечения TTL. Если приложение стартует, получает пароль и держит connection pool - всё хорошо до тех пор, пока connection pool не начнёт терять соединения после истечения TTL.

Мы решили это по-простому: приложение получает credentials при старте через Vault Agent, который сам занимается renewal. Vault Agent запускается как sidecar-процесс, держит token, запрашивает credentials и кладёт их в файл или передаёт через переменные окружения. При renewal - обновляет файл, приложение перечитывает при следующем подключении из пула.

Более чистое решение - использовать Vault SDK напрямую в приложении с логикой renewal. Но это требует изменений в коде приложения, а у нас задача была перевести существующие сервисы без переписывания.

Что получилось

Три production-сервиса на PostgreSQL переведены на dynamic secrets. Пароль к базе теперь не существует как постоянная сущность: нет файла с паролем, нет значения в переменных окружения которое можно стащить и использовать долго.

В pg_roles на сервере можно видеть текущих пользователей: обычно это один-два активных Vault-созданных аккаунта плюс статические сервисные. Картина сразу показывает кто сейчас имеет доступ - это само по себе полезно для мониторинга.

Из неожиданного: нашли несколько мест где credentials хранились не только в конфигах приложения, но и в скриптах резервного копирования, в Ansible-inventory, в .pgpass на серверах. Dynamic secrets это не закрывает автоматически - пришлось отдельно проходить по каждому месту использования.

Аудит куда утекают credentials - отдельная работа, которую dynamic secrets мотивируют сделать, но не делают за тебя.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.