С приходом контейнеров и оркестрации появилась проблема: где безопасно хранить секреты? На рынке достаточно решений - Sealed Secrets, Kubernetes Secrets, Infisical - но сейчас стандартом остаётся HashiCorp Vault. Vault - это не просто хранилище паролей. Это централизованное управление секретами с динамической выдачей секретов, гибкими политиками доступа и полным аудитом.
Но и у Vault есть свои проблемы: высокий порог входа, настройка политик доступа и самая большая боль - unseal. Дело в том, что Vault при запуске находится в sealed-состоянии и не отдаёт секреты, пока его не разблокируют. Перезагрузился под, упала VM - и все сервисы, зависящие от Vault, перестанут запускаться (уже запущенные, кстати, продолжат работать). Вопрос в том, как это решать правильно.
Самый простой способ хранить unseal-ключи - записать на листочек и спрятать в сейф. Но как передать ключи коллеге? Что если единственный носитель ключа уволился, ушёл в отпуск или просто недоступен в 3 часа ночи? Это классическая единая точка отказа - и строить на ней безопасность продакшена как минимум рискованно, а как максимум приведёт к потере секретов навечно.
Решение этой проблемы - auto-unseal через облачные KMS: AWS KMS, GCP Cloud KMS, Azure Key Vault. Сервисы берут на себя расшифровку мастер-ключа при старте Vault, и ручное вмешательство больше не нужно. Но что если вы работаете только в on-premise и отправлять даже ключи шифрования во внешние сервисы - не вариант?
Можно поднять второй Vault, который будет unseалить первый через Transit. Рабочая схема, но это дополнительная инфраструктура, которую тоже нужно обслуживать и мониторить. А если хочется полной отказоустойчивости, цепочка Vault'ов начинает расти.
Я столкнулся с этой проблемой на практике: Kubernetes-кластер, тысяча секретов, десять проектов - и только on-premise.
Наше решение - автоматический unseal внутри кластера. В pod template Vault добавлен lifecycle hook PostStart, который при старте выполняет vault operator unseal тремя ключами Shamir.
Вот как это выглядит:
extraSecretEnvironmentVars:
- envName: VAULT_UNSEAL_KEY_1
secretName: vault-unseal-keys
secretKey: VAULT_UNSEAL_KEY_1
- envName: VAULT_UNSEAL_KEY_2
secretName: vault-unseal-keys
secretKey: VAULT_UNSEAL_KEY_2
- envName: VAULT_UNSEAL_KEY_3
secretName: vault-unseal-keys
secretKey: VAULT_UNSEAL_KEY_3
postStart:
- /bin/sh
- -c
- |
sleep 5;
for i in $(seq 1 30); do
vault status >/dev/null 2>&1;
[ $? -ne 1 ] && break;
sleep 2;
done;
vault operator unseal "$VAULT_UNSEAL_KEY_1" >/dev/null 2>&1;
vault operator unseal "$VAULT_UNSEAL_KEY_2" >/dev/null 2>&1;
vault operator unseal "$VAULT_UNSEAL_KEY_3" >/dev/null 2>&1;Параллельно работает sidecar-контейнер, который мониторит состояние Vault - и если тот уходит в sealed, повторяет unseal без перезагрузки пода. Мы реализовали это так:
extraContainers:
- name: auto-unseal
image: hashicorp/vault:1.18
command:
- /bin/sh
- -c
- |
export VAULT_ADDR=http://127.0.0.1:8200
echo "Auto-unseal watcher started"
while true; do
vault status >/dev/null 2>&1
if [ $? -eq 2 ]; then
echo "$(date) Vault is sealed, unsealing..."
vault operator unseal "$VAULT_UNSEAL_KEY_1" >/dev/null 2>&1
vault operator unseal "$VAULT_UNSEAL_KEY_2" >/dev/null 2>&1
vault operator unseal "$VAULT_UNSEAL_KEY_3" >/dev/null 2>&1
echo "$(date) Unseal complete"
fi
sleep 10
done
env:
- name: VAULT_UNSEAL_KEY_1
valueFrom:
secretKeyRef:
name: vault-unseal-keys
key: VAULT_UNSEAL_KEY_1
- name: VAULT_UNSEAL_KEY_2
valueFrom:
secretKeyRef:
name: vault-unseal-keys
key: VAULT_UNSEAL_KEY_2
- name: VAULT_UNSEAL_KEY_3
valueFrom:
secretKeyRef:
name: vault-unseal-keys
key: VAULT_UNSEAL_KEY_3
resources:
requests:
memory: 16Mi
cpu: 10m
limits:
memory: 64Mi
cpu: 50mКонтейнер потребляет минимум ресурсов, но всегда отработает - ничего кроме проверки он не делает.
Сами unseal-ключи хранятся в кластере как Sealed Secrets. Доступ к namespace Vault закрыт строгим RBAC - credentials выдаются вручную ограниченному кругу инженеров и только на ограниченное время. Без явного разрешения ни увидеть, ни расшифровать ключи невозможно.
Я уверен, что можно придумать способ лучше - но для on-premise без внешнего KMS это решение, которое работает в проде уже не первый месяц. Vault перезагружается, unseалится сам, сервисы не замечают даунтайма.
Good Luck & Have DevOps fun.