RAPPS-WP-0016-T01: founder-run targets rotate the forgejo_admin password into OpenBao (platform/workloads/forgejo/break-glass-admin) and record a value-free sign-in. T01 stays in progress until the founder runs them. 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
59 lines
2.4 KiB
Bash
Executable file
59 lines
2.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Verify one break-glass sign-in (RAPPS-WP-0016-T01) and write value-free evidence.
|
|
# Founder-run. Reads the password from OpenBao into memory, authenticates against
|
|
# the Forgejo API with local credentials, and records only the outcome.
|
|
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_BASE_URL="${FORGEJO_BASE_URL:-https://forgejo.coulomb.social}"
|
|
EVIDENCE="${FORGEJO_BREAKGLASS_EVIDENCE:-docs/evidence/$(date -u +%Y-%m-%d)-forgejo-breakglass-signin.json}"
|
|
|
|
command -v bao >/dev/null 2>&1 || { echo "ERROR: bao CLI not found" >&2; exit 1; }
|
|
if [[ -z "${BAO_TOKEN:-}" ]]; then
|
|
read -r -s -p "OpenBao operator token (coulombcore / bao.coulomb.social): " BAO_TOKEN
|
|
echo >&2
|
|
fi
|
|
export BAO_ADDR BAO_TOKEN
|
|
|
|
password="$(bao kv get -field=PASSWORD "$BREAKGLASS_PATH")"
|
|
version="$(bao kv metadata get -format=json "$BREAKGLASS_PATH" \
|
|
| python3 -c 'import json,sys; print(json.load(sys.stdin)["data"]["current_version"])')"
|
|
|
|
# Credentials reach curl through a config on stdin, not argv.
|
|
body_file="$(mktemp)"
|
|
trap 'rm -f "$body_file"' EXIT
|
|
code="$(printf 'user = "%s:%s"\n' "$BREAKGLASS_USER" "$password" \
|
|
| curl -sS -K - -o "$body_file" -w '%{http_code}' "$FORGEJO_BASE_URL/api/v1/user")"
|
|
unset password
|
|
|
|
python3 - "$code" "$BREAKGLASS_USER" "$BREAKGLASS_PATH" "$version" "$FORGEJO_BASE_URL" \
|
|
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$EVIDENCE" "$body_file" <<'EOF'
|
|
import json, sys
|
|
code, user, path, version, base, at, out, body = sys.argv[1:]
|
|
try:
|
|
me = json.load(open(body))
|
|
except ValueError:
|
|
me = {}
|
|
ok = code == "200" and me.get("login") == user and me.get("is_admin") is True
|
|
record = {
|
|
"schema": "railiance-apps.forgejo-breakglass-signin.v1",
|
|
"task": "RAPPS-WP-0016-T01",
|
|
"forgejo": base,
|
|
"account": user,
|
|
"auth": "local password (API basic auth)",
|
|
"http_status": int(code) if code.isdigit() else code,
|
|
"login_matches": me.get("login") == user,
|
|
"is_admin": me.get("is_admin"),
|
|
"custody": {"openbao_path": path, "field": "PASSWORD", "version": version},
|
|
"verified_at": at,
|
|
"result": "pass" if ok else "fail",
|
|
"values_emitted": False,
|
|
}
|
|
with open(out, "w") as f:
|
|
json.dump(record, f, indent=2)
|
|
f.write("\n")
|
|
print(f"{record['result']}: {user} sign-in (HTTP {code}); evidence {out}")
|
|
sys.exit(0 if ok else 1)
|
|
EOF
|