feat(operators): per-session attribution — session role + impersonate RBAC#45
Merged
Conversation
…mpersonate RBAC The platform side of fab's per-session human attribution (nanohype/fab#30). A Platform opts in with spec.attribution; the operator then provisions the two resources fab's role-session entrypoint needs, per tenant, and tears them down on removal or deletion. ─── API (api/platform/v1alpha1) ────────────────────────────────────────── - PlatformSpec.Attribution (*AttributionSpec, optional): Operators []string (required, min 1) and SessionRoleMaxDurationSeconds (*int32, 900–43200, default 3600). nil = unattributed, the default. - PlatformStatus.SessionRoleArn carries the provisioned session role ARN. - Each operator string is reused verbatim as BOTH an allowed STS SourceIdentity and an impersonate resourceName, so the same identity binds the AWS and Kubernetes audit records (the contract fab documents in docs/attribution.md). ─── Session role (platform_session_iam.go) ─────────────────────────────── - ensureSessionRole mints <env>-<platform>-session (same 64-char + FNV-1a hash scheme as the tenant role). Trust: only the tenant IRSA role may assume, and only while setting one of the Platform's operators as SourceIdentity — Action [sts:AssumeRole, sts:SetSourceIdentity] with a StringEquals condition on sts:SourceIdentity. Permissions: the tenant baseline policy (Bedrock invoke) and the same permissions boundary — never broad sts:AssumeRole. - Idempotent: GetRole→Create on miss; on hit it refreshes the trust policy (the operator list can change) via UpdateAssumeRolePolicy and converges the baseline attachment. - Kill-switch parity: when the Platform is suspended the session role's baseline is DETACHED, not attached — otherwise a suspended tenant could keep invoking Bedrock through the session role after its tenant role's baseline was pulled. - deleteIamRole/deleteSessionRole now share a detachAndDeleteRole helper. ─── Impersonate RBAC (platform_rbac.go) ────────────────────────────────── - ensureOperatorImpersonateRBAC creates a ClusterRole granting `impersonate` on exactly the named operator users (never `impersonate *`) bound to the tenant-runtime ServiceAccount, named <tenant-ns>-impersonate. fab's session kubeconfig authenticates with that SA token while impersonating the operator, so apiserver audit records impersonatedUser=<operator>. - Cluster-scoped, so (like the tenant namespace) it's reaped through the finalizer, not OwnerReferences. ─── Wiring (platform_controller.go) ────────────────────────────────────── - Reconcile provisions the pair when spec.attribution is set (after the tenant IRSA role + SA exist), records status.SessionRoleArn, and tears the pair down when attribution is removed. Finalizer cleans up both (no-ops when never enabled). - RBAC markers added for clusterroles/clusterrolebindings and (for escalation prevention) impersonate on users — regenerated into config/rbac/role.yaml. ─── Tests / codegen ────────────────────────────────────────────────────── - 10 unit tests: session-role trust/baseline/duration/idempotency/suspend/ delete (fakeIAM) and the impersonate RBAC create/update/delete (the controller-runtime fake client). go build, vet, golangci-lint, and the internal+api unit suites are green. - make generate + make manifests regenerated deepcopy, the CRD (config + Helm chart copy), the RBAC role, and the CRD reference doc. Draft: pairs with nanohype/fab#30 (also draft). The conformance/envtest suite was not run here (needs setup-envtest); the new logic is unit-covered. Co-authored-by: stxkxsbot <275011021+stxkxsbot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft — the platform side of fab's per-session human attribution. Pairs with nanohype/fab#30 (also draft), which is blocked until this exists.
What
A
Platformopts in withspec.attribution; the operator then reconciles, per tenant, the two resources fab'srole-sessionentrypoint needs:<env>-<platform>-session) — assumable only by the tenant IRSA role, only while setting one of the Platform's operators as STSSourceIdentity(sts:AssumeRole+sts:SetSourceIdentity, with aStringEqualscondition onsts:SourceIdentity). Scoped to the tenant baseline policy (Bedrock invoke) + the same permissions boundary — never broadsts:AssumeRole.ClusterRolegrantingimpersonateon exactly the named operator users (neverimpersonate *), bound to thetenant-runtimeServiceAccount. fab's session kubeconfig authenticates with that SA token while impersonating the operator, so apiserver audit recordsimpersonatedUser=<operator>.Each operator string is reused verbatim as both the allowed
SourceIdentityand the impersonateresourceName, so the same human binds the AWS and Kubernetes audit records.Behavior
spec.attributionis set, torn down when it's removed, and cleaned up in the finalizer (no-ops when never enabled).Verification
fakeIAM; impersonate RBAC create/update/delete via the controller-runtime fake client).go build,go vet,golangci-lint, and theinternal/...+api/...unit suites are green.make generate+make manifestsregenerated deepcopy, CRD (config + Helm chart),config/rbac/role.yaml, and the CRD reference doc.setup-envtest); the new logic is unit-covered.🤖 Generated with Claude Code