NK-WP-0058-T05: proposal for a read-only agent status grant (not applied), with guard tests
Dedicated ServiceAccount sso/nk-agent-status (SI-14), read-only Jobs/pods/Deployments/ExternalSecrets in sso, one named ConfigMap in informed-decision and one Argo Application; no Secrets, logs or exec. Options A-D for the founder decision; tests keep the grant narrow and sso workloads free of literal secrets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 943911@bnt-lap001 Assistant-Session: fc322940-1c8e-4bed-ab39-86cec99f23a0
This commit is contained in:
parent
d0fb5dd4aa
commit
9c73a5fa09
3 changed files with 232 additions and 0 deletions
|
|
@ -0,0 +1,69 @@
|
|||
# NK-WP-0058-T05: read-only status for the agent identity (proposal, 2026-11-06)
|
||||
|
||||
**Status: proposed, for founder decision.** Nothing is applied.
|
||||
|
||||
## Why
|
||||
|
||||
On 2026-11-04/05, every status the agent needed was asked of the founder: Job and pod status, the run's result
|
||||
line, ExternalSecret `Ready`, KeyCape and Authelia rollouts, and the live informed-decision registry. The agent's
|
||||
Kubernetes identity today is `ops-mason-builder`, ops-mason's builder account. It can read NetworkPolicies in the
|
||||
path's namespaces and nothing else that matters here (`kubectl auth can-i`, 2026-11-05; contract table part 3).
|
||||
|
||||
## What SI-14 asks
|
||||
|
||||
`canon/standards/service-identity-privilege_v0.1.md` SI-14:
|
||||
- an agent presents as itself (`AGENT`);
|
||||
- its tokens last at most 60 minutes;
|
||||
- its effective authority is everything its tools can reach.
|
||||
|
||||
So widening `ops-mason-builder` is the wrong place: it would give ops-mason's builder status reads it does not
|
||||
need, and the agent would still act as ops-mason.
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Grant | Pod logs | Assessment |
|
||||
| --- | --- | --- | --- |
|
||||
| **A (recommended)** | ServiceAccount `sso/nk-agent-status` with [`rbac.yaml`](rbac.yaml): read Jobs, pods, Deployments, ExternalSecrets in `sso`; one ConfigMap in `informed-decision`; one Argo Application | founder-only | Covers every status but logs. `bx status` already carries the run's result, and `--preflight-only` replaces diagnostic pods |
|
||||
| B | A plus `pods/log` in `sso` | all `sso` pods | RBAC cannot limit logs to the executor's pods by label: this would also read KeyCape, Authelia, LLDAP and privacyIDEA logs. Not recommended |
|
||||
| C | widen `ops-mason-builder` | — | against SI-14 (two actors on one identity). Not recommended |
|
||||
| D | no grant | founder-only | everything stays as on 2026-11-05 |
|
||||
|
||||
## Option A in detail
|
||||
|
||||
**Readable after the grant:**
|
||||
|
||||
| Status asked of the founder on 2026-11-04/05 | Readable with A |
|
||||
| --- | --- |
|
||||
| Run and request Job status, pod phase and exit code | yes (`sso` Jobs, pods) |
|
||||
| The run's result line | no, it is in the pod log. `bx status` and the result receipt carry it |
|
||||
| Diagnostic pod output | not needed: `make preflight` reports every check |
|
||||
| ExternalSecret `Ready` for both custody deliveries | yes |
|
||||
| KeyCape and Authelia rollout status | yes (`sso` Deployments) |
|
||||
| Live NetworkPolicies | yes, already today |
|
||||
| informed-decision registry and routing, live | yes (`informed-decision-workload-registry`, by name) |
|
||||
| Argo pin vs live for informed-decision | yes (the `informed-decision` Application, by name) |
|
||||
|
||||
**Stays founder-only:**
|
||||
- OpenBao: the write role, audit and custody, under `platform-admin`;
|
||||
- Secrets, pod logs, exec and port-forward;
|
||||
- any write.
|
||||
|
||||
**Pod specs are readable,** because `get` on a pod or Deployment includes its env. `sso`'s workloads hold no
|
||||
literal secret values (only file paths, references and URLs). `tools/contract-tests/tests/test_agent_status_grant.py`
|
||||
keeps it that way and keeps the grant read-only and narrow.
|
||||
|
||||
**Tokens:** at most 1 hour (SI-14), minted by the founder once per working session. This is read-only, so it isn't a
|
||||
warden lane:
|
||||
|
||||
```bash
|
||||
kubectl --kubeconfig ~/.kube/config-railiance01 -n sso create token nk-agent-status --duration=1h \
|
||||
> ~/.kube/nk-agent-status.token && chmod 600 ~/.kube/nk-agent-status.token
|
||||
```
|
||||
|
||||
The agent's kubeconfig `~/.kube/config-agent-status` names the cluster and `tokenFile: ~/.kube/nk-agent-status.token`.
|
||||
It holds no token itself. A longer token is a deviation from SI-14 and would need its own decision.
|
||||
|
||||
**Who applies it:** railiance-cluster owns cluster RBAC, so after the decision the change goes there as a reviewed
|
||||
change. The founder applies it, server-side. Verification:
|
||||
- `kubectl auth can-i --list` as `nk-agent-status` shows only these rules;
|
||||
- `can-i get secrets -n sso` and `can-i get pods --subresource=log -n sso` both answer `no`.
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# NK-WP-0058-T05 proposal: read-only status for the agent's own Kubernetes identity. PROPOSED, NOT APPLIED.
|
||||
# Owner of the change: railiance-cluster (cluster RBAC), reviewed with railiance-platform. Founder decision first.
|
||||
#
|
||||
# Option A (recommended): status of the bounded-execution path, no pod logs. A dedicated ServiceAccount, not a
|
||||
# wider ops-mason-builder: SI-14 wants the agent to present as itself, with tokens of at most 60 minutes.
|
||||
# Never: Secrets, pod logs, exec, port-forward, create, update, patch, delete, or anything cluster-wide.
|
||||
# Tests: tools/contract-tests/tests/test_agent_status_grant.py.
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: nk-agent-status
|
||||
namespace: sso
|
||||
labels:
|
||||
app.kubernetes.io/name: nk-agent-status
|
||||
app.kubernetes.io/part-of: net-kingdom
|
||||
automountServiceAccountToken: false # no pod runs as it; the founder mints a 1 h token per session
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: nk-agent-status
|
||||
namespace: sso
|
||||
rules:
|
||||
# executor Jobs and their pods (phase, conditions, exit codes); Deployments for KeyCape/Authelia rollouts
|
||||
- apiGroups: ["batch"]
|
||||
resources: ["jobs"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
# Ready condition of the custody deliveries; an ExternalSecret holds references, never values
|
||||
- apiGroups: ["external-secrets.io"]
|
||||
resources: ["externalsecrets"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: nk-agent-status
|
||||
namespace: sso
|
||||
roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: nk-agent-status}
|
||||
subjects:
|
||||
- {kind: ServiceAccount, name: nk-agent-status, namespace: sso}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: nk-agent-status
|
||||
namespace: informed-decision
|
||||
rules:
|
||||
# the live routing and submitter registry (review-groups.json, intake-submitters.json): non-secret configuration
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps"]
|
||||
resourceNames: ["informed-decision-workload-registry"]
|
||||
verbs: ["get"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: nk-agent-status
|
||||
namespace: informed-decision
|
||||
roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: nk-agent-status}
|
||||
subjects:
|
||||
- {kind: ServiceAccount, name: nk-agent-status, namespace: sso}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: nk-agent-status
|
||||
namespace: argocd
|
||||
rules:
|
||||
# the informed-decision Application's sync revision and status: is live the pinned revision? (contract row O1)
|
||||
- apiGroups: ["argoproj.io"]
|
||||
resources: ["applications"]
|
||||
resourceNames: ["informed-decision"]
|
||||
verbs: ["get"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: nk-agent-status
|
||||
namespace: argocd
|
||||
roleRef: {apiGroup: rbac.authorization.k8s.io, kind: Role, name: nk-agent-status}
|
||||
subjects:
|
||||
- {kind: ServiceAccount, name: nk-agent-status, namespace: sso}
|
||||
Loading…
Add table
Add a link
Reference in a new issue