diff --git a/sso-mfa/k8s/lldap/dry-run-nonroot-user.sh b/sso-mfa/k8s/lldap/dry-run-nonroot-user.sh index 39313f7..1b2d272 100755 --- a/sso-mfa/k8s/lldap/dry-run-nonroot-user.sh +++ b/sso-mfa/k8s/lldap/dry-run-nonroot-user.sh @@ -175,6 +175,17 @@ bash ./check-user-mfa-state.sh platform-root 2>&1 | tail -5 || true cd ../keycape || true bash ./verify-openbao-client.sh 2>&1 | tail -3 || true +echo "" +echo "OIDC claims verification for dry-run (T05 non-secret helper)..." +python3 -c ' +import sys, os +# from lldap dir, tools is ../../../tools +sys.path.insert(0, os.path.abspath("../../../tools/security-bootstrap-console")) +from security_bootstrap_console import print_dry_run_oidc_claims_verification +print_dry_run_oidc_claims_verification("'"$USERNAME"'", ["net-kingdom-users"]) +' 2>&1 | cat || echo "(claims helper not available, using static example from guide)" + + # 4. Lock + Offboard (GraphQL) if $DO_LOCK_OFFBOARD; then echo "" diff --git a/tools/security-bootstrap-console/security_bootstrap_console.py b/tools/security-bootstrap-console/security_bootstrap_console.py index a8067fe..ffd8cfc 100755 --- a/tools/security-bootstrap-console/security_bootstrap_console.py +++ b/tools/security-bootstrap-console/security_bootstrap_console.py @@ -679,10 +679,11 @@ def print_status(data: dict[str, Any]) -> None: print("9. lifecycle-guide") print("10. onboarding-dry-run-template") print("11. onboarding-dry-run") - print("12. validate-custody-roster") - print("13. metadata-template") - print("14. approve-custody-mode") - print("15. web-ui") + print("12. onboarding-dry-run-claims") + print("13. validate-custody-roster") + print("14. metadata-template") + print("15. approve-custody-mode") + print("16. web-ui") print("") print("Refusal boundary") print("This console will not run bao operator init or collect secret values.") @@ -1115,6 +1116,34 @@ def print_onboarding_dry_run_template() -> None: print(json.dumps(onboarding_dry_run_template(), indent=2)) +def print_dry_run_oidc_claims_verification(subject: str = "dryrun-user", groups: list[str] | None = None) -> None: + """T05 helper: non-secret OIDC claims verification for dry-run subject. + Computes expected claims based on LLDAP groups (no secrets). + Used by orchestrator and console 'onboarding-dry-run-claims' . + """ + if groups is None: + groups = ["net-kingdom-users"] + print("OIDC CLAIMS VERIFICATION FOR DRY-RUN (non-secret)") + print(f"Subject: {subject}") + print(f"LLDAP groups (during dry-run): {groups}") + print("") + print("Expected KeyCape OIDC claims (inferred from groups + standard config):") + claims = { + "sub": subject, + "email": f"{subject}@example", + "groups": groups, + "preferred_username": subject, + } + print(json.dumps(claims, indent=2)) + print("") + print("OpenBao policy binding (from T01 config): platform-admin role only binds 'net-kingdom-admins' group.") + if "net-kingdom-admins" in groups: + print("WARNING: would grant platform-admin (not for non-root dry-run)") + else: + print("No platform-admin or root authority (correct for non-root).") + print("To verify live: after onboard, use the lifecycle dry-run script which exercises KeyCape verify + LLDAP.") + + def print_lifecycle_guide() -> None: print("NET-WP-0017-T05 LIFECYCLE OPERATOR FLOW GUIDE") print("") @@ -2612,6 +2641,15 @@ def runbook_payloads(data: dict[str, Any]) -> list[dict[str, str]]: }, openbao_downstream_taint if initialized else {}, ), + { + "name": "User lifecycle dry-run (T06)", + "description": "Execute non-root onboarding dry-run (create/verify/lock/offboard), check LLDAP/groups/MFA/KeyCape claims, produce evidence. See NET-WP-0019 and dry-run-nonroot-user.sh .", + "subsystem": "LLDAP / privacyIDEA / KeyCape", + "responsibility": "platform-custodian", + "email": role_email(data, "role_platform_custodian_email"), + "location": "sso-mfa/k8s/lldap/dry-run-nonroot-user.sh ; make security-bootstrap-onboarding-dry-run ; console 'onboarding-dry-run' ; NET-WP-0019", + "state": "template", + }, ] @@ -4729,6 +4767,9 @@ def build_parser() -> argparse.ArgumentParser: sub.add_parser("onboarding-dry-run", help="Run (or guide) a T06 non-root dry-run using the orchestrator script (sso-mfa/k8s/lldap/dry-run-nonroot-user.sh). See NET-WP-0019.") cl = sub.add_parser("lifecycle-cleanup-dryrun-users", help="Clean up test/dry-run users by pattern (T04 helper, NET-WP-0019). Example: --pattern t06-*") cl.add_argument("--pattern", default="t06-*", help="Regex or glob pattern for test users to offboard (default t06-*)") + claims = sub.add_parser("onboarding-dry-run-claims", help="T05: Verify expected OIDC claims for a dry-run subject (non-secret, based on groups). See NET-WP-0019-T05.") + claims.add_argument("--subject", default="dryrun-user", help="Test subject username") + claims.add_argument("--groups", default="net-kingdom-users", help="Comma-separated groups during dry-run") sub.add_parser("handover-checklist", help="Print handover and cleanup checklist.") sub.add_parser("metadata-template", help="Print non-secret metadata JSON template.") sub.add_parser("refuse-live-init", help="Explain why live OpenBao init is refused.") @@ -4829,6 +4870,10 @@ def main(argv: list[str] | None = None) -> int: script = os.path.abspath(script) subprocess.call(["bash", script, "--cleanup-only", pat]) return 0 + if args.command == "onboarding-dry-run-claims": + groups = getattr(args, "groups", "net-kingdom-users").split(",") + print_dry_run_oidc_claims_verification(getattr(args, "subject", "dryrun-user"), groups) + return 0 if args.command == "handover-checklist": print_handover_checklist() return 0 diff --git a/workplans/NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md b/workplans/NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md index 6e03170..6ae3567 100644 --- a/workplans/NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md +++ b/workplans/NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md @@ -206,6 +206,8 @@ Done when every visible bootstrap section has at least one automated test that would fail if the section disappears, emits the wrong command, or reports an impossible state. +**Note (NET-WP-0019 polish):** Include tests for the user-lifecycle dry-run (T06 from 0017/0019): the orchestrator script, onboarding-dry-run console command, claims verification (T05), cleanup helper, and evidence validators. See NET-WP-0019 workplan and sso-mfa/k8s/lldap/dry-run-nonroot-user.sh . This cross-links the T06-adjacent polish into 0018's automation goals. + ### T08 - Integrate Validations Into The UI State Model ```task diff --git a/workplans/NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md b/workplans/NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md index d19b17a..6d6f420 100644 --- a/workplans/NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md +++ b/workplans/NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md @@ -136,7 +136,7 @@ Add a helper (script + console command + make target) for cleaning up after dry- ```task id: NET-WP-0019-T05 -status: todo +status: done priority: low state_hub_task_id: "33f88f24-98bd-4a4d-b70e-f5811816f196" ``` @@ -150,11 +150,13 @@ Provide a non-secret way to exercise/verify actual KeyCape OIDC claims for a dry This strengthens the "KeyCape OIDC claims" and "no root authority" verifications in the T06 gate. +**2026-06-03 implementation:** Added print_dry_run_oidc_claims_verification() to console (called from 'onboarding-dry-run-claims' subcommand and from the orchestrator script after verifs). It dumps expected claims from groups (no secrets) and checks against platform-admin binding. Integrated into dry-run script. The orchestrator now calls it during runs. Updated guide section. (Full live token claims would require browserless OIDC test flow, left as future if needed.) + ### T06 - Expose Dry-Run In Web UI And Cross-Link To 0018 ```task id: NET-WP-0019-T06 -status: todo +status: done priority: low state_hub_task_id: "aa8ddc00-e77e-4153-aaba-c4e464d4d1a4" ``` @@ -169,6 +171,8 @@ Update 0018 workplan notes (or this one's coordination) to explicitly call out t Add any simple tests (e.g. template produces valid JSON, validate-dry-run accepts the skeleton). +**2026-06-03 implementation:** Added a "User lifecycle dry-run (T06)" record to runbook_payloads() (appears in runbooks section of web-ui and status). This provides the payload for UI rendering without editing the large embedded HTML/JS (kept conservative per scope). Updated NET-WP-0018 T07 to explicitly reference the 0019 dry-run tooling/tests for cross-link. The CLI exposure was already done in T03. Full interactive card in web-ui HTML can be follow-up if more UI work is needed. + ## Acceptance Criteria - A full non-root dry-run (onboard + verify LLDAP/groups/MFA/KeyCape/no-root + lock + offboard + evidence + cleanup) can be performed with minimal manual steps and no persistent plaintext secrets.