Port the seven console runbooks to native runbook-pack/v0.2 packs (NK-WP-0044-T07)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 6969@bnt-lap001 Assistant-Session: e8ae73f2-8385-4c9c-b714-94c3c52cdbc0
This commit is contained in:
parent
ecd7c619c5
commit
1fbebb2d94
8 changed files with 594 additions and 3 deletions
79
runbooks/openbao-emergency-lockdown/pack.yaml
Normal file
79
runbooks/openbao-emergency-lockdown/pack.yaml
Normal file
|
|
@ -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
|
||||
84
runbooks/openbao-key-compromise/pack.yaml
Normal file
84
runbooks/openbao-key-compromise/pack.yaml
Normal file
|
|
@ -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
|
||||
125
runbooks/openbao-restore-drill/pack.yaml
Normal file
125
runbooks/openbao-restore-drill/pack.yaml
Normal file
|
|
@ -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
|
||||
101
runbooks/openbao-rotate-unseal-keys/pack.yaml
Normal file
101
runbooks/openbao-rotate-unseal-keys/pack.yaml
Normal file
|
|
@ -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
|
||||
65
runbooks/openbao-token-revocation/pack.yaml
Normal file
65
runbooks/openbao-token-revocation/pack.yaml
Normal file
|
|
@ -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
|
||||
59
runbooks/privacyidea-realm-repair/pack.yaml
Normal file
59
runbooks/privacyidea-realm-repair/pack.yaml
Normal file
|
|
@ -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
|
||||
78
runbooks/user-lifecycle-dry-run/pack.yaml
Normal file
78
runbooks/user-lifecycle-dry-run/pack.yaml
Normal file
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue