FLEX-WP-0037: register ops-warden actor agt-custodian in the production registry
Add agt-custodian (WARDEN-WP-0045) to examples/ops-warden/production_registry_snapshot.json so flex-auth-ops-warden stops refusing it with unknown_actor_resource (CUST-WP-0073-T02). Additions only; regenerated-inventory drift (agt-rehearsal, adm/codex zone fields) not taken. Tests cover own-principal proceed and foreign-principal disallowed_principal. 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:
parent
a310f3fe35
commit
9132f9b677
5 changed files with 233 additions and 1 deletions
|
|
@ -46,6 +46,7 @@
|
|||
| workplan | FLEX-WP-0034 | finished | — | workplans/FLEX-WP-0034-informed-decision-review-discovery.md |
|
||||
| workplan | FLEX-WP-0035 | finished | — | workplans/FLEX-WP-0035-per-identity-application-readback.md |
|
||||
| workplan | FLEX-WP-0036 | active | — | workplans/FLEX-WP-0036-runtime-rename-access-engine.md |
|
||||
| workplan | FLEX-WP-0037 | active | — | workplans/FLEX-WP-0037-ops-warden-agt-custodian-actor.md |
|
||||
| task | FLEX-WP-ADHOC-2026-10-03-T01 | done | — | workplans/ADHOC-2026-10-03.md |
|
||||
| task | FLEX-WP-ADHOC-2026-10-03-T02 | wait | — | workplans/ADHOC-2026-10-03.md |
|
||||
| task | FLEX-WP-ADHOC-2026-10-11-T01 | done | — | workplans/ADHOC-2026-10-11.md |
|
||||
|
|
@ -224,6 +225,9 @@
|
|||
| task | FLEX-WP-0036-T06 | wait | — | workplans/FLEX-WP-0036-runtime-rename-access-engine.md |
|
||||
| task | FLEX-WP-0036-T07 | todo | — | workplans/FLEX-WP-0036-runtime-rename-access-engine.md |
|
||||
| task | FLEX-WP-0036-T08 | todo | — | workplans/FLEX-WP-0036-runtime-rename-access-engine.md |
|
||||
| task | FLEX-WP-0037-T01 | done | — | workplans/FLEX-WP-0037-ops-warden-agt-custodian-actor.md |
|
||||
| task | FLEX-WP-0037-T02 | progress | — | workplans/FLEX-WP-0037-ops-warden-agt-custodian-actor.md |
|
||||
| task | FLEX-WP-0037-T03 | wait | — | workplans/FLEX-WP-0037-ops-warden-agt-custodian-actor.md |
|
||||
| intake | FLEX-IN-0001 | closed | — | intakes/intakes.md |
|
||||
| intake | FLEX-IN-0002 | closed | — | intakes/intakes.md |
|
||||
| intake | FLEX-IN-0003 | open | — | intakes/intakes.md |
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ func TestRunLoadRegistryOpsWardenProduction(t *testing.T) {
|
|||
if err := json.Unmarshal(stdout.Bytes(), &result); err != nil {
|
||||
t.Fatalf("unmarshal load-registry output: %v; stdout = %s", err, stdout.String())
|
||||
}
|
||||
if result["subjects"] != float64(4) || result["relationships"] != float64(4) || result["resource_manifests"] != float64(1) {
|
||||
if result["subjects"] != float64(5) || result["relationships"] != float64(5) || result["resource_manifests"] != float64(1) {
|
||||
t.Fatalf("load-registry result = %+v; want production actor registry counts", result)
|
||||
}
|
||||
}
|
||||
|
|
@ -336,6 +336,36 @@ func TestOpsWardenProductionRegistryActors(t *testing.T) {
|
|||
wantEffect: api.DecisionEffectAuditOnly,
|
||||
wantReason: "advisory_would_signing_policy_matched",
|
||||
},
|
||||
{
|
||||
name: "custodian agent proceeds with its own principal while zone is unknown",
|
||||
subjectID: "agt-custodian",
|
||||
actor: "agt-custodian",
|
||||
actorType: "agt",
|
||||
principal: "agt-custodian",
|
||||
ttlHours: 12,
|
||||
wantEffect: api.DecisionEffectAuditOnly,
|
||||
wantReason: "advisory_would_signing_policy_matched",
|
||||
},
|
||||
{
|
||||
name: "custodian agent IAM subject proceeds while zone is unknown",
|
||||
subjectID: "iam:agt-custodian",
|
||||
actor: "agt-custodian",
|
||||
actorType: "agt",
|
||||
principal: "agt-custodian",
|
||||
ttlHours: 1,
|
||||
wantEffect: api.DecisionEffectAuditOnly,
|
||||
wantReason: "advisory_would_signing_policy_matched",
|
||||
},
|
||||
{
|
||||
name: "custodian agent unknown principal is not matched",
|
||||
subjectID: "agt-custodian",
|
||||
actor: "agt-custodian",
|
||||
actorType: "agt",
|
||||
principal: "adm-full",
|
||||
ttlHours: 1,
|
||||
wantEffect: api.DecisionEffectAuditOnly,
|
||||
wantReason: "advisory_would_disallowed_principal",
|
||||
},
|
||||
{
|
||||
name: "admin actor allow",
|
||||
subjectID: "adm-example",
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ It registers:
|
|||
| --- | --- | --- | --- | --- |
|
||||
| adm-example | adm | adm-full | 48 | adm-example, iam:adm-example |
|
||||
| agt-codex-interhub-bootstrap | agt | agt-interhub-bootstrap | 2 | agt-codex-interhub-bootstrap, iam:agt-codex-interhub-bootstrap |
|
||||
| agt-custodian | agt | agt-custodian | 12 | agt-custodian, iam:agt-custodian (added 2026-10-20, FLEX-WP-0037) |
|
||||
| agt-state-hub-bridge | agt | agt-task-bridge | 24 | agt-state-hub-bridge, iam:agt-state-hub-bridge |
|
||||
| atm-backup-daily | atm | atm-backup-daily | 8 | atm-backup-daily, iam:atm-backup-daily |
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,31 @@
|
|||
"security_zone_reason": "workload_resolution_absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ssh-cert:actor/agt-custodian",
|
||||
"type": "ssh-certificate",
|
||||
"labels": [
|
||||
"ssh-signing",
|
||||
"agt"
|
||||
],
|
||||
"owner": "team:platform-security",
|
||||
"attributes": {
|
||||
"actor_id": "agt-custodian",
|
||||
"actor_type": "agt",
|
||||
"allowed_subjects": [
|
||||
"agt-custodian",
|
||||
"iam:agt-custodian"
|
||||
],
|
||||
"allowed_principals": [
|
||||
"agt-custodian"
|
||||
],
|
||||
"max_ttl_hours": 12,
|
||||
"workload_id": "custodian-agents",
|
||||
"security_zone": "unknown",
|
||||
"security_zone_admission": "unknown",
|
||||
"security_zone_reason": "workload_resolution_absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ssh-cert:actor/agt-state-hub-bridge",
|
||||
"type": "ssh-certificate",
|
||||
|
|
@ -208,6 +233,22 @@
|
|||
"actor_type": "agt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "agt-custodian",
|
||||
"type": "Agent",
|
||||
"display_name": "Supervised interactive custodian agent (custodian-agt on Railiance01)",
|
||||
"organization_relation": "ServiceProvider",
|
||||
"roles": [
|
||||
"Operator"
|
||||
],
|
||||
"groups": [
|
||||
"group:ops-warden-agents"
|
||||
],
|
||||
"tenant": "tenant:platform",
|
||||
"metadata": {
|
||||
"actor_type": "agt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "agt-state-hub-bridge",
|
||||
"type": "Agent",
|
||||
|
|
@ -255,6 +296,7 @@
|
|||
"display_name": "Ops Warden Agents",
|
||||
"members": [
|
||||
"agt-codex-interhub-bootstrap",
|
||||
"agt-custodian",
|
||||
"agt-state-hub-bridge"
|
||||
],
|
||||
"tenant": "tenant:platform"
|
||||
|
|
@ -363,6 +405,53 @@
|
|||
"access_path": "mediated"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rel:agt-custodian-sign-agt-custodian",
|
||||
"system": "ops-warden",
|
||||
"subject": "group:ops-warden-agents",
|
||||
"relation": "signer",
|
||||
"object": "ssh-cert:actor/agt-custodian",
|
||||
"tenant": "tenant:platform",
|
||||
"conditions": [
|
||||
"TimeLimited",
|
||||
"Logged"
|
||||
],
|
||||
"caring": {
|
||||
"id": "descriptor:ops-warden-agt-signer",
|
||||
"profile": "caring-0.4.0-rc2",
|
||||
"subject_type": "Group",
|
||||
"organization_relation": "ServiceProvider",
|
||||
"canonical_role": "Operator",
|
||||
"scope": {
|
||||
"level": "Resource",
|
||||
"id": "ssh-cert:actor/agt-custodian",
|
||||
"tenant": "tenant:platform",
|
||||
"resource": "ssh-cert:actor/agt-custodian"
|
||||
},
|
||||
"planes": [
|
||||
"Identity",
|
||||
"Secret",
|
||||
"Audit"
|
||||
],
|
||||
"capabilities": [
|
||||
"Use",
|
||||
"Operate",
|
||||
"Audit"
|
||||
],
|
||||
"exposure_modes": [
|
||||
"Metadata"
|
||||
],
|
||||
"conditions": [
|
||||
"TimeLimited",
|
||||
"Logged"
|
||||
],
|
||||
"restrictions": [
|
||||
"PrivilegeEscalationBlocked",
|
||||
"SecretAccessBlocked"
|
||||
],
|
||||
"access_path": "mediated"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rel:agt-state-hub-bridge-sign-agt-state-hub-bridge",
|
||||
"system": "ops-warden",
|
||||
|
|
|
|||
108
workplans/FLEX-WP-0037-ops-warden-agt-custodian-actor.md
Normal file
108
workplans/FLEX-WP-0037-ops-warden-agt-custodian-actor.md
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
---
|
||||
id: FLEX-WP-0037
|
||||
type: workplan
|
||||
title: "Register ops-warden actor agt-custodian in the signing-policy registry and roll the flex-auth-ops-warden pin"
|
||||
domain: infotech
|
||||
repo: access-engine
|
||||
status: active
|
||||
owner: claude
|
||||
topic_slug: netkingdom
|
||||
created: "2026-10-20"
|
||||
updated: "2026-10-20"
|
||||
origin: demand
|
||||
origin_ref: CUST-WP-0073-T02 (warden sign agt-custodian refused with unknown_actor_resource)
|
||||
related: [CUST-WP-0073, WARDEN-WP-0045, FLEX-WP-0016, FLEX-WP-0007]
|
||||
---
|
||||
|
||||
ops-warden `WARDEN-WP-0045` (commits `e62763c`, `a07c6c7`) added the actor
|
||||
`agt-custodian` (type `agt`, principal `agt-custodian`, default and max TTL
|
||||
12h, extension `permit-pty`, zone subject workload `custodian-agents`) for the
|
||||
supervised interactive custodian agent of `CUST-WP-0073-T02`. The in-cluster
|
||||
deployment `flex-auth/flex-auth-ops-warden` serves the registry snapshot baked
|
||||
into its image (`/opt/flex-auth/examples/ops-warden/production_registry_snapshot.json`),
|
||||
which does not know the actor, so `warden sign agt-custodian` is refused with
|
||||
`unknown_actor_resource`.
|
||||
|
||||
## Add agt-custodian to the production fixture
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0037-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Regenerated the snapshot with ops-warden `scripts/build_flex_auth_registry.py`
|
||||
from the live inventory and took **only** the `agt-custodian` elements
|
||||
(resource `ssh-cert:actor/agt-custodian`, subject `agt-custodian`, membership
|
||||
in `group:ops-warden-agents`, relationship
|
||||
`rel:agt-custodian-sign-agt-custodian`). The diff to
|
||||
`examples/ops-warden/production_registry_snapshot.json` is additions only.
|
||||
|
||||
Drift deliberately not taken (needs its own owner decision):
|
||||
|
||||
- live inventory also carries `agt-rehearsal` (principal
|
||||
`rehearsal-inert-nohost`, 2h), which is not in ops-warden's committed
|
||||
snapshot either;
|
||||
- the generator now emits `security_zone_admission: unknown` /
|
||||
`catalog_applicability_absent` for `adm-example` (fixture: `not-applicable`,
|
||||
human operator) and drops `workload_id: codex-interhub-bootstrap` from
|
||||
`agt-codex-interhub-bootstrap`.
|
||||
|
||||
Validation: `access-engine load-registry` loads 5 subjects / 5 relationships;
|
||||
`go test ./...` passes. `TestOpsWardenProductionRegistryActors` gains
|
||||
agt-custodian cases: own principal (actor and `iam:` subject) proceeds with
|
||||
`advisory_would_signing_policy_matched`; a foreign principal yields
|
||||
`advisory_would_disallowed_principal`. Both are `audit_only` because the
|
||||
actor's security zone resolves `unknown` (same advisory phase as every other
|
||||
agt actor in this fixture).
|
||||
|
||||
## Build the image and pin flex-auth-ops-warden
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0037-T02
|
||||
status: progress
|
||||
priority: high
|
||||
```
|
||||
|
||||
Pushing `examples/**` to main triggers `.forgejo/workflows/image.yaml`, which
|
||||
publishes `forgejo.coulomb.social/coulomb/flex-auth:main-<short-sha>`. Take the
|
||||
digest from the "Report immutable digest" step and set it in
|
||||
`values/ops-warden.yaml` only (the fetch pin, `values/ops-warden-fetch.yaml`,
|
||||
serves a different registry and stays where it is). Rollback target is the
|
||||
current pin `sha256:138aa3471c46bca6e814691fa1e6520aedda3dffd743e6b09141ab433afdb64b`.
|
||||
flex-auth has no Argo Application; the pin is applied by an attended Helm
|
||||
upgrade, so a pushed pin does not deploy by itself.
|
||||
|
||||
## Attended rollout and smoke
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0037-T03
|
||||
status: wait
|
||||
priority: high
|
||||
needs_human: true
|
||||
blocking_reason: "Attended helm upgrade of flex-auth-ops-warden on railiance01 plus an allow smoke; agents may not change the live cluster."
|
||||
```
|
||||
|
||||
```bash
|
||||
helm upgrade --install flex-auth-ops-warden charts/flex-auth \
|
||||
--namespace flex-auth -f values/ops-warden.yaml --wait --timeout 2m
|
||||
kubectl -n flex-auth get deploy flex-auth-ops-warden \
|
||||
-o jsonpath='{.spec.template.spec.containers[0].image}'
|
||||
```
|
||||
|
||||
Smoke through the `127.0.0.1:19090` tunnel (caller auth is `enforce`, so the
|
||||
request needs the ops-warden ServiceAccount bearer; the simplest real smoke is
|
||||
`warden sign agt-custodian` itself):
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:19090/v1/check \
|
||||
-H 'Content-Type: application/json' -H "Authorization: Bearer $OPS_WARDEN_SA_TOKEN" \
|
||||
-d '{"id":"check:smoke-agt-custodian","tenant":"tenant:platform",
|
||||
"subject":{"id":"agt-custodian","type":"agt"},"action":"sign",
|
||||
"resource":{"id":"ssh-cert:actor/agt-custodian","type":"ssh-certificate","system":"ops-warden"},
|
||||
"context":{"principals":["agt-custodian"],"actor_type":"agt","ttl_hours":1,
|
||||
"pubkey_fingerprint":"SHA256:smoke"}}'
|
||||
```
|
||||
|
||||
Expected: `effect` `audit_only` (or `allow` once the zone resolves), reason
|
||||
`advisory_would_signing_policy_matched` (not `unknown_actor_resource`).
|
||||
Loading…
Add table
Add a link
Reference in a new issue