fix: scope suspension events via impersonated parent context (#748) - #749
fix: scope suspension events via impersonated parent context (#748)#749yahyafakhroji wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
@scotwells could you review this file? All other files, except this and internal/apiserver/events/eventsv1_rest.go looks correct.
There was a problem hiding this comment.
@scotwells could you take a look at this file? everything else, expect this file and internal/apiserver/events/scope.go looks correct
084bdf2 to
d463fcd
Compare
|
@yahyafakhroji we should not fallback to annotations. They're untrusted. Upstream controllers must always use impersonation to authenticate to a project. |
d463fcd to
43b9f91
Compare
|
@scotwells Agreed — fallback removed entirely. Scope now comes only from the request's parent context; nothing on the event body can influence it, and the old fallback tests are inverted to assert exactly that. The suspension controller impersonates Separately: the quota controllers emit via the shared EventRecorder with no parent context, so they're unscoped too — happy to open a follow-up. |
… context Controller-emitted Suspended/Reinstated events carried no tenant scope, so they never reached consumer activity feeds. Emit them through a client that impersonates the controller's service account plus the project's parent context, so the events proxy scope-tags them through its existing path rather than a second scope-derivation mechanism. Also in this change: - Redact ProjectSuspension resource names from the event message and from the Suspended condition; both now carry only the reason category. - Fix the ActivityPolicy eventRules to read event.regarding.name and event.note, matching the events.k8s.io/v1 CEL contract. - Filter status.suspensions[] to active phase and document the contract. - Add impersonate and events-create RBAC via kubebuilder markers. The involved-object scope fallback is retained for now; impersonation supplies full parent context, so it is never reached for these events. Fixes #748
43b9f91 to
fff813b
Compare
Fixes #748.
What
1. Suspension events now carry tenant scope, set via impersonation.
Scope annotations (
platform.miloapis.com/scope.*) are derived from the requesting user'siam.miloapis.com/parent-type/parent-nameextras. The propagator emitted through the manager's shared EventRecorder, which authenticates as the controller and carries no parent context — so its events were stored untagged and invisible to project-scoped feed queries.ProjectEventEmitteremits through a client that impersonates the controller's service account plusparent-type: Project/parent-name: <project>, soinjectScopeAnnotationstakes its existing full-parent-context branch unchanged. Per @scotwells' review, no second scope-derivation path.Verified at the code level:
WithImpersonationis enabled in Milo's handler chain (cmd/milo/apiserver/config.go:525) and runs before the Project/Org context decorators, which pass through untouched when the request isn't project-scoped (pkg/server/filters/projects.go:86-91).Events are created as
events.k8s.io/v1so they genuinely carry theRegarding/Notefields the ActivityPolicy reads, with no conversion in between.2. Internal admin detail redacted. The
Suspendedevent message andProject.status.conditions["Suspended"].messageboth embedded ProjectSuspension resource names. Both now carry only the reason category (e.g.Abuse). Project status is tenant-readable, so it needed the same treatment as the event.3. ActivityPolicy CEL fields corrected.
eventRulesreadevent.involvedObject.name/event.message; theevents.k8s.io/v1contract exposesevent.regarding.name/event.note. The summary would have silently rendered empty.4.
status.suspensions[]filtered to active phase, with the active-only contract documented on the API types. No-op today since lift is implemented as delete.5. RBAC regenerated from new kubebuilder markers:
impersonateonserviceaccountsand on the twouserextrasparent keys, pluscreateonevents.k8s.io/events— the generated ClusterRole had no events-create rule in any API group.Notes for reviewers
The involved-object scope fallback is still in the diff. I kept it rather than removing it here: impersonation is verified at the code level but has not yet run at runtime (see below). The two coexist safely — impersonation supplies full parent context, so the fallback branch is never reached for these events. Happy to remove it if you'd rather the PR show only the impersonation path.
The impersonation RBAC is not exercised by CI. The controller-manager authenticates as
admin/system:mastersin test-infra, so a green run there will not prove the new rules are correct. Moving it onto its service account identity changes authentication for every controller in that binary at once and would surface any other gaps in the generated ClusterRole, so it belongs in its own change.Test plan
go build ./...,gofmt -l .— cleango vet ./...— one pre-existing, unrelated finding (organization_types.go:127), confirmed present onmaingo test -timeout 5m ./...— 772 passed, 72 packages