fix(agent): apply pod-security labels from namespaceLabels again - #5687
Conversation
namespaceLabels in fleet.yaml exists so that a bundle can label the namespace it deploys into, and setting Pod Security Admission labels that way is what SURE-5906 (rancher#1484) asked for. Since rancher#5156 the agent dropped every pod-security.kubernetes.io/ key from the options and skipped those keys when pruning, so labels a user declares are silently ignored (SURE-11931, rancher#5550). That filter was the fix for GHSA-864g-863m-vcvq, where a bundle could weaken PSA enforcement on its target namespace because the agent applied namespaceLabels with its own cluster-admin credentials. rancher#5351 changed that: the namespace is patched as the deployment's service account, so downstream RBAC decides what a bundle may write to it, and the prefix filter is no longer the control it was. The filter never covered the whole path either. The same label reaches the same API call, under the same credentials, when it is declared on a Namespace object in the bundle, or when a pre-provisioned namespace is adopted with helm.takeOwnership. It also matched on the key alone, so it refused tightening enforcement, and setting warn or audit levels, just as it refused weakening. Pod-security labels are therefore treated like every other namespace label again: applied when declared, pruned when they are not. Dropping the pruning exemption as well keeps un-setting working, which that exemption broke for these keys specifically. Deployments that resolve to no service account still patch the namespace as the agent, which is cluster admin. Restricting those requires pinning a service account, in the bundle or through a Policy. Refers to rancher#5550
| func addLabelsFromOptions(nsLabels map[string]string, optLabels map[string]string) { | ||
| maps.Copy(nsLabels, optLabels) |
There was a problem hiding this comment.
#5550 came from operators, not from tenants. The complaint is that an operator writes pod-security labels in namespaceLabels and the agent silently drops them. Those deployments are exactly the ones in the d.client fallback: no fleet-default service account ships with the agent chart, so out of the box nothing is pinned. Restricting pod-security labels to the impersonated path would keep ignoring them for every one of the people who reported this, and silently at that.
The tenant path is the one that's already handled. Since #5351 the namespace is patched as the deployment's service account, so whether a bundle may touch its target namespace at all is decided by that account's downstream RBAC. That's a coarser gate than the prefix filter, since RBAC scopes the namespace and not individual labels, but it's the same gate Kubernetes itself uses for PSA, and it's the one operators control through Policy and allowedServiceAccounts. The filter is redundant there.
On the fallback path being an escalation: there's no service account in that path, so every manifest in the bundle is already applied with the agent's cluster-admin credentials. You're right that PodSecurity admission still applies to cluster-admin, so relabeling a namespace is a genuine capability gain, but the filter never prevented it. The same actor can point the bundle at a new namespace (Fleet creates it, and with no cluster-wide AdmissionConfiguration default it has no enforcement at all), ship a Namespace object carrying the labels, or adopt an existing namespace with helm.takeOwnership. All three go through the same credentials and the same API call, and none of them were ever filtered. This was the narrowest of four doors.
Where you have a point is the pruning. Dropping the exemption means a bundle that declares one unrelated label removes an admin-set enforce=restricted without ever naming a pod-security key. That's #4564's clobber bug reaching the labels where it matters most, and it's a surprise rather than a deliberate act.
namespaceLabels in fleet.yaml exists so that a bundle can label the namespace it deploys into, and setting Pod Security Admission labels that way is what SURE-5906 (#1484) asked for. Since #5156 the agent dropped every pod-security.kubernetes.io/ key from the options and skipped those keys when pruning, so labels a user declares are silently ignored (SURE-11931, #5550).
That filter was the fix for GHSA-864g-863m-vcvq, where a bundle could weaken PSA enforcement on its target namespace because the agent applied namespaceLabels with its own cluster-admin credentials. #5351 changed that: the namespace is patched as the deployment's service account, so downstream RBAC decides what a bundle may write to it, and the prefix filter is no longer the control it was.
The filter never covered the whole path either. The same label reaches the same API call, under the same credentials, when it is declared on a Namespace object in the bundle, or when a pre-provisioned namespace is adopted with helm.takeOwnership. It also matched on the key alone, so it refused tightening enforcement, and setting warn or audit levels, just as it refused weakening.
Pod-security labels are therefore treated like every other namespace label again: applied when declared, pruned when they are not. Dropping the pruning exemption as well keeps un-setting working, which that exemption broke for these keys specifically.
Deployments that resolve to no service account still patch the namespace as the agent, which is cluster admin. Restricting those requires pinning a service account, in the bundle or through a Policy.
Refers to #5550
Additional Information
Checklist