# 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](https://openbao.org/) 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) ```yaml # 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 ```bash # 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//sign/`). ### Example — Railiance01 (browser / operator workstation) ```yaml 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) ```yaml 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): ```bash cd ~/railiance-platform scripts/credential.py exec --grant ops-warden/warden-sign --ttl 15m -- \ warden sign --pubkey ``` `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: ```bash export VAULT_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). ```bash # 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: ```bash 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`) ```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`. ```yaml 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`: ```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 -i `. See `wiki/CertCommandInterface.md` for the full contract and `wiki/playbooks/ops-bridge-tunnel-cert.md` for static-key → cert_command migration.