During a control-plane version bump from v1.31 to v1.32 on a tenant with spec.oidc.mode other than None, the two apiserver versions serve side by side while the Deployment rolls (controlPlane.replicas defaults to 2), and for that window an aggregated API server inside the tenant can be handed a UID the client chose.
Mechanism, read at the pinned client-go. A v1.31 aggregator strips X-Remote-User, X-Remote-Group and X-Remote-Extra-* from the incoming request before proxying and does not know X-Remote-Uid, so a client-supplied one passes through (transport/round_trippers.go, SetAuthProxyHeaders at v0.31.14). From v1.32 the same function deletes X-Remote-Uid as well (v0.32.13). Once the first v1.32 replica is up it publishes requestheader-uid-headers into extension-apiserver-authentication, which is what #3539 renders, so an extension server built on k8s.io/apiserver starts trusting that header. A request that lands on a v1.31 replica during the roll therefore reaches the extension server with whatever X-Remote-Uid the client sent.
Severity is low. The username and groups still come from the v1.31 aggregator's own authentication and cannot be spoofed this way, RBAC does not consult the UID, and the window closes when the last v1.31 replica is gone. I have not checked whether a v1.31 trust controller drops the key again when it rewrites the ConfigMap; if it does, the window flickers instead of holding.
Two possible homes: a sentence in docs/oidc-tenant.md telling operators that UID-based decisions in tenant extension servers are unreliable until a version bump from v1.31 has finished, or nothing, on the grounds that this is upstream version-skew behaviour. Found during review of #3539 and not changed there.
During a control-plane version bump from v1.31 to v1.32 on a tenant with
spec.oidc.modeother thanNone, the two apiserver versions serve side by side while the Deployment rolls (controlPlane.replicasdefaults to 2), and for that window an aggregated API server inside the tenant can be handed a UID the client chose.Mechanism, read at the pinned client-go. A v1.31 aggregator strips
X-Remote-User,X-Remote-GroupandX-Remote-Extra-*from the incoming request before proxying and does not knowX-Remote-Uid, so a client-supplied one passes through (transport/round_trippers.go,SetAuthProxyHeadersat v0.31.14). From v1.32 the same function deletesX-Remote-Uidas well (v0.32.13). Once the first v1.32 replica is up it publishesrequestheader-uid-headersintoextension-apiserver-authentication, which is what #3539 renders, so an extension server built onk8s.io/apiserverstarts trusting that header. A request that lands on a v1.31 replica during the roll therefore reaches the extension server with whateverX-Remote-Uidthe client sent.Severity is low. The username and groups still come from the v1.31 aggregator's own authentication and cannot be spoofed this way, RBAC does not consult the UID, and the window closes when the last v1.31 replica is gone. I have not checked whether a v1.31 trust controller drops the key again when it rewrites the ConfigMap; if it does, the window flickers instead of holding.
Two possible homes: a sentence in
docs/oidc-tenant.mdtelling operators that UID-based decisions in tenant extension servers are unreliable until a version bump from v1.31 has finished, or nothing, on the grounds that this is upstream version-skew behaviour. Found during review of #3539 and not changed there.