Skip to content

Impersonate only ACE extras#645

Open
ArnobKumarSaha wants to merge 1 commit into
masterfrom
arnob-extra-allow
Open

Impersonate only ACE extras#645
ArnobKumarSaha wants to merge 1 commit into
masterfrom
arnob-extra-allow

Conversation

@ArnobKumarSaha

Copy link
Copy Markdown
Member

Problem

DelegatingClient.Impersonate forwards every extra key from the caller's identity into the impersonation config (both the rest config and the impersonating round tripper).

Kubernetes authorizes impersonated extras per key, as a subresource: impersonating extra key k requires impersonate on userextras/k in authentication.k8s.io. Our components only hold that grant for userextras/ace.appscode.com/org-id. So any caller arriving with provider-injected extras had the entire request denied in impersonation authorization (round 1), before the impersonated user's own RBAC was ever consulted.

Real-world hit: a spoke cluster imported into ACE through Rancher. Rancher authenticates its own user and forwards principalid, username, requesthost, requesttokenid as extras; none is ace.appscode.com/org-id. Result:

userextras.authentication.k8s.io "local://user-5klht" is forbidden: User
"system:serviceaccount:kubeops:kube-ui-server" cannot impersonate resource
"userextras/principalid" in API group "authentication.k8s.io" at the cluster scope

On Kubernetes 1.36 the same denial surfaces through the new constrained-impersonation path with a much more confusing message (cannot impersonate-on:user-info:get resource "appreleases" ...). Granting the constrained verbs does not help: constrained impersonation additionally rejects extra keys that are not domain-prefixed, so bare keys like principalid can only ever go through the legacy path.

Fix

Allowlist instead of pass-through: keep only ace.appscode.com/* extras, drop the rest.

Every platform invents its own extras — Rancher sends principalid/username, EKS sends arn/sessionName/principalId, OpenShift sends scopes.authorization.openshift.io, and the apiserver itself adds authentication.kubernetes.io/*. Chasing each of them with wider RBAC never converges; an allowlist is correct on any provider.

Standard RBAC ignores extras entirely, so on an ordinary cluster this changes no authorization decision.

Compatibility note

This is a shared library, so the behavior change reaches every consumer of Impersonate, not just the caller that hit the bug. A consumer running a custom authorization webhook that keys off provider extras would see different decisions, since those extras no longer reach the apiserver. I checked kubeops.dev/ui-server; I have not audited the full consumer set — worth a second opinion from someone who knows whether any deployment relies on extras for authorization.

Refs

DelegatingClient.Impersonate forwarded every extra key from the caller's
identity. Impersonating an extra key requires an `impersonate` grant on
userextras/<key>, so a caller carrying provider-injected extras -- Rancher's
principalid/username, EKS' arn, the reserved authentication.kubernetes.io/*
keys -- had the whole request denied during impersonation authorization.

Keep only ace.appscode.com/* extras, which are the ones our components hold
RBAC for and the only ones that carry meaning downstream. RBAC ignores extras,
so dropping the rest changes no authorization decision on an ordinary cluster.

Signed-off-by: Arnob Kumar Saha <arnob@appscode.com>
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.

1 participant