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

@@ -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"