From 26391b0479799430ffcbd46c7caf678f202a0554 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 15 May 2026 14:33:12 +0200 Subject: [PATCH] chore(workplan): mark WARDEN-WP-0001 all tasks done All 10 tasks complete; 42 tests passing, ruff clean. Co-Authored-By: Claude Sonnet 4.6 --- .../WARDEN-WP-0001-initial-implementation.md | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/workplans/WARDEN-WP-0001-initial-implementation.md b/workplans/WARDEN-WP-0001-initial-implementation.md index c673631..1761cea 100644 --- a/workplans/WARDEN-WP-0001-initial-implementation.md +++ b/workplans/WARDEN-WP-0001-initial-implementation.md @@ -4,7 +4,7 @@ type: workplan title: "OpsWarden Initial Implementation" domain: custodian repo: ops-warden -status: active +status: done owner: Bernd topic_slug: custodian created: "2026-03-28" @@ -95,50 +95,50 @@ Writes the signed certificate to stdout (the cert text). Exits non-zero on failu ```task id: WARDEN-WP-0001-T1 state_hub_task_id: 6d643e9d-5e97-4224-9d82-87267b5ba6bc -status: todo +status: done priority: high ``` -- [ ] Create `ops-warden` repo; copy CLAUDE.md template from `ops-bridge`; add +- [x] Create `ops-warden` repo; copy CLAUDE.md template from `ops-bridge`; add `workplans/WARDEN-WP-0001-initial-implementation.md` (this file) -- [ ] Write `SCOPE.md` (see template in §SCOPE below) -- [ ] `pyproject.toml`: `[project.scripts] warden = "warden.cli:app"` -- [ ] Register repo with state-hub (`register_repo`) -- [ ] Create state-hub workstream for this workplan +- [x] Write `SCOPE.md` (see template in §SCOPE below) +- [x] `pyproject.toml`: `[project.scripts] warden = "warden.cli:app"` +- [x] Register repo with state-hub (`register_repo`) +- [x] Create state-hub workstream for this workplan ### T2 — Models and config ```task id: WARDEN-WP-0001-T2 state_hub_task_id: c66fc65a-0b16-4ba2-9e70-a83d875572ec -status: todo +status: done priority: high ``` -- [ ] `models.py`: `ActorType` enum (`adm | agt | atm`); `CertSpec` (actor_name, pubkey_path, +- [x] `models.py`: `ActorType` enum (`adm | agt | atm`); `CertSpec` (actor_name, pubkey_path, ttl_hours, principals); `CertRecord` (identity, valid_before, cert_path, signed_at) -- [ ] `config.py`: load `~/.config/warden/warden.yaml`; required fields: `backend`, +- [x] `config.py`: load `~/.config/warden/warden.yaml`; required fields: `backend`, `ca_key` (local) or `vault_addr` + `vault_role_map` (vault); optional: `inventory_path`, `state_dir` -- [ ] Validate actor name prefix matches `ActorType` (`adm-*`, `agt-*`, `atm-*`) +- [x] Validate actor name prefix matches `ActorType` (`adm-*`, `agt-*`, `atm-*`) ### T3 — LocalCA backend ```task id: WARDEN-WP-0001-T3 state_hub_task_id: a5a41e58-1c6d-42a9-9b11-2088f17c29b5 -status: todo +status: done priority: high ``` -- [ ] `ca.py`: `LocalCA.sign(spec: CertSpec) -> CertRecord` +- [x] `ca.py`: `LocalCA.sign(spec: CertSpec) -> CertRecord` - Calls `ssh-keygen -s -I -n -V +h ` - Parses `ssh-keygen -L -f ` output to extract `Valid before`, `Key ID`, `Principals` - Returns `CertRecord`; writes cert to `~/.local/state/warden/.cert.pub` -- [ ] Default TTLs enforced per `ActorType`: adm → 48 h, agt → 24 h, atm → 8 h +- [x] Default TTLs enforced per `ActorType`: adm → 48 h, agt → 24 h, atm → 8 h (overridable per actor in inventory) -- [ ] `LocalCA.generate_keypair(actor_name) -> (privkey_path, pubkey_path)` — for agt/atm +- [x] `LocalCA.generate_keypair(actor_name) -> (privkey_path, pubkey_path)` — for agt/atm actors that do not bring their own key ### T4 — VaultCA backend @@ -146,27 +146,27 @@ priority: high ```task id: WARDEN-WP-0001-T4 state_hub_task_id: b2067ee6-c9ce-423b-9d60-0d28069fb304 -status: todo +status: done priority: medium ``` -- [ ] `vault.py`: `VaultCA.sign(spec: CertSpec) -> CertRecord` +- [x] `vault.py`: `VaultCA.sign(spec: CertSpec) -> CertRecord` - `POST /v1/ssh/sign/` with `public_key`, `valid_principals`, `ttl` - Parse response `signed_key` field; write to state dir; extract metadata via `ssh-keygen -L` -- [ ] Role map in config: `vault_role_map: {adm: adm-role, agt: agt-role, atm: atm-role}` -- [ ] Graceful error message when Vault is unreachable (with `--backend local` fallback hint) +- [x] Role map in config: `vault_role_map: {adm: adm-role, agt: agt-role, atm: atm-role}` +- [x] Graceful error message when Vault is unreachable (with `--backend local` fallback hint) ### T5 — Principals inventory ```task id: WARDEN-WP-0001-T5 state_hub_task_id: 6d13f8cd-1850-44c9-b769-b21250348319 -status: todo +status: done priority: high ``` -- [ ] `inventory.py`: load/save `inventory.yaml` (format mirrors §4.1 of directive): +- [x] `inventory.py`: load/save `inventory.yaml` (format mirrors §4.1 of directive): ```yaml actors: agt-state-hub-bridge: @@ -180,91 +180,91 @@ priority: high agt: [agt-task-bridge] atm: [atm-backup-daily] ``` -- [ ] `warden inventory list` — print table -- [ ] `warden inventory add --type --principals <...>` -- [ ] `warden inventory remove ` +- [x] `warden inventory list` — print table +- [x] `warden inventory add --type --principals <...>` +- [x] `warden inventory remove ` ### T6 — CLI commands ```task id: WARDEN-WP-0001-T6 state_hub_task_id: 656a4615-92bb-4b5d-9406-e86d24fa15d0 -status: todo +status: done priority: high ``` -- [ ] `warden sign --pubkey ` — sign existing pubkey; write cert to +- [x] `warden sign --pubkey ` — sign existing pubkey; write cert to stdout (the `cert_command` interface for ops-bridge) -- [ ] `warden issue ` — generate keypair + sign; output JSON with +- [x] `warden issue ` — generate keypair + sign; output JSON with `privkey`, `cert`, `valid_before`, `identity` -- [ ] `warden status [actor-name]` — show cert validity, identity, principals, TTL +- [x] `warden status [actor-name]` — show cert validity, identity, principals, TTL remaining; `--all` flag to show all actors in state dir -- [ ] `warden scorecard` — run §5 checks (see T7) -- [ ] `warden inventory ` (list / add / remove) +- [x] `warden scorecard` — run §5 checks (see T7) +- [x] `warden inventory ` (list / add / remove) ### T7 — Scorecard runner ```task id: WARDEN-WP-0001-T7 state_hub_task_id: 7818bcc5-f40e-4793-b117-d36f653ffeed -status: todo +status: done priority: medium ``` -- [ ] `scorecard.py`: implement each §5 row as a named check function returning +- [x] `scorecard.py`: implement each §5 row as a named check function returning `CheckResult(name, passed, detail)` -- [ ] Checks in scope for `ops-warden` (local checks, not host-side): +- [x] Checks in scope for `ops-warden` (local checks, not host-side): - All certs in state dir respect TTL policy for their `ActorType` - No actor in inventory lacks a `principals` entry - Actor name prefix matches declared type - No cert expired by more than 5 min still present in state dir (stale cleanup) -- [ ] Host-side checks (password auth disabled, root login disabled, etc.) are out of scope +- [x] Host-side checks (password auth disabled, root login disabled, etc.) are out of scope — those live in the Ansible `ssh-access-audit.yml` playbook in `railiance-infra` -- [ ] `warden scorecard --json` for machine-readable output +- [x] `warden scorecard --json` for machine-readable output ### T8 — ops-ssh-wrapper script ```task id: WARDEN-WP-0001-T8 state_hub_task_id: e9c28152-5785-4995-83a5-439985ed3db9 -status: todo +status: done priority: medium ``` -- [ ] Ship `scripts/ops-ssh-wrapper` (the Python snippet from §4.1, hardened): +- [x] Ship `scripts/ops-ssh-wrapper` (the Python snippet from §4.1, hardened): - Reads `WARDEN_ACTOR` and `SSH_PUBKEY` env vars - Calls `warden sign $WARDEN_ACTOR --pubkey $SSH_PUBKEY` - Loads cert via `ssh-add`; execs the given command -- [ ] Install as part of `uv tool install` entry points +- [x] Install as part of `uv tool install` entry points ### T9 — Tests ```task id: WARDEN-WP-0001-T9 state_hub_task_id: 950139ab-cc17-4f1d-9a17-d5744e402ddf -status: todo +status: done priority: high ``` -- [ ] Unit tests for `LocalCA` (mock `ssh-keygen` subprocess) -- [ ] Unit tests for inventory YAML round-trip -- [ ] Unit tests for actor name prefix validation -- [ ] Integration test: `LocalCA.sign` on a real test keypair (requires `ssh-keygen` in PATH) -- [ ] Scorecard unit tests (mock cert records) +- [x] Unit tests for `LocalCA` (mock `ssh-keygen` subprocess) +- [x] Unit tests for inventory YAML round-trip +- [x] Unit tests for actor name prefix validation +- [x] Integration test: `LocalCA.sign` on a real test keypair (requires `ssh-keygen` in PATH) +- [x] Scorecard unit tests (mock cert records) ### T10 — Documentation ```task id: WARDEN-WP-0001-T10 state_hub_task_id: 271d6759-e359-41ce-80e4-76c574634a87 -status: todo +status: done priority: medium ``` -- [ ] `SCOPE.md` (see below) -- [ ] `wiki/AccessManagementDirective.md` — copy from `ops-bridge/wiki/` -- [ ] `wiki/OpsWardenConfig.md` — annotated `warden.yaml` reference -- [ ] `wiki/CertCommandInterface.md` — contract for `cert_command` callers (ops-bridge etc.) +- [x] `SCOPE.md` (see below) +- [x] `wiki/AccessManagementDirective.md` — copy from `ops-bridge/wiki/` +- [x] `wiki/OpsWardenConfig.md` — annotated `warden.yaml` reference +- [x] `wiki/CertCommandInterface.md` — contract for `cert_command` callers (ops-bridge etc.) --- @@ -325,9 +325,9 @@ Status: planned (WARDEN-WP-0001 not yet started) ## Acceptance Criteria -- [ ] `warden sign agt-test-actor --pubkey /tmp/test.pub` outputs a valid cert (local backend) -- [ ] `warden status agt-test-actor` shows correct identity, principals, and time-to-expiry -- [ ] `warden scorecard` returns 5/5 on a clean test inventory -- [ ] `warden sign` called from ops-bridge `cert_command` in an integration test tunnel -- [ ] All tests pass: `uv run pytest` -- [ ] All lints pass: `uv run ruff check .` +- [x] `warden sign agt-test-actor --pubkey /tmp/test.pub` outputs a valid cert (local backend) +- [x] `warden status agt-test-actor` shows correct identity, principals, and time-to-expiry +- [x] `warden scorecard` returns 5/5 on a clean test inventory +- [x] `warden sign` called from ops-bridge `cert_command` in an integration test tunnel +- [x] All tests pass: `uv run pytest` +- [x] All lints pass: `uv run ruff check .`