feat(security): Vault — self-hosted стор секретов (standalone + raft) #6
@@ -7,6 +7,8 @@
|
||||
/deploy.key
|
||||
/deploy.key.pub
|
||||
/flux-webhook-token.txt
|
||||
/vault-unseal-keys
|
||||
/vault-root-token
|
||||
# Template config files
|
||||
/cluster.toml
|
||||
# Kubernetes
|
||||
|
||||
@@ -14,6 +14,9 @@ mod? kube 'kubernetes'
|
||||
[group('talos')]
|
||||
mod? talos 'talos'
|
||||
|
||||
[group('security')]
|
||||
mod? vault 'vault'
|
||||
|
||||
[private]
|
||||
log lvl msg *args:
|
||||
gum log -t rfc3339 -s -l "{{ lvl }}" "{{ msg }}" {{ args }}
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
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
|
||||
Reference in New Issue
Block a user