security: bind Playwright activation and recovery to exact issuer state
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a1346b-72a4-7d40-b8bd-8b8ca346cfa2
This commit is contained in:
tegwick 2026-10-13 23:39:58 +02:00
parent 5dd74dfbdf
commit eba6e1fdbb
4 changed files with 536 additions and 1 deletions

View file

@ -97,6 +97,73 @@ through the transport owner. No immediate global revocation is claimed.
## Verification and completion
### Guarded activation patch preparation
`sso-mfa/k8s/keycape/playwright-activation.py` now implements the private proposal,
ordered patch preparation and readback checks for the existing protected owner
path. It has no CLI or Kubernetes connection. `build` takes original Secret and
Deployment API objects, the complete identity-reference inventory and the exact
reviewed source revision. Its result contains the private original registry and
must remain inside that execution path. Only `summary` is a public receipt.
Source revision and digest bindings do not constitute an approval or custody grant.
The adapter admits one replica, `Recreate`, the pinned immutable KeyCape image,
the expected registry mount and explicit environment bindings. It preserves
unrelated settings and refuses differing bindings. `check_registry` pins the
native verifier's binary digest, checks both configurations using their exact
environment-variable names and policy-store mode, passes private input on stdin,
clears ambient environment overrides and contains diagnostics. The operator must
separately establish verifier compatibility with the deployed image; a locally
built verifier is not production artifact admission.
The source deployment cannot atomically update its registry and environment:
an environment edit starts a rollout, and pending authorization codes are
process-local. The prepared sequence therefore requires an explicit exclusive
maintenance window with a planned authentication interruption:
1. `stop`: bind the original object UIDs, resource versions, data and complete
Deployment spec; require the original single replica Ready; scale to zero.
2. Wait for the controller to observe that generation with zero replicas and
for **every** pod consuming `keycape-config` to disappear. Supply an unfiltered,
complete `sso` PodList; terminating pods still count. Direct/projected volumes,
env references and init/ephemeral containers are checked.
3. `registry`: conditionally write the exact proposed registry data while stopped.
4. `bindings`: conditionally add the agent environment bindings while still stopped.
5. `resume`: after fresh matching registry/spec and quiescence checks, restore one
replica. Then the protected executor must verify generation, exact pod/image,
readiness, real issuance and existing human/service login. Scaling is not that
acceptance proof.
Fresh reads of **both** objects precede every `propose_step`. Each patch tests
the target UID, resourceVersion and complete data/spec before replacement. Use
`verify_response` on the API response and subsequent fresh GET, retaining each
sanitized receipt. Refuse and stop on any mismatch or unknown outcome; no automatic
retry or stale whole-registry replay. Kubernetes offers no cross-object transaction,
so concurrent issuer writers must be excluded by the owner maintenance procedure.
Rollback uses the retained original proposal, never a newly inferred baseline:
`rollback-stop`, observed quiescence, `rollback-registry`, `rollback-bindings`,
`rollback-resume`, then the same native readiness/login verification. Exact
original/candidate matching preserves registrations and bindings that predated
this proposal. Any unrelated intervening data/spec change refuses rollback for
owner reconciliation. No completed changes means no rollback outage. Retain
private recovery state after failure; the adapter neither writes it to disk nor
destroys credentials. Token expiry and connection-termination limits above remain.
Before wiring this into live execution, T02 still requires real identity/custody
references, the protected executor and recovery-state custody, cluster/current
image pinning, the exact supervisor disposition, and source/GitOps reconciliation.
Do not invoke the generic browser helper with a fabricated `--case-acceptance`.
Activation tests use the independent `jsonpatch` Python package to apply RFC 6902
patches to synthetic API objects (test prerequisites: PyYAML and jsonpatch).
They check ordered/partial recovery, created-only preservation, stale writes,
quiescence, revision/readback refusal and verifier containment. Controller state
is simulated; neither these tests nor native `check-config` replace live API and
issuer acceptance.
### Registration and consumer checks
Python tests exercise preservation, add/no-op/remove, lifetime bounds, identity
conflicts, distinct credentials and contained invalid-input output. The native
Go fixture `tests/playwright_registration_native_test.go` is copied into a