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)

This commit is contained in:
2026-06-03 02:11:56 +02:00
parent 8a3d7a8aff
commit fe052f3a37
2 changed files with 89 additions and 4 deletions

View File

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

View File

@@ -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 <username> <email> \"Display Name\" --test")
print(" # Verify immediately:")
print(" cd ../privacyidea; $KUBECTL=... ./check-user-mfa-state.sh <username> # 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: \\\"<username>\\\", groupId: 4) { ok } }\"}'")
print("")
print("4. Exercise offboard:")
print(" curl ... -d '{\"query\":\"mutation { deleteUser(userId: \\\"<username>\\\") { 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