config-hosteurope names port 16443, but the k3s-api-railiance01 ops-bridge tunnel listens on 16444, so every default target failed with connection refused. Switch Makefile, tools and docs to ~/.kube/config-railiance01 and fix the restore recipe to rewrite the API address to the tunnel port. The node-side hostPath in STATE.md is unrelated and unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 1031639@bnt-lap001 Assistant-Session: e5073fee-f4ae-4fc3-a75a-6c1fc83e0576
58 lines
2.6 KiB
Bash
Executable file
58 lines
2.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Seal the Forgejo break-glass administrator (RAPPS-WP-0016-T01).
|
|
# Founder-run, interactive. Generates a fresh password for the local site admin,
|
|
# stores it only in OpenBao (coulombcore), then sets it in Forgejo. The value is
|
|
# never printed, logged or written to disk.
|
|
set -euo pipefail
|
|
|
|
BREAKGLASS_USER="${FORGEJO_BREAKGLASS_USER:-forgejo_admin}"
|
|
BREAKGLASS_PATH="${FORGEJO_BREAKGLASS_PATH:-platform/workloads/forgejo/break-glass-admin}"
|
|
BAO_ADDR="${BAO_ADDR:-https://bao.coulomb.social}"
|
|
FORGEJO_KUBECONFIG="${FORGEJO_KUBECONFIG:-$HOME/.kube/config-railiance01}"
|
|
FORGEJO_NAMESPACE="${FORGEJO_NAMESPACE:-forgejo}"
|
|
|
|
for cmd in bao kubectl openssl; do
|
|
command -v "$cmd" >/dev/null 2>&1 || { echo "ERROR: $cmd not found" >&2; exit 1; }
|
|
done
|
|
|
|
if [[ -z "${BAO_TOKEN:-}" ]]; then
|
|
read -r -s -p "OpenBao operator token (coulombcore / bao.coulomb.social): " BAO_TOKEN
|
|
echo >&2
|
|
fi
|
|
[[ -n "${BAO_TOKEN:-}" ]] || { echo "ERROR: empty OpenBao token" >&2; exit 1; }
|
|
export BAO_ADDR BAO_TOKEN
|
|
|
|
if curl -fsS "$BAO_ADDR/v1/sys/health" | grep -q '"sealed":true'; then
|
|
echo "ERROR: OpenBao at $BAO_ADDR reports sealed" >&2
|
|
exit 1
|
|
fi
|
|
|
|
pod="$(KUBECONFIG="$FORGEJO_KUBECONFIG" kubectl -n "$FORGEJO_NAMESPACE" get pod \
|
|
-l app.kubernetes.io/name=gitea -o jsonpath='{.items[0].metadata.name}')"
|
|
[[ -n "$pod" ]] || { echo "ERROR: no Forgejo pod in $FORGEJO_NAMESPACE" >&2; exit 1; }
|
|
|
|
echo "Break-glass user: $BREAKGLASS_USER (site admin, local sign-in)"
|
|
echo "Custody path: $BREAKGLASS_PATH (field PASSWORD) on $BAO_ADDR"
|
|
echo "Forgejo pod: $pod"
|
|
read -r -p "Rotate the break-glass password now? [y/N] " answer
|
|
[[ "$answer" == "y" ]] || { echo "Aborted; nothing changed."; exit 1; }
|
|
|
|
password="$(openssl rand -base64 48 | tr -d '\n/+=' | cut -c1-40)"
|
|
|
|
# Store first: if this fails, Forgejo still has the old password.
|
|
printf '%s' "$password" | bao kv put "$BREAKGLASS_PATH" \
|
|
PASSWORD=- USERNAME="$BREAKGLASS_USER" ROTATED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)" >/dev/null
|
|
stored="$(bao kv get -field=PASSWORD "$BREAKGLASS_PATH")"
|
|
[[ "$stored" == "$password" ]] || { echo "ERROR: OpenBao read-back mismatch; Forgejo unchanged" >&2; exit 1; }
|
|
unset stored
|
|
|
|
# Password goes to the pod on stdin so it never appears in process arguments
|
|
# on the workstation.
|
|
printf '%s\n' "$password" | KUBECONFIG="$FORGEJO_KUBECONFIG" kubectl -n "$FORGEJO_NAMESPACE" \
|
|
exec -i "$pod" -c gitea -- sh -c \
|
|
'read -r p; forgejo admin user change-password --username "$0" --password "$p" --must-change-password=false >/dev/null' \
|
|
"$BREAKGLASS_USER"
|
|
unset password
|
|
|
|
echo "Sealed: $BREAKGLASS_USER password rotated and stored at $BREAKGLASS_PATH."
|
|
echo "Next: make forgejo-breakglass-verify"
|