net-kingdom/runbooks/ssh-certificates/pack.yaml
tegwick 90112f3ca7
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
Port the NetKingdom packs to runbook-pack/v0.2 (ssh, openbao, flex-auth); update NK-WP-0044
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 295952@bnt-lap001
Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477
2026-09-29 21:40:47 +02:00

144 lines
5.6 KiB
YAML

spec: runbook-pack/v0.2
id: nk.ssh-certificates
title: Short-lived SSH credentials for admins, agents and automations
owner: net-kingdom
outcome: An actor obtains a short-lived CA-signed SSH certificate and uses it through an ops-bridge tunnel, with no static key doing the work.
engine: native
framing:
- Certificate files must be mode 600 and are never reused across reconnects.
- A non-zero cert_command exit is a failure and must trigger backoff.
- ops-warden never vends API keys or passwords; route them with warden route find.
actors:
- id: operator
role: Operator
description: Signs and inspects the certificate and brings the tunnel up, using their own identity.
privileges: [sign-certificate, open-tunnel]
datapoints:
- id: actor_name
label: Actor name
type: string
default: agt-claude-railiance01
pattern: "(adm|agt|atm)-[a-z0-9-]+"
help: "Actor type prefix decides the maximum TTL: adm 48h, agt 24h, atm 8h."
dimensions: {actor: operator, kind: identity, subsystem: ops-warden}
ask_in: ready
- id: pubkey
label: Public key path
type: path
default: ~/.ssh/id_ed25519.pub
dimensions: {actor: operator, kind: location, subsystem: ops-warden}
ask_in: ready
- id: tunnel
label: Tunnel name
type: string
default: k3s-api-railiance01
pattern: "[a-z0-9-]+"
dimensions: {kind: target, subsystem: ops-bridge}
ask_in: ready
stages:
- id: ready
title: Before you start
goal: You know what is in place and which certificates exist already.
steps:
- id: prerequisites
title: Confirm the prerequisites
actor: operator
owner: workstation
description: "In place before you start: the warden CLI is installed and your actor is in the principals inventory (ops-warden); the bridge CLI and a tunnel definition exist (ops-bridge); the target hosts trust the SSH CA and carry the actor principal (railiance-infra)."
idempotent: idempotent
verify:
done_when: All three prerequisites hold
expect: manual
- id: status-before
title: Look at current certificates
actor: operator
owner: ops-warden
requires: [prerequisites]
command: warden status
idempotent: idempotent
verify:
done_when: You know which certificates are current and which are expired
expect: manual
- id: get-certificate
title: Get a certificate
goal: A short-lived certificate for the actor exists and its lifetime is within the limit for the actor type.
requires: [ready]
steps:
- id: sign
title: Sign a public key for the actor
actor: operator
owner: ops-warden
uses: [actor_name, pubkey]
command: warden sign {{actor_name}} --pubkey {{pubkey}} > /tmp/{{actor_name}}-cert.pub
idempotent: idempotent
idempotent_note: Each run issues a new certificate and overwrites the file; the old one expires on its own.
rollback: Delete /tmp/{{actor_name}}-cert.pub; the certificate expires on its own.
evidence: [certificate_valid_before]
verify:
done_when: A certificate file exists and is readable as a certificate
command: ssh-keygen -L -f /tmp/{{actor_name}}-cert.pub
expect: exit-0
- id: inspect-ttl
title: Check the certificate lifetime
actor: operator
owner: ops-warden
requires: [sign]
uses: [actor_name]
command: ssh-keygen -L -f /tmp/{{actor_name}}-cert.pub | grep -E "Key ID|Principals|Valid"
idempotent: idempotent
verify:
done_when: "Valid before is within the actor-type TTL (adm 48h, agt 24h, atm 8h)"
expect: manual
gates:
- id: certificate-valid
title: The certificate is valid and short-lived
goal: short-lived-credentials
blocking: true
downstream_risk: A tunnel opened with a certificate that outlives its limit defeats the point of short-lived credentials.
checks:
- done_when: The lifetime is within the limit for the actor type
expect: manual
- done_when: The certificate file can be read
command: ssh-keygen -L -f /tmp/{{actor_name}}-cert.pub
expect: exit-0
- id: connect
title: Use it through a tunnel
goal: The tunnel that uses the certificate is up and the signing is on record.
requires: ["gate:certificate-valid"]
steps:
- id: tunnel-up
title: Bring up the tunnel that uses cert_command
actor: operator
owner: ops-bridge
uses: [tunnel]
command: bridge up {{tunnel}}
idempotent: idempotent
rollback: bridge down {{tunnel}}
verify:
done_when: The tunnel shows connected
command: bridge status
expect: output-contains
contains: "{{tunnel}}"
- id: audit
title: Confirm the signing was audited
actor: operator
owner: ops-warden
requires: [tunnel-up]
command: warden log | tail -5
idempotent: idempotent
verify:
done_when: Your signing appears in the history
expect: manual
gates:
- id: tunnel-connected
title: The tunnel is connected
goal: private-access
blocking: true
checks:
- done_when: bridge reports the tunnel as connected
command: bridge status | grep -E "{{tunnel}} +connected"
expect: exit-0
ownership:
- {concern: Certificate issuance and TTL policy, owner: ops-warden}
- {concern: Tunnel lifecycle and refresh, owner: ops-bridge}
- {concern: Host CA trust and principals, owner: railiance-infra}