Skip to content

feat(iam): re-home audit-log-querier grant to a standalone activity role (replaces #677)#679

Draft
ecv wants to merge 1 commit into
mainfrom
iam-rehome-audit-log-querier-standalone-role
Draft

feat(iam): re-home audit-log-querier grant to a standalone activity role (replaces #677)#679
ecv wants to merge 1 commit into
mainfrom
iam-rehome-audit-log-querier-standalone-role

Conversation

@ecv

@ecv ecv commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces #677. Closes #676.

Delivers the "users can query their own audit logs" capability from the activity service overlay without mutating the shared iam-user-self-manage Role — keeping the foundational milo control plane free of any activity dependency.

Why #677 doesn't work

#677 added a partial Role/iam-user-self-manage manifest (only spec.inheritedRoles) to the activity overlay and relied on server-side apply to merge that single entry into the core role.

That assumption fails under the actual GitOps setup (datum-cloud/infra):

  • The core role is applied by the milo-core-control-plane-crds Flux Kustomization (./crd/overlays/core-control-plane../../../roles).
  • The partial overlay would be applied by milo-activity-policies (./services/activity).
  • Flux's kustomize-controller server-side-applies every Kustomization under a single shared field manager (kustomize-controller) — there is no per-Kustomization manager and no field-manager override on the Kustomization spec.

With one shared manager, each apply re-declares its full field set, so SSA prunes any field that manager owned but no longer lists. The two Kustomizations therefore prune each other's fields on every reconcile (interval: 1h each). The role flip-flops:

After reconcile of… Result
milo-core-control-plane-crds includedPermissions present, audit inheritance gone
milo-activity-policies audit inheritance present, all self-manage permissions gone

inheritedRoles being listType=map only enables clean merge across distinct field managers owning distinct keys — which Flux does not provide here. (The companion infra PR's premise that inheritedRoles is "an atomic list" is also incorrect — it is map-type — but the shared-field-manager problem stands regardless.)

What this PR does instead

  • New: config/services/activity/roles/activity-self-audit-log-querier.yaml — a standalone Role that inherits activity.miloapis.com-audit-log-querier. A distinct object with a single owner → no cross-Kustomization field contention.
  • Edit: config/services/activity/kustomization.yaml — wires the role into the activity overlay.
  • Webhook (user_webhook.go): grants the role per-user, scoped to the user's own User resource, mirroring the existing user-self-manage PolicyBinding, so self-audit access stays self-scoped. Gated on a configurable role name (empty → skip), so the core control plane carries zero activity coupling when activity is not deployed.
  • Controller (user_controller.go): attaches an owner reference to the binding for GC on user deletion, IsNotFound-guarded so it is inert when the activity overlay is absent.

Why not a static group binding

A single PolicyBinding to system:authenticated (like organization-creator-policy) needs no per-user code — but only resourceKind: User is available for a Group subject, which would grant every user query access to all users' audit logs. Rejected as a scope regression.

Validation

  • go build on edited packages → clean.
  • task generate:code → no manifest diff (no new API types/markers).
  • Full builds/tests via CI.

Coordination

  • Part of: datum-cloud/infra#2943, datum-cloud/infra#2939.
  • Pairs with: datum-cloud/infra#2953 (its "atomic list" justification should be corrected to "map-type, but shared Flux field manager").

🤖 Generated with Claude Code

Replaces #677. That PR added a partial Role manifest (only
spec.inheritedRoles) for the existing iam-user-self-manage role to the
activity overlay, relying on server-side apply to merge it into the core
role. That does not hold under the actual GitOps setup: both the core role
(via milo-core-control-plane-crds) and the partial overlay (via
milo-activity-policies) are applied by Flux's kustomize-controller, which
uses a single shared field manager (kustomize-controller) for every
Kustomization. With one shared manager, each apply re-declares its full
field set, so the two Kustomizations prune each other's fields on every
reconcile -- the role flip-flops between "has includedPermissions, no audit
inheritance" and "has audit inheritance, no permissions". The map-type of
inheritedRoles only helps across distinct field managers, which Flux does
not provide here.

This instead delivers the self-audit capability without mutating the shared
core role:

- New standalone Role activity-self-audit-log-querier in the activity
  service overlay, inheriting activity.miloapis.com-audit-log-querier. It is
  a distinct object with a single owner -- no cross-Kustomization field
  contention.
- The user webhook grants it per-user, scoped to the user's own User
  resource (mirroring the existing user-self-manage PolicyBinding), so
  self-audit access stays self-scoped. A static Group binding to
  system:authenticated was rejected because only resourceKind=User is
  available for a group subject, which would grant every user access to all
  users' audit logs.
- The grant is gated on a configurable role name (empty -> skip), so the
  core control plane carries zero activity coupling when activity is not
  deployed -- preserving the dependency-decoupling goal of #676.
- The user controller attaches an owner reference to the binding for GC on
  user deletion, guarded by IsNotFound so it is inert when the activity
  overlay is absent.

Refs: #676
Replaces: #677

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ecv ecv marked this pull request as draft June 30, 2026 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move audit-log-querier role inheritance from core CRD bundle to activity service

1 participant