Skip to content

Commit ecb0220

Browse files
Merge pull request #437 from tnierman/revert-cluster-monitoring-label-restriction
SREP-2508 - Revert openshift.io/cluster-monitoring label enforcement
2 parents 0070812 + 8ef8df9 commit ecb0220

File tree

2 files changed

+45
-267
lines changed

2 files changed

+45
-267
lines changed

pkg/webhooks/namespace/namespace.go

Lines changed: 45 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ package namespace
22

33
import (
44
"fmt"
5-
"maps"
65
"net/http"
76
"os"
87
"regexp"
98
"slices"
10-
"strings"
119
"sync"
1210

1311
hookconfig "github.com/openshift/managed-cluster-validating-webhooks/pkg/config"
@@ -40,22 +38,13 @@ var (
4038
sreAdminGroups = []string{"system:serviceaccounts:openshift-backplane-srep"}
4139
privilegedServiceAccountsRe = regexp.MustCompile(utils.PrivilegedServiceAccountGroups)
4240
layeredProductNamespaceRe = regexp.MustCompile(layeredProductNamespace)
43-
// protectedLabels are labels which managed customers should not be allowed change
41+
// protectedLabels are labels which managed customers should not be allowed
42+
// change by dedicated-admins.
4443
protectedLabels = []string{
4544
// https://github.com/openshift/managed-cluster-config/tree/master/deploy/resource-quotas
4645
"managed.openshift.io/storage-pv-quota-exempt",
4746
"managed.openshift.io/service-lb-quota-exempt",
4847
}
49-
// removableProtectedLabels defines labels that unprivileged users can remove (but not add!) to unprivileged namespaces
50-
removableProtectedLabels = []string{
51-
"openshift.io/cluster-monitoring",
52-
}
53-
// https://issues.redhat.com/browse/SREP-1770 - nvidia-gpu-operator should be allowed to label namespaces
54-
labelUserExceptions = []string{"system:serviceaccount:nvidia-gpu-operator:gpu-operator"}
55-
56-
// https://issues.redhat.com/browse/SREP-2070 - multiclusterhub-operator should be allowed to label namespaces
57-
// labelUserRegExceptions is the list of service account names that have exceptions to modify namespace labels. The service account names here is the last column of the full service account name, and the exception will grant on any namespace.
58-
labelUserRegExceptions = []string{"multiclusterhub-operator"}
5948

6049
log = logf.Log.WithName(WebhookName)
6150

@@ -237,17 +226,9 @@ func (s *NamespaceWebhook) authorized(request admissionctl.Request) admissionctl
237226
ret.UID = request.AdmissionRequest.UID
238227
return ret
239228
}
240-
241-
// If the user making the request has a specific exception, allow them to change labels on non-platform and non-protected namespaces
242-
if allowLabelChanges(request) {
243-
ret = admissionctl.Allowed("User allowed to modify namespace labels")
244-
ret.UID = request.AdmissionRequest.UID
245-
return ret
246-
}
247-
248-
// Unprivileged users cannot modify certain labels on unprivileged namespaces
229+
// Check labels.
249230
unauthorized, err := s.unauthorizedLabelChanges(request)
250-
if unauthorized {
231+
if !amIAdmin(request) && unauthorized {
251232
ret = admissionctl.Denied(fmt.Sprintf("Denied. Err %+v", err))
252233
ret.UID = request.AdmissionRequest.UID
253234
return ret
@@ -270,70 +251,57 @@ func (s *NamespaceWebhook) unauthorizedLabelChanges(req admissionctl.Request) (b
270251
return true, err
271252
}
272253
if req.Operation == admissionv1.Create {
273-
// Ensure no protected labels are set at creation-time
274-
protectedLabelsFound := protectedLabelsOnNamespace(newNamespace)
275-
removableProtectedLabelsFound := removableProtectedLabelsOnNamespace(newNamespace)
276-
277-
if len(protectedLabelsFound) == 0 && len(removableProtectedLabelsFound) == 0 {
254+
// For creations, we look to newNamespace and ensure no protectedLabels are set
255+
// We don't care about oldNamespace.
256+
protectedLabelsFound := doesNamespaceContainProtectedLabels(newNamespace)
257+
if len(protectedLabelsFound) == 0 {
278258
return false, nil
279259
}
280-
return true, fmt.Errorf("Managed OpenShift customers may not directly set certain protected labels (%s) on Namespaces", strings.Join(append(protectedLabels, removableProtectedLabels...), ", "))
281-
}
282-
if req.Operation == admissionv1.Update {
283-
// Check whether protected labels had their key or value altered
284-
protectedLabelsFoundInOld := protectedLabelsOnNamespace(oldNamespace)
285-
protectedLabelsFoundInNew := protectedLabelsOnNamespace(newNamespace)
286-
protectedLabelsUnchanged := maps.Equal(protectedLabelsFoundInOld, protectedLabelsFoundInNew)
287-
if !protectedLabelsUnchanged {
288-
return true, fmt.Errorf("Managed OpenShift customers may not add or remove the following protected labels from Namespaces: (%s)", protectedLabels)
260+
// There were some found
261+
return true, fmt.Errorf("Managed OpenShift customers may not directly set certain protected labels (%s) on Namespaces", protectedLabels)
262+
} else if req.Operation == admissionv1.Update {
263+
// For Updates we must see if the new object is making a change to the old one for any protected labels.
264+
// First, let's see if the old object had any protected labels we ought to
265+
// care about. If it has, then we can use that resulting list to compare to
266+
// the newNamespace for any changes. However, just because the oldNamespace
267+
// did not have any protected labels doesn't necessarily mean that we can
268+
// ignore potential setting of those labels' values in the newNamespace.
269+
270+
// protectedLabelsFoundInOld is a slice of all instances of protectedLabels
271+
// that appeared in the oldNamespace that we need to be sure have not
272+
// changed.
273+
protectedLabelsFoundInOld := doesNamespaceContainProtectedLabels(oldNamespace)
274+
// protectedLabelsFoundInNew is a slice of all instances of protectedLabels
275+
// that appeared in the newNamespace that we need to be sure do not have a
276+
// value different than oldNamespace.
277+
protectedLabelsFoundInNew := doesNamespaceContainProtectedLabels(newNamespace)
278+
279+
// First check: Were any protectedLabels deleted?
280+
if len(protectedLabelsFoundInOld) != len(protectedLabelsFoundInNew) {
281+
// If we have x protectedLabels in the oldNamespace then we expect to also
282+
// have x protectedLabels in the newNamespace. Any difference is a removal or addition
283+
return true, fmt.Errorf("Managed OpenShift customers may not add or remove protected labels (%s) from Namespaces", protectedLabels)
289284
}
290-
291-
// Check whether a removableProtectedLabel was added
292-
removableProtectedLabelsFoundInOld := removableProtectedLabelsOnNamespace(oldNamespace)
293-
removableProtectedLabelsFoundInNew := removableProtectedLabelsOnNamespace(newNamespace)
294-
removableProtectedLabelsAdded := unauthorizedRemovableProtectedLabelChange(removableProtectedLabelsFoundInOld, removableProtectedLabelsFoundInNew)
295-
296-
if removableProtectedLabelsAdded {
297-
return true, fmt.Errorf("Managed OpenShift customers may only remove the following protected labels from Namespaces: (%s)", removableProtectedLabels)
285+
// Next check: Compare values to ensure there are no changes in the protected labels
286+
for _, labelKey := range protectedLabelsFoundInOld {
287+
if oldNamespace.Labels[labelKey] != newNamespace.ObjectMeta.Labels[labelKey] {
288+
return true, fmt.Errorf("Managed OpenShift customers may not change the value or certain protected labels (%s) on Namespaces. %s changed from %s to %s", protectedLabels, labelKey, oldNamespace.Labels[labelKey], newNamespace.ObjectMeta.Labels[labelKey])
289+
}
298290
}
299291
}
300292
return false, nil
301293
}
302294

303-
// unauthorizedRemovableProtectedLabelChange returns true if a protectedRemovableLabel was added or had it's value changed
304-
func unauthorizedRemovableProtectedLabelChange(oldLabels, newLabels map[string]string) bool {
305-
// All we need to validate is that every given new label was present in the set of old labels
306-
for key, newValue := range newLabels {
307-
oldValue, found := oldLabels[key]
308-
if !found {
309-
return true
310-
}
311-
if newValue != oldValue {
312-
return true
295+
// doesNamespaceContainProtectedLabels checks the namespace for any instances of
296+
// protectedLabels and returns a slice of any instances of matches
297+
func doesNamespaceContainProtectedLabels(ns *corev1.Namespace) []string {
298+
foundLabelNames := make([]string, 0)
299+
for _, label := range protectedLabels {
300+
if _, found := ns.ObjectMeta.Labels[label]; found {
301+
foundLabelNames = append(foundLabelNames, label)
313302
}
314303
}
315-
return false
316-
}
317-
318-
// protectedLabelsInNamespace returns any protectedLabels present in the namespace object
319-
func protectedLabelsOnNamespace(ns *corev1.Namespace) map[string]string {
320-
return labelSetInNamespace(ns, protectedLabels)
321-
}
322-
323-
// removableProtectedLabelsInNamespace returns any removableProtectedLabels in the namespace object
324-
func removableProtectedLabelsOnNamespace(ns *corev1.Namespace) map[string]string {
325-
return labelSetInNamespace(ns, removableProtectedLabels)
326-
}
327-
328-
func labelSetInNamespace(ns *corev1.Namespace, labels []string) map[string]string {
329-
foundLabels := map[string]string{}
330-
for _, label := range labels {
331-
value, found := ns.Labels[label]
332-
if found {
333-
foundLabels[label] = value
334-
}
335-
}
336-
return foundLabels
304+
return foundLabelNames
337305
}
338306

339307
// SyncSetLabelSelector returns the label selector to use in the SyncSet.
@@ -378,15 +346,3 @@ func amIAdmin(request admissionctl.Request) bool {
378346

379347
return false
380348
}
381-
382-
func allowLabelChanges(request admissionctl.Request) bool {
383-
if slices.Contains(labelUserExceptions, request.UserInfo.Username) {
384-
return true
385-
}
386-
387-
parts := strings.Split(request.UserInfo.Username, ":")
388-
if len(parts) > 0 && slices.Contains(labelUserRegExceptions, parts[len(parts)-1]) {
389-
return true
390-
}
391-
return false
392-
}

pkg/webhooks/namespace/namespace_test.go

Lines changed: 0 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,184 +1047,6 @@ func TestLabellingUpdates(t *testing.T) {
10471047
},
10481048
shouldBeAllowed: true,
10491049
},
1050-
{
1051-
testID: "user-can-remove-removable-label-from-unpriv-ns",
1052-
targetNamespace: "my-customer-ns",
1053-
username: "test@user",
1054-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1055-
operation: admissionv1.Update,
1056-
oldObject: createOldObject("my-customer-ns", "user-can-remove-removable-label-from-unpriv-ns", map[string]string{
1057-
"openshift.io/cluster-monitoring": "true",
1058-
}),
1059-
labels: map[string]string{},
1060-
shouldBeAllowed: true,
1061-
},
1062-
{
1063-
testID: "user-cant-alter-removable-label-key-unpriv-ns",
1064-
targetNamespace: "my-customer-ns",
1065-
username: "test@user",
1066-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1067-
operation: admissionv1.Update,
1068-
oldObject: createOldObject("my-customer-ns", "user-can-remove-removable-label-from-unpriv-ns", map[string]string{
1069-
"openshift.io/cluster-monitoring": "true",
1070-
}),
1071-
labels: map[string]string{"openshift.io/cluster-monitoring": "false"},
1072-
shouldBeAllowed: false,
1073-
},
1074-
{
1075-
testID: "user-cant-add-removable-label-on-unpriv-ns",
1076-
targetNamespace: "my-customer-ns",
1077-
username: "test@user",
1078-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1079-
operation: admissionv1.Update,
1080-
oldObject: createOldObject("my-customer-ns", "user-can-remove-removable-label-from-unpriv-ns", map[string]string{}),
1081-
labels: map[string]string{"openshift.io/cluster-monitoring": "true"},
1082-
shouldBeAllowed: false,
1083-
},
1084-
{
1085-
testID: "cluster-admin-can-add-removable-label-on-unpriv-ns",
1086-
targetNamespace: "my-customer-ns",
1087-
username: "test@user",
1088-
userGroups: []string{"cluster-admins", "system:authenticated", "system:authenticated:oauth"},
1089-
operation: admissionv1.Update,
1090-
oldObject: createOldObject("my-customer-ns", "user-can-remove-removable-label-from-unpriv-ns", map[string]string{}),
1091-
labels: map[string]string{"openshift.io/cluster-monitoring": "true"},
1092-
shouldBeAllowed: true,
1093-
},
1094-
{
1095-
testID: "backplane-cluster-admin-can-add-removable-label-on-unpriv-ns",
1096-
targetNamespace: "my-customer-ns",
1097-
username: "backplane-cluster-admin",
1098-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1099-
operation: admissionv1.Update,
1100-
oldObject: createOldObject("my-customer-ns", "user-can-remove-removable-label-from-unpriv-ns", map[string]string{}),
1101-
labels: map[string]string{"openshift.io/cluster-monitoring": "true"},
1102-
shouldBeAllowed: true,
1103-
},
1104-
{
1105-
testID: "backplane-cluster-admin-can-add-removable-label-on-priv-ns",
1106-
targetNamespace: "openshift-kube-apiserver",
1107-
username: "backplane-cluster-admin",
1108-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1109-
operation: admissionv1.Update,
1110-
oldObject: createOldObject("my-customer-ns", "user-can-remove-removable-label-from-unpriv-ns", map[string]string{}),
1111-
labels: map[string]string{"openshift.io/cluster-monitoring": "true"},
1112-
shouldBeAllowed: true,
1113-
},
1114-
{
1115-
testID: "backplane-cluster-admin-can-remove-removable-label-on-priv-ns",
1116-
targetNamespace: "openshift-kube-apiserver",
1117-
username: "backplane-cluster-admin",
1118-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1119-
operation: admissionv1.Update,
1120-
oldObject: createOldObject("my-customer-ns", "user-can-remove-removable-label-from-unpriv-ns", map[string]string{
1121-
"openshift.io/cluster-monitoring": "true",
1122-
}),
1123-
labels: map[string]string{},
1124-
shouldBeAllowed: true,
1125-
},
1126-
// https://issues.redhat.com/browse/SREP-1770 - test explicit exception for nvidia-gpu-operator
1127-
{
1128-
testID: "nvidia-gpu-operator-can-add-label-to-unprotected-ns",
1129-
targetNamespace: "nvidia-gpu-operator",
1130-
username: "system:serviceaccount:nvidia-gpu-operator:gpu-operator",
1131-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1132-
operation: admissionv1.Update,
1133-
oldObject: createOldObject("nvidia-gpu-operator", "nvidia-gpu-operato-can-add-label-to-unprotected-ns", map[string]string{}),
1134-
labels: map[string]string{"openshift.io/cluster-monitoring": "true"},
1135-
shouldBeAllowed: true,
1136-
},
1137-
{
1138-
testID: "nvidia-gpu-operator-can-remove-label-from-unprotected-ns",
1139-
targetNamespace: "nvidia-gpu-operator",
1140-
username: "system:serviceaccount:nvidia-gpu-operator:gpu-operator",
1141-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1142-
operation: admissionv1.Update,
1143-
oldObject: createOldObject("nvidia-gpu-operator", "nvidia-gpu-operato-can-remove-label-from-unprotected-ns", map[string]string{
1144-
"openshift.io/cluster-monitoring": "true",
1145-
}),
1146-
labels: map[string]string{},
1147-
shouldBeAllowed: true,
1148-
},
1149-
{
1150-
testID: "nvidia-gpu-operator-cannot-remove-label-from-protected-ns",
1151-
targetNamespace: "nvidia-gpu-operator",
1152-
username: "system:serviceaccount:nvidia-gpu-operator:gpu-operator",
1153-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1154-
operation: admissionv1.Update,
1155-
oldObject: createOldObject("openshift-kube-apiserver", "nvidia-gpu-operato-cannot-remove-label-from-protected-ns", map[string]string{
1156-
"openshift.io/cluster-monitoring": "true",
1157-
}),
1158-
labels: map[string]string{},
1159-
shouldBeAllowed: false,
1160-
},
1161-
// https://issues.redhat.com/browse/SREP-2070 - test explicit exception for multiclusterhub-operator
1162-
{
1163-
testID: "multiclusterhub-operator-can-add-label-to-unprotected-ns",
1164-
targetNamespace: "open-cluster-management",
1165-
username: "system:serviceaccount:open-cluster-management:multiclusterhub-operator",
1166-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1167-
operation: admissionv1.Update,
1168-
oldObject: createOldObject("open-cluster-management", "multiclusterhub-operator-can-add-label-to-unprotected-ns", map[string]string{}),
1169-
labels: map[string]string{"openshift.io/cluster-monitoring": "true"},
1170-
shouldBeAllowed: true,
1171-
},
1172-
{
1173-
testID: "multiclusterhub-operator-can-remove-label-from-unprotected-ns",
1174-
targetNamespace: "open-cluster-management",
1175-
username: "system:serviceaccount:open-cluster-management:multiclusterhub-operator",
1176-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1177-
operation: admissionv1.Update,
1178-
oldObject: createOldObject("open-cluster-management", "multiclusterhub-operator-can-remove-label-from-unprotected-ns", map[string]string{
1179-
"openshift.io/cluster-monitoring": "true",
1180-
}),
1181-
labels: map[string]string{},
1182-
shouldBeAllowed: true,
1183-
},
1184-
{
1185-
testID: "multiclusterhub-operator-can-modify-label-on-unprotected-ns",
1186-
targetNamespace: "open-cluster-management",
1187-
username: "system:serviceaccount:open-cluster-management:multiclusterhub-operator",
1188-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1189-
operation: admissionv1.Update,
1190-
oldObject: createOldObject("open-cluster-management", "multiclusterhub-operator-can-modify-label-on-unprotected-ns", map[string]string{
1191-
"openshift.io/cluster-monitoring": "false",
1192-
}),
1193-
labels: map[string]string{"openshift.io/cluster-monitoring": "true"},
1194-
shouldBeAllowed: true,
1195-
},
1196-
{
1197-
testID: "multiclusterhub-operator-different-namespace-can-add-label",
1198-
targetNamespace: "some-other-namespace",
1199-
username: "system:serviceaccount:different-namespace:multiclusterhub-operator",
1200-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1201-
operation: admissionv1.Update,
1202-
oldObject: createOldObject("some-other-namespace", "multiclusterhub-operator-different-namespace-can-add-label", map[string]string{}),
1203-
labels: map[string]string{"openshift.io/cluster-monitoring": "true"},
1204-
shouldBeAllowed: true,
1205-
},
1206-
{
1207-
testID: "multiclusterhub-operator-cannot-access-protected-ns",
1208-
targetNamespace: "openshift-kube-apiserver",
1209-
username: "system:serviceaccount:open-cluster-management:multiclusterhub-operator",
1210-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1211-
operation: admissionv1.Update,
1212-
oldObject: createOldObject("openshift-kube-apiserver", "multiclusterhub-operator-cannot-access-protected-ns", map[string]string{
1213-
"openshift.io/cluster-monitoring": "true",
1214-
}),
1215-
labels: map[string]string{},
1216-
shouldBeAllowed: false,
1217-
},
1218-
{
1219-
testID: "non-excepted-operator-cannot-add-label",
1220-
targetNamespace: "some-namespace",
1221-
username: "system:serviceaccount:some-namespace:some-other-operator",
1222-
userGroups: []string{"system:authenticated", "system:authenticated:oauth"},
1223-
operation: admissionv1.Update,
1224-
oldObject: createOldObject("some-namespace", "non-excepted-operator-cannot-add-label", map[string]string{}),
1225-
labels: map[string]string{"openshift.io/cluster-monitoring": "true"},
1226-
shouldBeAllowed: false,
1227-
},
12281050
}
12291051
runNamespaceTests(t, tests)
12301052
}

0 commit comments

Comments
 (0)