From 0046dc65b6eb4534904ca8f8545057ac95ea3dcb Mon Sep 17 00:00:00 2001 From: Unai Arrien Date: Fri, 22 Nov 2024 14:03:35 +0100 Subject: [PATCH] Capsule user should be treated as a tenant owner (#19) --- charts/capsule/templates/deployment.yaml | 4 ++++ main.go | 14 +++++++++++--- pkg/webhook/namespace/patch.go | 12 ++++++++---- pkg/webhook/ownerreference/patching.go | 12 +++++++----- pkg/webhook/utils/is_tenant_owner.go | 5 ++++- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/charts/capsule/templates/deployment.yaml b/charts/capsule/templates/deployment.yaml index 60a2e4c1..1910ba8a 100644 --- a/charts/capsule/templates/deployment.yaml +++ b/charts/capsule/templates/deployment.yaml @@ -73,6 +73,10 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: SERVICE_ACCOUNT_NAME + valueFrom: + fieldRef: + fieldPath: spec.serviceAccountName ports: - name: webhook-server containerPort: {{ .Values.manager.webhookPort }} diff --git a/main.go b/main.go index f4918fbd..d6505a24 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "fmt" "os" goRuntime "runtime" + "strings" flag "github.com/spf13/pflag" _ "go.uber.org/automaxprocs" @@ -81,7 +82,7 @@ func printVersion() { func main() { var enableLeaderElection, version bool - var metricsAddr, namespace, configurationName string + var metricsAddr, namespace, serviceAccountName, capsuleUserName, configurationName string var webhookPort int @@ -118,6 +119,13 @@ func main() { os.Exit(1) } + if serviceAccountName = os.Getenv("SERVICE_ACCOUNT_NAME"); len(serviceAccountName) == 0 { + setupLog.Error(fmt.Errorf("unable to determinate the Namespace Capsule is running on"), "unable to start manager") + os.Exit(1) + } + + capsuleUserName = strings.Join([]string{"system:serviceaccount", namespace, serviceAccountName}, ":") + if len(configurationName) == 0 { setupLog.Error(fmt.Errorf("missing CapsuleConfiguration resource name"), "unable to start manager") os.Exit(1) @@ -220,14 +228,14 @@ func main() { webhooksList := append( make([]webhook.Webhook, 0), route.Pod(pod.ImagePullPolicy(), pod.ContainerRegistry(), pod.PriorityClass(), pod.RuntimeClass()), - route.Namespace(utils.InCapsuleGroups(cfg, namespacewebhook.PatchHandler(), namespacewebhook.QuotaHandler(), namespacewebhook.FreezeHandler(cfg), namespacewebhook.PrefixHandler(cfg), namespacewebhook.UserMetadataHandler())), + route.Namespace(utils.InCapsuleGroups(cfg, namespacewebhook.PatchHandler(capsuleUserName), namespacewebhook.QuotaHandler(), namespacewebhook.FreezeHandler(cfg), namespacewebhook.PrefixHandler(cfg), namespacewebhook.UserMetadataHandler())), route.Ingress(ingress.Class(cfg, kubeVersion), ingress.Hostnames(cfg), ingress.Collision(cfg), ingress.Wildcard()), route.PVC(pvc.Validating(), pvc.PersistentVolumeReuse()), route.Service(service.Handler()), route.TenantResourceObjects(utils.InCapsuleGroups(cfg, tntresource.WriteOpsHandler())), route.NetworkPolicy(utils.InCapsuleGroups(cfg, networkpolicy.Handler())), route.Tenant(tenant.NameHandler(), tenant.RoleBindingRegexHandler(), tenant.IngressClassRegexHandler(), tenant.StorageClassRegexHandler(), tenant.ContainerRegistryRegexHandler(), tenant.HostnameRegexHandler(), tenant.FreezedEmitter(), tenant.ServiceAccountNameHandler(), tenant.ForbiddenAnnotationsRegexHandler(), tenant.ProtectedHandler(), tenant.MetaHandler()), - route.OwnerReference(utils.InCapsuleGroups(cfg, ownerreference.Handler(cfg))), + route.OwnerReference(utils.InCapsuleGroups(cfg, ownerreference.Handler(cfg,capsuleUserName))), route.Cordoning(tenant.CordoningHandler(cfg), tenant.ResourceCounterHandler(manager.GetClient())), route.Node(utils.InCapsuleGroups(cfg, node.UserMetadataHandler(cfg, kubeVersion))), route.Defaults(defaults.Handler(cfg, kubeVersion)), diff --git a/pkg/webhook/namespace/patch.go b/pkg/webhook/namespace/patch.go index 2fa3fffb..9424e42a 100644 --- a/pkg/webhook/namespace/patch.go +++ b/pkg/webhook/namespace/patch.go @@ -20,10 +20,14 @@ import ( "github.com/projectcapsule/capsule/pkg/webhook/utils" ) -type patchHandler struct{} +type patchHandler struct{ + capsuleUserName string +} -func PatchHandler() capsulewebhook.Handler { - return &patchHandler{} +func PatchHandler(capsuleUserName string) capsulewebhook.Handler { + return &patchHandler{ + capsuleUserName: capsuleUserName, + } } func (r *patchHandler) OnCreate(client.Client, admission.Decoder, record.EventRecorder) capsulewebhook.Func { @@ -66,7 +70,7 @@ func (r *patchHandler) OnUpdate(c client.Client, decoder admission.Decoder, reco return &response } - if !utils.IsTenantOwner(tnt.Spec.Owners, req.UserInfo) { + if !utils.IsTenantOwner(tnt.Spec.Owners, req.UserInfo, r.capsuleUserName) { recorder.Eventf(tnt, corev1.EventTypeWarning, "NamespacePatch", e) response := admission.Denied(e) diff --git a/pkg/webhook/ownerreference/patching.go b/pkg/webhook/ownerreference/patching.go index 18913c4f..6103bfbc 100644 --- a/pkg/webhook/ownerreference/patching.go +++ b/pkg/webhook/ownerreference/patching.go @@ -29,12 +29,14 @@ import ( ) type handler struct { - cfg configuration.Configuration + cfg configuration.Configuration + capsuleUserName string } -func Handler(cfg configuration.Configuration) capsulewebhook.Handler { +func Handler(cfg configuration.Configuration, capsuleUserName string) capsulewebhook.Handler { return &handler{ - cfg: cfg, + cfg: cfg, + capsuleUserName: capsuleUserName, } } @@ -120,7 +122,7 @@ func (h *handler) namespaceIsOwned(ns *corev1.Namespace, tenantList *capsulev1be continue } - if ownerRef.UID == tenant.UID && utils.IsTenantOwner(tenant.Spec.Owners, req.UserInfo) { + if ownerRef.UID == tenant.UID && utils.IsTenantOwner(tenant.Spec.Owners, req.UserInfo, h.capsuleUserName) { return true } } @@ -153,7 +155,7 @@ func (h *handler) setOwnerRef(ctx context.Context, req admission.Request, client return &response } // Tenant owner must adhere to user that asked for NS creation - if !utils.IsTenantOwner(tnt.Spec.Owners, req.UserInfo) { + if !utils.IsTenantOwner(tnt.Spec.Owners, req.UserInfo, h.capsuleUserName) { recorder.Eventf(tnt, corev1.EventTypeWarning, "NonOwnedTenant", "Namespace %s cannot be assigned to the current Tenant", ns.GetName()) response := admission.Denied("Cannot assign the desired namespace to a non-owned Tenant") diff --git a/pkg/webhook/utils/is_tenant_owner.go b/pkg/webhook/utils/is_tenant_owner.go index f1d1c488..1ba135dd 100644 --- a/pkg/webhook/utils/is_tenant_owner.go +++ b/pkg/webhook/utils/is_tenant_owner.go @@ -9,7 +9,10 @@ import ( capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" ) -func IsTenantOwner(owners capsulev1beta2.OwnerListSpec, userInfo authenticationv1.UserInfo) bool { +func IsTenantOwner(owners capsulev1beta2.OwnerListSpec, userInfo authenticationv1.UserInfo, capsuleUserName string) bool { + if userInfo.Username == capsuleUserName { + return true + } for _, owner := range owners { switch owner.Kind { case capsulev1beta2.UserOwner, capsulev1beta2.ServiceAccountOwner: