Retire bao.coulomb.social callbacks from the KeyCape openbao-admin client
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 4s

The name was retired on 2026-09-15. Remove both public UI callbacks from
the code-defined client (openbao-client-config.py, create-secrets.sh), stop
probing them in verify-openbao-client.sh, and fail verification in
openbao-client-config.py and verify-t07.sh if either is registered again.
The CLI and operator-tunneled 127.0.0.1:18200 callbacks remain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 299762@bnt-lap001
Assistant-Session: d3d3cea1-869c-44f1-be2a-3d6d3550e72e
This commit is contained in:
tegwick 2026-09-23 19:41:35 +02:00
parent a356f640ac
commit 3b14469afb
6 changed files with 51 additions and 18 deletions

View file

@ -128,22 +128,21 @@ kubectl rollout restart deployment/keycape -n sso
``` ```
The `openbao-admin` client is intentionally a public PKCE client for the The `openbao-admin` client is intentionally a public PKCE client for the
current operator flow. It registers both the OpenBao CLI callback URIs and the current operator flow. It registers the OpenBao CLI callback URIs and the
browser UI callbacks for `bao.coulomb.social`: operator-tunneled browser UI callback:
```text ```text
http://localhost:8250/oidc/callback http://localhost:8250/oidc/callback
http://127.0.0.1:8250/oidc/callback http://127.0.0.1:8250/oidc/callback
http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback
https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback
``` ```
The preferred browser UI callback is paired with the named Railiance Platform The browser UI callback is paired with the named Railiance Platform operator
operator tunnel at `http://127.0.0.1:18200`. The public callback remains during tunnel at `http://127.0.0.1:18200`. The public `bao.coulomb.social` callbacks
the bounded listener rollback window. The preferred browser auth mount is were retired with that name on 2026-09-15; `openbao-client-config.py` and
`netkingdom`; `keycape` remains a compatibility alias. Keep the CLI callbacks `verify-t07.sh` fail if either is registered again. The preferred browser auth
unless there is a separate decision to retire CLI login. mount is `netkingdom`; `keycape` remains a compatibility alias. Keep the CLI
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 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 bootstrap secret bundle and do not re-run the full secret generator. Patch the

View file

@ -140,8 +140,6 @@ clients:
- "http://localhost:8250/oidc/callback" - "http://localhost:8250/oidc/callback"
- "http://127.0.0.1:8250/oidc/callback" - "http://127.0.0.1:8250/oidc/callback"
- "http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback" - "http://127.0.0.1:18200/ui/vault/auth/netkingdom/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"] allowedScopes: ["openid", "profile", "email", "groups"]
grantTypes: ["authorization_code"] grantTypes: ["authorization_code"]
clientType: "public" clientType: "public"

View file

@ -31,14 +31,18 @@ OPENBAO_CLIENT = {
"http://localhost:8250/oidc/callback", "http://localhost:8250/oidc/callback",
"http://127.0.0.1:8250/oidc/callback", "http://127.0.0.1:8250/oidc/callback",
"http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback", "http://127.0.0.1:18200/ui/vault/auth/netkingdom/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"], "allowedScopes": ["openid", "profile", "email", "groups"],
"grantTypes": ["authorization_code"], "grantTypes": ["authorization_code"],
"clientType": "public", "clientType": "public",
} }
# bao.coulomb.social was retired on 2026-09-15; its callbacks must not return.
RETIRED_OPENBAO_REDIRECT_URIS = (
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
"https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback",
)
RAPP_QONTO_CLIENT = { RAPP_QONTO_CLIENT = {
"clientId": "rapp-qonto-client", "clientId": "rapp-qonto-client",
"displayName": "rapp-qonto workload", "displayName": "rapp-qonto workload",
@ -223,6 +227,16 @@ def client_errors(config: dict[str, Any], required: dict[str, Any]) -> list[str]
return errors return errors
def retired_redirect_errors(config: dict[str, Any]) -> list[str]:
clients = config.get("clients") if isinstance(config.get("clients"), list) else []
target = next(
(client for client in clients if isinstance(client, dict) and client.get("clientId") == OPENBAO_CLIENT["clientId"]),
{},
)
retired = sorted(set(RETIRED_OPENBAO_REDIRECT_URIS) & set(target.get("redirectUris") or []))
return [f"{OPENBAO_CLIENT['clientId']}.redirectUris retired: {', '.join(retired)}"] if retired else []
def upsert_client(config: dict[str, Any], required: dict[str, Any]) -> dict[str, Any]: def upsert_client(config: dict[str, Any], required: dict[str, Any]) -> dict[str, Any]:
clients = config.get("clients") clients = config.get("clients")
if not isinstance(clients, list): if not isinstance(clients, list):
@ -265,7 +279,7 @@ def render_patch(config: dict[str, Any]) -> None:
def verify(config: dict[str, Any]) -> None: def verify(config: dict[str, Any]) -> None:
errors = client_errors(config, OPENBAO_CLIENT) + client_errors(config, RAPP_QONTO_CLIENT) + lldap_errors(config) errors = client_errors(config, OPENBAO_CLIENT) + client_errors(config, RAPP_QONTO_CLIENT) + retired_redirect_errors(config) + lldap_errors(config)
if errors: if errors:
for error in errors: for error in errors:
print(f"[FAIL] {error}") print(f"[FAIL] {error}")

View file

@ -32,6 +32,25 @@ class OpenBaoClientConfigTest(unittest.TestCase):
[f"openbao-admin.redirectUris missing: {LOOPBACK_CALLBACK}"], [f"openbao-admin.redirectUris missing: {LOOPBACK_CALLBACK}"],
) )
def test_retired_public_callbacks_are_not_declared(self) -> None:
for uri in MODULE.RETIRED_OPENBAO_REDIRECT_URIS:
self.assertNotIn(uri, MODULE.OPENBAO_CLIENT["redirectUris"])
def test_retired_public_callback_fails_verification(self) -> None:
client = dict(MODULE.OPENBAO_CLIENT)
retired = MODULE.RETIRED_OPENBAO_REDIRECT_URIS[0]
client["redirectUris"] = [*MODULE.OPENBAO_CLIENT["redirectUris"], retired]
self.assertEqual(
MODULE.retired_redirect_errors({"clients": [client]}),
[f"openbao-admin.redirectUris retired: {retired}"],
)
def test_upsert_removes_retired_callbacks(self) -> None:
stale = dict(MODULE.OPENBAO_CLIENT)
stale["redirectUris"] = [*MODULE.OPENBAO_CLIENT["redirectUris"], *MODULE.RETIRED_OPENBAO_REDIRECT_URIS]
updated = MODULE.upsert_client({"clients": [stale]}, MODULE.OPENBAO_CLIENT)
self.assertEqual(MODULE.retired_redirect_errors(updated), [])
def test_upsert_preserves_unrelated_clients(self) -> None: def test_upsert_preserves_unrelated_clients(self) -> None:
config = {"clients": [{"clientId": "unrelated", "clientType": "public"}]} config = {"clients": [{"clientId": "unrelated", "clientType": "public"}]}
updated = MODULE.upsert_client(config, MODULE.OPENBAO_CLIENT) updated = MODULE.upsert_client(config, MODULE.OPENBAO_CLIENT)

View file

@ -49,8 +49,6 @@ probe_redirect() {
probe_redirect "CLI" "http://localhost:8250/oidc/callback" probe_redirect "CLI" "http://localhost:8250/oidc/callback"
probe_redirect "operator-tunneled browser UI netkingdom mount" "http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback" probe_redirect "operator-tunneled browser UI netkingdom mount" "http://127.0.0.1:18200/ui/vault/auth/netkingdom/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" \ KC_POD=$("$KUBECTL" get pod -n "$NAMESPACE" \
-l app.kubernetes.io/name=keycape \ -l app.kubernetes.io/name=keycape \

View file

@ -188,8 +188,6 @@ required_redirects = {
"http://localhost:8250/oidc/callback", "http://localhost:8250/oidc/callback",
"http://127.0.0.1:8250/oidc/callback", "http://127.0.0.1:8250/oidc/callback",
"http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback", "http://127.0.0.1:18200/ui/vault/auth/netkingdom/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"} required_scopes = {"openid", "profile", "email", "groups"}
missing_redirects = sorted(required_redirects - set(target.get("redirectUris") or [])) missing_redirects = sorted(required_redirects - set(target.get("redirectUris") or []))
@ -200,6 +198,13 @@ if target.get("clientType") != "public":
if missing_redirects: if missing_redirects:
print("openbao-admin missing redirect URI(s): " + ", ".join(missing_redirects)) print("openbao-admin missing redirect URI(s): " + ", ".join(missing_redirects))
raise SystemExit(4) raise SystemExit(4)
retired_redirects = sorted({
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
"https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback",
} & set(target.get("redirectUris") or []))
if retired_redirects:
print("openbao-admin still registers retired redirect URI(s): " + ", ".join(retired_redirects))
raise SystemExit(6)
if missing_scopes: if missing_scopes:
print("openbao-admin missing scope(s): " + ", ".join(missing_scopes)) print("openbao-admin missing scope(s): " + ", ".join(missing_scopes))
raise SystemExit(5) raise SystemExit(5)