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