Fix vault-restore: authenticate raft snapshot restore with a real token

raft snapshot restore is a privileged operation - unsealing alone isn't
enough, it needs an authenticated token. Missed this on first pass, caught
live: 'Code: 403. Errors: * permission denied'.

First run: uses the throwaway init's own fresh root token. Steady state
(already restored at least once): uses a copy of home's real root token,
which becomes valid on this Vault the moment the first restore completes
(its auth data becomes byte-for-byte home's as of that snapshot). Stored
the same way as the unseal key - kubectl directly on the VPS, not git.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Scooby Husky
2026-08-17 21:46:23 -05:00
co-authored by Claude Sonnet 5
parent 62751f7d75
commit e2bdab8940
@@ -71,11 +71,25 @@ data:
# the chart's default "vault-0". # the chart's default "vault-0".
VAULT_POD="vps-vault-0" VAULT_POD="vps-vault-0"
UNSEAL_KEY="$(kubectl -n vault get secret vault-unseal-key -o jsonpath='{.data.key}' | base64 -d)" UNSEAL_KEY="$(kubectl -n vault get secret vault-unseal-key -o jsonpath='{.data.key}' | base64 -d)"
# Home's real root token - after the first successful restore, this
# Vault's auth data is byte-for-byte home's (as of that snapshot), so
# home's root token is valid here too. Needed to authenticate
# `raft snapshot restore`, which is a privileged operation - unseal
# alone isn't enough. Stored the same way as the unseal key (kubectl,
# not git):
# kubectl -n vault create secret generic vault-root-token \
# --from-literal=token=<home's VAULT_ROOT_TOKEN>
HOME_ROOT_TOKEN="$(kubectl -n vault get secret vault-root-token -o jsonpath='{.data.token}' | base64 -d)"
RESTORE_TOKEN=""
vault_exec() { vault_exec() {
kubectl -n vault exec -i "$VAULT_POD" -- env VAULT_ADDR=http://127.0.0.1:8200 vault "$@" kubectl -n vault exec -i "$VAULT_POD" -- env VAULT_ADDR=http://127.0.0.1:8200 vault "$@"
} }
vault_exec_auth() {
kubectl -n vault exec -i "$VAULT_POD" -- env VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN="$RESTORE_TOKEN" vault "$@"
}
echo "==> Checking Vault status..." echo "==> Checking Vault status..."
STATUS_JSON="$(vault_exec status -format=json 2>&1 || true)" STATUS_JSON="$(vault_exec status -format=json 2>&1 || true)"
INITIALIZED="$(echo "$STATUS_JSON" | jq -r '.initialized // empty' 2>/dev/null || echo "")" INITIALIZED="$(echo "$STATUS_JSON" | jq -r '.initialized // empty' 2>/dev/null || echo "")"
@@ -85,14 +99,20 @@ data:
echo "==> First run: initializing with a throwaway single-key seal..." echo "==> First run: initializing with a throwaway single-key seal..."
INIT_JSON="$(vault_exec operator init -key-shares=1 -key-threshold=1 -format=json)" INIT_JSON="$(vault_exec operator init -key-shares=1 -key-threshold=1 -format=json)"
THROWAWAY_KEY="$(echo "$INIT_JSON" | jq -r '.unseal_keys_b64[0]')" THROWAWAY_KEY="$(echo "$INIT_JSON" | jq -r '.unseal_keys_b64[0]')"
RESTORE_TOKEN="$(echo "$INIT_JSON" | jq -r '.root_token')"
echo "==> Unsealing with throwaway key for first restore..." echo "==> Unsealing with throwaway key for first restore..."
vault_exec operator unseal "$THROWAWAY_KEY" >/dev/null vault_exec operator unseal "$THROWAWAY_KEY" >/dev/null
unset THROWAWAY_KEY INIT_JSON unset THROWAWAY_KEY INIT_JSON
elif [ "$SEALED" = "true" ]; then
echo "==> Sealed - unsealing with the stored home unseal key..."
vault_exec operator unseal "$UNSEAL_KEY" >/dev/null
else else
echo "==> Already unsealed." # Already restored at least once before - home's root token is valid
# here regardless of seal state, since it's restored FROM home.
RESTORE_TOKEN="$HOME_ROOT_TOKEN"
if [ "$SEALED" = "true" ]; then
echo "==> Sealed - unsealing with the stored home unseal key..."
vault_exec operator unseal "$UNSEAL_KEY" >/dev/null
else
echo "==> Already unsealed."
fi
fi fi
echo "==> Installing mc (MinIO client)..." echo "==> Installing mc (MinIO client)..."
@@ -117,8 +137,7 @@ data:
kubectl -n vault cp "/tmp/${LATEST}" "${VAULT_POD}:/tmp/${LATEST}" kubectl -n vault cp "/tmp/${LATEST}" "${VAULT_POD}:/tmp/${LATEST}"
echo "==> Restoring raft snapshot (this replaces all data + the keyring)..." echo "==> Restoring raft snapshot (this replaces all data + the keyring)..."
kubectl -n vault exec -i "$VAULT_POD" -- env VAULT_ADDR=http://127.0.0.1:8200 \ vault_exec_auth operator raft snapshot restore -force "/tmp/${LATEST}"
vault operator raft snapshot restore -force "/tmp/${LATEST}"
kubectl -n vault exec "$VAULT_POD" -- rm -f "/tmp/${LATEST}" kubectl -n vault exec "$VAULT_POD" -- rm -f "/tmp/${LATEST}"
echo "==> Restarting Vault to fully reload post-restore state..." echo "==> Restarting Vault to fully reload post-restore state..."