Skip to content

Commit 00d16dd

Browse files
authored
fix: allow aks-support user in fleet guard rail (#957)
1 parent b6dd791 commit 00d16dd

File tree

3 files changed

+87
-1
lines changed

3 files changed

+87
-1
lines changed

pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,28 @@ func TestHandleV1Alpha1MemberCluster(t *testing.T) {
367367
},
368368
wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.MCV1Alpha1MetaGVK, "", types.NamespacedName{Name: "test-mc"})),
369369
},
370+
// added as UT since testing this case as an E2E requires
371+
// creating a new user called aks-support in our test environment.
372+
"allow delete of member cluster by aks-support user": {
373+
req: admission.Request{
374+
AdmissionRequest: admissionv1.AdmissionRequest{
375+
Name: "test-mc",
376+
OldObject: runtime.RawExtension{
377+
Raw: MCObjectBytes,
378+
},
379+
UserInfo: authenticationv1.UserInfo{
380+
Username: "aks-support",
381+
Groups: []string{"system:authenticated"},
382+
},
383+
RequestKind: &utils.MCV1Alpha1MetaGVK,
384+
Operation: admissionv1.Delete,
385+
},
386+
},
387+
resourceValidator: fleetResourceValidator{
388+
decoder: decoder,
389+
},
390+
wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "aks-support", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Delete, &utils.MCV1Alpha1MetaGVK, "", types.NamespacedName{Name: "test-mc"})),
391+
},
370392
}
371393

372394
for testName, testCase := range testCases {
@@ -570,6 +592,28 @@ func TestHandleMemberCluster(t *testing.T) {
570592
},
571593
wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Update, &utils.MCMetaGVK, "status", types.NamespacedName{Name: "test-mc"})),
572594
},
595+
// added as UT since testing this case as an E2E requires
596+
// creating a new user called aks-support in our test environment.
597+
"allow delete for fleet MC by aks-support user": {
598+
req: admission.Request{
599+
AdmissionRequest: admissionv1.AdmissionRequest{
600+
Name: "test-mc",
601+
OldObject: runtime.RawExtension{
602+
Raw: fleetMCObjectBytes,
603+
},
604+
UserInfo: authenticationv1.UserInfo{
605+
Username: "aks-support",
606+
Groups: []string{"system:authenticated"},
607+
},
608+
RequestKind: &utils.MCMetaGVK,
609+
Operation: admissionv1.Delete,
610+
},
611+
},
612+
resourceValidator: fleetResourceValidator{
613+
decoder: decoder,
614+
},
615+
wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "aks-support", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Delete, &utils.MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})),
616+
},
573617
}
574618

575619
for testName, testCase := range testCases {
@@ -873,6 +917,27 @@ func TestHandleFleetReservedNamespacedResource(t *testing.T) {
873917
},
874918
wantResponse: admission.Denied(fmt.Sprintf(validation.ResourceDeniedFormat, "testUser", utils.GenerateGroupString([]string{"testGroup"}), admissionv1.Create, &utils.EndpointSliceExportMetaGVK, "", types.NamespacedName{Name: "test-net-eps", Namespace: "fleet-system"})),
875919
},
920+
// added as UT since testing this case as an E2E requires
921+
// creating a new user called aks-support in our test environment.
922+
"allow delete on v1beta1 IMC in fleet-member namespace": {
923+
req: admission.Request{
924+
AdmissionRequest: admissionv1.AdmissionRequest{
925+
Name: "test-mc",
926+
Namespace: "fleet-member-test-mc",
927+
RequestKind: &utils.IMCMetaGVK,
928+
UserInfo: authenticationv1.UserInfo{
929+
Username: "aks-support",
930+
Groups: []string{"system:authenticated"},
931+
},
932+
Operation: admissionv1.Delete,
933+
},
934+
},
935+
resourceValidator: fleetResourceValidator{
936+
client: mockClient,
937+
isFleetV1Beta1API: true,
938+
},
939+
wantResponse: admission.Allowed(fmt.Sprintf(validation.ResourceAllowedFormat, "aks-support", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Delete, &utils.IMCMetaGVK, "", types.NamespacedName{Name: "test-mc", Namespace: "fleet-member-test-mc"})),
940+
},
876941
}
877942
for testName, testCase := range testCases {
878943
t.Run(testName, func(t *testing.T) {

pkg/webhook/validation/uservalidation.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const (
2929
nodeGroup = "system:nodes"
3030
kubeSchedulerUser = "system:kube-scheduler"
3131
kubeControllerManagerUser = "system:kube-controller-manager"
32+
aksSupportUser = "aks-support"
3233
serviceAccountFmt = "system:serviceaccount:fleet-system:%s"
3334

3435
allowedModifyResource = "user in groups is allowed to modify resource"
@@ -61,7 +62,7 @@ func ValidateUserForFleetCRD(req admission.Request, whiteListedUsers []string, g
6162
func ValidateUserForResource(req admission.Request, whiteListedUsers []string) admission.Response {
6263
namespacedName := types.NamespacedName{Name: req.Name, Namespace: req.Namespace}
6364
userInfo := req.UserInfo
64-
if isAdminGroupUserOrWhiteListedUser(whiteListedUsers, userInfo) || isUserAuthenticatedServiceAccount(userInfo) || isUserKubeScheduler(userInfo) || isUserKubeControllerManager(userInfo) || isNodeGroupUser(userInfo) {
65+
if isAdminGroupUserOrWhiteListedUser(whiteListedUsers, userInfo) || isUserAuthenticatedServiceAccount(userInfo) || isUserKubeScheduler(userInfo) || isUserKubeControllerManager(userInfo) || isNodeGroupUser(userInfo) || isAKSSupportUser(userInfo) {
6566
klog.V(3).InfoS(allowedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
6667
return admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, userInfo.Username, utils.GenerateGroupString(userInfo.Groups), req.Operation, req.RequestKind, req.SubResource, namespacedName))
6768
}
@@ -155,6 +156,12 @@ func isUserKubeControllerManager(userInfo authenticationv1.UserInfo) bool {
155156
return userInfo.Username == kubeControllerManagerUser
156157
}
157158

159+
// isUserKubeControllerManager return true if user is aks-support.
160+
func isAKSSupportUser(userInfo authenticationv1.UserInfo) bool {
161+
// aks-support user only belongs to system:authenticated group hence comparing username.
162+
return userInfo.Username == aksSupportUser
163+
}
164+
158165
// isNodeGroupUser returns true if user belongs to system:nodes group.
159166
func isNodeGroupUser(userInfo authenticationv1.UserInfo) bool {
160167
return slices.Contains(userInfo.Groups, nodeGroup)

pkg/webhook/validation/uservalidation_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,20 @@ func TestValidateUserForResource(t *testing.T) {
159159
},
160160
wantResponse: admission.Denied(fmt.Sprintf(ResourceDeniedFormat, "test-user", utils.GenerateGroupString([]string{"test-group"}), admissionv1.Delete, &utils.RoleMetaGVK, "", types.NamespacedName{Name: "test-role", Namespace: "test-namespace"})),
161161
},
162+
"allow aks-support user": {
163+
req: admission.Request{
164+
AdmissionRequest: admissionv1.AdmissionRequest{
165+
Name: "test-mc",
166+
RequestKind: &utils.MCMetaGVK,
167+
UserInfo: authenticationv1.UserInfo{
168+
Username: "aks-support",
169+
Groups: []string{"system:authenticated"},
170+
},
171+
Operation: admissionv1.Create,
172+
},
173+
},
174+
wantResponse: admission.Allowed(fmt.Sprintf(ResourceAllowedFormat, "aks-support", utils.GenerateGroupString([]string{"system:authenticated"}), admissionv1.Create, &utils.MCMetaGVK, "", types.NamespacedName{Name: "test-mc"})),
175+
},
162176
}
163177

164178
for testName, testCase := range testCases {

0 commit comments

Comments
 (0)