From cd362c3ba6ae2f50ae5d0abc0b54c323ae054e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Mon, 23 Sep 2024 15:13:47 +0200 Subject: [PATCH 1/9] add support for filtering public ingress traffic by IP or CIDR --- .../templates/radixapplication.yaml | 44 ++++++++++ json-schema/radixapplication.json | 54 ++++++++++++ pkg/apis/deployment/radixcomponent.go | 17 ++++ pkg/apis/deployment/radixcomponent_test.go | 70 ++++++++++++++++ pkg/apis/ingress/ingress.go | 1 + pkg/apis/ingress/ingressannotationprovider.go | 27 ++++++ .../ingress/ingressannotationprovider_test.go | 50 +++++++++++ pkg/apis/ingress/oauth.go | 5 +- pkg/apis/radix/v1/radixapptypes.go | 43 ++++++++++ pkg/apis/radix/v1/radixdeploytypes.go | 10 +++ pkg/apis/radix/v1/zz_generated.deepcopy.go | 82 ++++++++++++++++++ .../utils/applicationcomponent_builder.go | 84 ++++++++++--------- .../utils/componentenvironment_builder.go | 66 ++++++++------- radix-operator/deployment/handler_test.go | 1 + 14 files changed, 486 insertions(+), 68 deletions(-) diff --git a/charts/radix-operator/templates/radixapplication.yaml b/charts/radix-operator/templates/radixapplication.yaml index 9706bdf74..2d2f57b25 100644 --- a/charts/radix-operator/templates/radixapplication.yaml +++ b/charts/radix-operator/templates/radixapplication.yaml @@ -687,6 +687,29 @@ spec: Enabled or disables collection of custom Prometheus metrics. More info: https://www.radix.equinor.com/references/reference-radix-config/#monitoring type: boolean + network: + description: Environment specific network settings. + properties: + ingress: + description: Ingress defines settings for ingress + traffic. + properties: + public: + description: Public defines settings for public + traffic. + properties: + allow: + description: |- + Allow defines a list of public IP adresses or CIDRs which are allowed to access the component. + All IP adresses are allowed if this field is empty or not set. + items: + description: IP address or CIDR. + pattern: ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$ + type: string + type: array + type: object + type: object + type: object node: description: |- Environment specific GPU requirements for the component. @@ -1406,6 +1429,27 @@ spec: minLength: 1 pattern: ^(([a-z0-9][-a-z0-9]*)?[a-z0-9])?$ type: string + network: + description: Network settings. + properties: + ingress: + description: Ingress defines settings for ingress traffic. + properties: + public: + description: Public defines settings for public traffic. + properties: + allow: + description: |- + Allow defines a list of public IP adresses or CIDRs which are allowed to access the component. + All IP adresses are allowed if this field is empty or not set. + items: + description: IP address or CIDR. + pattern: ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$ + type: string + type: array + type: object + type: object + type: object node: description: |- Defines GPU requirements for the component. diff --git a/json-schema/radixapplication.json b/json-schema/radixapplication.json index 2084caad3..22e9c711f 100644 --- a/json-schema/radixapplication.json +++ b/json-schema/radixapplication.json @@ -665,6 +665,33 @@ "description": "Enabled or disables collection of custom Prometheus metrics.\nMore info: https://www.radix.equinor.com/references/reference-radix-config/#monitoring", "type": "boolean" }, + "network": { + "description": "Environment specific network settings.", + "properties": { + "ingress": { + "description": "Ingress defines settings for ingress traffic.", + "properties": { + "public": { + "description": "Public defines settings for public traffic.", + "properties": { + "allow": { + "description": "Allow defines a list of public IP adresses or CIDRs which are allowed to access the component.\nAll IP adresses are allowed if this field is empty or not set.", + "items": { + "description": "IP address or CIDR.", + "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\/([0-9]|[1-2][0-9]|3[0-2]))?$", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, "node": { "description": "Environment specific GPU requirements for the component.\nMore info: https://www.radix.equinor.com/references/reference-radix-config/#node", "properties": { @@ -1394,6 +1421,33 @@ "pattern": "^(([a-z0-9][-a-z0-9]*)?[a-z0-9])?$", "type": "string" }, + "network": { + "description": "Network settings.", + "properties": { + "ingress": { + "description": "Ingress defines settings for ingress traffic.", + "properties": { + "public": { + "description": "Public defines settings for public traffic.", + "properties": { + "allow": { + "description": "Allow defines a list of public IP adresses or CIDRs which are allowed to access the component.\nAll IP adresses are allowed if this field is empty or not set.", + "items": { + "description": "IP address or CIDR.", + "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\/([0-9]|[1-2][0-9]|3[0-2]))?$", + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, "node": { "description": "Defines GPU requirements for the component.\nMore info: https://www.radix.equinor.com/references/reference-radix-config/#node", "properties": { diff --git a/pkg/apis/deployment/radixcomponent.go b/pkg/apis/deployment/radixcomponent.go index 68f77ffab..a4467bb64 100644 --- a/pkg/apis/deployment/radixcomponent.go +++ b/pkg/apis/deployment/radixcomponent.go @@ -81,6 +81,7 @@ func GetRadixComponentsForEnv(ctx context.Context, radixApplication *radixv1.Rad deployComponent.Monitoring = getRadixCommonComponentMonitoring(&radixComponent, environmentSpecificConfig) deployComponent.HorizontalScaling = getRadixCommonComponentHorizontalScaling(&radixComponent, environmentSpecificConfig) deployComponent.Runtime = componentImage.Runtime + deployComponent.Network = getRadixComponentNetwork(&radixComponent, environmentSpecificConfig) if deployComponent.VolumeMounts, err = getRadixCommonComponentVolumeMounts(&radixComponent, environmentSpecificConfig); err != nil { return nil, err } @@ -90,6 +91,22 @@ func GetRadixComponentsForEnv(ctx context.Context, radixApplication *radixv1.Rad return deployComponents, nil } +func getRadixComponentNetwork(component *radixv1.RadixComponent, environmentConfig *radixv1.RadixEnvironmentConfig) *radixv1.Network { + var dst *radixv1.Network + if component.Network != nil { + dst = component.Network.DeepCopy() + } + + if environmentConfig != nil && environmentConfig.Network != nil { + if dst == nil { + dst = &radixv1.Network{} + } + mergo.Merge(dst, environmentConfig.Network, mergo.WithOverride, mergo.WithOverrideEmptySlice) + } + + return dst +} + func getRadixCommonComponentReadOnlyFileSystem(radixComponent radixv1.RadixCommonComponent, environmentSpecificConfig radixv1.RadixCommonEnvironmentConfig) *bool { if !commonutils.IsNil(environmentSpecificConfig) && environmentSpecificConfig.GetReadOnlyFileSystem() != nil { return environmentSpecificConfig.GetReadOnlyFileSystem() diff --git a/pkg/apis/deployment/radixcomponent_test.go b/pkg/apis/deployment/radixcomponent_test.go index addba559c..512882474 100644 --- a/pkg/apis/deployment/radixcomponent_test.go +++ b/pkg/apis/deployment/radixcomponent_test.go @@ -1547,6 +1547,76 @@ func Test_GetRadixComponentsForEnv_Runtime_AlwaysUseFromDeployComponentImages(t } } +func Test_Test_GetRadixComponentsForEnv_NetworkIngressPublicAllow(t *testing.T) { + tests := map[string]struct { + commonConfig *radixv1.Network + envConfig *radixv1.Network + setEnv bool + expected *radixv1.Network + }{ + "nil in common": { + setEnv: false, + expected: nil, + }, + "empty in common, env not set": { + commonConfig: &radixv1.Network{}, + setEnv: false, + expected: &radixv1.Network{}, + }, + "empty in common and nil in env": { + commonConfig: &radixv1.Network{}, + setEnv: true, + expected: &radixv1.Network{}, + }, + "empty in common and env": { + commonConfig: &radixv1.Network{}, + setEnv: true, + envConfig: &radixv1.Network{}, + expected: &radixv1.Network{}, + }, + "allow set in common, nil in env": { + commonConfig: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("commonip1"), radixv1.IPOrCIDR("commonip2")}}}}, + setEnv: true, + envConfig: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{}}}, + expected: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("commonip1"), radixv1.IPOrCIDR("commonip2")}}}}, + }, + "allow set in common, empty in env": { + commonConfig: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("commonip1"), radixv1.IPOrCIDR("commonip2")}}}}, + setEnv: true, + envConfig: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{}}}}, + expected: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{}}}}, + }, + "allow set in common and env": { + commonConfig: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("commonip1"), radixv1.IPOrCIDR("commonip2")}}}}, + setEnv: true, + envConfig: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("envip1"), radixv1.IPOrCIDR("envip2")}}}}, + expected: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("envip1"), radixv1.IPOrCIDR("envip2")}}}}, + }, + "allow nil in common set in env": { + commonConfig: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{}}}, + setEnv: true, + envConfig: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("envip1"), radixv1.IPOrCIDR("envip2")}}}}, + expected: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("envip1"), radixv1.IPOrCIDR("envip2")}}}}, + }, + } + + for testName, test := range tests { + t.Run(testName, func(t *testing.T) { + const envName = "anyenv" + component := utils.AnApplicationComponent().WithName("anycomponent").WithNetwork(test.commonConfig) + if test.setEnv { + component = component.WithEnvironmentConfigs( + utils.AnEnvironmentConfig().WithEnvironment(envName).WithNetwork(test.envConfig), + ) + } + ra := utils.ARadixApplication().WithComponents(component).BuildRA() + components, err := GetRadixComponentsForEnv(context.Background(), ra, nil, envName, make(pipeline.DeployComponentImages), make(radixv1.EnvVarsMap), nil) + require.NoError(t, err) + assert.Equal(t, test.expected, components[0].Network) + }) + } +} + func convertRadixDeployComponentToNameSet(deployComponents []radixv1.RadixDeployComponent) map[string]bool { set := make(map[string]bool) for _, deployComponent := range deployComponents { diff --git a/pkg/apis/ingress/ingress.go b/pkg/apis/ingress/ingress.go index c2514b487..9579cc215 100644 --- a/pkg/apis/ingress/ingress.go +++ b/pkg/apis/ingress/ingress.go @@ -115,5 +115,6 @@ func GetAnnotationProvider(ingressConfiguration IngressConfiguration, certificat NewIngressConfigurationAnnotationProvider(ingressConfiguration), NewClientCertificateAnnotationProvider(certificateNamespace), NewOAuth2AnnotationProvider(oauth2DefaultConfig), + NewIngressPublicAllowListAnnotationProvider(), } } diff --git a/pkg/apis/ingress/ingressannotationprovider.go b/pkg/apis/ingress/ingressannotationprovider.go index 3bb307325..dadeb2f77 100644 --- a/pkg/apis/ingress/ingressannotationprovider.go +++ b/pkg/apis/ingress/ingressannotationprovider.go @@ -128,3 +128,30 @@ func (provider *oauth2AnnotationProvider) GetAnnotations(component radixv1.Radix return annotations, nil } + +// NewIngressPublicAllowListAnnotationProvider provides Ingress annotations for allowing +// only public traffic from IP adresses defined in Network.Ingress.Public.Allow field +func NewIngressPublicAllowListAnnotationProvider() AnnotationProvider { + return &ingressPublicAllowListAnnotationProvider{} +} + +type ingressPublicAllowListAnnotationProvider struct{} + +func (*ingressPublicAllowListAnnotationProvider) GetAnnotations(component radixv1.RadixCommonDeployComponent, _ string) (map[string]string, error) { + if network := component.GetNetwork(); network == nil || network.Ingress == nil || network.Ingress.Public == nil || network.Ingress.Public.Allow == nil || len(*network.Ingress.Public.Allow) == 0 { + return nil, nil + } + + annotations := make(map[string]string, 1) + var sb strings.Builder + + for i, addr := range *component.GetNetwork().Ingress.Public.Allow { + if i > 0 { + sb.WriteString(",") + } + sb.WriteString(string(addr)) + } + annotations["nginx.ingress.kubernetes.io/whitelist-source-range"] = sb.String() + + return annotations, nil +} diff --git a/pkg/apis/ingress/ingressannotationprovider_test.go b/pkg/apis/ingress/ingressannotationprovider_test.go index 8d36d2744..ef3ce1f7d 100644 --- a/pkg/apis/ingress/ingressannotationprovider_test.go +++ b/pkg/apis/ingress/ingressannotationprovider_test.go @@ -129,6 +129,56 @@ func Test_ClientCertificateAnnotations(t *testing.T) { assert.Empty(t, result, "Expected Annotations to be empty") } +func Test_PublicIngressAllowAnnotationProvider(t *testing.T) { + tests := map[string]struct { + component radixv1.RadixCommonDeployComponent + expectedAnnotation map[string]string + }{ + "network is not set": { + component: &radixv1.RadixDeployComponent{}, + expectedAnnotation: make(map[string]string), + }, + "network.ingress is not set": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{}}, + expectedAnnotation: make(map[string]string), + }, + "network.ingress.public is not set": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{Ingress: &radixv1.Ingress{}}}, + expectedAnnotation: make(map[string]string), + }, + "network.ingress.public.allow is not set": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{}}}}, + expectedAnnotation: make(map[string]string), + }, + "network.ingress.public.allow is empty": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{}}}}}, + expectedAnnotation: make(map[string]string), + }, + "network.ingress.public.allow single entry": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("10.0.0.1")}}}}}, + expectedAnnotation: map[string]string{"nginx.ingress.kubernetes.io/whitelist-source-range": "10.0.0.1"}, + }, + "network.ingress.public.allow multiple entries": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("10.0.0.1"), radixv1.IPOrCIDR("10.10.10.10/30")}}}}}, + expectedAnnotation: map[string]string{"nginx.ingress.kubernetes.io/whitelist-source-range": "10.0.0.1,10.10.10.10/30"}, + }, + } + + sut := &ingressPublicAllowListAnnotationProvider{} + for testName, testSpec := range tests { + t.Run(testName, func(t *testing.T) { + actual, err := sut.GetAnnotations(testSpec.component, "") + assert.NoError(t, err) + if len(testSpec.expectedAnnotation) == 0 { + assert.Empty(t, actual) + } else { + assert.Equal(t, actual, testSpec.expectedAnnotation) + } + }) + } + +} + type OAuth2AnnotationsTestSuite struct { suite.Suite oauth2Config *defaults.MockOAuth2Config diff --git a/pkg/apis/ingress/oauth.go b/pkg/apis/ingress/oauth.go index 613a6d7e3..c08ab887b 100644 --- a/pkg/apis/ingress/oauth.go +++ b/pkg/apis/ingress/oauth.go @@ -12,7 +12,10 @@ import ( // GetAuxOAuthProxyAnnotationProviders Gets aux OAuth proxy annotation providers func GetAuxOAuthProxyAnnotationProviders() []AnnotationProvider { - return []AnnotationProvider{NewForceSslRedirectAnnotationProvider()} + return []AnnotationProvider{ + NewForceSslRedirectAnnotationProvider(), + NewIngressPublicAllowListAnnotationProvider(), + } } // BuildOAuthProxyIngressForComponentIngress builds OAuth proxy ingress for RadixDeploy component ingress diff --git a/pkg/apis/radix/v1/radixapptypes.go b/pkg/apis/radix/v1/radixapptypes.go index 79b1d84f1..2e26a11c6 100644 --- a/pkg/apis/radix/v1/radixapptypes.go +++ b/pkg/apis/radix/v1/radixapptypes.go @@ -460,6 +460,10 @@ type RadixComponent struct { // Runtime defines the target runtime requirements for the component // +optional Runtime *Runtime `json:"runtime,omitempty"` + + // Network settings. + // +optional + Network *Network `json:"network,omitempty"` } // RadixEnvironmentConfig defines environment specific settings for component. @@ -558,6 +562,10 @@ type RadixEnvironmentConfig struct { // Runtime defines environment specific target runtime requirements for the component // +optional Runtime *Runtime `json:"runtime,omitempty"` + + // Environment specific network settings. + // +optional + Network *Network `json:"network,omitempty"` } // RadixJobComponent defines a single job component within a RadixApplication @@ -1440,6 +1448,41 @@ const ( OperatorNotIn Operator = "NotIn" ) +// Network defines settings for network traffic. +// Currently, only public ingress traffic is supported +type Network struct { + // Ingress defines settings for ingress traffic. + // +optional + Ingress *Ingress `json:"ingress,omitempty"` + + // If we decide to add support for egress configuration (managed by standard NetworkPolicy or more advanced systems like Cilium), + // we will add a `Egress` fields here. +} + +// Ingress defines settings for ingress traffic. +type Ingress struct { + // Public defines settings for public traffic. + // +optional + Public *IngressPublic `json:"public,omitempty"` + + // If we decide to add support for private/internal ingress configuration (managed by NetworkPolicies), + // we will add a `Private` fields here. +} + +// IP address or CIDR. +// +kubebuilder:validation:Pattern=`^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$` +type IPOrCIDR string + +// Ingress defines settings for ingress traffic. +type IngressPublic struct { + // Allow defines a list of public IP adresses or CIDRs which are allowed to access the component. + // All IP adresses are allowed if this field is empty or not set. + // +optional + Allow *[]IPOrCIDR `json:"allow,omitempty"` + + // This is where we will add additional fields used for configuring the Ingress Nginx controller. +} + // RadixCommonComponent defines a common component interface for Radix components type RadixCommonComponent interface { // GetName Gets component name diff --git a/pkg/apis/radix/v1/radixdeploytypes.go b/pkg/apis/radix/v1/radixdeploytypes.go index cc96197e7..d1d8c9a5c 100644 --- a/pkg/apis/radix/v1/radixdeploytypes.go +++ b/pkg/apis/radix/v1/radixdeploytypes.go @@ -129,6 +129,7 @@ type RadixDeployComponent struct { Identity *Identity `json:"identity,omitempty"` ReadOnlyFileSystem *bool `json:"readOnlyFileSystem,omitempty"` Runtime *Runtime `json:"runtime,omitempty"` + Network *Network `json:"network,omitempty"` } func (deployComponent *RadixDeployComponent) GetName() string { @@ -235,6 +236,10 @@ func (deployComponent *RadixDeployComponent) GetRuntime() *Runtime { return deployComponent.Runtime } +func (deployComponent *RadixDeployComponent) GetNetwork() *Network { + return deployComponent.Network +} + func (deployComponent *RadixDeployComponent) SetName(name string) { deployComponent.Name = name } @@ -354,6 +359,10 @@ func (deployJobComponent *RadixDeployJobComponent) GetRuntime() *Runtime { return deployJobComponent.Runtime } +func (deployJobComponent *RadixDeployJobComponent) GetNetwork() *Network { + return nil +} + func (deployJobComponent *RadixDeployJobComponent) SetName(name string) { deployJobComponent.Name = name } @@ -443,6 +452,7 @@ type RadixCommonDeployComponent interface { GetIdentity() *Identity GetReadOnlyFileSystem() *bool GetRuntime() *Runtime + GetNetwork() *Network } // RadixCommonDeployComponentFactory defines a common component factory diff --git a/pkg/apis/radix/v1/zz_generated.deepcopy.go b/pkg/apis/radix/v1/zz_generated.deepcopy.go index 99b1d5c58..1d1bd22ec 100644 --- a/pkg/apis/radix/v1/zz_generated.deepcopy.go +++ b/pkg/apis/radix/v1/zz_generated.deepcopy.go @@ -420,6 +420,52 @@ func (in *Identity) DeepCopy() *Identity { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Ingress) DeepCopyInto(out *Ingress) { + *out = *in + if in.Public != nil { + in, out := &in.Public, &out.Public + *out = new(IngressPublic) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Ingress. +func (in *Ingress) DeepCopy() *Ingress { + if in == nil { + return nil + } + out := new(Ingress) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressPublic) DeepCopyInto(out *IngressPublic) { + *out = *in + if in.Allow != nil { + in, out := &in.Allow, &out.Allow + *out = new([]IPOrCIDR) + if **in != nil { + in, out := *in, *out + *out = make([]IPOrCIDR, len(*in)) + copy(*out, *in) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressPublic. +func (in *IngressPublic) DeepCopy() *IngressPublic { + if in == nil { + return nil + } + out := new(IngressPublic) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LocalObjectReference) DeepCopyInto(out *LocalObjectReference) { *out = *in @@ -452,6 +498,27 @@ func (in *MonitoringConfig) DeepCopy() *MonitoringConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Network) DeepCopyInto(out *Network) { + *out = *in + if in.Ingress != nil { + in, out := &in.Ingress, &out.Ingress + *out = new(Ingress) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Network. +func (in *Network) DeepCopy() *Network { + if in == nil { + return nil + } + out := new(Network) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Notifications) DeepCopyInto(out *Notifications) { *out = *in @@ -1349,6 +1416,11 @@ func (in *RadixComponent) DeepCopyInto(out *RadixComponent) { *out = new(Runtime) **out = **in } + if in.Network != nil { + in, out := &in.Network, &out.Network + *out = new(Network) + (*in).DeepCopyInto(*out) + } return } @@ -1540,6 +1612,11 @@ func (in *RadixDeployComponent) DeepCopyInto(out *RadixDeployComponent) { *out = new(Runtime) **out = **in } + if in.Network != nil { + in, out := &in.Network, &out.Network + *out = new(Network) + (*in).DeepCopyInto(*out) + } return } @@ -1962,6 +2039,11 @@ func (in *RadixEnvironmentConfig) DeepCopyInto(out *RadixEnvironmentConfig) { *out = new(Runtime) **out = **in } + if in.Network != nil { + in, out := &in.Network, &out.Network + *out = new(Network) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/apis/utils/applicationcomponent_builder.go b/pkg/apis/utils/applicationcomponent_builder.go index 1ce1ff46a..5fcbe5e33 100644 --- a/pkg/apis/utils/applicationcomponent_builder.go +++ b/pkg/apis/utils/applicationcomponent_builder.go @@ -1,7 +1,7 @@ package utils import ( - v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" + radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" ) // RadixApplicationComponentBuilder Handles construction of RA component @@ -15,25 +15,26 @@ type RadixApplicationComponentBuilder interface { WithPublic(bool) RadixApplicationComponentBuilder // Deprecated: For backwards comptibility WithPublic is still supported, new code should use WithPublicPort instead WithPublicPort(string) RadixApplicationComponentBuilder WithPort(string, int32) RadixApplicationComponentBuilder - WithPorts([]v1.ComponentPort) RadixApplicationComponentBuilder + WithPorts([]radixv1.ComponentPort) RadixApplicationComponentBuilder WithSecrets(...string) RadixApplicationComponentBuilder - WithSecretRefs(v1.RadixSecretRefs) RadixApplicationComponentBuilder - WithMonitoringConfig(v1.MonitoringConfig) RadixApplicationComponentBuilder + WithSecretRefs(radixv1.RadixSecretRefs) RadixApplicationComponentBuilder + WithMonitoringConfig(radixv1.MonitoringConfig) RadixApplicationComponentBuilder WithMonitoring(monitoring *bool) RadixApplicationComponentBuilder WithIngressConfiguration(...string) RadixApplicationComponentBuilder WithEnvironmentConfig(RadixEnvironmentConfigBuilder) RadixApplicationComponentBuilder WithEnvironmentConfigs(...RadixEnvironmentConfigBuilder) RadixApplicationComponentBuilder WithCommonEnvironmentVariable(string, string) RadixApplicationComponentBuilder WithCommonResource(map[string]string, map[string]string) RadixApplicationComponentBuilder - WithNode(v1.RadixNode) RadixApplicationComponentBuilder - WithAuthentication(*v1.Authentication) RadixApplicationComponentBuilder - WithVolumeMounts([]v1.RadixVolumeMount) RadixApplicationComponentBuilder + WithNode(radixv1.RadixNode) RadixApplicationComponentBuilder + WithAuthentication(*radixv1.Authentication) RadixApplicationComponentBuilder + WithVolumeMounts([]radixv1.RadixVolumeMount) RadixApplicationComponentBuilder WithEnabled(bool) RadixApplicationComponentBuilder - WithIdentity(*v1.Identity) RadixApplicationComponentBuilder + WithIdentity(*radixv1.Identity) RadixApplicationComponentBuilder WithReadOnlyFileSystem(*bool) RadixApplicationComponentBuilder - WithHorizontalScaling(scaling *v1.RadixHorizontalScaling) RadixApplicationComponentBuilder - WithRuntime(runtime *v1.Runtime) RadixApplicationComponentBuilder - BuildComponent() v1.RadixComponent + WithHorizontalScaling(scaling *radixv1.RadixHorizontalScaling) RadixApplicationComponentBuilder + WithRuntime(runtime *radixv1.Runtime) RadixApplicationComponentBuilder + WithNetwork(network *radixv1.Network) RadixApplicationComponentBuilder + BuildComponent() radixv1.RadixComponent } type radixApplicationComponentBuilder struct { @@ -44,24 +45,25 @@ type radixApplicationComponentBuilder struct { alwaysPullImageOnDeploy *bool public bool // Deprecated: For backwards compatibility public is still supported, new code should use publicPort instead publicPort string - monitoringConfig v1.MonitoringConfig - ports []v1.ComponentPort + monitoringConfig radixv1.MonitoringConfig + ports []radixv1.ComponentPort secrets []string - secretRefs v1.RadixSecretRefs + secretRefs radixv1.RadixSecretRefs ingressConfiguration []string environmentConfig []RadixEnvironmentConfigBuilder - variables v1.EnvVarsMap - resources v1.ResourceRequirements - node v1.RadixNode - authentication *v1.Authentication - volumeMounts []v1.RadixVolumeMount + variables radixv1.EnvVarsMap + resources radixv1.ResourceRequirements + node radixv1.RadixNode + authentication *radixv1.Authentication + volumeMounts []radixv1.RadixVolumeMount enabled *bool - identity *v1.Identity + identity *radixv1.Identity readOnlyFileSystem *bool monitoring *bool imageTagName string - horizontalScaling *v1.RadixHorizontalScaling - runtime *v1.Runtime + horizontalScaling *radixv1.RadixHorizontalScaling + runtime *radixv1.Runtime + network *radixv1.Network } func (rcb *radixApplicationComponentBuilder) WithName(name string) RadixApplicationComponentBuilder { @@ -110,12 +112,12 @@ func (rcb *radixApplicationComponentBuilder) WithSecrets(secrets ...string) Radi return rcb } -func (rcb *radixApplicationComponentBuilder) WithSecretRefs(secretRefs v1.RadixSecretRefs) RadixApplicationComponentBuilder { +func (rcb *radixApplicationComponentBuilder) WithSecretRefs(secretRefs radixv1.RadixSecretRefs) RadixApplicationComponentBuilder { rcb.secretRefs = secretRefs return rcb } -func (rcb *radixApplicationComponentBuilder) WithMonitoringConfig(monitoringConfig v1.MonitoringConfig) RadixApplicationComponentBuilder { +func (rcb *radixApplicationComponentBuilder) WithMonitoringConfig(monitoringConfig radixv1.MonitoringConfig) RadixApplicationComponentBuilder { rcb.monitoringConfig = monitoringConfig return rcb } @@ -132,14 +134,14 @@ func (rcb *radixApplicationComponentBuilder) WithIngressConfiguration(ingressCon func (rcb *radixApplicationComponentBuilder) WithPort(name string, port int32) RadixApplicationComponentBuilder { if rcb.ports == nil { - rcb.ports = make([]v1.ComponentPort, 0) + rcb.ports = make([]radixv1.ComponentPort, 0) } - rcb.ports = append(rcb.ports, v1.ComponentPort{Name: name, Port: port}) + rcb.ports = append(rcb.ports, radixv1.ComponentPort{Name: name, Port: port}) return rcb } -func (rcb *radixApplicationComponentBuilder) WithPorts(ports []v1.ComponentPort) RadixApplicationComponentBuilder { +func (rcb *radixApplicationComponentBuilder) WithPorts(ports []radixv1.ComponentPort) RadixApplicationComponentBuilder { for i := range ports { rcb.WithPort(ports[i].Name, ports[i].Port) } @@ -158,24 +160,24 @@ func (rcb *radixApplicationComponentBuilder) WithEnvironmentConfigs(environmentC func (rcb *radixApplicationComponentBuilder) WithCommonEnvironmentVariable(name, value string) RadixApplicationComponentBuilder { if rcb.variables == nil { - rcb.variables = make(v1.EnvVarsMap) + rcb.variables = make(radixv1.EnvVarsMap) } rcb.variables[name] = value return rcb } -func (rcb *radixApplicationComponentBuilder) WithNode(node v1.RadixNode) RadixApplicationComponentBuilder { +func (rcb *radixApplicationComponentBuilder) WithNode(node radixv1.RadixNode) RadixApplicationComponentBuilder { rcb.node = node return rcb } -func (rcb *radixApplicationComponentBuilder) WithAuthentication(authentication *v1.Authentication) RadixApplicationComponentBuilder { +func (rcb *radixApplicationComponentBuilder) WithAuthentication(authentication *radixv1.Authentication) RadixApplicationComponentBuilder { rcb.authentication = authentication return rcb } -func (rcb *radixApplicationComponentBuilder) WithVolumeMounts(volumes []v1.RadixVolumeMount) RadixApplicationComponentBuilder { +func (rcb *radixApplicationComponentBuilder) WithVolumeMounts(volumes []radixv1.RadixVolumeMount) RadixApplicationComponentBuilder { rcb.volumeMounts = volumes return rcb } @@ -185,13 +187,13 @@ func (rcb *radixApplicationComponentBuilder) WithEnabled(enabled bool) RadixAppl return rcb } -func (rcb *radixApplicationComponentBuilder) WithIdentity(identity *v1.Identity) RadixApplicationComponentBuilder { +func (rcb *radixApplicationComponentBuilder) WithIdentity(identity *radixv1.Identity) RadixApplicationComponentBuilder { rcb.identity = identity return rcb } func (rcb *radixApplicationComponentBuilder) WithCommonResource(request map[string]string, limit map[string]string) RadixApplicationComponentBuilder { - rcb.resources = v1.ResourceRequirements{ + rcb.resources = radixv1.ResourceRequirements{ Limits: limit, Requests: request, } @@ -203,23 +205,28 @@ func (rcb *radixApplicationComponentBuilder) WithReadOnlyFileSystem(readOnlyFile return rcb } -func (rcb *radixApplicationComponentBuilder) WithHorizontalScaling(scaling *v1.RadixHorizontalScaling) RadixApplicationComponentBuilder { +func (rcb *radixApplicationComponentBuilder) WithHorizontalScaling(scaling *radixv1.RadixHorizontalScaling) RadixApplicationComponentBuilder { rcb.horizontalScaling = scaling return rcb } -func (rcb *radixApplicationComponentBuilder) WithRuntime(runtime *v1.Runtime) RadixApplicationComponentBuilder { +func (rcb *radixApplicationComponentBuilder) WithRuntime(runtime *radixv1.Runtime) RadixApplicationComponentBuilder { rcb.runtime = runtime return rcb } -func (rcb *radixApplicationComponentBuilder) BuildComponent() v1.RadixComponent { - var environmentConfig = make([]v1.RadixEnvironmentConfig, 0) +func (rcb *radixApplicationComponentBuilder) WithNetwork(network *radixv1.Network) RadixApplicationComponentBuilder { + rcb.network = network + return rcb +} + +func (rcb *radixApplicationComponentBuilder) BuildComponent() radixv1.RadixComponent { + var environmentConfig = make([]radixv1.RadixEnvironmentConfig, 0) for _, env := range rcb.environmentConfig { environmentConfig = append(environmentConfig, env.BuildEnvironmentConfig()) } - return v1.RadixComponent{ + return radixv1.RadixComponent{ Name: rcb.name, SourceFolder: rcb.sourceFolder, DockerfileName: rcb.dockerfileName, @@ -245,6 +252,7 @@ func (rcb *radixApplicationComponentBuilder) BuildComponent() v1.RadixComponent HorizontalScaling: rcb.horizontalScaling, VolumeMounts: rcb.volumeMounts, Runtime: rcb.runtime, + Network: rcb.network, } } diff --git a/pkg/apis/utils/componentenvironment_builder.go b/pkg/apis/utils/componentenvironment_builder.go index 09517d36f..73bd4a728 100644 --- a/pkg/apis/utils/componentenvironment_builder.go +++ b/pkg/apis/utils/componentenvironment_builder.go @@ -1,7 +1,7 @@ package utils import ( - v1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" + radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" ) // RadixEnvironmentConfigBuilder Handles construction of RA component environment @@ -13,19 +13,20 @@ type RadixEnvironmentConfigBuilder interface { WithReplicas(*int) RadixEnvironmentConfigBuilder WithEnvironmentVariable(string, string) RadixEnvironmentConfigBuilder WithResource(map[string]string, map[string]string) RadixEnvironmentConfigBuilder - WithVolumeMounts([]v1.RadixVolumeMount) RadixEnvironmentConfigBuilder - BuildEnvironmentConfig() v1.RadixEnvironmentConfig + WithVolumeMounts([]radixv1.RadixVolumeMount) RadixEnvironmentConfigBuilder + BuildEnvironmentConfig() radixv1.RadixEnvironmentConfig WithAlwaysPullImageOnDeploy(bool) RadixEnvironmentConfigBuilder WithMonitoring(monitoring *bool) RadixEnvironmentConfigBuilder - WithNode(v1.RadixNode) RadixEnvironmentConfigBuilder - WithAuthentication(*v1.Authentication) RadixEnvironmentConfigBuilder - WithSecretRefs(v1.RadixSecretRefs) RadixEnvironmentConfigBuilder + WithNode(radixv1.RadixNode) RadixEnvironmentConfigBuilder + WithAuthentication(*radixv1.Authentication) RadixEnvironmentConfigBuilder + WithSecretRefs(radixv1.RadixSecretRefs) RadixEnvironmentConfigBuilder WithEnabled(bool) RadixEnvironmentConfigBuilder - WithIdentity(*v1.Identity) RadixEnvironmentConfigBuilder + WithIdentity(*radixv1.Identity) RadixEnvironmentConfigBuilder WithImageTagName(string) RadixEnvironmentConfigBuilder - WithHorizontalScaling(scaling *v1.RadixHorizontalScaling) RadixEnvironmentConfigBuilder + WithHorizontalScaling(scaling *radixv1.RadixHorizontalScaling) RadixEnvironmentConfigBuilder WithReadOnlyFileSystem(*bool) RadixEnvironmentConfigBuilder - WithRuntime(*v1.Runtime) RadixEnvironmentConfigBuilder + WithRuntime(*radixv1.Runtime) RadixEnvironmentConfigBuilder + WithNetwork(*radixv1.Network) RadixEnvironmentConfigBuilder } type radixEnvironmentConfigBuilder struct { @@ -33,37 +34,38 @@ type radixEnvironmentConfigBuilder struct { sourceFolder string dockerfileName string image string - variables v1.EnvVarsMap + variables radixv1.EnvVarsMap replicas *int - resources v1.ResourceRequirements + resources radixv1.ResourceRequirements alwaysPullImageOnDeploy *bool - volumeMounts []v1.RadixVolumeMount + volumeMounts []radixv1.RadixVolumeMount monitoring *bool - node v1.RadixNode - secretRefs v1.RadixSecretRefs - authentication *v1.Authentication + node radixv1.RadixNode + secretRefs radixv1.RadixSecretRefs + authentication *radixv1.Authentication enabled *bool - identity *v1.Identity + identity *radixv1.Identity imageTagName string - horizontalScaling *v1.RadixHorizontalScaling + horizontalScaling *radixv1.RadixHorizontalScaling readOnlyFileSystem *bool - runtime *v1.Runtime + runtime *radixv1.Runtime + network *radixv1.Network } -func (ceb *radixEnvironmentConfigBuilder) WithHorizontalScaling(scaling *v1.RadixHorizontalScaling) RadixEnvironmentConfigBuilder { +func (ceb *radixEnvironmentConfigBuilder) WithHorizontalScaling(scaling *radixv1.RadixHorizontalScaling) RadixEnvironmentConfigBuilder { ceb.horizontalScaling = scaling return ceb } func (ceb *radixEnvironmentConfigBuilder) WithResource(request map[string]string, limit map[string]string) RadixEnvironmentConfigBuilder { - ceb.resources = v1.ResourceRequirements{ + ceb.resources = radixv1.ResourceRequirements{ Limits: limit, Requests: request, } return ceb } -func (ceb *radixEnvironmentConfigBuilder) WithVolumeMounts(volumeMounts []v1.RadixVolumeMount) RadixEnvironmentConfigBuilder { +func (ceb *radixEnvironmentConfigBuilder) WithVolumeMounts(volumeMounts []radixv1.RadixVolumeMount) RadixEnvironmentConfigBuilder { ceb.volumeMounts = volumeMounts return ceb } @@ -95,7 +97,7 @@ func (ceb *radixEnvironmentConfigBuilder) WithReplicas(replicas *int) RadixEnvir func (ceb *radixEnvironmentConfigBuilder) WithEnvironmentVariable(name, value string) RadixEnvironmentConfigBuilder { if ceb.variables == nil { - ceb.variables = make(v1.EnvVarsMap) + ceb.variables = make(radixv1.EnvVarsMap) } ceb.variables[name] = value @@ -112,17 +114,17 @@ func (ceb *radixEnvironmentConfigBuilder) WithMonitoring(monitoring *bool) Radix return ceb } -func (ceb *radixEnvironmentConfigBuilder) WithNode(node v1.RadixNode) RadixEnvironmentConfigBuilder { +func (ceb *radixEnvironmentConfigBuilder) WithNode(node radixv1.RadixNode) RadixEnvironmentConfigBuilder { ceb.node = node return ceb } -func (ceb *radixEnvironmentConfigBuilder) WithAuthentication(authentication *v1.Authentication) RadixEnvironmentConfigBuilder { +func (ceb *radixEnvironmentConfigBuilder) WithAuthentication(authentication *radixv1.Authentication) RadixEnvironmentConfigBuilder { ceb.authentication = authentication return ceb } -func (ceb *radixEnvironmentConfigBuilder) WithSecretRefs(secretRefs v1.RadixSecretRefs) RadixEnvironmentConfigBuilder { +func (ceb *radixEnvironmentConfigBuilder) WithSecretRefs(secretRefs radixv1.RadixSecretRefs) RadixEnvironmentConfigBuilder { ceb.secretRefs = secretRefs return ceb } @@ -132,7 +134,7 @@ func (ceb *radixEnvironmentConfigBuilder) WithEnabled(enabled bool) RadixEnviron return ceb } -func (ceb *radixEnvironmentConfigBuilder) WithIdentity(identity *v1.Identity) RadixEnvironmentConfigBuilder { +func (ceb *radixEnvironmentConfigBuilder) WithIdentity(identity *radixv1.Identity) RadixEnvironmentConfigBuilder { ceb.identity = identity return ceb } @@ -147,13 +149,18 @@ func (ceb *radixEnvironmentConfigBuilder) WithReadOnlyFileSystem(readOnlyFileSys return ceb } -func (ceb *radixEnvironmentConfigBuilder) WithRuntime(runtime *v1.Runtime) RadixEnvironmentConfigBuilder { +func (ceb *radixEnvironmentConfigBuilder) WithRuntime(runtime *radixv1.Runtime) RadixEnvironmentConfigBuilder { ceb.runtime = runtime return ceb } -func (ceb *radixEnvironmentConfigBuilder) BuildEnvironmentConfig() v1.RadixEnvironmentConfig { - return v1.RadixEnvironmentConfig{ +func (ceb *radixEnvironmentConfigBuilder) WithNetwork(network *radixv1.Network) RadixEnvironmentConfigBuilder { + ceb.network = network + return ceb +} + +func (ceb *radixEnvironmentConfigBuilder) BuildEnvironmentConfig() radixv1.RadixEnvironmentConfig { + return radixv1.RadixEnvironmentConfig{ Environment: ceb.environment, SourceFolder: ceb.sourceFolder, DockerfileName: ceb.dockerfileName, @@ -173,6 +180,7 @@ func (ceb *radixEnvironmentConfigBuilder) BuildEnvironmentConfig() v1.RadixEnvir HorizontalScaling: ceb.horizontalScaling, ReadOnlyFileSystem: ceb.readOnlyFileSystem, Runtime: ceb.runtime, + Network: ceb.network, } } diff --git a/radix-operator/deployment/handler_test.go b/radix-operator/deployment/handler_test.go index 568a75cc4..a04cdf603 100644 --- a/radix-operator/deployment/handler_test.go +++ b/radix-operator/deployment/handler_test.go @@ -157,6 +157,7 @@ func (s *handlerSuite) Test_Sync() { ingress.NewIngressConfigurationAnnotationProvider(ingressConfig), ingress.NewClientCertificateAnnotationProvider(activeRd.Namespace), ingress.NewOAuth2AnnotationProvider(oauthConfig), + ingress.NewIngressPublicAllowListAnnotationProvider(), } expectedAuxResources := []deployment.AuxiliaryResourceManager{ deployment.NewOAuthProxyResourceManager(activeRd, rr, s.kubeUtil, oauthConfig, ingress.GetAuxOAuthProxyAnnotationProviders(), "oauth:123"), From 61e3eadc02f5babffb1100351db85c32c0a2986e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Tue, 24 Sep 2024 12:29:28 +0200 Subject: [PATCH 2/9] add validation of network in RA --- pkg/apis/radixvalidators/errors.go | 1 + .../radixvalidators/testdata/radixconfig.yaml | 12 + pkg/apis/radixvalidators/validate_ra.go | 322 +++++++++++------- pkg/apis/radixvalidators/validate_ra_test.go | 19 ++ pkg/apis/radixvalidators/validate_rr.go | 2 +- 5 files changed, 230 insertions(+), 126 deletions(-) diff --git a/pkg/apis/radixvalidators/errors.go b/pkg/apis/radixvalidators/errors.go index 1c6cddd77..43ffdedc5 100644 --- a/pkg/apis/radixvalidators/errors.go +++ b/pkg/apis/radixvalidators/errors.go @@ -107,6 +107,7 @@ var ( ErrInvalidUseOfPublicPortInWebhookUrl = errors.New("invalid use of public port in webhook") ErrMissingAzureIdentity = errors.New("missing identity") ErrInvalidRuntimeArchitecture = errors.New("invalid runtime architecture") + ErrInvalidIPv4OrCIDR = errors.New("invalid IPv4 or CIDR") ) // DuplicateAliasForDNSAliasError Error when aliases are duplicate diff --git a/pkg/apis/radixvalidators/testdata/radixconfig.yaml b/pkg/apis/radixvalidators/testdata/radixconfig.yaml index 6e9dc5568..3855a77c5 100644 --- a/pkg/apis/radixvalidators/testdata/radixconfig.yaml +++ b/pkg/apis/radixvalidators/testdata/radixconfig.yaml @@ -31,6 +31,12 @@ spec: clientId: 11111111-2222-BBBB-cccc-555555555555 runtime: architecture: arm64 + network: + ingress: + public: + allow: + - 143.10.0.0/30 + - 143.20.0.0 environmentConfig: - environment: prod replicas: 4 @@ -65,6 +71,12 @@ spec: path: /path/to/mount runtime: architecture: amd64 + network: + ingress: + public: + allow: + - 144.10.0.0/30 + - 144.20.0.0 - name: redis src: redis/ ports: diff --git a/pkg/apis/radixvalidators/validate_ra.go b/pkg/apis/radixvalidators/validate_ra.go index 7c5d517e0..8f1e49c84 100644 --- a/pkg/apis/radixvalidators/validate_ra.go +++ b/pkg/apis/radixvalidators/validate_ra.go @@ -56,6 +56,8 @@ var ( validateVolumeMountConfigForRA, ValidateNotificationsForRA, } + + ipOrCidrRegExp = regexp.MustCompile(`^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$`) ) // RadixApplicationValidator defines a validator function for a RadixApplication @@ -278,78 +280,96 @@ func validateNoDuplicateComponentAndJobNames(app *radixv1.RadixApplication) erro func validateComponents(app *radixv1.RadixApplication) error { var errs []error for _, component := range app.Spec.Components { - if component.Image != "" && - (component.SourceFolder != "" || component.DockerfileName != "") { - errs = append(errs, PublicImageComponentCannotHaveSourceOrDockerfileSetWithMessage(component.Name)) + if err := validateComponent(app, component); err != nil { + errs = append(errs, fmt.Errorf("invalid configuration for component %s: %w", component.Name, err)) } + } - err := validateComponentName(component.Name, "component") - if err != nil { - errs = append(errs, err) - } + return errors.Join(errs...) +} - errList := validatePorts(component.Name, component.Ports) - if len(errList) > 0 { - errs = append(errs, errList...) - } +func validateComponent(app *radixv1.RadixApplication, component radixv1.RadixComponent) error { + var errs []error - errList = validatePublicPort(component) - if len(errList) > 0 { - errs = append(errs, errList...) - } + if component.Image != "" && + (component.SourceFolder != "" || component.DockerfileName != "") { + errs = append(errs, PublicImageComponentCannotHaveSourceOrDockerfileSetWithMessage(component.Name)) + } - // Common resource requirements - errList = validateResourceRequirements(&component.Resources) - if len(errList) > 0 { - errs = append(errs, errList...) - } + if err := validateComponentName(component.Name, "component"); err != nil { + errs = append(errs, err) + } - err = validateMonitoring(&component) - if err != nil { - errs = append(errs, err) - } + if err := validatePorts(component.Ports); err != nil { + errs = append(errs, err) + } - errs = append(errs, validateAuthentication(&component, app.Spec.Environments)...) + if err := validatePublicPort(component); err != nil { + errs = append(errs, err) + } - err = validateIdentity(component.Identity) - if err != nil { - errs = append(errs, err) - } + // Common resource requirements + if err := validateResourceRequirements(&component.Resources); err != nil { + errs = append(errs, err) + } - if err := validateRuntime(component.Runtime); err != nil { - errs = append(errs, err) + if err := validateMonitoring(&component); err != nil { + errs = append(errs, err) + } + + errs = append(errs, validateAuthentication(&component, app.Spec.Environments)...) + + if err := validateIdentity(component.Identity); err != nil { + errs = append(errs, err) + } + + if err := validateRuntime(component.Runtime); err != nil { + errs = append(errs, err) + } + + if err := validateNetwork(component.Network); err != nil { + errs = append(errs, fmt.Errorf("invalid network configuration: %w", err)) + } + + for _, environment := range component.EnvironmentConfig { + if err := validateComponentEnvironment(app, component, environment); err != nil { + errs = append(errs, fmt.Errorf("invalid configuration for environment %s: %w", environment.Environment, err)) } + } - for _, environment := range component.EnvironmentConfig { - if !doesEnvExist(app, environment.Environment) { - err = EnvironmentReferencedByComponentDoesNotExistErrorWithMessage(environment.Environment, component.Name) - errs = append(errs, err) - } + return errors.Join(errs...) +} - err = validateReplica(environment.Replicas, "environment replicas") - if err != nil { - errs = append(errs, err) - } +func validateComponentEnvironment(app *radixv1.RadixApplication, component radixv1.RadixComponent, environment radixv1.RadixEnvironmentConfig) error { + var errs []error - errList = validateResourceRequirements(&environment.Resources) - if len(errList) > 0 { - errs = append(errs, errList...) - } + if !doesEnvExist(app, environment.Environment) { + errs = append(errs, EnvironmentReferencedByComponentDoesNotExistErrorWithMessage(environment.Environment, component.Name)) + } - if environmentHasDynamicTaggingButImageLacksTag(environment.ImageTagName, component.Image) { - errs = append(errs, - ComponentWithTagInEnvironmentConfigForEnvironmentRequiresDynamicTagWithMessage(component.Name, environment.Environment)) - } + if err := validateReplica(environment.Replicas, "environment replicas"); err != nil { + errs = append(errs, err) + } - err = validateIdentity(environment.Identity) - if err != nil { - errs = append(errs, err) - } + if err := validateResourceRequirements(&environment.Resources); err != nil { + errs = append(errs, err) + } - if err := validateRuntime(environment.Runtime); err != nil { - errs = append(errs, err) - } - } + if environmentHasDynamicTaggingButImageLacksTag(environment.ImageTagName, component.Image) { + errs = append(errs, + ComponentWithTagInEnvironmentConfigForEnvironmentRequiresDynamicTagWithMessage(component.Name, environment.Environment)) + } + + if err := validateIdentity(environment.Identity); err != nil { + errs = append(errs, err) + } + + if err := validateRuntime(environment.Runtime); err != nil { + errs = append(errs, err) + } + + if err := validateNetwork(environment.Network); err != nil { + errs = append(errs, fmt.Errorf("invalid network configuration: %w", err)) } return errors.Join(errs...) @@ -358,75 +378,87 @@ func validateComponents(app *radixv1.RadixApplication) error { func validateJobComponents(app *radixv1.RadixApplication) error { var errs []error for _, job := range app.Spec.Jobs { - if job.Image != "" && (job.SourceFolder != "" || job.DockerfileName != "") { - errs = append(errs, PublicImageComponentCannotHaveSourceOrDockerfileSetWithMessage(job.Name)) + if err := validateJobComponent(app, job); err != nil { + errs = append(errs, fmt.Errorf("invalid configuration for job %s: %w", job.Name, err)) } + } - err := validateComponentName(job.Name, "job") - if err != nil { - errs = append(errs, err) - } + return errors.Join(errs...) +} - if err = validateJobSchedulerPort(&job); err != nil { - errs = append(errs, err) - } +func validateJobComponent(app *radixv1.RadixApplication, job radixv1.RadixJobComponent) error { + var errs []error - if err = validateJobPayload(&job); err != nil { - errs = append(errs, err) - } + if job.Image != "" && (job.SourceFolder != "" || job.DockerfileName != "") { + errs = append(errs, PublicImageComponentCannotHaveSourceOrDockerfileSetWithMessage(job.Name)) + } - if len(job.Ports) > 0 { - errList := validatePorts(job.Name, job.Ports) - if len(errList) > 0 { - errs = append(errs, errList...) - } - } + if err := validateComponentName(job.Name, "job"); err != nil { + errs = append(errs, err) + } - // Common resource requirements - errList := validateResourceRequirements(&job.Resources) - if len(errList) > 0 { - errs = append(errs, errList...) - } + if err := validateJobSchedulerPort(&job); err != nil { + errs = append(errs, err) + } - err = validateMonitoring(&job) - if err != nil { - errs = append(errs, err) - } + if err := validateJobPayload(&job); err != nil { + errs = append(errs, err) + } - err = validateIdentity(job.Identity) - if err != nil { + if len(job.Ports) > 0 { + if err := validatePorts(job.Ports); err != nil { errs = append(errs, err) } + } - if err := validateRuntime(job.Runtime); err != nil { - errs = append(errs, err) + // Common resource requirements + if err := validateResourceRequirements(&job.Resources); err != nil { + errs = append(errs, err) + } + + if err := validateMonitoring(&job); err != nil { + errs = append(errs, err) + } + + if err := validateIdentity(job.Identity); err != nil { + errs = append(errs, err) + } + + if err := validateRuntime(job.Runtime); err != nil { + errs = append(errs, err) + } + + for _, environment := range job.EnvironmentConfig { + if err := validateJobComponentEnvironment(app, job, environment); err != nil { + errs = append(errs, fmt.Errorf("invalid configuration for environment %s: %w", environment.Environment, err)) } + } - for _, environment := range job.EnvironmentConfig { - if !doesEnvExist(app, environment.Environment) { - err = EnvironmentReferencedByComponentDoesNotExistErrorWithMessage(environment.Environment, job.Name) - errs = append(errs, err) - } + return errors.Join(errs...) +} - errList = validateResourceRequirements(&environment.Resources) - if len(errList) > 0 { - errs = append(errs, errList...) - } +func validateJobComponentEnvironment(app *radixv1.RadixApplication, job radixv1.RadixJobComponent, environment radixv1.RadixJobComponentEnvironmentConfig) error { + var errs []error - if environmentHasDynamicTaggingButImageLacksTag(environment.ImageTagName, job.Image) { - errs = append(errs, - ComponentWithTagInEnvironmentConfigForEnvironmentRequiresDynamicTagWithMessage(job.Name, environment.Environment)) - } + if !doesEnvExist(app, environment.Environment) { + errs = append(errs, EnvironmentReferencedByComponentDoesNotExistErrorWithMessage(environment.Environment, job.Name)) + } - err = validateIdentity(environment.Identity) - if err != nil { - errs = append(errs, err) - } + if err := validateResourceRequirements(&environment.Resources); err != nil { + errs = append(errs, err) + } - if err := validateRuntime(environment.Runtime); err != nil { - errs = append(errs, err) - } - } + if environmentHasDynamicTaggingButImageLacksTag(environment.ImageTagName, job.Image) { + errs = append(errs, + ComponentWithTagInEnvironmentConfigForEnvironmentRequiresDynamicTagWithMessage(job.Name, environment.Environment)) + } + + if err := validateIdentity(environment.Identity); err != nil { + errs = append(errs, err) + } + + if err := validateRuntime(environment.Runtime); err != nil { + errs = append(errs, err) } return errors.Join(errs...) @@ -630,33 +662,27 @@ func validateJobPayload(job *radixv1.RadixJobComponent) error { return nil } -func validatePorts(componentName string, ports []radixv1.ComponentPort) []error { +func validatePorts(ports []radixv1.ComponentPort) error { errs := []error{} for _, port := range ports { if len(port.Name) > maxPortNameLength { - err := InvalidPortNameLengthErrorWithMessage(port.Name) - if err != nil { - errs = append(errs, err) - } + errs = append(errs, InvalidPortNameLengthErrorWithMessage(port.Name)) } - err := validateRequiredResourceName("port name", port.Name, 15) - if err != nil { + if err := validateRequiredResourceName("port name", port.Name, 15); err != nil { errs = append(errs, err) } if port.Port < minimumPortNumber || port.Port > maximumPortNumber { - if err := InvalidPortNumberErrorWithMessage(port.Port); err != nil { - errs = append(errs, err) - } + errs = append(errs, InvalidPortNumberErrorWithMessage(port.Port)) } } - return errs + return errors.Join(errs...) } -func validatePublicPort(component radixv1.RadixComponent) []error { +func validatePublicPort(component radixv1.RadixComponent) error { errs := []error{} publicPortName := component.PublicPort @@ -675,7 +701,7 @@ func validatePublicPort(component radixv1.RadixComponent) []error { } } - return errs + return errors.Join(errs...) } func validateMonitoring(component radixv1.RadixCommonComponent) error { @@ -697,10 +723,10 @@ func validateMonitoring(component radixv1.RadixCommonComponent) error { return nil } -func validateResourceRequirements(resourceRequirements *radixv1.ResourceRequirements) []error { - errs := []error{} +func validateResourceRequirements(resourceRequirements *radixv1.ResourceRequirements) error { + var errs []error if resourceRequirements == nil { - return errs + return nil } limitQuantities := make(map[string]resource.Quantity) for name, value := range resourceRequirements.Limits { @@ -721,7 +747,8 @@ func validateResourceRequirements(resourceRequirements *radixv1.ResourceRequirem errs = append(errs, ResourceRequestOverLimitErrorWithMessage(name, value, limit.String())) } } - return errs + + return errors.Join(errs...) } func validateQuantity(name, value string) (resource.Quantity, error) { @@ -1616,3 +1643,48 @@ func validateComponentName(componentName, componentType string) error { } return nil } + +func validateNetwork(network *radixv1.Network) error { + if network == nil { + return nil + } + + if err := validateNetworkIngress(network.Ingress); err != nil { + return fmt.Errorf("invalid ingress configuration: %w", err) + } + + return nil +} + +func validateNetworkIngress(ingress *radixv1.Ingress) error { + if ingress == nil { + return nil + } + if err := validateNetworkIngressPublic(ingress.Public); err != nil { + return fmt.Errorf("invalid public configuration: %w", err) + } + + return nil +} + +func validateNetworkIngressPublic(public *radixv1.IngressPublic) error { + var errs []error + + if allow := public.Allow; allow != nil { + for _, ipOrCidr := range *allow { + if err := validateIPOrCIDR(ipOrCidr); err != nil { + errs = append(errs, fmt.Errorf("invalid value '%s' in allow list: %w", ipOrCidr, err)) + } + } + } + + return errors.Join(errs...) +} + +func validateIPOrCIDR(ipOrCidr radixv1.IPOrCIDR) error { + if !ipOrCidrRegExp.Match([]byte(ipOrCidr)) { + return ErrInvalidIPv4OrCIDR + } + + return nil +} diff --git a/pkg/apis/radixvalidators/validate_ra_test.go b/pkg/apis/radixvalidators/validate_ra_test.go index 00a25b50b..b96a03899 100644 --- a/pkg/apis/radixvalidators/validate_ra_test.go +++ b/pkg/apis/radixvalidators/validate_ra_test.go @@ -653,6 +653,24 @@ func Test_invalid_ra(t *testing.T) { {"invalid runtime architecture for job environment config", radixvalidators.ErrInvalidRuntimeArchitecture, func(ra *radixv1.RadixApplication) { ra.Spec.Jobs[0].EnvironmentConfig[0].Runtime.Architecture = "anyarch" }}, + {"invalid CIDR in network.ingress.public.allow for component", radixvalidators.ErrInvalidIPv4OrCIDR, func(ra *radixv1.RadixApplication) { + ra.Spec.Components[0].Network = &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("143.10.0.0/33")}}}} + }}, + {"invalid IP network.ingress.public.allow for component", radixvalidators.ErrInvalidIPv4OrCIDR, func(ra *radixv1.RadixApplication) { + ra.Spec.Components[0].Network = &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("256.0.0.0")}}}} + }}, + {"invalid value network.ingress.public.allow for component", radixvalidators.ErrInvalidIPv4OrCIDR, func(ra *radixv1.RadixApplication) { + ra.Spec.Components[0].Network = &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("any")}}}} + }}, + {"invalid CIDR in network.ingress.public.allow for component environment config", radixvalidators.ErrInvalidIPv4OrCIDR, func(ra *radixv1.RadixApplication) { + ra.Spec.Components[0].EnvironmentConfig[0].Network = &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("143.10.0.0/33")}}}} + }}, + {"invalid IP network.ingress.public.allow for component environment config", radixvalidators.ErrInvalidIPv4OrCIDR, func(ra *radixv1.RadixApplication) { + ra.Spec.Components[0].EnvironmentConfig[0].Network = &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("256.0.0.0")}}}} + }}, + {"invalid value network.ingress.public.allow for component environment config", radixvalidators.ErrInvalidIPv4OrCIDR, func(ra *radixv1.RadixApplication) { + ra.Spec.Components[0].EnvironmentConfig[0].Network = &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("any")}}}} + }}, } _, client := validRASetup() @@ -663,6 +681,7 @@ func Test_invalid_ra(t *testing.T) { err := radixvalidators.CanRadixApplicationBeInserted(context.Background(), client, validRA, getDNSAliasConfig()) if testcase.expectedError != nil { + fmt.Println(err) assert.Error(t, err) assertErrorCauseIs(t, err, testcase.expectedError, "Expected error is not contained in list of errors") } else { diff --git a/pkg/apis/radixvalidators/validate_rr.go b/pkg/apis/radixvalidators/validate_rr.go index 761bb735a..763acd7d1 100644 --- a/pkg/apis/radixvalidators/validate_rr.go +++ b/pkg/apis/radixvalidators/validate_rr.go @@ -194,7 +194,7 @@ func validateRadixRegistrationSSHKey(rr *v1.RadixRegistration) error { return validateSSHKey(rr.Spec.DeployKey) } -func validateSSHKey(deployKey string) error { +func validateSSHKey(_ string) error { // todo - how can this be validated..e.g. checked that the key isn't protected by a password return nil } From 345ddcb585a9f347fea154264be4fccf7bb6a4ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Tue, 24 Sep 2024 13:52:07 +0200 Subject: [PATCH 3/9] handle error returned by mergo.Merge --- pkg/apis/deployment/radixcomponent.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/apis/deployment/radixcomponent.go b/pkg/apis/deployment/radixcomponent.go index a4467bb64..4328c99f3 100644 --- a/pkg/apis/deployment/radixcomponent.go +++ b/pkg/apis/deployment/radixcomponent.go @@ -81,17 +81,19 @@ func GetRadixComponentsForEnv(ctx context.Context, radixApplication *radixv1.Rad deployComponent.Monitoring = getRadixCommonComponentMonitoring(&radixComponent, environmentSpecificConfig) deployComponent.HorizontalScaling = getRadixCommonComponentHorizontalScaling(&radixComponent, environmentSpecificConfig) deployComponent.Runtime = componentImage.Runtime - deployComponent.Network = getRadixComponentNetwork(&radixComponent, environmentSpecificConfig) if deployComponent.VolumeMounts, err = getRadixCommonComponentVolumeMounts(&radixComponent, environmentSpecificConfig); err != nil { return nil, err } + if deployComponent.Network, err = getRadixComponentNetwork(&radixComponent, environmentSpecificConfig); err != nil { + return nil, err + } deployComponents = append(deployComponents, deployComponent) } return deployComponents, nil } -func getRadixComponentNetwork(component *radixv1.RadixComponent, environmentConfig *radixv1.RadixEnvironmentConfig) *radixv1.Network { +func getRadixComponentNetwork(component *radixv1.RadixComponent, environmentConfig *radixv1.RadixEnvironmentConfig) (*radixv1.Network, error) { var dst *radixv1.Network if component.Network != nil { dst = component.Network.DeepCopy() @@ -101,10 +103,12 @@ func getRadixComponentNetwork(component *radixv1.RadixComponent, environmentConf if dst == nil { dst = &radixv1.Network{} } - mergo.Merge(dst, environmentConfig.Network, mergo.WithOverride, mergo.WithOverrideEmptySlice) + if err := mergo.Merge(dst, environmentConfig.Network, mergo.WithOverride, mergo.WithOverrideEmptySlice); err != nil { + return nil, err + } } - return dst + return dst, nil } func getRadixCommonComponentReadOnlyFileSystem(radixComponent radixv1.RadixCommonComponent, environmentSpecificConfig radixv1.RadixCommonEnvironmentConfig) *bool { From 6e940af1147abbfc924dd95cc9a68818257690ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Tue, 24 Sep 2024 15:22:34 +0200 Subject: [PATCH 4/9] update chart version --- charts/radix-operator/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/radix-operator/Chart.yaml b/charts/radix-operator/Chart.yaml index 26848bb7d..cb05e0a44 100644 --- a/charts/radix-operator/Chart.yaml +++ b/charts/radix-operator/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: radix-operator -version: 1.40.0 -appVersion: 1.60.0 +version: 1.41.0 +appVersion: 1.61.0 kubeVersion: ">=1.24.0" description: Radix Operator keywords: From e8a312f5a2e892a289af267fbedd9cbc7d35e1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= <65334626+nilsgstrabo@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:26:57 +0200 Subject: [PATCH 5/9] Update pkg/apis/ingress/ingressannotationprovider.go Co-authored-by: Sergey Smolnikov --- pkg/apis/ingress/ingressannotationprovider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/apis/ingress/ingressannotationprovider.go b/pkg/apis/ingress/ingressannotationprovider.go index dadeb2f77..c66799c8a 100644 --- a/pkg/apis/ingress/ingressannotationprovider.go +++ b/pkg/apis/ingress/ingressannotationprovider.go @@ -130,7 +130,7 @@ func (provider *oauth2AnnotationProvider) GetAnnotations(component radixv1.Radix } // NewIngressPublicAllowListAnnotationProvider provides Ingress annotations for allowing -// only public traffic from IP adresses defined in Network.Ingress.Public.Allow field +// only public traffic from IP addresses defined in Network.Ingress.Public.Allow field func NewIngressPublicAllowListAnnotationProvider() AnnotationProvider { return &ingressPublicAllowListAnnotationProvider{} } From 0b83ae8193c543a41e1228d54f0b1df56fff01a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Tue, 24 Sep 2024 15:27:35 +0200 Subject: [PATCH 6/9] fix spelling --- charts/radix-operator/templates/radixapplication.yaml | 8 ++++---- json-schema/radixapplication.json | 4 ++-- pkg/apis/radix/v1/radixapptypes.go | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/charts/radix-operator/templates/radixapplication.yaml b/charts/radix-operator/templates/radixapplication.yaml index 2d2f57b25..d3d409cbd 100644 --- a/charts/radix-operator/templates/radixapplication.yaml +++ b/charts/radix-operator/templates/radixapplication.yaml @@ -700,8 +700,8 @@ spec: properties: allow: description: |- - Allow defines a list of public IP adresses or CIDRs which are allowed to access the component. - All IP adresses are allowed if this field is empty or not set. + Allow defines a list of public IP addresses or CIDRs which are allowed to access the component. + All IP addresses are allowed if this field is empty or not set. items: description: IP address or CIDR. pattern: ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$ @@ -1440,8 +1440,8 @@ spec: properties: allow: description: |- - Allow defines a list of public IP adresses or CIDRs which are allowed to access the component. - All IP adresses are allowed if this field is empty or not set. + Allow defines a list of public IP addresses or CIDRs which are allowed to access the component. + All IP addresses are allowed if this field is empty or not set. items: description: IP address or CIDR. pattern: ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$ diff --git a/json-schema/radixapplication.json b/json-schema/radixapplication.json index 22e9c711f..04bb51881 100644 --- a/json-schema/radixapplication.json +++ b/json-schema/radixapplication.json @@ -675,7 +675,7 @@ "description": "Public defines settings for public traffic.", "properties": { "allow": { - "description": "Allow defines a list of public IP adresses or CIDRs which are allowed to access the component.\nAll IP adresses are allowed if this field is empty or not set.", + "description": "Allow defines a list of public IP addresses or CIDRs which are allowed to access the component.\nAll IP addresses are allowed if this field is empty or not set.", "items": { "description": "IP address or CIDR.", "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\/([0-9]|[1-2][0-9]|3[0-2]))?$", @@ -1431,7 +1431,7 @@ "description": "Public defines settings for public traffic.", "properties": { "allow": { - "description": "Allow defines a list of public IP adresses or CIDRs which are allowed to access the component.\nAll IP adresses are allowed if this field is empty or not set.", + "description": "Allow defines a list of public IP addresses or CIDRs which are allowed to access the component.\nAll IP addresses are allowed if this field is empty or not set.", "items": { "description": "IP address or CIDR.", "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\/([0-9]|[1-2][0-9]|3[0-2]))?$", diff --git a/pkg/apis/radix/v1/radixapptypes.go b/pkg/apis/radix/v1/radixapptypes.go index 2e26a11c6..7bcb9ceeb 100644 --- a/pkg/apis/radix/v1/radixapptypes.go +++ b/pkg/apis/radix/v1/radixapptypes.go @@ -1475,8 +1475,8 @@ type IPOrCIDR string // Ingress defines settings for ingress traffic. type IngressPublic struct { - // Allow defines a list of public IP adresses or CIDRs which are allowed to access the component. - // All IP adresses are allowed if this field is empty or not set. + // Allow defines a list of public IP addresses or CIDRs which are allowed to access the component. + // All IP addresses are allowed if this field is empty or not set. // +optional Allow *[]IPOrCIDR `json:"allow,omitempty"` From 2dfd3c50215d2485ec2f3cbced399701e1d4c70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Tue, 24 Sep 2024 15:32:20 +0200 Subject: [PATCH 7/9] add docs comment to new annotation provider --- pkg/apis/ingress/ingressannotationprovider.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/apis/ingress/ingressannotationprovider.go b/pkg/apis/ingress/ingressannotationprovider.go index c66799c8a..e031bde07 100644 --- a/pkg/apis/ingress/ingressannotationprovider.go +++ b/pkg/apis/ingress/ingressannotationprovider.go @@ -11,6 +11,7 @@ import ( ) type AnnotationProvider interface { + // GetAnnotations returns annotations for use on Ingress resources GetAnnotations(component radixv1.RadixCommonDeployComponent, namespace string) (map[string]string, error) } @@ -137,6 +138,8 @@ func NewIngressPublicAllowListAnnotationProvider() AnnotationProvider { type ingressPublicAllowListAnnotationProvider struct{} +// GetAnnotations returns annotations for only allowing public ingress traffic +// for IPs or CIDRs defined in Network.Ingress.Public.Allow for a component func (*ingressPublicAllowListAnnotationProvider) GetAnnotations(component radixv1.RadixCommonDeployComponent, _ string) (map[string]string, error) { if network := component.GetNetwork(); network == nil || network.Ingress == nil || network.Ingress.Public == nil || network.Ingress.Public.Allow == nil || len(*network.Ingress.Public.Allow) == 0 { return nil, nil From 594d99df6b6131d552c5a896efae119d6276396c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Tue, 24 Sep 2024 15:42:15 +0200 Subject: [PATCH 8/9] simplify function --- pkg/apis/ingress/ingressannotationprovider.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/pkg/apis/ingress/ingressannotationprovider.go b/pkg/apis/ingress/ingressannotationprovider.go index e031bde07..abf3abdc7 100644 --- a/pkg/apis/ingress/ingressannotationprovider.go +++ b/pkg/apis/ingress/ingressannotationprovider.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/equinor/radix-common/utils/slice" "github.com/equinor/radix-operator/pkg/apis/defaults" radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1" "github.com/equinor/radix-operator/pkg/apis/utils" @@ -145,16 +146,9 @@ func (*ingressPublicAllowListAnnotationProvider) GetAnnotations(component radixv return nil, nil } - annotations := make(map[string]string, 1) - var sb strings.Builder - - for i, addr := range *component.GetNetwork().Ingress.Public.Allow { - if i > 0 { - sb.WriteString(",") - } - sb.WriteString(string(addr)) + addressList := slice.Map(*component.GetNetwork().Ingress.Public.Allow, func(v radixv1.IPOrCIDR) string { return string(v) }) + annotations := map[string]string{ + "nginx.ingress.kubernetes.io/whitelist-source-range": strings.Join(addressList, ","), } - annotations["nginx.ingress.kubernetes.io/whitelist-source-range"] = sb.String() - return annotations, nil } From 82bab8d3d61756266acd317d99c3dd383ea9be85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Tue, 24 Sep 2024 15:43:33 +0200 Subject: [PATCH 9/9] simplyfy annotation function --- pkg/apis/ingress/ingressannotationprovider.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/apis/ingress/ingressannotationprovider.go b/pkg/apis/ingress/ingressannotationprovider.go index abf3abdc7..78b06a7b7 100644 --- a/pkg/apis/ingress/ingressannotationprovider.go +++ b/pkg/apis/ingress/ingressannotationprovider.go @@ -147,8 +147,5 @@ func (*ingressPublicAllowListAnnotationProvider) GetAnnotations(component radixv } addressList := slice.Map(*component.GetNetwork().Ingress.Public.Allow, func(v radixv1.IPOrCIDR) string { return string(v) }) - annotations := map[string]string{ - "nginx.ingress.kubernetes.io/whitelist-source-range": strings.Join(addressList, ","), - } - return annotations, nil + return map[string]string{"nginx.ingress.kubernetes.io/whitelist-source-range": strings.Join(addressList, ",")}, nil }