generated from coulomb/repo-seed
- creds-bootstrap-agent.sh: skip Phase 3 if all secrets already applied (avoids CNPG SSL connection drops from repeated reconciliation) - creds-bootstrap-agent.sh: wait for rollout to complete after restart before running enckey/admin bootstrap (fixes race with old pod) - creds-bootstrap-agent.sh: only restart privacyIDEA when Phase 3 ran - create-pi-token.sh: use env-var + retry for token fetch (no heredoc stdin; handles transient 500 from idle connection pool) - create-pi-token.sh: create keycape-pi-token K8s Secret after fetching - creds-verify.sh: map keycape-pi-token to secrets_applied.keycape (not pi_admin_created, which caused spurious Phase 5 re-runs) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
119 lines
4.4 KiB
Bash
119 lines
4.4 KiB
Bash
#!/usr/bin/env bash
|
|
# create-pi-token.sh — fetch a privacyIDEA admin JWT and store it for KeyCape
|
|
#
|
|
# Usage:
|
|
# ./create-pi-token.sh [secrets-dir]
|
|
#
|
|
# Run this script AFTER T04 bootstrap (privacyIDEA admin account created).
|
|
# It authenticates to the privacyIDEA API, fetches a long-lived admin JWT,
|
|
# and writes it to secrets/keycape/pi_admin_token.
|
|
#
|
|
# After running this script, re-run create-secrets.sh to update the
|
|
# keycape-config K8s Secret with the real token, then restart KeyCape:
|
|
# ./create-secrets.sh
|
|
# kubectl rollout restart deployment/keycape -n sso
|
|
#
|
|
# The privacyIDEA admin token does NOT expire by default (it is a permanent
|
|
# service account token). Store it in KeePassXC as:
|
|
# net-kingdom/KeyCape/pi-admin-token
|
|
#
|
|
# Requires: kubectl, curl, jq
|
|
|
|
set -euo pipefail
|
|
|
|
SECRETS_DIR="${1:-../../bootstrap/secrets}"
|
|
PI_ENV="$SECRETS_DIR/privacyidea/secrets.env"
|
|
TOKEN_FILE="$SECRETS_DIR/keycape/pi_admin_token"
|
|
|
|
if [[ ! -f "$PI_ENV" ]]; then
|
|
echo "ERROR: $PI_ENV not found — run sso-mfa/bootstrap/gen-secrets.sh first." >&2
|
|
exit 1
|
|
fi
|
|
|
|
read_env() { bash -c "source '$1' 2>/dev/null; echo \${$2}"; }
|
|
PI_ADMIN_PASSWORD=$(read_env "$PI_ENV" PI_ADMIN_PASSWORD)
|
|
|
|
if [[ -z "$PI_ADMIN_PASSWORD" ]]; then
|
|
echo "ERROR: PI_ADMIN_PASSWORD is empty in $PI_ENV" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Fetch token by exec-ing into the privacyIDEA pod (localhost call, bypasses
|
|
# NetworkPolicy which restricts external ingress to the service).
|
|
PI_POD=$(kubectl get pod -n mfa \
|
|
-l app.kubernetes.io/name=privacyidea \
|
|
--field-selector=status.phase=Running \
|
|
-o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
|
|
|
|
if [[ -z "$PI_POD" ]]; then
|
|
# Fall back to public hostname if pod is not directly accessible
|
|
PI_POD=""
|
|
fi
|
|
|
|
echo "Fetching privacyIDEA admin token..."
|
|
|
|
if [[ -n "$PI_POD" ]]; then
|
|
echo " Method: kubectl exec into $PI_POD (avoids NetworkPolicy restriction)"
|
|
# Pass the password via env var. Retry up to 3 times — the PostgreSQL connection
|
|
# pool can return 500 if the idle SSL connection was dropped; it recovers on retry.
|
|
TOKEN=""
|
|
for _ATTEMPT in 1 2 3; do
|
|
TOKEN=$(kubectl exec -n mfa "$PI_POD" -- \
|
|
env PI_ADMIN_PASSWORD="${PI_ADMIN_PASSWORD}" \
|
|
python3 -c '
|
|
import urllib.request, urllib.parse, json, os, sys
|
|
pw = os.environ["PI_ADMIN_PASSWORD"]
|
|
data = urllib.parse.urlencode({"username": "pi-admin", "password": pw}).encode()
|
|
req = urllib.request.Request("http://localhost:8080/auth", data=data)
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=10) as r:
|
|
body = json.load(r)
|
|
print(body["result"]["value"]["token"])
|
|
except Exception as e:
|
|
print(str(e), file=__import__("sys").stderr)
|
|
sys.exit(1)
|
|
' 2>/dev/null || echo "")
|
|
if [[ -n "$TOKEN" ]]; then break; fi
|
|
echo " Attempt $_ATTEMPT failed (likely transient DB connection drop) — retrying in 5s..."
|
|
sleep 5
|
|
done
|
|
else
|
|
echo " Method: public URL https://pink.coulomb.social"
|
|
TOKEN=$(curl -sf \
|
|
-X POST "https://pink.coulomb.social/auth" \
|
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
-d "username=pi-admin&password=${PI_ADMIN_PASSWORD}" \
|
|
| python3 -c "import sys,json; data=json.load(sys.stdin); print(data['result']['value']['token'])" \
|
|
2>/dev/null || echo "")
|
|
fi
|
|
|
|
if [[ -z "$TOKEN" ]]; then
|
|
echo "ERROR: failed to fetch token from privacyIDEA." >&2
|
|
echo " Verify that privacyIDEA is Running and the pi-admin account exists." >&2
|
|
echo " Check: kubectl logs -n mfa \$(kubectl get pod -n mfa -l app.kubernetes.io/name=privacyidea -o name | head -1)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$(dirname "$TOKEN_FILE")"
|
|
echo -n "$TOKEN" > "$TOKEN_FILE"
|
|
chmod 600 "$TOKEN_FILE"
|
|
|
|
# Create the keycape-pi-token K8s Secret (KeyCape reads it at startup)
|
|
echo "Creating K8s Secret: keycape-pi-token (namespace: sso)"
|
|
kubectl create secret generic keycape-pi-token \
|
|
--namespace=sso \
|
|
--from-literal=token="$TOKEN" \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
echo " Done."
|
|
|
|
echo ""
|
|
echo "Token written to: $TOKEN_FILE"
|
|
echo "Token preview : ${TOKEN:0:32}…"
|
|
echo ""
|
|
echo "IMPORTANT: Store this token in KeePassXC → net-kingdom/KeyCape/pi-admin-token"
|
|
echo " as a password entry. It cannot be recovered without re-authenticating."
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Re-run create-secrets.sh to update keycape-config with the real token."
|
|
echo " 2. Restart KeyCape: kubectl rollout restart deployment/keycape -n sso"
|