109 lines
4.3 KiB
Bash
Executable File
109 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
#MISE description="Токен Terraform для Vault, проверка политик, отзыв root-токена (нужен ROOT_TOKEN)"
|
||
set -euo pipefail
|
||
|
||
MOUNT=$VAULT_MOUNT
|
||
ENV_FILE=.env
|
||
KEYS_FILE=vault_keys.txt
|
||
REVOKE_ROOT=false
|
||
[[ "${1:-}" == "--revoke-root" ]] && REVOKE_ROOT=true
|
||
|
||
bao_as() {
|
||
local token=$1
|
||
shift
|
||
docker compose exec -T -e BAO_TOKEN="$token" vault bao "$@"
|
||
}
|
||
|
||
step() { printf '\n== %s\n' "$*"; }
|
||
ok() { printf ' ok: %s\n' "$*"; }
|
||
fail() {
|
||
printf ' ОШИБКА: %s\n' "$*" >&2
|
||
exit 1
|
||
}
|
||
|
||
ROOT_TOKEN=${ROOT_TOKEN:-$(sed -n 's/^Initial Root Token: *//p' "$KEYS_FILE" 2>/dev/null || true)}
|
||
[[ -n "$ROOT_TOKEN" ]] || fail "нет root-токена: задайте ROOT_TOKEN или положите $KEYS_FILE"
|
||
bao_as "$ROOT_TOKEN" token lookup >/dev/null 2>&1 || fail "root-токен не принят (отозван или Vault запечатан)"
|
||
|
||
step "Шаг 1. Токен для Terraform"
|
||
TF_TOKEN=${VAULT_TOKEN:-}
|
||
if [[ -n "$TF_TOKEN" ]] && bao_as "$TF_TOKEN" token lookup -format=json 2>/dev/null |
|
||
jq -e '.data.policies | index("terraform")' >/dev/null; then
|
||
ok "в $ENV_FILE уже есть рабочий токен, новый не создаю"
|
||
else
|
||
TF_TOKEN=$(bao_as "$ROOT_TOKEN" token create -orphan -policy=terraform -period=720h \
|
||
-display-name=terraform -format=json | jq -r .auth.client_token)
|
||
[[ -n "$TF_TOKEN" && "$TF_TOKEN" != null ]] || fail "токен не создан"
|
||
sed -i '/^VAULT_TOKEN=/d' "$ENV_FILE"
|
||
printf 'VAULT_TOKEN=%s\n' "$TF_TOKEN" >>"$ENV_FILE"
|
||
ok "токен создан и записан в $ENV_FILE как VAULT_TOKEN"
|
||
fi
|
||
bao_as "$TF_TOKEN" token lookup -format=json |
|
||
jq -r '.data | " политики: \(.policies | join(", ")); orphan: \(.orphan); period: \(.period)s"'
|
||
|
||
step "Шаг 2. terraform plan в vault/ с новым токеном"
|
||
set +e
|
||
TF_VAR_vault_token=$TF_TOKEN terraform -chdir=vault plan -input=false -no-color -detailed-exitcode >/tmp/vault-plan.$$ 2>&1
|
||
PLAN_EXIT=$?
|
||
set -e
|
||
case $PLAN_EXIT in
|
||
0) ok "No changes: политики terraform хватает" ;;
|
||
2)
|
||
grep -E '^ # |^Plan:' /tmp/vault-plan.$$ || true
|
||
rm -f /tmp/vault-plan.$$
|
||
fail "plan хочет что-то изменить, прочитайте его: terraform -chdir=vault plan"
|
||
;;
|
||
*)
|
||
grep -vE 'Refreshing state' /tmp/vault-plan.$$ | tail -30
|
||
rm -f /tmp/vault-plan.$$
|
||
fail "plan упал, скорее всего в политике terraform не хватает пути из ошибки выше"
|
||
;;
|
||
esac
|
||
rm -f /tmp/vault-plan.$$
|
||
|
||
step "Шаг 3. Границы сервисов через AppRole"
|
||
ROLES_JSON=$(terraform -chdir=vault output -json approle_role_ids)
|
||
mapfile -t SERVICES < <(jq -r 'keys[]' <<<"$ROLES_JSON")
|
||
|
||
cleanup() {
|
||
for svc in "${SERVICES[@]}"; do
|
||
bao_as "$ROOT_TOKEN" kv metadata delete -mount="$MOUNT" "$svc/bootstrap-check" >/dev/null 2>&1 || true
|
||
done
|
||
}
|
||
trap cleanup EXIT
|
||
|
||
for svc in "${SERVICES[@]}"; do
|
||
bao_as "$ROOT_TOKEN" kv put -mount="$MOUNT" "$svc/bootstrap-check" ok=1 >/dev/null
|
||
done
|
||
|
||
for svc in "${SERVICES[@]}"; do
|
||
role_id=$(jq -r --arg s "$svc" '.[$s]' <<<"$ROLES_JSON")
|
||
secret_id=$(bao_as "$ROOT_TOKEN" write -f -field=secret_id "auth/approle/role/$svc/secret-id")
|
||
svc_token=$(bao_as "$ROOT_TOKEN" write -field=token auth/approle/login role_id="$role_id" secret_id="$secret_id")
|
||
|
||
for target in "${SERVICES[@]}"; do
|
||
set +e
|
||
out=$(bao_as "$svc_token" kv get -mount="$MOUNT" "$target/bootstrap-check" 2>&1)
|
||
rc=$?
|
||
set -e
|
||
if [[ "$target" == "$svc" ]]; then
|
||
[[ $rc -eq 0 ]] || fail "$svc не читает свой путь $MOUNT/$target: $out"
|
||
ok "$svc читает $MOUNT/$target/*"
|
||
else
|
||
[[ $rc -ne 0 && "$out" == *"permission denied"* ]] || fail "$svc прочитал чужой путь $MOUNT/$target"
|
||
ok "$svc получает permission denied на $MOUNT/$target/*"
|
||
fi
|
||
done
|
||
bao_as "$svc_token" token revoke -self >/dev/null
|
||
done
|
||
|
||
step "Шаг 4. Root-токен"
|
||
if $REVOKE_ROOT; then
|
||
cleanup
|
||
trap - EXIT
|
||
bao_as "$ROOT_TOKEN" token revoke -self >/dev/null
|
||
ok "root-токен отозван; новый выпускается через bao operator generate-root с unseal-ключом"
|
||
else
|
||
ok "не отозван (запустите с --revoke-root, когда будете готовы)"
|
||
fi
|