ops-warden/wiki/CertCommandInterface.md
tegwick 661176e37a
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Retire CoulombCore references; correct the 16443 diagnosis
CoulombCore is being retired, so the docs stop using it as the reference host:
state-hub-coulombcore examples become state-hub-railiance01, and the reuse-surface
playbook no longer attributes bao.coulomb.social to it — that resolves to
92.205.62.239, which is railiance01. The openrouter lane keeps its factual note
about ESO on the CoulombCore cluster, with a retirement flag for its owner.

Also corrects this session's own error. The WP-0031 evidence blamed the
Unauthorized on a local-port collision between k3s-api-coulombcore and
k3s-api-haskelseed. That was wrong: the haskelseed tunnel is a reverse forward,
where local_port is a destination rather than a listener, so they never competed.
16443 was simply CoulombCore's k3s — a different cluster whose client CA does not
know that cert. The wrong reason had already gone to flex-auth, so it is
corrected in the file rather than quietly dropped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 19:31:41 +02:00

3.3 KiB

cert_command Interface

Version: 1.0 Date: 2026-03-28 Purpose: Define the contract between OpsWarden (issuer) and callers such as ops-bridge (consumer) for just-in-time SSH certificate acquisition.


Overview

cert_command is a shell string that a caller executes to obtain a short-lived, CA-signed SSH certificate for a named actor. The caller passes the cert to the SSH process alongside the actor's private key.

This interface is intentionally tool-agnostic: the caller (ops-bridge, a script, a CI pipeline) does not need to know whether the CA is a local file, OpenBao, or another Vault-compatible SSH secrets engine. Any command that writes a cert to stdout and exits 0 satisfies the contract.


Contract

Invocation

warden sign <actor-name> --pubkey <path/to/actor.pub>

Or any equivalent shell command:

bao write -field=signed_key ssh/sign/agt-role public_key=@/tmp/key.pub
ssh-keygen -s /path/to/ca -I agt-test -n agt-task -V +24h /tmp/key.pub && cat /tmp/key-cert.pub

Success (exit 0)

  • Stdout: certificate text only — a single line starting with the key type, e.g.:
    ssh-ed25519-cert-v01@openssh.com AAAA...
    
  • Stderr: ignored by the caller (warden may print warnings there)
  • Side effect: cert is also written to ~/.local/state/warden/<actor>-cert.pub by warden (for use by warden status and warden scorecard)

Failure (exit non-zero)

  • Exit code: any non-zero value
  • Stdout: ignored
  • Stderr: passed through to caller logs / audit detail field
  • Caller behaviour: treat as a transient error; apply reconnect backoff and retry

Caller Responsibilities (ops-bridge)

  1. Run cert_command via subprocess.run(shell=True) before each SSH subprocess launch
  2. Write stdout to a tempfile in the state dir: ~/.local/state/bridge/<tunnel>-cert.pub
  3. Add -i <cert_path> after -i <key_path> in the ssh command
  4. Parse ssh-keygen -L -f <cert> to extract Key ID → log as cert_identity in audit
  5. Parse Valid before: → schedule pre-emptive cert refresh ~5 min before expiry
  6. On cert_command failure: log BRIDGE_DISCONNECTED with stderr; apply backoff

What the Caller Must NOT Do

  • Cache or reuse a cert across reconnects (always re-run cert_command per reconnect)
  • Write the cert to disk with world-readable permissions (mode 600)
  • Ignore a non-zero exit from cert_command (must treat as failure, trigger backoff)

Example: ops-bridge tunnels.yaml

tunnels:
  state-hub-railiance01:
    host: railiance01
    remote_port: 8001
    local_port: 8000
    ssh_user: agt-state-hub-bridge
    ssh_key: ~/.ssh/agt-state-hub-bridge_ed25519
    actor: agt-state-hub-bridge
    # cert_command is optional. When absent, ssh_key is used directly (static key mode).
    cert_command: "warden sign agt-state-hub-bridge --pubkey ~/.ssh/agt-state-hub-bridge_ed25519.pub"

TTL Guidelines (AccessManagementDirective §2)

Actor type Max TTL Pre-emptive refresh
adm 48 h 5 min before expiry
agt 24 h 5 min before expiry
atm 8 h 5 min before expiry

ops-bridge enforces the refresh schedule. OpsWarden enforces the max TTL at signing time.


Backward Compatibility

Callers that do not set cert_command continue to use the static key (ssh_key) with no TTL, cert logic, or refresh. The two modes are fully independent.