feat(keycape): add netkingdom OIDC mount and bao.coulomb.social callbacks

Configure OpenBao auth for both netkingdom and keycape mounts with browser
redirect URIs; update verify scripts and runtime architecture notes.
This commit is contained in:
2026-06-18 01:23:02 +02:00
parent 8a05dd0db7
commit efbdab4652
8 changed files with 95 additions and 54 deletions

View File

@@ -24,8 +24,8 @@ Recursive trust rule: Normal tenant admin (even Coulomb) must never suffice to a
- MFA/Token: privacyIDEA (self-service enrollment for TOTP; pi-admin for setup/repair; used for assurance on privileged actions).
- OIDC Provider: KeyCape (issuer https://kc.coulomb.social; conforms to NetKingdom IAM Profile v0.2).
- KeyCape issues tokens with required claims: tenant, principal_type, groups, roles, scope/scp, assurance.
- Registered clients include: netkingdom-bootstrap-console (for console OIDC login), openbao-admin (for OpenBao OIDC auth).
- Redirects: http://localhost:8250/oidc/callback, http://127.0.0.1:8250/oidc/callback.
- Registered clients include: netkingdom-bootstrap-console (for console OIDC login), openbao-admin (for OpenBao OIDC auth).
- Redirects: http://localhost:8250/oidc/callback, http://127.0.0.1:8250/oidc/callback, https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback, https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback.
- Groups/roles for bootstrap: net-kingdom-admins (for platform-admin OpenBao policy), net-kingdom-users (for scoped non-root).
- platform-root / king credential: dedicated LLDAP user (separate from personal accounts like tegwick). Password in operator password safe; TOTP via privacyIDEA; roles include platform-root-custodian, openbao-admin, identity-admin.
@@ -56,6 +56,10 @@ Authelia acts as the SSO proxy/authenticator in lightweight mode, fronting LLDAP
- Delivery: direct clients, External Secrets Operator -> K8s Secrets, CSI mounts.
- Auth: OIDC/JWT against KeyCape (maps claims/groups to policies, e.g. platform-admin for net-kingdom-admins group).
- platform-root can obtain platform-admin policy via KeyCape/MFA (proven in 0015/0017).
- Browser operator access uses `https://bao.coulomb.social` for the OpenBao UI
and redirects to KeyCape at `kc.coulomb.social`; use auth path `netkingdom`
and role `platform-admin`, not root-token browser login. The `keycape` auth
path is retained only as a compatibility alias.
- Root token: revoked/dispositioned after init; used only for bootstrap/break-glass. Unseal keys in custody (age/SOPS protected, offline packets, king credential).
**Bootstrap to runtime transition:**
@@ -202,4 +206,4 @@ See NET-WP-0019 and sso-mfa/k8s/lldap/dry-run-nonroot-user.sh:
- NET-WP-0017, 0019 workplans + their evidence
- DECISIONS.md, ADRs (e.g. 0007, 0010), canon/standards/iam-profile_v0.2.md
This document will be updated as T03 retrospective, T05 guide, T06/T08 work, and T09 risk assessment proceed. It is the single source for "what the running system actually is" for rebuild guidance.
This document will be updated as T03 retrospective, T05 guide, T06/T08 work, and T09 risk assessment proceed. It is the single source for "what the running system actually is" for rebuild guidance.

View File

@@ -117,7 +117,7 @@ keeps KeyCape from using the admin token-list API as the MFA-required check.
Downstream applications are registered in the `clients:` block in
`keycape/create-secrets.sh`. The NetKingdom bootstrap console and Railiance
OpenBao admin CLI clients are code-defined there; operators should not create
OpenBao admin clients are code-defined there; operators should not create
those clients manually in a separate UI. After changing the block:
```bash
@@ -126,16 +126,20 @@ kubectl rollout restart deployment/keycape -n sso
```
The `openbao-admin` client is intentionally a public PKCE client for the
current local operator CLI flow. It registers the OpenBao CLI callback URIs:
current operator flow. It registers both the OpenBao CLI callback URIs and the
browser UI callbacks for `bao.coulomb.social`:
```text
http://localhost:8250/oidc/callback
http://127.0.0.1:8250/oidc/callback
https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback
```
OpenBao browser UI callbacks are not registered yet because Railiance OpenBao
currently has public ingress disabled. Add exact UI callback URIs only after
the OpenBao UI exposure model is explicitly designed.
The browser UI callback is paired with the Railiance Platform OpenBao ingress
at `https://bao.coulomb.social`. The preferred browser auth mount is
`netkingdom`; `keycape` remains a compatibility alias. Keep the localhost
callbacks unless there is a separate decision to retire CLI login.
To add or refresh only the OpenBao client in a live cluster, do not decrypt the
bootstrap secret bundle and do not re-run the full secret generator. Patch the
@@ -161,6 +165,13 @@ KeyCape:
bash ./configure-openbao-oidc.sh
```
That script registers the browser UI callbacks on the OpenBao
`auth/netkingdom/role/platform-admin` role and the compatibility
`auth/keycape/role/platform-admin` role. Browser operators should use the
OpenBao UI at `https://bao.coulomb.social`, leave namespace blank, choose
OIDC, set mount path `netkingdom`, and use role `platform-admin`; root-token
browser use is outside the approved operator path.
The script prompts for a root/sudo-capable OpenBao token inside the pod TTY.
OpenBao currently requires `oidc_client_secret` for OIDC auth config, while
KeyCape's `openbao-admin` client is public PKCE and does not validate a

View File

@@ -22,25 +22,12 @@ OPENBAO_POD="${OPENBAO_POD:-openbao-0}"
printf "\n" >&2
export BAO_TOKEN
bao auth enable -path=keycape oidc >/tmp/keycape-auth-enable.out 2>/tmp/keycape-auth-enable.err || {
if grep -q "path is already in use" /tmp/keycape-auth-enable.err; then
printf "auth/keycape already exists\n" >&2
else
cat /tmp/keycape-auth-enable.err >&2
exit 1
fi
}
# OpenBao requires oidc_client_secret for OIDC auth config. The current
# KeyCape openbao-admin profile is public PKCE and does not validate this
# downstream client-secret field, so this compatibility value is not a
# protected secret. Replace this with a real managed client secret when
# KeyCape supports confidential downstream clients.
bao write auth/keycape/config \
oidc_discovery_url="https://kc.coulomb.social" \
oidc_client_id="openbao-admin" \
oidc_client_secret="keycape-public-pkce-compatibility-value" \
default_role="platform-admin"
OPENBAO_OIDC_MOUNTS="netkingdom keycape"
# Keep array-valued groups in groups_claim/bound_claims only. OpenBao
# claim_mappings copy scalar claim values into metadata and will fail if the
@@ -53,7 +40,9 @@ OPENBAO_POD="${OPENBAO_POD:-openbao-0}"
"oidc_scopes": ["openid", "profile", "email", "groups"],
"allowed_redirect_uris": [
"http://localhost:8250/oidc/callback",
"http://127.0.0.1:8250/oidc/callback"
"http://127.0.0.1:8250/oidc/callback",
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
"https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback"
],
"bound_claims": {
"groups": ["net-kingdom-admins"]
@@ -67,7 +56,26 @@ OPENBAO_POD="${OPENBAO_POD:-openbao-0}"
}
ROLE_JSON
bao write auth/keycape/role/platform-admin @/tmp/openbao-platform-admin-role.json
rm -f /tmp/openbao-platform-admin-role.json /tmp/keycape-auth-enable.out /tmp/keycape-auth-enable.err
for mount in $OPENBAO_OIDC_MOUNTS; do
bao auth enable -path="$mount" oidc >/tmp/openbao-${mount}-auth-enable.out 2>/tmp/openbao-${mount}-auth-enable.err || {
if grep -q "path is already in use" /tmp/openbao-${mount}-auth-enable.err; then
printf "auth/%s already exists\n" "$mount" >&2
else
cat /tmp/openbao-${mount}-auth-enable.err >&2
exit 1
fi
}
bao write "auth/${mount}/config" \
oidc_discovery_url="https://kc.coulomb.social" \
oidc_client_id="openbao-admin" \
oidc_client_secret="keycape-public-pkce-compatibility-value" \
default_role="platform-admin"
bao write "auth/${mount}/role/platform-admin" @/tmp/openbao-platform-admin-role.json
printf "configured auth/%s/role/platform-admin\n" "$mount" >&2
done
rm -f /tmp/openbao-platform-admin-role.json /tmp/openbao-*-auth-enable.out /tmp/openbao-*-auth-enable.err
unset BAO_TOKEN
'

View File

@@ -122,10 +122,12 @@ clients:
grantTypes: ["authorization_code"]
clientType: "public"
- clientId: "openbao-admin"
displayName: "Railiance OpenBao Admin CLI"
displayName: "Railiance OpenBao Admin"
redirectUris:
- "http://localhost:8250/oidc/callback"
- "http://127.0.0.1:8250/oidc/callback"
- "https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback"
- "https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback"
allowedScopes: ["openid", "profile", "email", "groups"]
grantTypes: ["authorization_code"]
clientType: "public"

View File

@@ -22,10 +22,12 @@ except ImportError as exc: # pragma: no cover - operator environment guard
OPENBAO_CLIENT = {
"clientId": "openbao-admin",
"displayName": "Railiance OpenBao Admin CLI",
"displayName": "Railiance OpenBao Admin",
"redirectUris": [
"http://localhost:8250/oidc/callback",
"http://127.0.0.1:8250/oidc/callback",
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
"https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback",
],
"allowedScopes": ["openid", "profile", "email", "groups"],
"grantTypes": ["authorization_code"],

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env bash
# Patch the live KeyCape config Secret with non-secret code-defined settings:
# the OpenBao CLI client and LLDAP OU lookup paths.
# the OpenBao admin client, browser auth mount callbacks, and LLDAP OU lookup
# paths.
# This does not require decrypted bootstrap secrets and does not print existing
# Secret values.

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Verify the live KeyCape config carries the OpenBao CLI client and KeyCape is
# serving OIDC discovery after rollout.
# Verify the live KeyCape config carries the OpenBao admin client and KeyCape
# is serving OIDC discovery after rollout.
set -euo pipefail
@@ -15,30 +15,41 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PUBLIC_URL="${KEYCAPE_PUBLIC_URL:-https://kc.coulomb.social}"
PUBLIC_AUTHORIZE_URL="${PUBLIC_URL%/}/authorize"
PUBLIC_PROBE_OUTPUT=$(
curl -sS -i -G "$PUBLIC_AUTHORIZE_URL" \
--data-urlencode "client_id=openbao-admin" \
--data-urlencode "redirect_uri=http://localhost:8250/oidc/callback" \
--data-urlencode "response_type=code" \
--data-urlencode "scope=openid profile email groups" \
--data-urlencode "state=netkingdom-openbao-client-probe" \
--data-urlencode "code_challenge=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ" \
--data-urlencode "code_challenge_method=S256" \
2>&1 || true
)
if grep -q '"unknown client_id"' <<<"$PUBLIC_PROBE_OUTPUT"; then
echo "[FAIL] $PUBLIC_AUTHORIZE_URL rejects openbao-admin with unknown client_id" >&2
echo " Check DNS for kc.coulomb.social and ensure it reaches the KeyCape ingress that was patched." >&2
exit 1
fi
if ! grep -qE '^HTTP/[0-9.]+ 302 ' <<<"$PUBLIC_PROBE_OUTPUT"; then
echo "[FAIL] $PUBLIC_AUTHORIZE_URL did not return the expected OIDC redirect for openbao-admin" >&2
echo " First response:" >&2
sed -n '1,12p' <<<"$PUBLIC_PROBE_OUTPUT" >&2
exit 1
fi
echo "[PASS] public KeyCape authorize endpoint recognizes openbao-admin"
probe_redirect() {
local label="$1"
local redirect_uri="$2"
local output
output=$(
curl -sS -i -G "$PUBLIC_AUTHORIZE_URL" \
--data-urlencode "client_id=openbao-admin" \
--data-urlencode "redirect_uri=$redirect_uri" \
--data-urlencode "response_type=code" \
--data-urlencode "scope=openid profile email groups" \
--data-urlencode "state=netkingdom-openbao-client-probe" \
--data-urlencode "code_challenge=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ" \
--data-urlencode "code_challenge_method=S256" \
2>&1 || true
)
if grep -q '"unknown client_id"' <<<"$output"; then
echo "[FAIL] $PUBLIC_AUTHORIZE_URL rejects openbao-admin with unknown client_id" >&2
echo " Check DNS for kc.coulomb.social and ensure it reaches the KeyCape ingress that was patched." >&2
exit 1
fi
if ! grep -qE '^HTTP/[0-9.]+ 302 ' <<<"$output"; then
echo "[FAIL] $PUBLIC_AUTHORIZE_URL did not accept the $label redirect URI for openbao-admin" >&2
echo " Redirect URI: $redirect_uri" >&2
echo " First response:" >&2
sed -n '1,12p' <<<"$output" >&2
exit 1
fi
echo "[PASS] public KeyCape authorize endpoint accepts $label redirect"
}
probe_redirect "CLI" "http://localhost:8250/oidc/callback"
probe_redirect "browser UI netkingdom mount" "https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback"
probe_redirect "browser UI keycape compatibility mount" "https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback"
KC_POD=$("$KUBECTL" get pod -n "$NAMESPACE" \
-l app.kubernetes.io/name=keycape \

View File

@@ -187,6 +187,8 @@ if not target:
required_redirects = {
"http://localhost:8250/oidc/callback",
"http://127.0.0.1:8250/oidc/callback",
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
"https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback",
}
required_scopes = {"openid", "profile", "email", "groups"}
missing_redirects = sorted(required_redirects - set(target.get("redirectUris") or []))
@@ -200,7 +202,7 @@ if missing_redirects:
if missing_scopes:
print("openbao-admin missing scope(s): " + ", ".join(missing_scopes))
raise SystemExit(5)
print("openbao-admin client has local CLI redirects and required scopes")
print("openbao-admin client has CLI/browser redirects and required scopes")
' 2>/dev/null || echo "missing or invalid openbao-admin client")
if [[ "$OPENBAO_CLIENT_CHECK" == openbao-admin* ]]; then
pass "$OPENBAO_CLIENT_CHECK"