From 1fbebb2d94b67e3908a9a114a3e4eff75c7b3558 Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 29 Sep 2026 23:14:52 +0200 Subject: [PATCH] Port the seven console runbooks to native runbook-pack/v0.2 packs (NK-WP-0044-T07) Co-Authored-By: Claude Sonnet 5.5 Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 6969@bnt-lap001 Assistant-Session: e8ae73f2-8385-4c9c-b714-94c3c52cdbc0 --- runbooks/openbao-emergency-lockdown/pack.yaml | 79 +++++++++++ runbooks/openbao-key-compromise/pack.yaml | 84 ++++++++++++ runbooks/openbao-restore-drill/pack.yaml | 125 ++++++++++++++++++ runbooks/openbao-rotate-unseal-keys/pack.yaml | 101 ++++++++++++++ runbooks/openbao-token-revocation/pack.yaml | 65 +++++++++ runbooks/privacyidea-realm-repair/pack.yaml | 59 +++++++++ runbooks/user-lifecycle-dry-run/pack.yaml | 78 +++++++++++ ...044-runbook-packs-for-runbook-tutorials.md | 6 +- 8 files changed, 594 insertions(+), 3 deletions(-) create mode 100644 runbooks/openbao-emergency-lockdown/pack.yaml create mode 100644 runbooks/openbao-key-compromise/pack.yaml create mode 100644 runbooks/openbao-restore-drill/pack.yaml create mode 100644 runbooks/openbao-rotate-unseal-keys/pack.yaml create mode 100644 runbooks/openbao-token-revocation/pack.yaml create mode 100644 runbooks/privacyidea-realm-repair/pack.yaml create mode 100644 runbooks/user-lifecycle-dry-run/pack.yaml diff --git a/runbooks/openbao-emergency-lockdown/pack.yaml b/runbooks/openbao-emergency-lockdown/pack.yaml new file mode 100644 index 0000000..0017750 --- /dev/null +++ b/runbooks/openbao-emergency-lockdown/pack.yaml @@ -0,0 +1,79 @@ +spec: runbook-pack/v0.2 +id: nk.openbao-emergency-lockdown +title: OpenBao emergency lock-down +owner: net-kingdom +outcome: OpenBao is sealed, sealed state is confirmed, and the drill or incident outcome is recorded without secrets. +engine: native +confirm_phrase: I understand this cannot be undone +framing: + - Sealing stops all access to stored assets until a quorum of shares unseals it again. + - Confirm the quorum is available before sealing outside an incident. + - Ported from the security bootstrap console runbook "Emergency lock-down". +actors: + - id: operator + role: OpenBao ceremony operator + description: Holds a root or sudo-capable token and seals the instance. + privileges: [openbao-root-or-sudo] +datapoints: + - id: evidence_path + label: Emergency drill evidence file + type: path + default: /tmp/netkingdom-openbao-emergency-drill/evidence.json + dimensions: {actor: operator, kind: location, subsystem: openbao} + ask_in: seal +stages: + - id: seal + title: Seal and confirm + goal: OpenBao reports Sealed true. + steps: + - id: seal-now + title: Seal OpenBao + actor: operator + owner: openbao + command: | + printf "OpenBao token: " >&2; read -rs OPENBAO_TOKEN; printf "\n" >&2 + printf "%s\n" "$OPENBAO_TOKEN" | kubectl exec -i -n openbao openbao-0 -- sh -c 'read -r BAO_TOKEN; export BAO_TOKEN; bao operator seal' + unset OPENBAO_TOKEN + reversible: false + idempotent: idempotent + idempotent_note: Sealing an already sealed instance changes nothing. + auto_confirm: manual-only + rollback: Unseal with a quorum of shares (attended ceremony). + verify: + done_when: The seal command succeeded + expect: manual + - id: confirm-sealed + title: Confirm sealed status + actor: operator + owner: openbao + requires: [seal-now] + command: kubectl exec -n openbao openbao-0 -- bao status + idempotent: idempotent + verify: + done_when: Sealed is true + expect: manual + gates: + - id: sealed + title: OpenBao is sealed + goal: contained-exposure + blocking: true + downstream_risk: Stored assets stay reachable during the incident. + checks: + - done_when: Status reports Sealed true + expect: manual + - id: record + title: Record the outcome + goal: Non-secret drill or incident evidence validates. + requires: [seal] + steps: + - id: validate-evidence + title: Validate the emergency evidence + actor: operator + owner: railiance-platform + uses: [evidence_path] + command: make -C ../railiance-platform openbao-validate-emergency-evidence OPENBAO_EMERGENCY_EVIDENCE={{evidence_path}} + idempotent: idempotent + verify: + done_when: The validator accepts the evidence + command: make -C ../railiance-platform openbao-validate-emergency-evidence OPENBAO_EMERGENCY_EVIDENCE={{evidence_path}} + expect: exit-0 diff --git a/runbooks/openbao-key-compromise/pack.yaml b/runbooks/openbao-key-compromise/pack.yaml new file mode 100644 index 0000000..726b6c9 --- /dev/null +++ b/runbooks/openbao-key-compromise/pack.yaml @@ -0,0 +1,84 @@ +spec: runbook-pack/v0.2 +id: nk.openbao-key-compromise +title: OpenBao key material compromised +owner: net-kingdom +outcome: The exposure is recorded, reset versus rotation is chosen and carried out, and only the non-secret outcome is on record. +engine: native +confirm_phrase: I understand this cannot be undone +framing: + - "Nothing secret is recorded here: not init output, shares, tokens or their fragments." + - Treat everything the exposed material could reach as untrusted until the response is complete. + - Ported from the security bootstrap console runbook "Key material compromised". +actors: + - id: operator + role: OpenBao ceremony operator + description: Runs the attended OpenBao commands and records the outcome. + privileges: [openbao-root-or-sudo] +datapoints: + - id: response + label: Chosen response + type: enum + values: [reset, rotate-keys] + default: rotate-keys + help: Reset when the instance holds nothing yet worth keeping; rotate when it holds live data. + dimensions: {actor: operator, kind: decision, subsystem: openbao} + ask_in: respond + - id: exposure_summary + label: Non-secret exposure summary + type: text + default: "" + help: What escaped, where, when. No secret values. + dimensions: {actor: operator, kind: evidence, subsystem: openbao} + ask_in: assess +stages: + - id: assess + title: Assess the exposure + goal: You know what escaped and which paths it could reach. + steps: + - id: status + title: Check OpenBao state + actor: operator + owner: openbao + command: kubectl exec -n openbao openbao-0 -- bao status + idempotent: idempotent + verify: + done_when: Status is shown + expect: manual + - id: inspect-paths + title: Inspect affected mounts and auth methods + actor: operator + owner: openbao + requires: [status] + command: | + printf "OpenBao token: " >&2; read -rs OPENBAO_TOKEN; printf "\n" >&2 + printf "%s\n" "$OPENBAO_TOKEN" | kubectl exec -i -n openbao openbao-0 -- sh -c 'read -r BAO_TOKEN; export BAO_TOKEN; bao secrets list; bao auth list' + unset OPENBAO_TOKEN + idempotent: idempotent + verify: + done_when: The affected paths are listed and noted + expect: manual + - id: respond + title: Reset or rotate + goal: The exposed material is no longer valid. + requires: [assess] + steps: + - id: carry-out + title: Carry out the chosen response + actor: operator + owner: openbao + uses: [response] + description: "If rotate-keys, run the pack nk.openbao-rotate-unseal-keys and revoke exposed tokens with nk.openbao-token-revocation. If reset, reinitialize per the attended ceremony runbook (docs/openbao-attended-ceremony-runbook.md); this destroys stored data." + reversible: false + idempotent: non-idempotent + verify: + done_when: The response is complete and the old material no longer works + expect: manual + gates: + - id: response-complete + title: The compromise response is complete + goal: contained-exposure + blocking: true + downstream_risk: Production trust would rest on material an outsider may hold. + checks: + - done_when: Exposed shares and tokens are invalid and the non-secret outcome is recorded + expect: manual diff --git a/runbooks/openbao-restore-drill/pack.yaml b/runbooks/openbao-restore-drill/pack.yaml new file mode 100644 index 0000000..a328f6b --- /dev/null +++ b/runbooks/openbao-restore-drill/pack.yaml @@ -0,0 +1,125 @@ +spec: runbook-pack/v0.2 +id: nk.openbao-restore-drill +title: OpenBao restore drill +owner: net-kingdom +outcome: A Raft snapshot was taken, encrypted, restored into an isolated instance and verified, and the non-secret evidence validates. +engine: native +confirm_phrase: I understand this cannot be undone +framing: + - Never restore into namespace openbao; use a disposable cluster, VM or namespace. + - The plaintext snapshot is removed once encrypted; only hashes and flags go into the evidence. + - Execute after initial configuration exists and before live secrets move in. + - Ported from the security bootstrap console runbook "Restore drill". +actors: + - id: operator + role: OpenBao ceremony operator + description: Takes the snapshot and runs the isolated restore. + privileges: [openbao-root-or-sudo] +datapoints: + - id: drill_dir + label: Drill workspace + type: path + default: /tmp/netkingdom-openbao-restore-drill + dimensions: {actor: operator, kind: location, subsystem: openbao} + ask_in: snapshot + - id: age_recipient + label: Custodian age recipient (public key) + type: string + default: "" + help: The age public key; never a private key. + dimensions: {actor: operator, kind: reference, subsystem: sops-age} + ask_in: snapshot +stages: + - id: snapshot + title: Snapshot and encrypt + goal: An encrypted snapshot with recorded hashes exists and no plaintext remains. + steps: + - id: workspace + title: Prepare the workspace + actor: operator + owner: workstation + uses: [drill_dir] + command: mkdir -p {{drill_dir}} && chmod 700 {{drill_dir}} + idempotent: idempotent + verify: + done_when: The directory exists + command: test -d {{drill_dir}} + expect: exit-0 + - id: take + title: Take and copy out the snapshot + actor: operator + owner: openbao + requires: [workspace] + uses: [drill_dir] + command: | + printf "OpenBao token: " >&2; read -rs OPENBAO_TOKEN; printf "\n" >&2 + printf "%s\n" "$OPENBAO_TOKEN" | kubectl exec -i -n openbao openbao-0 -- sh -c 'read -r BAO_TOKEN; export BAO_TOKEN; bao operator raft snapshot save /tmp/openbao-raft.snap' + unset OPENBAO_TOKEN + kubectl cp openbao/openbao-0:/tmp/openbao-raft.snap {{drill_dir}}/openbao-raft.snap + kubectl exec -n openbao openbao-0 -- rm -f /tmp/openbao-raft.snap + sha256sum {{drill_dir}}/openbao-raft.snap | tee {{drill_dir}}/openbao-raft.snap.sha256 + idempotent: idempotent + idempotent_note: Each run overwrites the local copy with a fresh snapshot. + auto_confirm: manual-only + rollback: Delete the local snapshot and its hash file. + verify: + done_when: The snapshot exists locally + command: test -s {{drill_dir}}/openbao-raft.snap + expect: exit-0 + - id: encrypt + title: Encrypt and remove the plaintext + actor: operator + owner: workstation + requires: [take] + uses: [drill_dir, age_recipient] + command: | + age -r {{age_recipient}} -o {{drill_dir}}/openbao-raft.snap.age {{drill_dir}}/openbao-raft.snap + sha256sum {{drill_dir}}/openbao-raft.snap.age | tee {{drill_dir}}/openbao-raft.snap.age.sha256 + shred -u {{drill_dir}}/openbao-raft.snap + reversible: false + idempotent: non-idempotent + verify: + done_when: The encrypted file exists and the plaintext is gone + command: test -s {{drill_dir}}/openbao-raft.snap.age && test ! -e {{drill_dir}}/openbao-raft.snap + expect: exit-0 + - id: restore + title: Restore in isolation + goal: The snapshot restores into a fresh isolated instance and verifies. + requires: [snapshot] + steps: + - id: isolated-restore + title: Run the isolated restore proof + actor: operator + owner: openbao + description: "1. Use a disposable cluster, VM or namespace, never namespace=openbao. 2. Deploy a fresh OpenBao with empty storage. 3. Decrypt the snapshot only inside the isolated workspace. 4. bao operator raft snapshot restore -force /tmp/openbao-raft.snap. 5. Unseal with the current drill shares. 6. Verify status, mounts, auth methods, policies and a non-production test secret read. 7. Destroy the isolated environment." + idempotent: non-idempotent + auto_confirm: manual-only + rollback: Destroy the isolated environment. + verify: + done_when: Status, mounts, auth, policies and a test secret read verified, and the environment destroyed + expect: manual + gates: + - id: evidence-valid + title: Restore evidence validates + goal: recoverable-custody + blocking: true + downstream_risk: A backup that was never restored is not a backup. + checks: + - done_when: The non-secret evidence JSON validates + command: make -C ../railiance-platform openbao-validate-restore-evidence OPENBAO_RESTORE_EVIDENCE={{drill_dir}}/evidence.json + expect: exit-0 + - id: after + title: Post-restore readiness + goal: Post-unseal checks pass. + requires: ["gate:evidence-valid"] + steps: + - id: post-unseal + title: Run the post-restore readiness check + actor: operator + owner: railiance-platform + command: make -C ../railiance-platform openbao-verify-post-unseal + idempotent: idempotent + verify: + done_when: The readiness check passes + command: make -C ../railiance-platform openbao-verify-post-unseal + expect: exit-0 diff --git a/runbooks/openbao-rotate-unseal-keys/pack.yaml b/runbooks/openbao-rotate-unseal-keys/pack.yaml new file mode 100644 index 0000000..6161c4c --- /dev/null +++ b/runbooks/openbao-rotate-unseal-keys/pack.yaml @@ -0,0 +1,101 @@ +spec: runbook-pack/v0.2 +id: nk.openbao-rotate-unseal-keys +title: Generate new OpenBao unseal keys +owner: net-kingdom +outcome: OpenBao has replacement unseal shares held by their custodians and the rotation is confirmed. +engine: native +confirm_phrase: I understand this cannot be undone +framing: + - Shares and tokens are typed at hidden prompts only; never on a command line or in a log. + - Route each new share directly to its custody holder. + - Needs an unsealed OpenBao and a quorum of current shares. + - Ported from the security bootstrap console runbook "Generate new unseal keys". +actors: + - id: operator + role: OpenBao ceremony operator + description: Runs the rotation with the current-share holders present. + privileges: [openbao-root-or-sudo] +datapoints: + - id: keys_rotated + label: New unseal keys generated + type: enum + values: ["yes", "no"] + default: "no" + dimensions: {actor: operator, kind: state, subsystem: openbao} + ask_in: rotate +stages: + - id: rotate + title: Rotate the shares + goal: New 3-share, threshold-2 shares exist and are with their holders. + steps: + - id: status-before + title: Check for a rotation in progress + actor: operator + owner: openbao + command: | + kubectl exec -it -n openbao openbao-0 -- sh -lc 'printf "OpenBao token: " >&2; stty -echo; read -r BAO_TOKEN; stty echo; printf "\n" >&2; export BAO_TOKEN; bao operator rotate-keys -status' + idempotent: idempotent + auto_confirm: manual-only + verify: + done_when: You know whether a rotation is already in progress + expect: manual + - id: init + title: Start the rotation (once) + actor: operator + owner: openbao + requires: [status-before] + description: Run once. If a rotation is already in progress, do not rerun init; submit existing shares instead. + command: | + kubectl exec -it -n openbao openbao-0 -- sh -lc 'printf "OpenBao token: " >&2; stty -echo; read -r BAO_TOKEN; stty echo; printf "\n" >&2; export BAO_TOKEN; bao operator rotate-keys -init -key-shares=3 -key-threshold=2' + idempotent: non-idempotent + auto_confirm: manual-only + rollback: Cancel the rotation (step "cancel"); no share has changed until the threshold is reached. + verify: + done_when: A nonce was issued + expect: manual + - id: submit + title: Submit current shares + actor: operator + owner: openbao + requires: [init] + description: Enter the nonce, the token and one current share per prompt. Repeat with distinct current shares until progress reaches the threshold. + command: | + kubectl exec -it -n openbao openbao-0 -- sh -lc 'printf "Rotation nonce: " >&2; read -r ROTATION_NONCE; printf "OpenBao token: " >&2; stty -echo; read -r BAO_TOKEN; stty echo; printf "\n" >&2; export BAO_TOKEN; bao operator rotate-keys -nonce="$ROTATION_NONCE"' + reversible: false + idempotent: non-idempotent + auto_confirm: manual-only + verify: + done_when: The threshold is reached and new shares are shown once on screen + expect: manual + - id: distribute + title: Route the new shares to their holders + actor: operator + owner: custody + requires: [submit] + description: Give each new share to its custody holder directly; record only that each holder confirmed. + idempotent: non-idempotent + verify: + done_when: Every holder confirmed receipt + expect: manual + - id: cancel + title: Cancel the rotation (only if something went wrong) + actor: operator + owner: openbao + requires: [init] + description: Use if the nonce, share handling or ceremony context is wrong. Needs a root or sudo token. + command: | + kubectl exec -it -n openbao openbao-0 -- sh -lc 'printf "OpenBao token: " >&2; stty -echo; read -r BAO_TOKEN; stty echo; printf "\n" >&2; export BAO_TOKEN; bao operator rotate-keys -cancel' + idempotent: idempotent + auto_confirm: manual-only + verify: + done_when: The rotation is cancelled + expect: manual + gates: + - id: keys-rotated + title: The new shares are in custody + goal: recoverable-custody + blocking: true + downstream_risk: Old shares stay valid or new ones are lost; either breaks recovery. + checks: + - done_when: Every holder confirmed receipt and the old shares are destroyed + expect: manual diff --git a/runbooks/openbao-token-revocation/pack.yaml b/runbooks/openbao-token-revocation/pack.yaml new file mode 100644 index 0000000..370cc5d --- /dev/null +++ b/runbooks/openbao-token-revocation/pack.yaml @@ -0,0 +1,65 @@ +spec: runbook-pack/v0.2 +id: nk.openbao-token-revocation +title: OpenBao token revocation +owner: net-kingdom +outcome: Short-lived, temporary or disclosed OpenBao tokens are revoked without token values on the local command line. +engine: native +confirm_phrase: I understand this cannot be undone +framing: + - Token values are typed at hidden prompts only; the accessor is the handle for a disclosed token. + - Needs an initialized OpenBao instance. + - Ported from the security bootstrap console runbook "OpenBao token revocation". +actors: + - id: operator + role: OpenBao ceremony operator + description: Revokes tokens with a root or sudo-capable token. + privileges: [openbao-root-or-sudo] +datapoints: + - id: which + label: What to revoke + type: enum + values: [current-helper-token, disclosed-by-accessor] + default: current-helper-token + dimensions: {actor: operator, kind: decision, subsystem: openbao} + ask_in: revoke +stages: + - id: revoke + title: Revoke + goal: The token no longer works. + steps: + - id: revoke-self + title: Revoke the current helper token + actor: operator + owner: openbao + description: For the token in the pod token helper, usually right after an attended check. + command: | + kubectl exec -it -n openbao openbao-0 -- sh -lc 'export BAO_ADDR=http://127.0.0.1:8200; bao token lookup >/dev/null; bao token revoke -self' + reversible: false + idempotent: non-idempotent + auto_confirm: manual-only + verify: + done_when: The revoke command succeeded + expect: manual + - id: revoke-accessor + title: Revoke a disclosed token by accessor + actor: operator + owner: openbao + description: Prompts for a root or sudo token and the accessor of the disclosed token. + command: | + kubectl exec -it -n openbao openbao-0 -- sh -lc 'printf "OpenBao token: " >&2; stty -echo; read -r BAO_TOKEN; stty echo; printf "\n" >&2; export BAO_TOKEN; printf "Token accessor to revoke: " >&2; read -r TARGET_ACCESSOR; bao token revoke -accessor "$TARGET_ACCESSOR"; unset TARGET_ACCESSOR' + reversible: false + idempotent: idempotent + idempotent_note: Revoking an already revoked accessor fails harmlessly. + auto_confirm: manual-only + verify: + done_when: The accessor is revoked + expect: manual + gates: + - id: revoked + title: The token is dead + goal: contained-exposure + blocking: true + downstream_risk: A disclosed token keeps its access. + checks: + - done_when: Using the revoked token is refused + expect: manual diff --git a/runbooks/privacyidea-realm-repair/pack.yaml b/runbooks/privacyidea-realm-repair/pack.yaml new file mode 100644 index 0000000..379ee42 --- /dev/null +++ b/runbooks/privacyidea-realm-repair/pack.yaml @@ -0,0 +1,59 @@ +spec: runbook-pack/v0.2 +id: nk.privacyidea-realm-repair +title: privacyIDEA realm repair +owner: net-kingdom +outcome: The coulomb realm, LLDAP resolver and self-service policies exist again, and platform-root TOTP is enrolled in the repaired realm. +engine: native +framing: + - Passwords are entered at the script's prompts only; never on a command line, in chat or in a file. + - Ported from the security bootstrap console runbook "privacyIDEA realm repair". +actors: + - id: identity-admin + role: Identity admin + description: Holds the pi-admin and LLDAP bind passwords and runs the attended repair. + privileges: [privacyidea-admin, lldap-bind] +datapoints: + - id: realm_repaired + label: Realm repaired and verified + type: enum + values: ["yes", "no"] + default: "no" + dimensions: {actor: identity-admin, kind: state, subsystem: privacyIDEA} + ask_in: repair +stages: + - id: repair + title: Repair the realm + goal: The realm, resolver and policies are recreated and the T06 verification passes. + steps: + - id: run-repair + title: Run the attended realm repair + actor: identity-admin + owner: privacyIDEA + description: Prompts for the pi-admin and LLDAP bind passwords, recreates the realm, resolver, self-enrollment and passthrough policies, then runs T06 verification. Passwords stay in a private temp directory removed on exit. + command: bash sso-mfa/k8s/privacyidea/repair-realm-live.sh + idempotent: idempotent + idempotent_note: Recreates the same objects; safe to rerun. + auto_confirm: manual-only + verify: + done_when: The script's T06 verification reports success + expect: manual + - id: reenroll-totp + title: Enroll or re-enroll platform-root TOTP + actor: identity-admin + owner: privacyIDEA + requires: [run-repair] + description: In privacyIDEA self-service, log in as platform-root and enroll TOTP in the repaired realm. Record the recovery codes in the platform-root safe entry, not here. + idempotent: non-idempotent + rollback: Delete the token in self-service and enroll again. + verify: + done_when: platform-root can log in with a TOTP code + expect: manual + gates: + - id: realm-usable + title: The repaired realm is usable + goal: recoverable-identity + blocking: true + downstream_risk: Logins that need MFA fail for every user of the realm. + checks: + - done_when: The T06 verification passed and platform-root TOTP works + expect: manual diff --git a/runbooks/user-lifecycle-dry-run/pack.yaml b/runbooks/user-lifecycle-dry-run/pack.yaml new file mode 100644 index 0000000..8e8a83a --- /dev/null +++ b/runbooks/user-lifecycle-dry-run/pack.yaml @@ -0,0 +1,78 @@ +spec: runbook-pack/v0.2 +id: nk.user-lifecycle-dry-run +title: Non-root user lifecycle dry-run +owner: net-kingdom +outcome: A non-root user is created, verified, locked and offboarded, LLDAP, groups, MFA and KeyCape claims are checked, and non-secret evidence validates. +engine: native +framing: + - Uses a throwaway subject; no real account is touched. + - Evidence is non-secret; passwords and codes never appear in it. + - Ported from the security bootstrap console runbook "User lifecycle dry-run (T06)"; see NET-WP-0019. +actors: + - id: custodian + role: Platform custodian + description: Runs the dry-run and reviews the evidence. + privileges: [lldap-admin, keycape-read] +datapoints: + - id: subject + label: Dry-run subject + type: string + default: dryrun-user + pattern: "[a-z0-9-]+" + dimensions: {actor: custodian, kind: identity, subsystem: LLDAP} + ask_in: run + - id: email + label: Dry-run email + type: string + default: dryrun-user@example.com + dimensions: {actor: custodian, kind: identity, subsystem: LLDAP} + ask_in: run + - id: display + label: Display name + type: string + default: Dry Run + dimensions: {actor: custodian, kind: identity, subsystem: LLDAP} + ask_in: run + - id: evidence_path + label: Evidence file + type: path + default: /tmp/netkingdom-onboarding-dry-run/evidence.json + dimensions: {actor: custodian, kind: location, subsystem: LLDAP} + ask_in: run +stages: + - id: run + title: Run the lifecycle + goal: Create, verify, lock and offboard completed for the subject. + steps: + - id: template + title: Print the evidence template + actor: custodian + owner: net-kingdom + command: make security-bootstrap-onboarding-dry-run-template + idempotent: idempotent + verify: + done_when: A non-secret evidence JSON template is printed + expect: manual + - id: dry-run + title: Run the dry-run orchestrator + actor: custodian + owner: LLDAP + requires: [template] + uses: [subject, email, display] + command: make security-bootstrap-onboarding-dry-run SUBJECT={{subject}} EMAIL={{email}} DISPLAY="{{display}}" + idempotent: non-idempotent + auto_confirm: manual-only + rollback: Offboard the subject (the orchestrator's final step); delete it in LLDAP if that step did not run. + verify: + done_when: The orchestrator finished create, verify, lock and offboard + expect: manual + gates: + - id: evidence-valid + title: The dry-run evidence validates + goal: governed-lifecycle + blocking: true + downstream_risk: Onboarding is trusted without proof that offboarding works. + checks: + - done_when: The evidence validator accepts the file + command: make security-bootstrap-validate-onboarding-dry-run ONBOARDING_DRY_RUN_EVIDENCE={{evidence_path}} + expect: exit-0 diff --git a/workplans/NK-WP-0044-runbook-packs-for-runbook-tutorials.md b/workplans/NK-WP-0044-runbook-packs-for-runbook-tutorials.md index 8df4b8d..2108664 100644 --- a/workplans/NK-WP-0044-runbook-packs-for-runbook-tutorials.md +++ b/workplans/NK-WP-0044-runbook-packs-for-runbook-tutorials.md @@ -109,9 +109,7 @@ replaced by `runbooks-validate`, and `docs/tutorials/` becomes a pointer. ```task id: NK-WP-0044-T07 -status: wait -needs_human: true -blocking_reason: "Content work, not scheduled: needs a decision to start. The seven runbooks map without gaps (runbook-tutorials/docs/console-mapping.md)." +status: done priority: low state_hub_task_id: "8628d5eb-6e5d-501d-a8f5-960d6b5db8f4" ``` @@ -120,6 +118,8 @@ The security bootstrap console's seven runbooks (privacyIDEA realm repair, key m restore drill, token revocation, user lifecycle dry-run) can become native v0.2 packs. They are linear procedures with 23 command blocks and need none of the gaps G1 to G7 in `runbook-tutorials/docs/console-mapping.md`. The console itself stays a permanent legacy pack. +**Ported on 2026-09-29**: `runbooks/privacyidea-realm-repair`, `openbao-key-compromise`, `openbao-rotate-unseal-keys`, `openbao-emergency-lockdown`, `openbao-restore-drill`, `openbao-token-revocation` and `user-lifecycle-dry-run`. All validate (`make runbooks-validate`, 11 packs). They are `unexercised`: no command was run. Secret-handling steps are `auto_confirm: manual-only`, irreversible steps use the pack `confirm_phrase`, and token and share entry stays at hidden prompts. + ## Acceptance Criteria - The console still works and is launched by the engine without changes to it.