set no-exit-message set quiet set shell := ['bash', '-euo', 'pipefail', '-c'] set script-interpreter := ['bash', '-euo', 'pipefail'] set default-list set default-script # Unseal-ключами Vault владеем вручную: файл вне git (см. .gitignore), по одному # ключу на строку, строки, начинающиеся с '#', игнорируются. # # Почему не sops: age-ключ sops лежит в самом кластере (секрет sops-age), поэтому # любой sops-файл из этого репозитория расшифрует тот, у кого есть доступ к # кластеру, и смысл ручного unseal (PVC без ключей бесполезен) пропадёт. # Копии ключей держи в менеджере паролей и на бумаге: они показываются один раз # при `vault operator init`. keys_file := env('VAULT_UNSEAL_KEYS', clean(justfile_directory() + '/vault-unseal-keys')) vault_namespace := env('VAULT_NAMESPACE', 'security') vault_pod := env('VAULT_POD', 'vault-0') # Показать состояние Vault. [doc('Показать состояние Vault (sealed/unsealed и прогресс распечатывания)')] [group('vault')] status: #!/usr/bin/env bash set -uo pipefail for cmd in kubectl jq; do command -v "$cmd" > /dev/null || { echo "vault: не найден $cmd (активируй mise в каталоге репозитория)" >&2; exit 1; } done kubectl -n "{{ vault_namespace }}" exec -i "{{ vault_pod }}" -- vault status || true # Распечатать Vault ключами из файла. [doc('Распечатать Vault ключами из файла vault-unseal-keys')] [group('vault')] unseal: #!/usr/bin/env bash set -euo pipefail keys_file="{{ keys_file }}" ns="{{ vault_namespace }}" pod="{{ vault_pod }}" for cmd in kubectl jq; do command -v "$cmd" > /dev/null || { echo "vault: не найден $cmd (активируй mise в каталоге репозитория)" >&2; exit 1; } done if ! kubectl -n "$ns" get pod "$pod" > /dev/null 2>&1; then echo "vault: под $ns/$pod недоступен — Vault не запущен или kubeconfig не тот" >&2 exit 1 fi if [[ ! -f "$keys_file" ]]; then echo "vault: нет файла с unseal-ключами: $keys_file" >&2 echo "" >&2 echo "Создай его в корне репозитория (в git он не попадёт, см. .gitignore)" >&2 echo "и положи по одному ключу на строку, например:" >&2 echo "" >&2 echo " <ключ 1 из vault operator init>" >&2 echo " <ключ 2 из vault operator init>" >&2 echo " <ключ 3 из vault operator init>" >&2 echo "" >&2 echo "Vault инициализируется с 5 долями и порогом 3, ключи показываются один раз." >&2 exit 1 fi if find "$keys_file" -perm /077 -print -quit | grep -q .; then echo "vault: у файла ключей слишком широкие права — сделай chmod 600 $keys_file" >&2 fi sealed() { kubectl -n "$ns" exec -i "$pod" -- vault status -format=json 2> /dev/null | jq -r '.sealed | tostring' || true } if [[ "$(sealed)" == "false" ]]; then echo "vault: уже распечатан, ничего не делаю" exit 0 fi used=0 while IFS= read -r key || [[ -n "$key" ]]; do key="$(printf '%s' "$key" | tr -d '[:space:]')" [[ -z "$key" || "$key" == \#* ]] && continue used=$((used + 1)) echo "vault: отправляю долю $used" kubectl -n "$ns" exec -i "$pod" -- vault operator unseal "$key" || true if [[ "$(sealed)" == "false" ]]; then echo "vault: распечатан, использовано долей: $used" break fi done < "$keys_file" if [[ "$(sealed)" != "false" ]]; then echo "vault: всё ещё запечатан — проверь, что в файле есть порог (3) верных долей" >&2 exit 1 fi kubectl -n "$ns" exec -i "$pod" -- vault status || true