From 18912a002b2973153d1c3c6411fb586c0897f66d Mon Sep 17 00:00:00 2001 From: Dario Tranchitella Date: Thu, 22 Jul 2021 17:38:55 +0200 Subject: [PATCH] feat: allowed external IPs is grouped in ServiceOptions --- api/v1alpha1/conversion_hub.go | 13 ++++++++----- api/v1alpha1/conversion_hub_test.go | 6 +++--- ...ternal_service_ips.go => service_allowed_ips.go} | 0 api/v1beta1/service_allowed_types.go | 13 +++++++++++++ api/v1beta1/service_options.go | 11 ++--------- api/v1beta1/tenant_types.go | 2 -- api/v1beta1/zz_generated.deepcopy.go | 10 +++++----- pkg/webhook/service/validating.go | 6 +++--- 8 files changed, 34 insertions(+), 27 deletions(-) rename api/v1beta1/{external_service_ips.go => service_allowed_ips.go} (100%) create mode 100644 api/v1beta1/service_allowed_types.go diff --git a/api/v1alpha1/conversion_hub.go b/api/v1alpha1/conversion_hub.go index f9d46c05..d11060cb 100644 --- a/api/v1alpha1/conversion_hub.go +++ b/api/v1alpha1/conversion_hub.go @@ -201,12 +201,15 @@ func (t *Tenant) ConvertTo(dstRaw conversion.Hub) error { } } if t.Spec.ExternalServiceIPs != nil { - dst.Spec.ExternalServiceIPs = &capsulev1beta1.ExternalServiceIPsSpec{ + if dst.Spec.ServiceOptions == nil { + dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{} + } + dst.Spec.ServiceOptions.ExternalServiceIPs = &capsulev1beta1.ExternalServiceIPsSpec{ Allowed: make([]capsulev1beta1.AllowedIP, len(t.Spec.ExternalServiceIPs.Allowed)), } for i, IP := range t.Spec.ExternalServiceIPs.Allowed { - dst.Spec.ExternalServiceIPs.Allowed[i] = capsulev1beta1.AllowedIP(IP) + dst.Spec.ServiceOptions.ExternalServiceIPs.Allowed[i] = capsulev1beta1.AllowedIP(IP) } } @@ -459,12 +462,12 @@ func (t *Tenant) ConvertFrom(srcRaw conversion.Hub) error { }) } } - if src.Spec.ExternalServiceIPs != nil { + if src.Spec.ServiceOptions != nil && src.Spec.ServiceOptions.ExternalServiceIPs != nil { t.Spec.ExternalServiceIPs = &ExternalServiceIPsSpec{ - Allowed: make([]AllowedIP, len(src.Spec.ExternalServiceIPs.Allowed)), + Allowed: make([]AllowedIP, len(src.Spec.ServiceOptions.ExternalServiceIPs.Allowed)), } - for i, IP := range src.Spec.ExternalServiceIPs.Allowed { + for i, IP := range src.Spec.ServiceOptions.ExternalServiceIPs.Allowed { t.Spec.ExternalServiceIPs.Allowed[i] = AllowedIP(IP) } } diff --git a/api/v1alpha1/conversion_hub_test.go b/api/v1alpha1/conversion_hub_test.go index 98b30e20..983ea0fc 100644 --- a/api/v1alpha1/conversion_hub_test.go +++ b/api/v1alpha1/conversion_hub_test.go @@ -49,6 +49,9 @@ func generateTenantsSpecs() (Tenant, capsulev1beta1.Tenant) { NodePort: pointer.BoolPtr(false), ExternalName: pointer.BoolPtr(false), }, + ExternalServiceIPs: &capsulev1beta1.ExternalServiceIPsSpec{ + Allowed: []capsulev1beta1.AllowedIP{"192.168.0.1"}, + }, } var v1beta1AllowedListSpec = &capsulev1beta1.AllowedListSpec{ Exact: []string{"foo", "bar"}, @@ -251,9 +254,6 @@ func generateTenantsSpecs() (Tenant, capsulev1beta1.Tenant) { }, }, }, - ExternalServiceIPs: &capsulev1beta1.ExternalServiceIPsSpec{ - Allowed: []capsulev1beta1.AllowedIP{"192.168.0.1"}, - }, ImagePullPolicies: []capsulev1beta1.ImagePullPolicySpec{"Always", "IfNotPresent"}, PriorityClasses: &capsulev1beta1.AllowedListSpec{ Exact: []string{"default"}, diff --git a/api/v1beta1/external_service_ips.go b/api/v1beta1/service_allowed_ips.go similarity index 100% rename from api/v1beta1/external_service_ips.go rename to api/v1beta1/service_allowed_ips.go diff --git a/api/v1beta1/service_allowed_types.go b/api/v1beta1/service_allowed_types.go new file mode 100644 index 00000000..98732330 --- /dev/null +++ b/api/v1beta1/service_allowed_types.go @@ -0,0 +1,13 @@ +// Copyright 2020-2021 Clastix Labs +// SPDX-License-Identifier: Apache-2.0 + +package v1beta1 + +type AllowedServices struct { + //+kubebuilder:default=true + // Specifies if NodePort service type resources are allowed for the Tenant. Default is true. Optional. + NodePort *bool `json:"nodePort,omitempty"` + //+kubebuilder:default=true + // Specifies if ExternalName service type resources are allowed for the Tenant. Default is true. Optional. + ExternalName *bool `json:"externalName,omitempty"` +} diff --git a/api/v1beta1/service_options.go b/api/v1beta1/service_options.go index a9586c3f..885efb6b 100644 --- a/api/v1beta1/service_options.go +++ b/api/v1beta1/service_options.go @@ -8,13 +8,6 @@ type ServiceOptions struct { AdditionalMetadata *AdditionalMetadataSpec `json:"additionalMetadata,omitempty"` // Block or deny certain type of Services. Optional. AllowedServices *AllowedServices `json:"allowedServices,omitempty"` -} - -type AllowedServices struct { - //+kubebuilder:default=true - // Specifies if NodePort service type resources are allowed for the Tenant. Default is true. Optional. - NodePort *bool `json:"nodePort,omitempty"` - //+kubebuilder:default=true - // Specifies if ExternalName service type resources are allowed for the Tenant. Default is true. Optional. - ExternalName *bool `json:"externalName,omitempty"` + // Specifies the external IPs that can be used in Services with type ClusterIP. An empty list means all the IPs are allowed. Optional. + ExternalServiceIPs *ExternalServiceIPsSpec `json:"externalIPs,omitempty"` } diff --git a/api/v1beta1/tenant_types.go b/api/v1beta1/tenant_types.go index f7d2566f..09f9752a 100644 --- a/api/v1beta1/tenant_types.go +++ b/api/v1beta1/tenant_types.go @@ -37,8 +37,6 @@ type TenantSpec struct { ResourceQuota *ResourceQuotaSpec `json:"resourceQuotas,omitempty"` // Specifies additional RoleBindings assigned to the Tenant. Capsule will ensure that all namespaces in the Tenant always contain the RoleBinding for the given ClusterRole. Optional. AdditionalRoleBindings []AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"` - // Specifies the external IPs that can be used in Services with type ClusterIP. An empty list means all the IPs are allowed. Optional. - ExternalServiceIPs *ExternalServiceIPsSpec `json:"externalServiceIPs,omitempty"` // Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional. ImagePullPolicies []ImagePullPolicySpec `json:"imagePullPolicies,omitempty"` // Specifies the allowed IngressClasses assigned to the Tenant. Capsule assures that all Ingress resources created in the Tenant can use only one of the allowed IngressClasses. Optional. diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 4f384305..62f9fcf0 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -291,6 +291,11 @@ func (in *ServiceOptions) DeepCopyInto(out *ServiceOptions) { *out = new(AllowedServices) (*in).DeepCopyInto(*out) } + if in.ExternalServiceIPs != nil { + in, out := &in.ExternalServiceIPs, &out.ExternalServiceIPs + *out = new(ExternalServiceIPsSpec) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceOptions. @@ -436,11 +441,6 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.ExternalServiceIPs != nil { - in, out := &in.ExternalServiceIPs, &out.ExternalServiceIPs - *out = new(ExternalServiceIPsSpec) - (*in).DeepCopyInto(*out) - } if in.ImagePullPolicies != nil { in, out := &in.ImagePullPolicies, &out.ImagePullPolicies *out = make([]ImagePullPolicySpec, len(*in)) diff --git a/pkg/webhook/service/validating.go b/pkg/webhook/service/validating.go index 04c2dada..ea9d52f9 100644 --- a/pkg/webhook/service/validating.go +++ b/pkg/webhook/service/validating.go @@ -60,12 +60,12 @@ func (r *handler) handleService(ctx context.Context, clt client.Client, decoder return &response } - if svc.Spec.ExternalIPs == nil || tnt.Spec.ExternalServiceIPs == nil { + if svc.Spec.ExternalIPs == nil || (tnt.Spec.ServiceOptions == nil || tnt.Spec.ServiceOptions.ExternalServiceIPs == nil) { return nil } ipInCIDR := func(ip net.IP) bool { - for _, allowed := range tnt.Spec.ExternalServiceIPs.Allowed { + for _, allowed := range tnt.Spec.ServiceOptions.ExternalServiceIPs.Allowed { if !strings.Contains(string(allowed), "/") { allowed += "/32" } @@ -85,7 +85,7 @@ func (r *handler) handleService(ctx context.Context, clt client.Client, decoder if !ipInCIDR(ip) { recorder.Eventf(&tnt, corev1.EventTypeWarning, "ForbiddenExternalServiceIP", "Service %s/%s external IP %s is forbidden for the current Tenant", req.Namespace, req.Name, ip.String()) - response := admission.Denied(NewExternalServiceIPForbidden(tnt.Spec.ExternalServiceIPs.Allowed).Error()) + response := admission.Denied(NewExternalServiceIPForbidden(tnt.Spec.ServiceOptions.ExternalServiceIPs.Allowed).Error()) return &response }