diff --git a/Makefile b/Makefile index c1e4b3253..fe8586f97 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ REGISTRY ?= ghcr.io -KIND_IMAGE ?= kindest/node:v1.28.0 +KIND_IMAGE ?= kindest/node:v1.30.0 ifndef TAG TAG ?= $(shell git rev-parse --short=7 HEAD) endif diff --git a/README.md b/README.md index 38a609460..def82e86e 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ export this variable which specifies the number of member clusters that will be export MEMBER_CLUSTER_COUNT=1 ``` -from the root directory of the repo run the following command, by default a hub cluster gets created which is the control plane for fleet (**The makefile uses kindest/node:v1.28.0**) +from the root directory of the repo run the following command, by default a hub cluster gets created which is the control plane for fleet (**The makefile uses kindest/node:v1.30.0**) ```shell make setup-clusters diff --git a/pkg/webhook/validation/uservalidation.go b/pkg/webhook/validation/uservalidation.go index 0b929f46a..7b3e2b63c 100644 --- a/pkg/webhook/validation/uservalidation.go +++ b/pkg/webhook/validation/uservalidation.go @@ -24,6 +24,7 @@ import ( const ( mastersGroup = "system:masters" + kubeadmClusterAdminsGroup = "kubeadm:cluster-admins" serviceAccountsGroup = "system:serviceaccounts" nodeGroup = "system:nodes" kubeSchedulerUser = "system:kube-scheduler" @@ -48,7 +49,7 @@ var ( func ValidateUserForFleetCRD(req admission.Request, whiteListedUsers []string, group string) admission.Response { namespacedName := types.NamespacedName{Name: req.Name, Namespace: req.Namespace} userInfo := req.UserInfo - if checkCRDGroup(group) && !isMasterGroupUserOrWhiteListedUser(whiteListedUsers, userInfo) { + if checkCRDGroup(group) && !isAdminGroupUserOrWhiteListedUser(whiteListedUsers, userInfo) { klog.V(2).InfoS(deniedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName) return admission.Denied(fmt.Sprintf(ResourceDeniedFormat, userInfo.Username, utils.GenerateGroupString(userInfo.Groups), req.Operation, req.RequestKind, req.SubResource, namespacedName)) } @@ -60,7 +61,7 @@ func ValidateUserForFleetCRD(req admission.Request, whiteListedUsers []string, g func ValidateUserForResource(req admission.Request, whiteListedUsers []string) admission.Response { namespacedName := types.NamespacedName{Name: req.Name, Namespace: req.Namespace} userInfo := req.UserInfo - if isMasterGroupUserOrWhiteListedUser(whiteListedUsers, userInfo) || isUserAuthenticatedServiceAccount(userInfo) || isUserKubeScheduler(userInfo) || isUserKubeControllerManager(userInfo) || isNodeGroupUser(userInfo) { + if isAdminGroupUserOrWhiteListedUser(whiteListedUsers, userInfo) || isUserAuthenticatedServiceAccount(userInfo) || isUserKubeScheduler(userInfo) || isUserKubeControllerManager(userInfo) || isNodeGroupUser(userInfo) { klog.V(3).InfoS(allowedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName) return admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, userInfo.Username, utils.GenerateGroupString(userInfo.Groups), req.Operation, req.RequestKind, req.SubResource, namespacedName)) } @@ -130,9 +131,11 @@ func ValidatedUpstreamMemberClusterUpdate(currentMC, oldMC clusterv1beta1.Member return admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, userInfo.Username, utils.GenerateGroupString(userInfo.Groups), req.Operation, req.RequestKind, req.SubResource, namespacedName)) } -// isMasterGroupUserOrWhiteListedUser returns true is user belongs to white listed users or user belongs to system:masters group. -func isMasterGroupUserOrWhiteListedUser(whiteListedUsers []string, userInfo authenticationv1.UserInfo) bool { - return slices.Contains(whiteListedUsers, userInfo.Username) || slices.Contains(userInfo.Groups, mastersGroup) +// isAdminGroupUserOrWhiteListedUser returns true is user belongs to white listed users or user belongs to system:masters/kubeadm:cluster-admins group. +// In clusters using kubeadm, kubernetes-admin belongs to kubeadm:cluster-admins group and kubernetes-super-admin user belongs to system:masters group. +// https://kubernetes.io/docs/reference/setup-tools/kubeadm/implementation-details/#generate-kubeconfig-files-for-control-plane-components +func isAdminGroupUserOrWhiteListedUser(whiteListedUsers []string, userInfo authenticationv1.UserInfo) bool { + return slices.Contains(whiteListedUsers, userInfo.Username) || slices.Contains(userInfo.Groups, mastersGroup) || slices.Contains(userInfo.Groups, kubeadmClusterAdminsGroup) } // isUserAuthenticatedServiceAccount returns true if user is a valid service account. diff --git a/pkg/webhook/validation/uservalidation_test.go b/pkg/webhook/validation/uservalidation_test.go index ac92f41a5..37d39aa83 100644 --- a/pkg/webhook/validation/uservalidation_test.go +++ b/pkg/webhook/validation/uservalidation_test.go @@ -37,6 +37,21 @@ func TestValidateUserForResource(t *testing.T) { }, wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{mastersGroup}), admissionv1.Create, &utils.RoleMetaGVK, "", types.NamespacedName{Name: "test-role", Namespace: "test-namespace"})), }, + "allow user in kubeadm:cluster-admin group": { + req: admission.Request{ + AdmissionRequest: admissionv1.AdmissionRequest{ + Name: "test-role", + Namespace: "test-namespace", + RequestKind: &utils.RoleMetaGVK, + UserInfo: authenticationv1.UserInfo{ + Username: "test-user", + Groups: []string{kubeadmClusterAdminsGroup}, + }, + Operation: admissionv1.Create, + }, + }, + wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{kubeadmClusterAdminsGroup}), admissionv1.Create, &utils.RoleMetaGVK, "", types.NamespacedName{Name: "test-role", Namespace: "test-namespace"})), + }, // UT to test GenerateGroupString in pkg/utils/common.gp "allow user in system:masters group along with 10 other groups": { req: admission.Request{ diff --git a/test/e2e/setup.sh b/test/e2e/setup.sh index 9c2637a35..6caccc1d3 100755 --- a/test/e2e/setup.sh +++ b/test/e2e/setup.sh @@ -6,7 +6,7 @@ set -o pipefail # Before updating the default kind image to use, verify that the version is supported # by the current kind release. -KIND_IMAGE="${KIND_IMAGE:-kindest/node:v1.28.0}" +KIND_IMAGE="${KIND_IMAGE:-kindest/node:v1.30.0}" KUBECONFIG="${KUBECONFIG:-$HOME/.kube/config}" MEMBER_CLUSTER_COUNT=$1