From ff8c8d2ed10b504baf2e6c61f8d2d5eaa3963147 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: Mon, 7 Oct 2024 13:06:53 +0200 Subject: [PATCH] add configuration for ingress proxy body size and read and send timeouts (#1204) --- charts/radix-operator/Chart.yaml | 4 +- .../templates/radixapplication.yaml | 44 ++++++ json-schema/radixapplication.json | 30 ++++ pkg/apis/deployment/radixcomponent.go | 2 +- pkg/apis/deployment/radixcomponent_test.go | 146 +++++++++++------- pkg/apis/ingress/ingress.go | 1 + pkg/apis/ingress/ingressannotationprovider.go | 34 ++++ .../ingress/ingressannotationprovider_test.go | 60 +++++++ pkg/apis/radix/v1/radixapptypes.go | 29 +++- pkg/apis/radix/v1/zz_generated.deepcopy.go | 15 ++ radix-operator/deployment/handler_test.go | 1 + 11 files changed, 305 insertions(+), 61 deletions(-) diff --git a/charts/radix-operator/Chart.yaml b/charts/radix-operator/Chart.yaml index cb05e0a44..662c071c8 100644 --- a/charts/radix-operator/Chart.yaml +++ b/charts/radix-operator/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: radix-operator -version: 1.41.0 -appVersion: 1.61.0 +version: 1.42.0 +appVersion: 1.62.0 kubeVersion: ">=1.24.0" description: Radix Operator keywords: diff --git a/charts/radix-operator/templates/radixapplication.yaml b/charts/radix-operator/templates/radixapplication.yaml index d3d409cbd..20133fde9 100644 --- a/charts/radix-operator/templates/radixapplication.yaml +++ b/charts/radix-operator/templates/radixapplication.yaml @@ -707,6 +707,28 @@ spec: 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 + proxyBodySize: + description: |- + Sets the maximum allowed size of the client request body. + Sizes can be specified in bytes, kilobytes (suffixes k and K), megabytes (suffixes m and M), or gigabytes (suffixes g and G) for example, "1024", "64k", "32m", "2g" + If the size in a request exceeds the configured value, the 413 (Request Entity Too Large) error is returned to the client. + Setting size to 0 disables checking of client request body size. + pattern: ^(?:0|[1-9][0-9]*[kKmMgG]?)$ + type: string + proxyReadTimeout: + description: |- + Defines a timeout, in seconds, for reading a response from the proxied server. + The timeout is set only between two successive read operations, not for the transmission of the whole response. + If the proxied server does not transmit anything within this time, the connection is closed. + minimum: 0 + type: integer + proxySendTimeout: + description: |- + Defines a timeout, in seconds, for transmitting a request to the proxied server. + The timeout is set only between two successive write operations, not for the transmission of the whole request. + If the proxied server does not receive anything within this time, the connection is closed. + minimum: 0 + type: integer type: object type: object type: object @@ -1447,6 +1469,28 @@ spec: 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 + proxyBodySize: + description: |- + Sets the maximum allowed size of the client request body. + Sizes can be specified in bytes, kilobytes (suffixes k and K), megabytes (suffixes m and M), or gigabytes (suffixes g and G) for example, "1024", "64k", "32m", "2g" + If the size in a request exceeds the configured value, the 413 (Request Entity Too Large) error is returned to the client. + Setting size to 0 disables checking of client request body size. + pattern: ^(?:0|[1-9][0-9]*[kKmMgG]?)$ + type: string + proxyReadTimeout: + description: |- + Defines a timeout, in seconds, for reading a response from the proxied server. + The timeout is set only between two successive read operations, not for the transmission of the whole response. + If the proxied server does not transmit anything within this time, the connection is closed. + minimum: 0 + type: integer + proxySendTimeout: + description: |- + Defines a timeout, in seconds, for transmitting a request to the proxied server. + The timeout is set only between two successive write operations, not for the transmission of the whole request. + If the proxied server does not receive anything within this time, the connection is closed. + minimum: 0 + type: integer type: object type: object type: object diff --git a/json-schema/radixapplication.json b/json-schema/radixapplication.json index 04bb51881..c4c1a3ff3 100644 --- a/json-schema/radixapplication.json +++ b/json-schema/radixapplication.json @@ -682,6 +682,21 @@ "type": "string" }, "type": "array" + }, + "proxyBodySize": { + "description": "Sets the maximum allowed size of the client request body.\nSizes can be specified in bytes, kilobytes (suffixes k and K), megabytes (suffixes m and M), or gigabytes (suffixes g and G) for example, \"1024\", \"64k\", \"32m\", \"2g\"\nIf the size in a request exceeds the configured value, the 413 (Request Entity Too Large) error is returned to the client.\nSetting size to 0 disables checking of client request body size.", + "pattern": "^(?:0|[1-9][0-9]*[kKmMgG]?)$", + "type": "string" + }, + "proxyReadTimeout": { + "description": "Defines a timeout, in seconds, for reading a response from the proxied server.\nThe timeout is set only between two successive read operations, not for the transmission of the whole response.\nIf the proxied server does not transmit anything within this time, the connection is closed.", + "minimum": 0, + "type": "integer" + }, + "proxySendTimeout": { + "description": "Defines a timeout, in seconds, for transmitting a request to the proxied server.\nThe timeout is set only between two successive write operations, not for the transmission of the whole request.\nIf the proxied server does not receive anything within this time, the connection is closed.", + "minimum": 0, + "type": "integer" } }, "type": "object" @@ -1438,6 +1453,21 @@ "type": "string" }, "type": "array" + }, + "proxyBodySize": { + "description": "Sets the maximum allowed size of the client request body.\nSizes can be specified in bytes, kilobytes (suffixes k and K), megabytes (suffixes m and M), or gigabytes (suffixes g and G) for example, \"1024\", \"64k\", \"32m\", \"2g\"\nIf the size in a request exceeds the configured value, the 413 (Request Entity Too Large) error is returned to the client.\nSetting size to 0 disables checking of client request body size.", + "pattern": "^(?:0|[1-9][0-9]*[kKmMgG]?)$", + "type": "string" + }, + "proxyReadTimeout": { + "description": "Defines a timeout, in seconds, for reading a response from the proxied server.\nThe timeout is set only between two successive read operations, not for the transmission of the whole response.\nIf the proxied server does not transmit anything within this time, the connection is closed.", + "minimum": 0, + "type": "integer" + }, + "proxySendTimeout": { + "description": "Defines a timeout, in seconds, for transmitting a request to the proxied server.\nThe timeout is set only between two successive write operations, not for the transmission of the whole request.\nIf the proxied server does not receive anything within this time, the connection is closed.", + "minimum": 0, + "type": "integer" } }, "type": "object" diff --git a/pkg/apis/deployment/radixcomponent.go b/pkg/apis/deployment/radixcomponent.go index 4328c99f3..a9e5e9213 100644 --- a/pkg/apis/deployment/radixcomponent.go +++ b/pkg/apis/deployment/radixcomponent.go @@ -103,7 +103,7 @@ func getRadixComponentNetwork(component *radixv1.RadixComponent, environmentConf if dst == nil { dst = &radixv1.Network{} } - if err := mergo.Merge(dst, environmentConfig.Network, mergo.WithOverride, mergo.WithOverrideEmptySlice); err != nil { + if err := mergo.Merge(dst, environmentConfig.Network, mergo.WithOverride, mergo.WithOverrideEmptySlice, mergo.WithTransformers(booleanPointerTransformer)); err != nil { return nil, err } } diff --git a/pkg/apis/deployment/radixcomponent_test.go b/pkg/apis/deployment/radixcomponent_test.go index 512882474..dc5157b41 100644 --- a/pkg/apis/deployment/radixcomponent_test.go +++ b/pkg/apis/deployment/radixcomponent_test.go @@ -3,6 +3,7 @@ package deployment import ( "context" "fmt" + "math" "strings" "testing" @@ -1547,73 +1548,104 @@ 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, +func Test_Test_GetRadixComponentsForEnv_NetworkIngressPublicConfig(t *testing.T) { + exp2 := func(n int) int { + return int(math.Exp2(float64(n))) + } + + // Defines a list of functions that will set a single field in IngressPublic on component level (setCommonCfg) + // and in environmentConfig (setEnvCfg). Each field in IngressPublic should be represented in both listes. + // The corresponding field setter function in each list must set different values, or the tests won't be trustworthy. + // bool fields should have two functions, one for true and one for false value. + // All fields in IngressPublic should be represented in a function. + type setIngressFuncs []func(*radixv1.IngressPublic) + setCommonCfg := setIngressFuncs{ + func(cfg *radixv1.IngressPublic) { + cfg.Allow = &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("10.10.10.10"), radixv1.IPOrCIDR("20.20.20.20")} }, - "empty in common, env not set": { - commonConfig: &radixv1.Network{}, - setEnv: false, - expected: &radixv1.Network{}, + func(cfg *radixv1.IngressPublic) { + cfg.ProxyBodySize = pointers.Ptr(radixv1.NginxSizeFormat("20m")) }, - "empty in common and nil in env": { - commonConfig: &radixv1.Network{}, - setEnv: true, - expected: &radixv1.Network{}, + func(cfg *radixv1.IngressPublic) { + cfg.ProxyReadTimeout = pointers.Ptr[uint](100) }, - "empty in common and env": { - commonConfig: &radixv1.Network{}, - setEnv: true, - envConfig: &radixv1.Network{}, - expected: &radixv1.Network{}, + func(cfg *radixv1.IngressPublic) { + cfg.ProxySendTimeout = pointers.Ptr[uint](150) }, - "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")}}}}, + } + setEnvCfg := setIngressFuncs{ + func(cfg *radixv1.IngressPublic) { + cfg.Allow = &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("1.1.1.1"), radixv1.IPOrCIDR("2.2.2.2")} }, - "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{}}}}, + func(cfg *radixv1.IngressPublic) { + cfg.ProxyBodySize = pointers.Ptr(radixv1.NginxSizeFormat("10m")) }, - "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")}}}}, + func(cfg *radixv1.IngressPublic) { + cfg.ProxyReadTimeout = pointers.Ptr[uint](10) }, - "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")}}}}, + func(cfg *radixv1.IngressPublic) { + cfg.ProxySendTimeout = pointers.Ptr[uint](15) }, } - 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) - }) + /* + The tests will check every possible combination of component and environment specific configuration of the IngressPublic spec. + exp2 is used in the two for-loops to create a bitmap representation of each function in setCommonCfg and setEnvCfg. + The function is called with the corresponding config (common or env) and expectedCfg if the function's bit is set. + + How it works: + We have 4 functions in each slice. To iterate over every possible combination of function call (call none, some or all), + we calculate 2 pow 4 = 16, and iterate from 0 to 15. This binary representation for each value will then be: + 0: 0000 (no functions will be called) + 1: 0001 (function with index 0 will be called) + 2: 0010 (function with index 1 will be called) + 3: 0011 (functions with indexes 0 and 1 will be called) + 4: 0100 (function with index 2 will be called) + ... + 15: 1111 (all functions will be called) + + It is imortant that the setCommonCfg functions are applied to expectedCfg first and setEnvCfg last, + since we excpect environment config to take precedence over common config if the field is non-nil. + */ + for c := range exp2(len(setCommonCfg)) { + for e := range exp2(len(setEnvCfg)) { + // Include bitmap representation of which functions in common and env config that must be called + // This makes it a bit easier to identity what fields are set in common and env config in case a test fails + testName := fmt.Sprintf("common bitmap: %.4b - env bitmap: %.4b", c, e) + t.Run(testName, func(t *testing.T) { + commonCfg := &radixv1.IngressPublic{} + envCfg := &radixv1.IngressPublic{} + expectedCfg := &radixv1.IngressPublic{} + for i := range len(setCommonCfg) { + if c&exp2(i) > 0 { + setCommonCfg[i](commonCfg) + setCommonCfg[i](expectedCfg) + } + } + for i := range len(setEnvCfg) { + if e&exp2(i) > 0 { + setEnvCfg[i](envCfg) + setEnvCfg[i](expectedCfg) + } + } + + const envName = "anyenv" + ra := utils.ARadixApplication(). + WithComponents( + utils.AnApplicationComponent(). + WithName("anycomponent"). + WithNetwork(&radixv1.Network{Ingress: &radixv1.Ingress{Public: commonCfg}}). + WithEnvironmentConfigs( + utils.AnEnvironmentConfig(). + WithEnvironment(envName). + WithNetwork(&radixv1.Network{Ingress: &radixv1.Ingress{Public: envCfg}}), + ), + ).BuildRA() + components, err := GetRadixComponentsForEnv(context.Background(), ra, nil, envName, make(pipeline.DeployComponentImages), make(radixv1.EnvVarsMap), nil) + require.NoError(t, err) + assert.Equal(t, expectedCfg, components[0].Network.Ingress.Public) + }) + } } } diff --git a/pkg/apis/ingress/ingress.go b/pkg/apis/ingress/ingress.go index 9579cc215..bbbca0305 100644 --- a/pkg/apis/ingress/ingress.go +++ b/pkg/apis/ingress/ingress.go @@ -116,5 +116,6 @@ func GetAnnotationProvider(ingressConfiguration IngressConfiguration, certificat NewClientCertificateAnnotationProvider(certificateNamespace), NewOAuth2AnnotationProvider(oauth2DefaultConfig), NewIngressPublicAllowListAnnotationProvider(), + NewIngressPublicConfigAnnotationProvider(), } } diff --git a/pkg/apis/ingress/ingressannotationprovider.go b/pkg/apis/ingress/ingressannotationprovider.go index 78b06a7b7..54633403c 100644 --- a/pkg/apis/ingress/ingressannotationprovider.go +++ b/pkg/apis/ingress/ingressannotationprovider.go @@ -2,6 +2,7 @@ package ingress import ( "fmt" + "strconv" "strings" "github.com/equinor/radix-common/utils/slice" @@ -149,3 +150,36 @@ func (*ingressPublicAllowListAnnotationProvider) GetAnnotations(component radixv addressList := slice.Map(*component.GetNetwork().Ingress.Public.Allow, func(v radixv1.IPOrCIDR) string { return string(v) }) return map[string]string{"nginx.ingress.kubernetes.io/whitelist-source-range": strings.Join(addressList, ",")}, nil } + +// NewIngressPublicConfigAnnotationProvider provides Ingress annotations +// for fields in `Network.Ingress.Public`, except for `allow` +func NewIngressPublicConfigAnnotationProvider() AnnotationProvider { + return &ingressPublicConfigAnnotationProvider{} +} + +type ingressPublicConfigAnnotationProvider struct{} + +// GetAnnotations returns annotations for only allowing public ingress traffic +// for IPs or CIDRs defined in Network.Ingress.Public.Allow for a component +func (*ingressPublicConfigAnnotationProvider) GetAnnotations(component radixv1.RadixCommonDeployComponent, _ string) (map[string]string, error) { + if network := component.GetNetwork(); network == nil || network.Ingress == nil || network.Ingress.Public == nil { + return nil, nil + } + + annotations := map[string]string{} + cfg := component.GetNetwork().Ingress.Public + + if v := cfg.ProxyBodySize; v != nil { + annotations["nginx.ingress.kubernetes.io/proxy-body-size"] = string(*v) + } + + if v := cfg.ProxyReadTimeout; v != nil { + annotations["nginx.ingress.kubernetes.io/proxy-read-timeout"] = strconv.FormatUint(uint64(*v), 10) + } + + if v := cfg.ProxySendTimeout; v != nil { + annotations["nginx.ingress.kubernetes.io/proxy-send-timeout"] = strconv.FormatUint(uint64(*v), 10) + } + + return annotations, nil +} diff --git a/pkg/apis/ingress/ingressannotationprovider_test.go b/pkg/apis/ingress/ingressannotationprovider_test.go index ef3ce1f7d..3ab46ffcc 100644 --- a/pkg/apis/ingress/ingressannotationprovider_test.go +++ b/pkg/apis/ingress/ingressannotationprovider_test.go @@ -176,7 +176,67 @@ func Test_PublicIngressAllowAnnotationProvider(t *testing.T) { } }) } +} +func Test_PublicIngressConfigAnnotationProvider(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), + }, + "allow is ignored": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{Allow: &[]radixv1.IPOrCIDR{radixv1.IPOrCIDR("10.0.0.1")}}}}}, + expectedAnnotation: make(map[string]string), + }, + "proxyReadTimeout annotation set": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{ProxyReadTimeout: pointers.Ptr[uint](123)}}}}, + expectedAnnotation: map[string]string{"nginx.ingress.kubernetes.io/proxy-read-timeout": "123"}, + }, + "proxySendTimeout annotation set": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{ProxySendTimeout: pointers.Ptr[uint](456)}}}}, + expectedAnnotation: map[string]string{"nginx.ingress.kubernetes.io/proxy-send-timeout": "456"}, + }, + "proxyBodySize annotation set": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{ProxyBodySize: pointers.Ptr(radixv1.NginxSizeFormat("789k"))}}}}, + expectedAnnotation: map[string]string{"nginx.ingress.kubernetes.io/proxy-body-size": "789k"}, + }, + "all fields set": { + component: &radixv1.RadixDeployComponent{Network: &radixv1.Network{Ingress: &radixv1.Ingress{Public: &radixv1.IngressPublic{ + ProxyReadTimeout: pointers.Ptr[uint](123), + ProxySendTimeout: pointers.Ptr[uint](456), + ProxyBodySize: pointers.Ptr(radixv1.NginxSizeFormat("789k")), + }}}}, + expectedAnnotation: map[string]string{ + "nginx.ingress.kubernetes.io/proxy-read-timeout": "123", + "nginx.ingress.kubernetes.io/proxy-send-timeout": "456", + "nginx.ingress.kubernetes.io/proxy-body-size": "789k", + }, + }, + } + + sut := &ingressPublicConfigAnnotationProvider{} + 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 { diff --git a/pkg/apis/radix/v1/radixapptypes.go b/pkg/apis/radix/v1/radixapptypes.go index 7bcb9ceeb..9dc5f4604 100644 --- a/pkg/apis/radix/v1/radixapptypes.go +++ b/pkg/apis/radix/v1/radixapptypes.go @@ -1473,6 +1473,11 @@ type Ingress struct { // +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 +// NGINX size format. +// +// +kubebuilder:validation:Pattern=`^(?:0|[1-9][0-9]*[kKmMgG]?)$` +type NginxSizeFormat string + // Ingress defines settings for ingress traffic. type IngressPublic struct { // Allow defines a list of public IP addresses or CIDRs which are allowed to access the component. @@ -1480,7 +1485,29 @@ type IngressPublic struct { // +optional Allow *[]IPOrCIDR `json:"allow,omitempty"` - // This is where we will add additional fields used for configuring the Ingress Nginx controller. + // Defines a timeout, in seconds, for reading a response from the proxied server. + // The timeout is set only between two successive read operations, not for the transmission of the whole response. + // If the proxied server does not transmit anything within this time, the connection is closed. + // + // +kubebuilder:validation:Minimum=0 + // +optional + ProxyReadTimeout *uint `json:"proxyReadTimeout,omitempty"` + + // Defines a timeout, in seconds, for transmitting a request to the proxied server. + // The timeout is set only between two successive write operations, not for the transmission of the whole request. + // If the proxied server does not receive anything within this time, the connection is closed. + // + // +kubebuilder:validation:Minimum=0 + // +optional + ProxySendTimeout *uint `json:"proxySendTimeout,omitempty"` + + // Sets the maximum allowed size of the client request body. + // Sizes can be specified in bytes, kilobytes (suffixes k and K), megabytes (suffixes m and M), or gigabytes (suffixes g and G) for example, "1024", "64k", "32m", "2g" + // If the size in a request exceeds the configured value, the 413 (Request Entity Too Large) error is returned to the client. + // Setting size to 0 disables checking of client request body size. + // + // +optional + ProxyBodySize *NginxSizeFormat `json:"proxyBodySize,omitempty"` } // RadixCommonComponent defines a common component interface for Radix components diff --git a/pkg/apis/radix/v1/zz_generated.deepcopy.go b/pkg/apis/radix/v1/zz_generated.deepcopy.go index 12ceb0ecc..93a4979d7 100644 --- a/pkg/apis/radix/v1/zz_generated.deepcopy.go +++ b/pkg/apis/radix/v1/zz_generated.deepcopy.go @@ -453,6 +453,21 @@ func (in *IngressPublic) DeepCopyInto(out *IngressPublic) { copy(*out, *in) } } + if in.ProxyReadTimeout != nil { + in, out := &in.ProxyReadTimeout, &out.ProxyReadTimeout + *out = new(uint) + **out = **in + } + if in.ProxySendTimeout != nil { + in, out := &in.ProxySendTimeout, &out.ProxySendTimeout + *out = new(uint) + **out = **in + } + if in.ProxyBodySize != nil { + in, out := &in.ProxyBodySize, &out.ProxyBodySize + *out = new(NginxSizeFormat) + **out = **in + } return } diff --git a/radix-operator/deployment/handler_test.go b/radix-operator/deployment/handler_test.go index a04cdf603..eb9d6d63e 100644 --- a/radix-operator/deployment/handler_test.go +++ b/radix-operator/deployment/handler_test.go @@ -158,6 +158,7 @@ func (s *handlerSuite) Test_Sync() { ingress.NewClientCertificateAnnotationProvider(activeRd.Namespace), ingress.NewOAuth2AnnotationProvider(oauthConfig), ingress.NewIngressPublicAllowListAnnotationProvider(), + ingress.NewIngressPublicConfigAnnotationProvider(), } expectedAuxResources := []deployment.AuxiliaryResourceManager{ deployment.NewOAuthProxyResourceManager(activeRd, rr, s.kubeUtil, oauthConfig, ingress.GetAuxOAuthProxyAnnotationProviders(), "oauth:123"),