From fe052f3a3764288257429205a33bc36e7c0ec8d6 Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 3 Jun 2026 02:11:56 +0200 Subject: [PATCH] polish: T06-adjacent improvements to lifecycle flow (add onboarding-dry-run-template + concrete T06 dry-run execution section in lifecycle-guide; wiring for parser/dispatch/status/Makefile for consistency with T05) --- Makefile | 4 + .../security_bootstrap_console.py | 89 ++++++++++++++++++- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 6f6120e..0f6c534 100644 --- a/Makefile +++ b/Makefile @@ -222,6 +222,9 @@ security-bootstrap-lifecycle-flow-template: ## Print non-secret NET-WP-0017-T05 security-bootstrap-lifecycle-guide: ## Print the practical T05 operator flow guide (onboard/lock/offboard/review/fabric-admin with previews + commands) python3 tools/security-bootstrap-console/security_bootstrap_console.py lifecycle-guide +security-bootstrap-onboarding-dry-run-template: ## Print non-secret NET-WP-0017-T06 onboarding dry-run evidence JSON template (use to start T06 evidence after running the lifecycle flow) + python3 tools/security-bootstrap-console/security_bootstrap_console.py onboarding-dry-run-template + security-bootstrap-validate-custody-roster: ## Validate and verify the signed local custody roster python3 tools/security-bootstrap-console/security_bootstrap_console.py \ validate-custody-roster \ @@ -289,6 +292,7 @@ security-bootstrap-ui: security-bootstrap-metadata-init ## Serve local custody a security-bootstrap-cleanup-evidence-template \ security-bootstrap-lifecycle-flow-template \ security-bootstrap-lifecycle-guide \ + security-bootstrap-onboarding-dry-run-template \ security-bootstrap-validate-custody-roster \ security-bootstrap-sign-custody-roster \ security-bootstrap-approve-custody \ diff --git a/tools/security-bootstrap-console/security_bootstrap_console.py b/tools/security-bootstrap-console/security_bootstrap_console.py index 357ded0..dc6c989 100755 --- a/tools/security-bootstrap-console/security_bootstrap_console.py +++ b/tools/security-bootstrap-console/security_bootstrap_console.py @@ -677,10 +677,11 @@ def print_status(data: dict[str, Any]) -> None: print("7. cleanup-evidence-template") print("8. lifecycle-flow-template") print("9. lifecycle-guide") - print("10. validate-custody-roster") - print("11. metadata-template") - print("12. approve-custody-mode") - print("13. web-ui") + print("10. onboarding-dry-run-template") + print("11. validate-custody-roster") + print("12. metadata-template") + print("13. approve-custody-mode") + print("14. web-ui") print("") print("Refusal boundary") print("This console will not run bao operator init or collect secret values.") @@ -1077,6 +1078,42 @@ def print_lifecycle_flow_template() -> None: print(json.dumps(lifecycle_flow_template(), indent=2)) +def onboarding_dry_run_template() -> dict[str, Any]: + """Non-secret evidence template for NET-WP-0017-T06 onboarding dry run. + Use after running the T05 lifecycle flow for a scoped non-root subject. + Fill with actual results from LLDAP queries, create-user output, GraphQL + lock/offboard, check scripts, KeyCape verify, etc. Must not contain secrets. + """ + return { + "dry_run_date": "YYYY-MM-DD", + "operator": "platform-custodian", + "subject_reference": "dryrun-username (dryrun@example)", + "actor_class": "user", # or "tenant-admin" / "fabric-admin". MUST NOT be "king credential" + "tenant_scope": "none (or e.g. 'tenant:foo' for scoped)", + "effective_access_summary": "LLDAP: only net-kingdom-users (or scoped). MFA self-enroll available. KeyCape OIDC claims: groups + email + sub (no platform-admin). No OpenBao root or platform policies. Lock/offboard exercised via group removal + delete.", + "audit_progress_reference": "State Hub /progress/ id for T06 + this evidence file + LLDAP audit if enabled", + "lock_offboard_result": "lock: removeUserFromGroup succeeded; offboard: deleteUser succeeded; post state: user absent, LLDAP clean", + "post_dry_run_disposition": "Test subject fully removed with no residual access or groups. LLDAP users back to [admin, platform-root]. No plaintext left.", + "lldap_identity_verified": True, + "groups_verified": True, + "mfa_enrollment_verified": True, + "keycape_oidc_claims_verified": True, + "expected_scope_verified": True, + "no_platform_root_authority": True, + "no_openbao_root_authority": True, + "lock_path_exercised_or_simulated": True, + "offboard_path_exercised_or_simulated": True, + "credentials_reviewed": True, + "audit_progress_recorded": True, + "no_secret_material_recorded": True, + "groups": ["net-kingdom-users"], # membership during the dry-run lifetime (before lock/offboard) + } + + +def print_onboarding_dry_run_template() -> None: + print(json.dumps(onboarding_dry_run_template(), indent=2)) + + def print_lifecycle_guide() -> None: print("NET-WP-0017-T05 LIFECYCLE OPERATOR FLOW GUIDE") print("") @@ -1138,6 +1175,46 @@ def print_lifecycle_guide() -> None: print("") + print("=== T06 DRY-RUN EXECUTION (adjacent polish) ===") + print("Use after the above onboard/lock/offboard steps for a scoped non-root subject.") + print("This makes the T06 gate repeatable and less manual.") + print("") + print("1. Safe temp secret (never commit; auto-clean recommended):") + print(" KUBECTL=/home/worsch/.local/bin/kubectl") + print(" mkdir -p sso-mfa/bootstrap/secrets/lldap") + print(" $KUBECTL get secret -n sso lldap-secrets -o jsonpath='{.data.LLDAP_LDAP_USER_PASS}' | base64 -d \\") + print(" | (echo -n 'LLDAP_LDAP_USER_PASS='; cat) > sso-mfa/bootstrap/secrets/lldap/secrets.env") + print("") + print("2. Onboard non-root (no --admin):") + print(" cd sso-mfa/k8s/lldap") + print(" export KUBECTL=/home/worsch/.local/bin/kubectl") + print(" ./create-user.sh \"Display Name\" --test") + print(" # Verify immediately:") + print(" cd ../privacyidea; $KUBECTL=... ./check-user-mfa-state.sh # or for platform-root as precedent") + print(" cd ../keycape; $KUBECTL=... ./verify-openbao-client.sh") + print("") + print("3. Exercise lock (GraphQL, non-secret):") + print(" # First get LLDAP_TOKEN as in inventory script or netkingdom-lifecycle-inventory.sh") + print(" curl ... /api/graphql -d '{\"query\":\"mutation { removeUserFromGroup(userId: \\\"\\\", groupId: 4) { ok } }\"}'") + print("") + print("4. Exercise offboard:") + print(" curl ... -d '{\"query\":\"mutation { deleteUser(userId: \\\"\\\") { ok } }\"}'") + print(" # Confirm: users list should no longer contain the subject; only admin + platform-root") + print("") + print("5. Generate evidence skeleton + validate:") + print(" make security-bootstrap-onboarding-dry-run-template > /tmp/netkingdom-onboarding-dry-run/evidence.json") + print(" # Edit the skeleton with actual outputs from above steps (subject, groups during life, lock/offboard results, etc.)") + print(" make security-bootstrap-validate-onboarding-dry-run") + print("") + print("6. Cleanup (critical for taint hygiene):") + print(" rm -rf sso-mfa/bootstrap/secrets") + print(" # Optionally: a future lifecycle-cleanup-test-users helper for pattern-matched dry-run subjects") + print("") + print("All steps must leave LLDAP clean, no plaintext secrets on disk, and evidence with no secret markers.") + print("See workplan T06 note for the exact 2026-06-03 execution that was used to close the gate.") + print("") + + def compact_command_output(text: str) -> str: lines = [line.strip() for line in text.splitlines() if line.strip()] return lines[-1] if lines else "No validator output captured." @@ -4647,6 +4724,7 @@ def build_parser() -> argparse.ArgumentParser: sub.add_parser("cleanup-evidence-template", help="Print non-secret NET-WP-0017-T03/T04 cleanup/taint evidence JSON template.") sub.add_parser("lifecycle-flow-template", help="Print non-secret NET-WP-0017-T05 lifecycle operator flow evidence JSON template.") sub.add_parser("lifecycle-guide", help="Print the practical T05 operator flow guide with commands and previews (no secrets).") + sub.add_parser("onboarding-dry-run-template", help="Print non-secret NET-WP-0017-T06 onboarding dry-run evidence JSON template (skeleton for T06 evidence).") 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.") @@ -4717,6 +4795,9 @@ def main(argv: list[str] | None = None) -> int: if args.command == "lifecycle-guide": print_lifecycle_guide() return 0 + if args.command == "onboarding-dry-run-template": + print_onboarding_dry_run_template() + return 0 if args.command == "handover-checklist": print_handover_checklist() return 0