RPF-WP-0062-T01: custodian agent observer identity manifests (CUST-WP-0073-T02)

Namespace custodian-agents, ServiceAccount custodian-agent (no automount, no
token Secret), curated read-only ClusterRole custodian-supervised-observer and
its binding, per the founder decision of 2026-10-20. Offline pytest guard
asserts no forbidden resource, no write verb, no wildcard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 605711@bnt-lap001
Assistant-Session: 1f5e2256-181f-49ed-b7b1-a1c34bf37a61
This commit is contained in:
codex 2026-10-20 13:38:05 +02:00
parent acd1ade59a
commit 567d2c671b
8 changed files with 319 additions and 0 deletions

View file

@ -0,0 +1,105 @@
---
id: RPF-WP-0062
type: workplan
title: "Custodian agent observer identity: read-only ServiceAccount and curated ClusterRole on railiance01"
domain: financials
repo: railiance-platform
status: active
owner: claude
created: "2026-10-20"
updated: "2026-10-20"
flavor: implementation
origin: cross-repo
origin_ref: the-custodian CUST-WP-0073-T02 (founder decision 2026-10-20)
---
Demand: CUST-WP-0073-T02 needs a supervised agent identity on railiance01 that
can observe cluster state without reaching credentials or writing anything.
This repo owns the RBAC half. The earlier health-only candidate
(`the-custodian/docs/changes/CUST-WP-0073/credential-boundary-r1/identity-rbac.yaml`)
is superseded by the founder's 2026-10-20 decision.
Founder decision (Bernd Worsch, 2026-10-20):
- Namespace `custodian-agents`, ServiceAccount `custodian-agent`,
`automountServiceAccountToken: false`. No static token Secret.
- Credentials are short-lived TokenRequest tokens issued by the supervisor.
- ClusterRole `custodian-supervised-observer`: get/list/watch on namespaces,
nodes, pods, deployments/statefulsets/daemonsets/replicasets, services,
endpoints/endpointslices, ingresses, events (core + events.k8s.io),
persistentvolumeclaims, Argo CD applications/appprojects, CNPG
clusters/backups/scheduledbackups; get on `/version /healthz /livez /readyz`.
- Explicitly not: secrets, configmaps, pods/log, pods/exec, pods/attach,
pods/portforward, serviceaccounts/token, nodes/proxy, any write verb, any
wildcard. Privileged actions go through the separate gated executor.
Layout:
- Manifests: `argocd/platform-addons/custodian-agents/`
- Child Application (manual sync, pinned, no finalizer):
`argocd/railiance01/applications/custodian-agents.application.yaml`,
project `railiance-platform-addons` (already whitelists Namespace,
ClusterRole, ClusterRoleBinding, ServiceAccount).
- Offline guard: `tests/test_custodian_agent_rbac.py`.
The railiance01 root `railiance-apps-root` has no `automated` block, so a push
to `main` changes nothing live. Going live is an attended founder action.
## Token issuance (supervisor only)
Recommended TTL 24h (shorter is fine; the API server may cap it via
`--service-account-max-token-expiration`). Audience is the API server's own
service-account audience, so the token works against the API and nothing else:
```bash
kubectl -n custodian-agents create token custodian-agent \
--duration=24h --audience=https://kubernetes.default.svc.cluster.local
```
The token is handed to the agent's restricted kubeconfig by the supervisor; it
is never committed, logged, or stored in the State Hub. It cannot be renewed by
the agent (no `serviceaccounts/token` grant).
## Place the identity manifests and offline guard
```task
id: RPF-WP-0062-T01
status: done
priority: high
```
Namespace, ServiceAccount, ClusterRole and ClusterRoleBinding written; the
pytest guard parses the kustomization and asserts read-only verbs, the exact
curated resource set, no forbidden resource, no subresource, no wildcard, no
Secret, no automount, and a manual pinned Application.
## Place the pinned child Application
```task
id: RPF-WP-0062-T02
status: todo
priority: high
```
`custodian-agents.application.yaml` pins `targetRevision` to the commit that
introduced the manifests. No `automated` block, no finalizer.
## Attended sync and identity proof on railiance01
```task
id: RPF-WP-0062-T03
status: wait
priority: high
needs_human: true
blocking_reason: "Founder runs the attended root sync for the custodian-agents child, issues a token and runs the can-i proof; live production change."
```
1. `make argocd-pin-drift` — stop on any `DRIFT` line.
2. Sync only this child out of the root at the reviewed revision, then the child
(commands in the CUST-WP-0073-T02 handoff / this repo's commit log).
3. Issue a token (above) and, as the agent identity, prove:
`kubectl auth can-i get secrets -A` → no; `can-i create pods/exec -A` → no;
`can-i get pods/log -A` → no; `can-i get configmaps -A` → no;
`can-i list pods -A` → yes; `kubectl auth whoami` shows
`system:serviceaccount:custodian-agents:custodian-agent`, no `system:masters`.
4. Report the result to CUST-WP-0073-T02.