From 511d1c54b839b9a6ad02ee7a66563b5a7bec05b6 Mon Sep 17 00:00:00 2001 From: Unai Arrien Date: Fri, 22 Nov 2024 13:19:00 +0100 Subject: [PATCH] Add option to exclude groups from capsule (#26) --- api/v1beta2/capsuleconfiguration_types.go | 2 ++ .../crds/capsule.clastix.io_capsuleconfigurations.yaml | 5 +++++ charts/capsule/templates/configuration-default.yaml | 4 ++++ charts/capsule/values.yaml | 2 ++ .../capsule.clastix.io_capsuleconfigurations.yaml | 5 +++++ pkg/configuration/client.go | 4 ++++ pkg/configuration/configuration.go | 1 + pkg/webhook/namespace/freezed.go | 4 ++-- pkg/webhook/ownerreference/patching.go | 1 - pkg/webhook/tenant/cordoning.go | 2 +- pkg/webhook/utils/in_capsule_groups.go | 6 +++--- pkg/webhook/utils/is_capsule_user.go | 10 +++++----- pkg/webhook/utils/is_tenant_owner.go | 2 +- 13 files changed, 35 insertions(+), 13 deletions(-) diff --git a/api/v1beta2/capsuleconfiguration_types.go b/api/v1beta2/capsuleconfiguration_types.go index c1273457..e21e039e 100644 --- a/api/v1beta2/capsuleconfiguration_types.go +++ b/api/v1beta2/capsuleconfiguration_types.go @@ -14,6 +14,8 @@ type CapsuleConfigurationSpec struct { // Names of the groups for Capsule users. // +kubebuilder:default={capsule.clastix.io} UserGroups []string `json:"userGroups,omitempty"` + // Names of the groups for Capsule users. + ExcludeUserGroups []string `json:"excludeUserGroups,omitempty"` // Enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, // separated by a dash. This is useful to avoid Namespace name collision in a public CaaS environment. // +kubebuilder:default=false diff --git a/charts/capsule/crds/capsule.clastix.io_capsuleconfigurations.yaml b/charts/capsule/crds/capsule.clastix.io_capsuleconfigurations.yaml index 73ff15e4..94c66391 100644 --- a/charts/capsule/crds/capsule.clastix.io_capsuleconfigurations.yaml +++ b/charts/capsule/crds/capsule.clastix.io_capsuleconfigurations.yaml @@ -124,6 +124,11 @@ spec: items: type: string type: array + excludeUserGroups: + description: Names of the groups for Capsule users to exclude. + items: + type: string + type: array required: - enableTLSReconciler type: object diff --git a/charts/capsule/templates/configuration-default.yaml b/charts/capsule/templates/configuration-default.yaml index bc3b07ec..36a2c30d 100644 --- a/charts/capsule/templates/configuration-default.yaml +++ b/charts/capsule/templates/configuration-default.yaml @@ -19,6 +19,10 @@ spec: userGroups: {{- range .Values.manager.options.capsuleUserGroups }} - {{ . }} +{{- end}} + excludeUserGroups: +{{- range .Values.manager.options.capsuleExcludeUserGroups }} + - {{ . }} {{- end}} protectedNamespaceRegex: {{ .Values.manager.options.protectedNamespaceRegex | quote }} {{- with .Values.manager.options.nodeMetadata }} diff --git a/charts/capsule/values.yaml b/charts/capsule/values.yaml index 5d486395..d06a89d7 100644 --- a/charts/capsule/values.yaml +++ b/charts/capsule/values.yaml @@ -78,6 +78,8 @@ manager: forceTenantPrefix: false # -- Override the Capsule user groups capsuleUserGroups: ["projectcapsule.dev"] + # -- Override the Capsule exclude user groups + capsuleExcludeUserGroups: [] # -- If specified, disallows creation of namespaces matching the passed regexp protectedNamespaceRegex: "" # -- Specifies whether capsule webhooks certificates should be generated by capsule operator diff --git a/config/crd/bases/capsule.clastix.io_capsuleconfigurations.yaml b/config/crd/bases/capsule.clastix.io_capsuleconfigurations.yaml index c95f5c4d..ca3f28c1 100644 --- a/config/crd/bases/capsule.clastix.io_capsuleconfigurations.yaml +++ b/config/crd/bases/capsule.clastix.io_capsuleconfigurations.yaml @@ -124,6 +124,11 @@ spec: items: type: string type: array + excludeUserGroups: + description: Names of the groups for Capsule users to exclude. + items: + type: string + type: array required: - enableTLSReconciler type: object diff --git a/pkg/configuration/client.go b/pkg/configuration/client.go index b829c971..a7dfd80f 100644 --- a/pkg/configuration/client.go +++ b/pkg/configuration/client.go @@ -85,6 +85,10 @@ func (c *capsuleConfiguration) UserGroups() []string { return c.retrievalFn().Spec.UserGroups } +func (c *capsuleConfiguration) ExcludeUserGroups() []string { + return c.retrievalFn().Spec.ExcludeUserGroups +} + func (c *capsuleConfiguration) ForbiddenUserNodeLabels() *capsuleapi.ForbiddenListSpec { if c.retrievalFn().Spec.NodeMetadata == nil { return nil diff --git a/pkg/configuration/configuration.go b/pkg/configuration/configuration.go index e75f71d6..ab717907 100644 --- a/pkg/configuration/configuration.go +++ b/pkg/configuration/configuration.go @@ -24,6 +24,7 @@ type Configuration interface { ValidatingWebhookConfigurationName() string TenantCRDName() string UserGroups() []string + ExcludeUserGroups() []string ForbiddenUserNodeLabels() *capsuleapi.ForbiddenListSpec ForbiddenUserNodeAnnotations() *capsuleapi.ForbiddenListSpec } diff --git a/pkg/webhook/namespace/freezed.go b/pkg/webhook/namespace/freezed.go index 43b599fe..28733be4 100644 --- a/pkg/webhook/namespace/freezed.go +++ b/pkg/webhook/namespace/freezed.go @@ -74,7 +74,7 @@ func (r *freezedHandler) OnDelete(c client.Client, _ admission.Decoder, recorder tnt := tntList.Items[0] - if tnt.Spec.Cordoned && utils.IsCapsuleUser(ctx, req, c, r.configuration.UserGroups()) { + if tnt.Spec.Cordoned && utils.IsCapsuleUser(ctx, req, c, r.configuration.UserGroups(), r.configuration.ExcludeUserGroups()) { recorder.Eventf(&tnt, corev1.EventTypeWarning, "TenantFreezed", "Namespace %s cannot be deleted, the current Tenant is freezed", req.Name) response := admission.Denied("the selected Tenant is freezed") @@ -106,7 +106,7 @@ func (r *freezedHandler) OnUpdate(c client.Client, decoder admission.Decoder, re tnt := tntList.Items[0] - if tnt.Spec.Cordoned && utils.IsCapsuleUser(ctx, req, c, r.configuration.UserGroups()) { + if tnt.Spec.Cordoned && utils.IsCapsuleUser(ctx, req, c, r.configuration.UserGroups(), r.configuration.ExcludeUserGroups()) { recorder.Eventf(&tnt, corev1.EventTypeWarning, "TenantFreezed", "Namespace %s cannot be updated, the current Tenant is freezed", ns.GetName()) response := admission.Denied("the selected Tenant is freezed") diff --git a/pkg/webhook/ownerreference/patching.go b/pkg/webhook/ownerreference/patching.go index 825faf30..eed9f8dc 100644 --- a/pkg/webhook/ownerreference/patching.go +++ b/pkg/webhook/ownerreference/patching.go @@ -222,7 +222,6 @@ func (h *handler) setOwnerRef(ctx context.Context, req admission.Request, client return &response } - if h.cfg.ForceTenantPrefix() { for _, tnt := range tenants { if strings.HasPrefix(ns.GetName(), fmt.Sprintf("%s-", tnt.GetName())) { diff --git a/pkg/webhook/tenant/cordoning.go b/pkg/webhook/tenant/cordoning.go index 6172b941..6e1baa5d 100644 --- a/pkg/webhook/tenant/cordoning.go +++ b/pkg/webhook/tenant/cordoning.go @@ -44,7 +44,7 @@ func (h *cordoningHandler) cordonHandler(ctx context.Context, clt client.Client, } tnt := tntList.Items[0] - if tnt.Spec.Cordoned && utils.IsCapsuleUser(ctx, req, clt, h.configuration.UserGroups()) { + if tnt.Spec.Cordoned && utils.IsCapsuleUser(ctx, req, clt, h.configuration.UserGroups(), h.configuration.ExcludeUserGroups()) { recorder.Eventf(&tnt, corev1.EventTypeWarning, "TenantFreezed", "%s %s/%s cannot be %sd, current Tenant is freezed", req.Kind.String(), req.Namespace, req.Name, strings.ToLower(string(req.Operation))) response := admission.Denied(fmt.Sprintf("tenant %s is freezed: please, reach out to the system administrator", tnt.GetName())) diff --git a/pkg/webhook/utils/in_capsule_groups.go b/pkg/webhook/utils/in_capsule_groups.go index b1422e9a..20054754 100644 --- a/pkg/webhook/utils/in_capsule_groups.go +++ b/pkg/webhook/utils/in_capsule_groups.go @@ -28,7 +28,7 @@ type handler struct { func (h *handler) OnCreate(client client.Client, decoder admission.Decoder, recorder record.EventRecorder) webhook.Func { return func(ctx context.Context, req admission.Request) *admission.Response { - if !IsCapsuleUser(ctx, req, client, h.configuration.UserGroups()) { + if !IsCapsuleUser(ctx, req, client, h.configuration.UserGroups(), h.configuration.ExcludeUserGroups()) { return nil } @@ -44,7 +44,7 @@ func (h *handler) OnCreate(client client.Client, decoder admission.Decoder, reco func (h *handler) OnDelete(client client.Client, decoder admission.Decoder, recorder record.EventRecorder) webhook.Func { return func(ctx context.Context, req admission.Request) *admission.Response { - if !IsCapsuleUser(ctx, req, client, h.configuration.UserGroups()) { + if !IsCapsuleUser(ctx, req, client, h.configuration.UserGroups(), h.configuration.ExcludeUserGroups()) { return nil } @@ -60,7 +60,7 @@ func (h *handler) OnDelete(client client.Client, decoder admission.Decoder, reco func (h *handler) OnUpdate(client client.Client, decoder admission.Decoder, recorder record.EventRecorder) webhook.Func { return func(ctx context.Context, req admission.Request) *admission.Response { - if !IsCapsuleUser(ctx, req, client, h.configuration.UserGroups()) { + if !IsCapsuleUser(ctx, req, client, h.configuration.UserGroups(), h.configuration.ExcludeUserGroups()) { return nil } diff --git a/pkg/webhook/utils/is_capsule_user.go b/pkg/webhook/utils/is_capsule_user.go index 8072abc2..9fdf29ad 100644 --- a/pkg/webhook/utils/is_capsule_user.go +++ b/pkg/webhook/utils/is_capsule_user.go @@ -18,11 +18,11 @@ import ( func IsCapsuleUser(ctx context.Context, req admission.Request, clt client.Client, userGroups []string) bool { groupList := utils.NewUserGroupList(req.UserInfo.Groups) - // if the user is a ServiceAccount belonging to the kube-system namespace, definitely, it's not a Capsule user - // and we can skip the check in case of Capsule user group assigned to system:authenticated - // (ref: https://github.com/projectcapsule/capsule/issues/234) - if groupList.Find("system:serviceaccounts:kube-system") { - return false + + for _, group := range excludeUserGroups { + if groupList.Find(group) { + return false + } } //nolint:nestif if sets.NewString(req.UserInfo.Groups...).Has("system:serviceaccounts") { diff --git a/pkg/webhook/utils/is_tenant_owner.go b/pkg/webhook/utils/is_tenant_owner.go index de8bb413..1ba135dd 100644 --- a/pkg/webhook/utils/is_tenant_owner.go +++ b/pkg/webhook/utils/is_tenant_owner.go @@ -9,7 +9,7 @@ import ( capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" ) -func IsTenantOwner(owners capsulev1beta1.OwnerListSpec, userInfo authenticationv1.UserInfo, capsuleUserName string) bool { +func IsTenantOwner(owners capsulev1beta2.OwnerListSpec, userInfo authenticationv1.UserInfo, capsuleUserName string) bool { if userInfo.Username == capsuleUserName { return true }