From 61aeafef7135ea7b986012c619da72ab73128d61 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 23 Aug 2026 13:58:18 +0200 Subject: [PATCH] feat(NK-WP-0032): admit OpenBao operator loopback callback Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02b90-83bf-75c2-81c8-aa705414e4d4 --- docs/NetkingdomRuntimeArchitecture.md | 2 +- docs/smooth-bootstrap-guide.md | 6 +- sso-mfa/k8s/keycape/README.md | 17 ++-- sso-mfa/k8s/keycape/configure-openbao-oidc.sh | 1 + sso-mfa/k8s/keycape/create-secrets.sh | 1 + sso-mfa/k8s/keycape/openbao-client-config.py | 1 + .../k8s/keycape/test_openbao_client_config.py | 43 +++++++++ sso-mfa/k8s/keycape/verify-openbao-client.sh | 1 + sso-mfa/k8s/verify-t07.sh | 1 + ...0032-openbao-operator-loopback-callback.md | 87 +++++++++++++++++++ 10 files changed, 150 insertions(+), 10 deletions(-) create mode 100644 sso-mfa/k8s/keycape/test_openbao_client_config.py create mode 100644 workplans/NK-WP-0032-openbao-operator-loopback-callback.md diff --git a/docs/NetkingdomRuntimeArchitecture.md b/docs/NetkingdomRuntimeArchitecture.md index d267caa..e624efe 100644 --- a/docs/NetkingdomRuntimeArchitecture.md +++ b/docs/NetkingdomRuntimeArchitecture.md @@ -25,7 +25,7 @@ Recursive trust rule: Normal tenant admin (even Coulomb) must never suffice to a - 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, https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback, https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback. + - Redirects: http://localhost:8250/oidc/callback, http://127.0.0.1:8250/oidc/callback, http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback, plus the former public browser callbacks during the bounded listener rollback window. - 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. diff --git a/docs/smooth-bootstrap-guide.md b/docs/smooth-bootstrap-guide.md index bd1aae7..fb1e2e1 100644 --- a/docs/smooth-bootstrap-guide.md +++ b/docs/smooth-bootstrap-guide.md @@ -95,8 +95,10 @@ See T03 retrospective for past realm drift bumps (now partially automated via ru - Apply keycape-config Secret, restart KeyCape. - Register bootstrap clients (netkingdom-bootstrap-console, openbao-admin). - OpenBao admin redirects: localhost CLI callbacks plus - `https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback` for - preferred browser UI login; `keycape` remains a compatibility auth mount. + `http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback` for the + preferred operator-tunneled browser UI login. The former public callback is + retained only for the bounded listener rollback window; `keycape` remains a + compatibility auth mount. - Verify OIDC admin login: platform-root obtains OpenBao platform-admin via KeyCape/MFA. - Evidence: keycape client gates, openbao_oidc_* , oidc_login_verified. - Validate related in t02 / console. diff --git a/sso-mfa/k8s/keycape/README.md b/sso-mfa/k8s/keycape/README.md index 7f5a960..ef68313 100644 --- a/sso-mfa/k8s/keycape/README.md +++ b/sso-mfa/k8s/keycape/README.md @@ -132,14 +132,16 @@ browser UI callbacks for `bao.coulomb.social`: ```text http://localhost:8250/oidc/callback http://127.0.0.1:8250/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 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. +The preferred browser UI callback is paired with the named Railiance Platform +operator tunnel at `http://127.0.0.1:18200`. The public callback remains during +the bounded listener rollback window. The preferred browser auth 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 bootstrap secret bundle and do not re-run the full secret generator. Patch the @@ -168,9 +170,10 @@ 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. +OpenBao UI at `http://127.0.0.1:18200` through the named +`openbao-ui-railiance01` tunnel, 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 diff --git a/sso-mfa/k8s/keycape/configure-openbao-oidc.sh b/sso-mfa/k8s/keycape/configure-openbao-oidc.sh index 8106abd..a04a103 100644 --- a/sso-mfa/k8s/keycape/configure-openbao-oidc.sh +++ b/sso-mfa/k8s/keycape/configure-openbao-oidc.sh @@ -41,6 +41,7 @@ OPENBAO_POD="${OPENBAO_POD:-openbao-0}" "allowed_redirect_uris": [ "http://localhost:8250/oidc/callback", "http://127.0.0.1:8250/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" ], diff --git a/sso-mfa/k8s/keycape/create-secrets.sh b/sso-mfa/k8s/keycape/create-secrets.sh index d097f74..b70168a 100644 --- a/sso-mfa/k8s/keycape/create-secrets.sh +++ b/sso-mfa/k8s/keycape/create-secrets.sh @@ -126,6 +126,7 @@ clients: redirectUris: - "http://localhost:8250/oidc/callback" - "http://127.0.0.1:8250/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"] diff --git a/sso-mfa/k8s/keycape/openbao-client-config.py b/sso-mfa/k8s/keycape/openbao-client-config.py index 7df73dc..6bcef65 100644 --- a/sso-mfa/k8s/keycape/openbao-client-config.py +++ b/sso-mfa/k8s/keycape/openbao-client-config.py @@ -26,6 +26,7 @@ OPENBAO_CLIENT = { "redirectUris": [ "http://localhost:8250/oidc/callback", "http://127.0.0.1:8250/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", ], diff --git a/sso-mfa/k8s/keycape/test_openbao_client_config.py b/sso-mfa/k8s/keycape/test_openbao_client_config.py new file mode 100644 index 0000000..f3f644f --- /dev/null +++ b/sso-mfa/k8s/keycape/test_openbao_client_config.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +"""Focused regression tests for the code-defined OpenBao KeyCape client.""" + +from __future__ import annotations + +import importlib.util +import unittest +from pathlib import Path + + +MODULE_PATH = Path(__file__).with_name("openbao-client-config.py") +SPEC = importlib.util.spec_from_file_location("openbao_client_config", MODULE_PATH) +assert SPEC and SPEC.loader +MODULE = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(MODULE) + +LOOPBACK_CALLBACK = "http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback" + + +class OpenBaoClientConfigTest(unittest.TestCase): + def test_operator_tunnel_callback_is_required(self) -> None: + self.assertIn(LOOPBACK_CALLBACK, MODULE.OPENBAO_CLIENT["redirectUris"]) + + def test_missing_operator_tunnel_callback_fails_verification(self) -> None: + client = dict(MODULE.OPENBAO_CLIENT) + client["redirectUris"] = [ + uri for uri in MODULE.OPENBAO_CLIENT["redirectUris"] if uri != LOOPBACK_CALLBACK + ] + errors = MODULE.client_errors({"clients": [client]}, MODULE.OPENBAO_CLIENT) + self.assertEqual( + errors, + [f"openbao-admin.redirectUris missing: {LOOPBACK_CALLBACK}"], + ) + + def test_upsert_preserves_unrelated_clients(self) -> None: + config = {"clients": [{"clientId": "unrelated", "clientType": "public"}]} + updated = MODULE.upsert_client(config, MODULE.OPENBAO_CLIENT) + self.assertEqual(updated["clients"][0]["clientId"], "unrelated") + self.assertIn(LOOPBACK_CALLBACK, updated["clients"][1]["redirectUris"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/sso-mfa/k8s/keycape/verify-openbao-client.sh b/sso-mfa/k8s/keycape/verify-openbao-client.sh index 88098bb..924d19e 100755 --- a/sso-mfa/k8s/keycape/verify-openbao-client.sh +++ b/sso-mfa/k8s/keycape/verify-openbao-client.sh @@ -48,6 +48,7 @@ probe_redirect() { } 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 "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" diff --git a/sso-mfa/k8s/verify-t07.sh b/sso-mfa/k8s/verify-t07.sh index 893530f..911581b 100755 --- a/sso-mfa/k8s/verify-t07.sh +++ b/sso-mfa/k8s/verify-t07.sh @@ -187,6 +187,7 @@ if not target: required_redirects = { "http://localhost:8250/oidc/callback", "http://127.0.0.1:8250/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", } diff --git a/workplans/NK-WP-0032-openbao-operator-loopback-callback.md b/workplans/NK-WP-0032-openbao-operator-loopback-callback.md new file mode 100644 index 0000000..1ff33b4 --- /dev/null +++ b/workplans/NK-WP-0032-openbao-operator-loopback-callback.md @@ -0,0 +1,87 @@ +--- +id: NK-WP-0032 +type: workplan +title: "Admit the operator-tunneled OpenBao browser callback" +domain: infotech +repo: net-kingdom +status: blocked +owner: codex +topic_slug: net-kingdom +created: "2026-08-23" +updated: "2026-08-23" +related: + - RMASTER-WP-0020-T09 + - RAILIANCE-WP-0027-T03 +origin: routed +origin_ref: "State Hub message 5e56b413-d8ec-4718-b432-2debc40498ca" +--- + +# NK-WP-0032 — OpenBao operator loopback callback + +## Goal + +Admit the exact callback derived by the ClusterIP-only OpenBao UI gateway when +operators reach it through the named `openbao-ui-railiance01` tunnel, without +weakening MFA or broadening the OpenBao policy. + +## T01 — Update and validate the code-defined callback contract + +```task +id: NK-WP-0032-T01 +status: done +priority: high +``` + +Add `http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback` to the +KeyCape `openbao-admin` public-PKCE client, the OpenBao `platform-admin` role +template, bootstrap validation, live authorization verifier, and operator +documentation. Preserve the former public callbacks only for the bounded +listener rollback window. + +Completed 2026-08-23. Three focused regression tests, Python compilation, Bash +syntax validation, and `git diff --check` pass. + +## T02 — Apply and prove the live KeyCape client addition + +```task +id: NK-WP-0032-T02 +status: done +priority: high +``` + +Patch only the code-defined non-secret client configuration, restart KeyCape, +and prove both the live Secret contract and public authorize endpoint accept +the exact loopback URI. Do not record the Secret contents or an authorization +code. + +Completed 2026-08-23 on Railiance01. `sso/keycape-config` was patched without +printing decoded data, `deployment/keycape` rolled out successfully, and the +value-safe verifier proved the live client contract, exact loopback redirect, +legacy rollback redirects, and OIDC discovery endpoint. No authorization code +or Secret value was observed. + +## T03 — Apply and prove the live OpenBao role addition + +```task +id: NK-WP-0032-T03 +status: wait +priority: high +``` + +An attended platform-admin/root-sudo authority must apply the updated role +template to `auth/netkingdom/role/platform-admin`. Record only that the exact +URI is admitted. Do not record a token, accessor, authorization code, callback +query, browser storage, or role response body. + +## T04 — Return attended-login evidence to Railiance Platform + +```task +id: NK-WP-0032-T04 +status: wait +priority: high +``` + +After T02 and T03 pass, perform one attended MFA login through +`http://127.0.0.1:18200` and return only the success/failure outcome. This task +does not authorize public Ingress retraction; Railiance Platform retains that +separate guarded hold point.