Files
mdm-ordinis/docs/ops/vault-unseal.md
T
Zimin A.N. 0fb20dc8d4 docs(ops): vault unseal runbook + auto-unseal migration plan
Triage card для on-call: где взять unseal-keys (1Password placeholder,
уточнить с DevOps lead), как запустить script либо ручную процедуру,
что проверить после, troubleshooting типовых проблем.

Также: §7 — план миграции на auto-unseal (Transit / Yandex KMS) когда
определимся с master Vault или KMS-backend.
2026-05-05 16:56:45 +03:00

139 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Runbook: Vault unseal (staging / prod)
**Когда использовать:** Vault на кластере залип в `Sealed=true` после reboot
ноды или manual seal. Признаки:
- Spring Boot pod'ы (`ordinis-app`, `ordinis-read-api`, `ordinis-projection-writer`)
стоят в `Init:0/1`.
- В логах `vault-agent-init` контейнера: `Vault is sealed`, error 503 на
`auth/kubernetes/login`.
- `kubectl exec -n config vault-0 -- vault status``Sealed: true`.
**Auto-unseal сейчас НЕ настроен** (v1 closure backlog: Transit secret engine
с master Vault либо Yandex Cloud KMS). До миграции — ручная процедура ниже.
---
## 1. Где взять unseal-ключи
Ключи Shamir, threshold **3 из 5**. Хранение:
- **Corporate 1Password** — vault `Ordinis НСИ ЦУОД`:
- `vault-staging-unseal-keys` — для cluster.265 (192.168.100.161)
- `vault-prod-unseal-keys` — для cluster.142 (192.168.100.142)
- **Резерв** (cold storage) — у DevOps lead в KeePass.
Доступ к 1Password vault — у списка инженеров с on-call. При расширении —
добавлять через 1Password admin. Никогда не пересылать ключи в Slack /
Telegram / email.
> ⚠️ **При утере 5 из 5 ключей:** Vault инициализируется заново → ВСЕ
> secrets потеряны (postgres passwords, Kafka SASL, Keycloak signing keys
> в K8s secrets — отдельно). Plan: получить root token + replay `setup.sh` +
> заново положить secrets из 1Password (Postgres backup + Vault: писать
> только в зеркало). Делать только с письменным разрешением tech lead.
## 2. Быстрый unseal (script)
```bash
cd ~/code/ordinis-infra/k8s/vault
# staging
./unseal.sh staging "<KEY_1>" "<KEY_2>" "<KEY_3>"
# prod
./unseal.sh prod "<KEY_1>" "<KEY_2>" "<KEY_3>"
```
Скрипт делает:
1. Pull kubeconfig с ноды (если ещё не скачан).
2. Проверяет `vault status` — если уже unsealed, выходит.
3. Применяет 3 ключа последовательно через `vault operator unseal`.
4. Проверяет `Sealed: false`.
5. Force-deletes Spring Boot pod'ы — vault-agent-init иначе ждёт backoff
до 3+ минут на следующую попытку auth.
После — `kubectl get pods -n ordinis-{env}` показывает `2/2 Running`
через ~2 минуты.
## 3. Ручной unseal (если script недоступен)
```bash
# 1. Получить kubeconfig
ssh root@192.168.100.161 "cat /etc/kubernetes/admin.conf" > /tmp/kc-161
sed -i '' 's|server: https://[^:]*:|server: https://192.168.100.161:|' /tmp/kc-161
export KUBECONFIG=/tmp/kc-161
# 2. Проверить статус
kubectl exec -n config vault-0 -- vault status
# 3. Применить 3 ключа (по одному)
kubectl exec -n config vault-0 -- vault operator unseal '<KEY_1>'
kubectl exec -n config vault-0 -- vault operator unseal '<KEY_2>'
kubectl exec -n config vault-0 -- vault operator unseal '<KEY_3>'
# 4. Убедиться Sealed: false
kubectl exec -n config vault-0 -- vault status
# 5. Перезапустить Spring Boot pod'ы
kubectl delete pod -n ordinis-staging \
-l 'app.kubernetes.io/name in (ordinis-app,ordinis-read-api,ordinis-projection-writer)' \
--grace-period=0 --force
# 6. Проверить
kubectl get pods -n ordinis-staging
```
## 4. Подтверждение что всё работает
```bash
# read-api
curl -sk --resolve ordinis.k8s.265.nstart.cloud:443:192.168.100.161 \
https://ordinis.k8s.265.nstart.cloud/api/v1/spacecraft/records?lang=ru-RU \
| jq 'length'
# → должно вернуть число > 0
# admin
curl -sk -o /dev/null -w "%{http_code}\n" \
--resolve ordinis.k8s.265.nstart.cloud:443:192.168.100.161 \
"https://ordinis.k8s.265.nstart.cloud/api/v1/admin/audit?page=0&size=5"
# → 200
```
## 5. Эскалация / проблемы
| Симптом | Что делать |
|------------------------------------------------------|--|
| `vault status``connection refused` | Pod `vault-0` не Running. `kubectl get pods -n config` + `kubectl describe`. |
| `unseal` принимает ключ но Sealed остаётся true | Проверить count `Unseal Progress` — может нужен 4-й ключ если threshold увеличили. |
| Все 3 ключа отвергает (`invalid key`) | Whitespace / обрезанные символы. Скопировать заново из 1Password (полное поле). |
| Pod'ы Spring Boot не поднимаются после unseal | `kubectl logs <pod> -c vault-agent-init` — могут быть policy/role миграции после изменений. |
| Consul backend (Vault использует Consul) недоступен | `kubectl get pods -n config consul-server-0` + `kubectl logs`. |
При 30+ минут downtime → эскалация tech lead → решение о переносе
prod-нагрузки на запасной кластер либо публичная коммуникация в SLA-канал.
## 6. Связанные документы
- Установка Vault с нуля: `ordinis-infra/k8s/vault/setup.sh`
- Архитектура auth: `ordinis-auth/.../{ScopeContext,SecurityConfig}.java`
- Список секретов в Vault: `secret/ordinis/cuod/` (postgres, kafka, redis)
## 7. Переход к auto-unseal (TODO для v1 closure)
Когда будет master Vault либо Yandex Cloud KMS:
1. Добавить `seal "transit"` либо `seal "yandexcloudkms"` stanza в vault config
ConfigMap (`config/vault-config`).
2. Запустить migration:
```
kubectl exec -n config vault-0 -- vault operator unseal -migrate KEY_1
kubectl exec -n config vault-0 -- vault operator unseal -migrate KEY_2
kubectl exec -n config vault-0 -- vault operator unseal -migrate KEY_3
```
3. После миграции существующие Shamir-keys больше не работают — выдаются
Recovery Keys (5 шт, тот же threshold 3) для emergency rekey/operator-init
операций.
4. Обновить этот runbook: основной случай — auto-unseal, ручной unseal
только при downtime master Vault / KMS.