ops-warden/wiki/OpsWardenConfig.md
tegwick 7ce58ae638
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
feat: adopt security zones and explicit workload refs
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0291a-1e87-7151-9934-fcbfe3f65eb1
2026-08-22 15:36:37 +02:00

9 KiB

OpsWarden Configuration Reference

Config file: ~/.config/warden/warden.yaml (override with WARDEN_CONFIG env var)


Backend overview

Backend Config value Use when
Local CA backend: local Labs, CI, air-gapped dev, hosts without platform secrets access
Platform CA backend: vault Production and shared ops environments

Platform standard: Railiance S3 uses OpenBao as the runtime platform secrets service (RAIL-PL-WP-0002 in railiance-platform). OpenBao exposes a Vault-compatible HTTP API, so ops-warden keeps the config keys backend: vault and the vault: block — no separate OpenBao backend name is required. The same config works against OpenBao or HashiCorp Vault if you point vault.addr at either service.

ops-warden signs SSH certificates only. It does not deploy OpenBao, manage unseal keys, or store long-lived API secrets. Cluster bootstrap and custody live in railiance-platform and NetKingdom docs.


Local backend (lab / offline)

# Uses ssh-keygen -s with a CA private key on disk.
backend: local

# Path to the CA private key. Keep this file mode 600 and never commit it.
ca_key: ~/.ssh/ops-ca-user

inventory_path: ~/.config/warden/inventory.yaml
state_dir: ~/.local/state/warden

# Zone-aware flex-auth gate. With no URL, the explicit unknown/build profile
# fails open and records that no evaluator decision was available.
policy:
  flex_auth_url: http://127.0.0.1:8080
  zone_registry_path: /path/to/compiled-flex-auth-registry.json
  failure_modes:
    z0-experimental: fail_open
    z1-operational: fail_open
    z2-protected: fail_open
    z2-continuity: fail_open
    z3-critical: fail_closed
    unknown: fail_open
    not-applicable: fail_closed

Bootstrapping the local CA key

# Generate CA keypair once (offline, secure location)
ssh-keygen -t ed25519 -f ~/.ssh/ops-ca-user -C "Ops SSH User CA (2026)" -N ""
chmod 600 ~/.ssh/ops-ca-user
chmod 644 ~/.ssh/ops-ca-user.pub

# Distribute ops-ca-user.pub to every host:
#   TrustedUserCAKeys /etc/ssh/ca/ca_user.pub  (in sshd_config)
# See railiance-infra bootstrap-ssh-ca.yml playbook.

OpenBao / Vault-compatible backend (production)

Use this backend against the platform OpenBao instance or any other SSH secrets engine that implements the Vault signing API (POST /v1/<mount>/sign/<role>).

Example — Railiance01 (browser / operator workstation)

backend: vault

vault:
  # OpenBao UI/API (KeyCape OIDC). Prefer short-lived tokens from policy, not root.
  addr: https://bao.coulomb.social

  mount: ssh

  role_map:
    adm: adm-role
    agt: agt-role
    atm: atm-role

  # OpenBao accepts the same X-Vault-Token header name as Vault.
  token_env: VAULT_TOKEN

inventory_path: ~/.config/warden/inventory.yaml
state_dir: ~/.local/state/warden

# Configure after flex-auth ssh-certificate policies are deployed:
# policy:
#   flex_auth_url: http://flex-auth-ops-warden.flex-auth.svc.cluster.local:8080
#   zone_registry_path: /etc/warden/production_registry_snapshot.json

Example — in-cluster caller (pod or trusted host)

backend: vault

vault:
  addr: http://openbao.openbao.svc.cluster.local:8200
  mount: ssh
  role_map:
    adm: adm-role
    agt: agt-role
    atm: atm-role
  token_env: VAULT_TOKEN

Choose the addr that matches where warden runs: operators on a laptop use the external HTTPS endpoint; workloads inside the cluster use the internal service URL. See railiance-platform/docs/openbao.md for deployment and access paths.

Authentication

Preferred: use the railiance-platform credential broker so VAULT_TOKEN is injected only into the child process (no manual export):

cd ~/railiance-platform
scripts/credential.py exec --grant ops-warden/warden-sign --ttl 15m -- \
  warden sign <actor> --pubkey <path>

warden route show ops-warden-warden-sign-token · wiki/playbooks/ops-warden-warden-sign-token.md.

Manual fallback — export a scoped token for the current shell only:

export VAULT_TOKEN="<short-lived-warden-sign-token>"

warden reads the env var named in vault.token_env (default VAULT_TOKEN). OpenBao uses the same header; you do not need a separate BAO_TOKEN unless you configure token_env that way.

See wiki/playbooks/operator-openbao-token-hygiene.md for hygiene rules, OIDC routing, and HTTP 403 recovery.

On failure, warden sign suggests falling back to --backend local only for lab recovery — not as a production substitute.

SSH secrets engine setup (OpenBao)

Run once per environment after OpenBao is initialized and unsealed. Adjust TTL limits to match ActorType policy in wiki/AccessManagementDirective.md (adm 48 h, agt 24 h, atm 8 h).

# OpenBao CLI (bao) — preferred on Railiance
bao secrets enable ssh

bao write ssh/roles/agt-role \
    key_type=ca \
    allowed_users="*" \
    allow_user_certificates=true \
    default_user="agt" \
    ttl=24h max_ttl=24h

bao write ssh/roles/adm-role \
    key_type=ca \
    allowed_users="*" \
    allow_user_certificates=true \
    default_user="adm" \
    ttl=48h max_ttl=48h

bao write ssh/roles/atm-role \
    key_type=ca \
    allowed_users="*" \
    allow_user_certificates=true \
    default_user="atm" \
    ttl=8h max_ttl=8h

HashiCorp Vault uses the same paths with the vault CLI:

vault secrets enable ssh
vault write ssh/roles/agt-role key_type=ca ...  # same role parameters

Mount path defaults to ssh; override with vault.mount in warden.yaml if your engine lives elsewhere.

Platform references

Topic Location
OpenBao deploy, unseal, OIDC admin railiance-platform/docs/openbao.md
Host CA trust and principals railiance-infra Ansible playbooks
Signing contract for callers wiki/CertCommandInterface.md

Principals inventory (inventory.yaml)

actors:
  # Actor name must carry the prefix matching its type:
  #   adm-*  for adm, agt-*  for agt, atm-*  for atm
  agt-state-hub-bridge:
    type: agt
    # Principals embedded in the cert; matched against /etc/ssh/auth_principals/%u
    principals:
      - agt-task-bridge
    # Certificate TTL in hours. Defaults: adm=48, agt=24, atm=8
    ttl_hours: 24
    description: "ops-bridge tunnel agent for state-hub"

  adm-bernd:
    type: adm
    principals:
      - adm-full
    ttl_hours: 48

  atm-backup-daily:
    type: atm
    principals:
      - atm-backup-daily
    ttl_hours: 8
    description: "nightly backup automation"

hosts:
  # Optional: documents which principals are allowed on each host.
  # Not enforced by warden; used for reference and future tooling.
  railiance01:
    allowed_principals:
      agt:
        - agt-task-bridge
      atm:
        - atm-backup-daily

Policy gate (flex-auth, zone-aware)

warden sign and warden issue evaluate flex-auth whenever a URL is configured. The target resource's compiled security_zone selects the local PEP failure mode. A rendered deny always blocks; evaluator failure blocks or proceeds per zone. Signing records policy_decision_id when present plus policy_zone, policy_failure_mode, and policy_outcome.

policy:
  flex_auth_url: http://127.0.0.1:8080
  zone_registry_path: registry/flex-auth/production_registry_snapshot.json
  failure_modes:
    z0-experimental: fail_open
    z1-operational: fail_open
    z2-protected: fail_open
    z2-continuity: fail_open
    z3-critical: fail_closed
    unknown: fail_open
    not-applicable: fail_closed
  tenant: tenant:platform
  subject_env: WARDEN_POLICY_SUBJECT
  system: ops-warden

policy.enabled and the global policy.fail_closed are retired; configuration loading rejects them with a migration error. Stance is owned by flex-auth's versioned policy package, not this block. The failure-mode map is PEP behavior for an unavailable or invalid evaluator.

Full request shape and rollout notes: wiki/PolicyGatedSigning.md.


Environment variables

Variable Default Description
WARDEN_CONFIG ~/.config/warden/warden.yaml Config file path
VAULT_TOKEN API token for backend: vault (OpenBao or Vault; name configurable via vault.token_env)
WARDEN_POLICY_SUBJECT IAM subject id for flex-auth checks

cert_command integration with ops-bridge

Add cert_command to a tunnel in ~/.config/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: "warden sign agt-state-hub-bridge --pubkey ~/.ssh/agt-state-hub-bridge_ed25519.pub"

ops-bridge runs cert_command before each SSH launch, captures stdout as the cert, and passes it alongside the private key via ssh -i <key> -i <cert>. See wiki/CertCommandInterface.md for the full contract and wiki/playbooks/ops-bridge-tunnel-cert.md for static-key → cert_command migration.