diff --git a/internal/configs/annotations.go b/internal/configs/annotations.go index 8e1dc373ce..cf1a37e5b3 100644 --- a/internal/configs/annotations.go +++ b/internal/configs/annotations.go @@ -247,6 +247,14 @@ func parseAnnotations(ingEx *IngressEx, baseCfgParams *ConfigParams, isPlus bool cfgParams.ClientMaxBodySize = clientMaxBodySize } + if clientBodyBufferSize, exists := ingEx.Ingress.Annotations["nginx.org/client-body-buffer-size"]; exists { + size, err := ParseSize(clientBodyBufferSize) + if err != nil { + nl.Errorf(l, "Ingress %s/%s: Invalid value nginx.org/client-body-buffer-size: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), clientBodyBufferSize, err) + } + cfgParams.ClientBodyBufferSize = size + } + if redirectToHTTPS, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/redirect-to-https", ingEx.Ingress); exists { if err != nil { nl.Error(l, err) diff --git a/internal/configs/annotations_test.go b/internal/configs/annotations_test.go index de1d511402..c8f41a0e5d 100644 --- a/internal/configs/annotations_test.go +++ b/internal/configs/annotations_test.go @@ -755,3 +755,175 @@ func TestGetRewriteTargetWithComplexValues(t *testing.T) { }) } } + +func TestClientBodyBufferSizeAnnotationValid(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + clientBodyBufferSize string + expected string + }{ + { + name: "valid size with k", + clientBodyBufferSize: "16k", + expected: "16k", + }, + { + name: "valid size with M", + clientBodyBufferSize: "4M", + expected: "4M", + }, + { + name: "valid size without suffix", + clientBodyBufferSize: "8192", + expected: "8192", + }, + { + name: "single digit", + clientBodyBufferSize: "1k", + expected: "1k", + }, + { + name: "zero value", + clientBodyBufferSize: "0", + expected: "0", + }, + { + name: "large buffer size", + clientBodyBufferSize: "512K", + expected: "512K", + }, + { + name: "megabyte buffer size", + clientBodyBufferSize: "64M", + expected: "64M", + }, + { + name: "empty annotation", + clientBodyBufferSize: "", + expected: "", + }, + { + name: "very large number", + clientBodyBufferSize: "999999999", + expected: "999999999", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + ingress := &networking.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + Namespace: "default", + Annotations: map[string]string{ + "nginx.org/client-body-buffer-size": tt.clientBodyBufferSize, + }, + }, + } + + ingEx := &IngressEx{ + Ingress: ingress, + } + + baseCfgParams := NewDefaultConfigParams(context.Background(), false) + result := parseAnnotations(ingEx, baseCfgParams, false, false, false, false, false) + + if result.ClientBodyBufferSize != tt.expected { + t.Errorf("Test %q: expected ClientBodyBufferSize %q, got %q", tt.name, tt.expected, result.ClientBodyBufferSize) + } + + result2 := parseAnnotations(ingEx, baseCfgParams, false, false, false, false, false) + if result2.ClientBodyBufferSize != tt.expected { + t.Errorf("Test %q with other annotations: expected ClientBodyBufferSize %q, got %q", tt.name, tt.expected, result2.ClientBodyBufferSize) + } + }) + } +} + +func TestClientBodyBufferSizeAnnotationInvalid(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + annotations map[string]string + }{ + { + name: "annotation name case sensitivity", + annotations: map[string]string{ + "nginx.org/Client-Body-Buffer-Size": "16k", // capital letters + }, + }, + { + name: "invalid time unit h", + annotations: map[string]string{ + "nginx.org/client-body-buffer-size": "4h", + }, + }, + { + name: "invalid random string", + annotations: map[string]string{ + "nginx.org/client-body-buffer-size": "3cd2", // wrong format, too many chars + }, + }, + { + name: "invalid with g suffix", + annotations: map[string]string{ + "nginx.org/client-body-buffer-size": "2g", + }, + }, + + { + name: "invalid negative value", + annotations: map[string]string{ + "nginx.org/client-body-buffer-size": "-16k", + }, + }, + { + name: "invalid with text prefix", + annotations: map[string]string{ + "nginx.org/client-body-buffer-size": "abc16k", + }, + }, + { + name: "invalid with special characters", + annotations: map[string]string{ + "nginx.org/client-body-buffer-size": "16@k", + }, + }, + { + name: "invalid with letters", + annotations: map[string]string{ + "nginx.org/client-body-buffer-size": "abcdef", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + ingress := &networking.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + Namespace: "default", + Annotations: tt.annotations, + }, + } + + ingEx := &IngressEx{ + Ingress: ingress, + } + + baseCfgParams := NewDefaultConfigParams(context.Background(), false) + result := parseAnnotations(ingEx, baseCfgParams, false, false, false, false, false) + + if result.ClientBodyBufferSize != "" { + t.Errorf(`Test %q: expected ClientBodyBufferSize %q, got ""`, tt.name, result.ClientBodyBufferSize) + } + }) + } +} diff --git a/internal/configs/config_params.go b/internal/configs/config_params.go index 1410fd45f6..3b921d2675 100644 --- a/internal/configs/config_params.go +++ b/internal/configs/config_params.go @@ -12,6 +12,7 @@ import ( type ConfigParams struct { Context context.Context ClientMaxBodySize string + ClientBodyBufferSize string DefaultServerAccessLogOff bool DefaultServerReturn string FailTimeout string @@ -52,6 +53,7 @@ type ConfigParams struct { MainWorkerProcesses string MainWorkerRlimitNofile string MainWorkerShutdownTimeout string + MainClientBodyBufferSize string MaxConns int MaxFails int AppProtectEnable string diff --git a/internal/configs/configmaps.go b/internal/configs/configmaps.go index c4e514e431..7137e0d6c3 100644 --- a/internal/configs/configmaps.go +++ b/internal/configs/configmaps.go @@ -103,6 +103,17 @@ func ParseConfigMap(ctx context.Context, cfgm *v1.ConfigMap, nginxPlus bool, has cfgParams.ClientMaxBodySize = clientMaxBodySize } + if clientBodyBufferSize, exists := cfgm.Data["client-body-buffer-size"]; exists { + if parsedClientBodyBufferSize, err := ParseSize(clientBodyBufferSize); err != nil { + wrappedError := fmt.Errorf("ConfigMap %s/%s: invalid value for 'client-body-buffer-size': %w", cfgm.GetNamespace(), cfgm.GetName(), err) + nl.Errorf(l, "%s", wrappedError.Error()) + eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, wrappedError.Error()) + configOk = false + } else { + cfgParams.MainClientBodyBufferSize = parsedClientBodyBufferSize + } + } + if serverNamesHashBucketSize, exists := cfgm.Data["server-names-hash-bucket-size"]; exists { cfgParams.MainServerNamesHashBucketSize = serverNamesHashBucketSize } @@ -1146,6 +1157,7 @@ func GenerateNginxMainConfig(staticCfgParams *StaticConfigParams, config *Config ServerNamesHashMaxSize: config.MainServerNamesHashMaxSize, MapHashBucketSize: config.MainMapHashBucketSize, MapHashMaxSize: config.MainMapHashMaxSize, + ClientBodyBufferSize: config.MainClientBodyBufferSize, ServerTokens: config.ServerTokens, SSLCiphers: config.MainServerSSLCiphers, SSLDHParam: config.MainServerSSLDHParam, diff --git a/internal/configs/configmaps_test.go b/internal/configs/configmaps_test.go index 30e054f3be..6d73204040 100644 --- a/internal/configs/configmaps_test.go +++ b/internal/configs/configmaps_test.go @@ -2489,6 +2489,199 @@ func TestParseProxyBuffersInvalidFormat(t *testing.T) { }) } +func TestParseConfigMapClientBodyBufferSizeValid(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + value string + expected string + description string + }{ + { + name: "valid size with k suffix", + value: "12k", + expected: "12k", + description: "should accept valid size with k suffix", + }, + { + name: "valid size with K suffix", + value: "16K", + expected: "16K", + description: "should accept valid size with K suffix", + }, + { + name: "valid size with m suffix", + value: "6m", + expected: "6m", + description: "should accept valid size with m suffix", + }, + { + name: "valid size with M suffix", + value: "8M", + expected: "8M", + description: "should accept valid size with M suffix", + }, + { + name: "valid size without suffix", + value: "1024", + expected: "1024", + description: "should accept valid size without suffix", + }, + { + name: "not set", + value: "", + expected: "", // no default value anymore + description: "should use empty string when not set", + }, + } + + nginxPlus := false + hasAppProtect := false + hasAppProtectDos := false + hasTLSPassthrough := false + directiveAutoadjustEnabled := false + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-configmap", + Namespace: "default", + }, + } + + if tt.value != "" { + cm.Data = map[string]string{ + "client-body-buffer-size": tt.value, + } + } + + result, configOk := ParseConfigMap( + context.Background(), + cm, + nginxPlus, + hasAppProtect, + hasAppProtectDos, + hasTLSPassthrough, + directiveAutoadjustEnabled, + makeEventLogger(), + ) + + // Should always pass validation for valid cases + if !configOk { + t.Errorf("ParseConfigMap() for %s should have passed validation but failed", tt.description) + } + + if result.MainClientBodyBufferSize != tt.expected { + t.Errorf("ParseConfigMap() for %s returned MainClientBodyBufferSize=%q, expected %q", + tt.description, result.MainClientBodyBufferSize, tt.expected) + } + }) + } +} + +func TestParseConfigMapClientBodyBufferSizeInvalid(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + value string + description string + }{ + { + name: "invalid time unit h", + value: "4h", + description: "should reject invalid time units", + }, + { + name: "invalid random string", + value: "3cd2", + description: "should reject invalid strings", + }, + { + name: "invalid with g suffix", + value: "2g", + description: "should reject unsupported g suffix", + }, + { + name: "invalid with G suffix", + value: "1G", + description: "should reject unsupported G suffix", + }, + { + name: "invalid negative value", + value: "-16k", + description: "should reject negative values", + }, + { + name: "invalid whitespace only", + value: " ", + description: "should reject whitespace-only values", + }, + { + name: "invalid with text prefix", + value: "abc16k", + description: "should reject values with text prefix", + }, + { + name: "invalid with special characters", + value: "16@k", + description: "should reject values with special characters", + }, + { + name: "invalid decimal with unsupported suffix", + value: "16.5g", + description: "should reject decimal values with unsupported suffix", + }, + } + + nginxPlus := false + hasAppProtect := false + hasAppProtectDos := false + hasTLSPassthrough := false + directiveAutoadjustEnabled := false + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-configmap", + Namespace: "default", + }, + Data: map[string]string{ + "client-body-buffer-size": tt.value, + }, + } + + result, configOk := ParseConfigMap( + context.Background(), + cm, + nginxPlus, + hasAppProtect, + hasAppProtectDos, + hasTLSPassthrough, + directiveAutoadjustEnabled, + makeEventLogger(), + ) + + // Should always fail validation for invalid cases + if configOk { + t.Errorf("%s should have failed validation but passed", tt.name) + } + + if result.MainClientBodyBufferSize != "" { + t.Errorf(`%s returned MainClientBodyBufferSize=%q, expected ""`, + tt.name, result.MainClientBodyBufferSize) + } + }) + } +} + func TestParseErrorLogLevelToVirtualServer(t *testing.T) { t.Parallel() diff --git a/internal/configs/ingress.go b/internal/configs/ingress.go index 1838498e36..4542ed9f2b 100644 --- a/internal/configs/ingress.go +++ b/internal/configs/ingress.go @@ -498,6 +498,7 @@ func createLocation(path string, upstream version1.Upstream, cfg *ConfigParams, ProxySendTimeout: cfg.ProxySendTimeout, ProxySetHeaders: cfg.ProxySetHeaders, ClientMaxBodySize: cfg.ClientMaxBodySize, + ClientBodyBufferSize: cfg.ClientBodyBufferSize, Websocket: websocket, Rewrite: rewrite, RewriteTarget: rewriteTarget, diff --git a/internal/configs/version1/__snapshots__/template_test.snap b/internal/configs/version1/__snapshots__/template_test.snap index 61a3dbfaef..a8e907f669 100644 --- a/internal/configs/version1/__snapshots__/template_test.snap +++ b/internal/configs/version1/__snapshots__/template_test.snap @@ -21,7 +21,6 @@ http { map_hash_max_size ; map_hash_bucket_size ; - js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; @@ -2698,6 +2697,103 @@ server { --- +[TestExecuteTemplate_ForIngressWithClientBodyBufferSize - 1] +# configuration for default/cafe-ingress +upstream test { + zone test 256k; + server 127.0.0.1:8181 max_fails=0 fail_timeout=1s max_conns=0 slow_start=5s;keepalive 16; +} + + + + +server { + listen 443 ssl;listen [::]:443 ssl; + ssl_certificate secret.pem; + ssl_certificate_key secret.pem; + + server_tokens "off"; + + server_name test.example.com; + + status_zone test.example.com; + set $resource_type "ingress"; + set $resource_name "cafe-ingress"; + set $resource_namespace "default"; + app_protect_enable on; + app_protect_policy_file /etc/nginx/waf/nac-policies/default-dataguard-alarm; + app_protect_security_log_enable on; + app_protect_security_log /etc/nginx/waf/nac-logconfs/test_logconf syslog:server=127.0.0.1:514; + app_protect_security_log /etc/nginx/waf/nac-logconfs/test_logconf2; + + app_protect_dos_enable on; + app_protect_dos_policy_file /test/policy.json; + app_protect_dos_security_log_enable on; + app_protect_dos_security_log /test/logConf.json; + set $loggable '0'; + # app-protect-dos module will set it to '1' if a request doesn't pass the rate limit + access_log /var/log/dos log_dos if=$loggable; + app_protect_dos_monitor uri=/path/to/monitor protocol=http1 timeout=30; + app_protect_dos_name "testdos"; + app_protect_dos_access_file "/etc/nginx/dos/allowlist/default_test.example.com"; + + + if ($scheme = http) { + return 301 https://$host:443$request_uri; + } + + + auth_jwt_key_file /etc/nginx/secrets/key.jwk; + auth_jwt "closed site" token=$cookie_auth_token; + error_page 401 @login_url-default-cafe-ingress; + + location @hc-test { + proxy_set_header Test-Header "test-header-value"; + proxy_connect_timeout 0s; + proxy_read_timeout 0s; + proxy_send_timeout 0s; + proxy_pass ://test; + health_check uri= interval=1s fails=1 passes=1; + } + + location @login_url-default-cafe-ingress { + internal; + return 302 https://test.example.com/login; + } + + location /tea { + set $service ""; + status_zone ""; + # location for minion default/tea-minion + set $resource_name "tea-minion"; + set $resource_namespace "default"; + proxy_http_version 1.1; + proxy_set_header Connection ""; + auth_jwt_key_file /etc/nginx/secrets/location-key.jwk; + auth_jwt "closed site" token=$cookie_auth_token; + + proxy_connect_timeout 10s; + proxy_read_timeout 10s; + proxy_send_timeout 10s; + client_max_body_size 2m; + client_body_buffer_size 16k; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + proxy_pass http://test; + + + } + +} + +--- + [TestExecuteTemplate_ForMainForNGINXPlusTLSPassthroughPortDisabled - 1] worker_processes auto; worker_rlimit_nofile 65536; @@ -4384,7 +4480,6 @@ http { map_hash_max_size ; map_hash_bucket_size ; - js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; @@ -4505,7 +4600,6 @@ http { map_hash_max_size ; map_hash_bucket_size ; - js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; @@ -4626,7 +4720,6 @@ http { map_hash_max_size ; map_hash_bucket_size ; - js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; @@ -4747,7 +4840,6 @@ http { map_hash_max_size ; map_hash_bucket_size ; - js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; @@ -4868,7 +4960,6 @@ http { map_hash_max_size ; map_hash_bucket_size ; - js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; @@ -5008,7 +5099,6 @@ http { map_hash_max_size ; map_hash_bucket_size ; - js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; @@ -5129,7 +5219,6 @@ http { map_hash_max_size ; map_hash_bucket_size ; - js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; @@ -6124,7 +6213,6 @@ http { map_hash_max_size ; map_hash_bucket_size ; - js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; @@ -6245,7 +6333,6 @@ http { map_hash_max_size ; map_hash_bucket_size ; - js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; diff --git a/internal/configs/version1/config.go b/internal/configs/version1/config.go index 65a617737b..317157db97 100644 --- a/internal/configs/version1/config.go +++ b/internal/configs/version1/config.go @@ -175,6 +175,7 @@ type Location struct { ProxySendTimeout string ProxySetHeaders []version2.Header ClientMaxBodySize string + ClientBodyBufferSize string Websocket bool Rewrite string RewriteTarget string @@ -272,6 +273,7 @@ type MainConfig struct { ServerNamesHashMaxSize string MapHashBucketSize string MapHashMaxSize string + ClientBodyBufferSize string ServerTokens string SSLRejectHandshake bool SSLCiphers string diff --git a/internal/configs/version1/nginx-plus.ingress.tmpl b/internal/configs/version1/nginx-plus.ingress.tmpl index a7b343aa75..25b2049e5d 100644 --- a/internal/configs/version1/nginx-plus.ingress.tmpl +++ b/internal/configs/version1/nginx-plus.ingress.tmpl @@ -291,6 +291,9 @@ server { proxy_read_timeout {{$location.ProxyReadTimeout}}; proxy_send_timeout {{$location.ProxySendTimeout}}; client_max_body_size {{$location.ClientMaxBodySize}}; + {{- if $location.ClientBodyBufferSize }} + client_body_buffer_size {{$location.ClientBodyBufferSize}}; + {{- end}} {{- $proxySetHeaders := generateProxySetHeaders $location $.Ingress.Annotations }} {{$proxySetHeaders}} proxy_set_header Host $host; diff --git a/internal/configs/version1/nginx-plus.tmpl b/internal/configs/version1/nginx-plus.tmpl index 79bb9b0158..ee84896068 100644 --- a/internal/configs/version1/nginx-plus.tmpl +++ b/internal/configs/version1/nginx-plus.tmpl @@ -37,6 +37,10 @@ http { map_hash_max_size {{.MapHashMaxSize}}; map_hash_bucket_size {{.MapHashBucketSize}}; + {{- if .ClientBodyBufferSize }} + client_body_buffer_size {{.ClientBodyBufferSize}}; + {{- end }} + js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; diff --git a/internal/configs/version1/nginx.ingress.tmpl b/internal/configs/version1/nginx.ingress.tmpl index 47e28c5bbb..70d8f2cfa5 100644 --- a/internal/configs/version1/nginx.ingress.tmpl +++ b/internal/configs/version1/nginx.ingress.tmpl @@ -198,6 +198,9 @@ server { proxy_read_timeout {{$location.ProxyReadTimeout}}; proxy_send_timeout {{$location.ProxySendTimeout}}; client_max_body_size {{$location.ClientMaxBodySize}}; + {{- if $location.ClientBodyBufferSize }} + client_body_buffer_size {{$location.ClientBodyBufferSize}}; + {{- end}} {{- $proxySetHeaders := generateProxySetHeaders $location $.Ingress.Annotations -}} {{$proxySetHeaders}} proxy_set_header Host $host; diff --git a/internal/configs/version1/nginx.tmpl b/internal/configs/version1/nginx.tmpl index e5f9bf3e2c..b9a84ac712 100644 --- a/internal/configs/version1/nginx.tmpl +++ b/internal/configs/version1/nginx.tmpl @@ -30,6 +30,9 @@ http { map_hash_max_size {{.MapHashMaxSize}}; map_hash_bucket_size {{.MapHashBucketSize}}; + {{- if .ClientBodyBufferSize }} + client_body_buffer_size {{.ClientBodyBufferSize}}; + {{- end }} js_import /etc/nginx/njs/apikey_auth.js; js_set $apikey_auth_hash apikey_auth.hash; diff --git a/internal/configs/version1/template_test.go b/internal/configs/version1/template_test.go index 0f0d6b7714..62178b9de4 100644 --- a/internal/configs/version1/template_test.go +++ b/internal/configs/version1/template_test.go @@ -303,6 +303,25 @@ func TestExecuteTemplate_ForMergeableIngressWithOneMinionWithPathRegexAnnotation snaps.MatchSnapshot(t, buf.String()) } +func TestExecuteTemplate_ForIngressWithClientBodyBufferSize(t *testing.T) { + t.Parallel() + + tmpl := newNGINXPlusIngressTmpl(t) + buf := &bytes.Buffer{} + + err := tmpl.Execute(buf, ingressCfgWithClientBodyBufferSize) + t.Log(buf.String()) + if err != nil { + t.Fatal(err) + } + + want := "client_body_buffer_size 16k;" + if !strings.Contains(buf.String(), want) { + t.Errorf("want %q in generated config", want) + } + snaps.MatchSnapshot(t, buf.String()) +} + func TestExecuteTemplate_ForMergeableIngressWithSecondMinionWithPathRegexAnnotation(t *testing.T) { t.Parallel() @@ -2134,6 +2153,77 @@ var ( }, } + ingressCfgWithClientBodyBufferSize = IngressNginxConfig{ + Servers: []Server{ + { + Name: "test.example.com", + ServerTokens: "off", + StatusZone: "test.example.com", + JWTAuth: &JWTAuth{ + Key: "/etc/nginx/secrets/key.jwk", + Realm: "closed site", + Token: "$cookie_auth_token", + RedirectLocationName: "@login_url-default-cafe-ingress", + }, + SSL: true, + SSLCertificate: "secret.pem", + SSLCertificateKey: "secret.pem", + SSLPorts: []int{443}, + SSLRedirect: true, + Locations: []Location{ + { + Path: "/tea", + Upstream: testUpstream, + ProxyConnectTimeout: "10s", + ProxyReadTimeout: "10s", + ProxySendTimeout: "10s", + ClientMaxBodySize: "2m", + ClientBodyBufferSize: "16k", + JWTAuth: &JWTAuth{ + Key: "/etc/nginx/secrets/location-key.jwk", + Realm: "closed site", + Token: "$cookie_auth_token", + }, + MinionIngress: &Ingress{ + Name: "tea-minion", + Namespace: "default", + }, + }, + }, + HealthChecks: map[string]HealthCheck{"test": healthCheck}, + JWTRedirectLocations: []JWTRedirectLocation{ + { + Name: "@login_url-default-cafe-ingress", + LoginURL: "https://test.example.com/login", + }, + }, + AppProtectEnable: "on", + AppProtectPolicy: "/etc/nginx/waf/nac-policies/default-dataguard-alarm", + AppProtectLogConfs: []string{ + "/etc/nginx/waf/nac-logconfs/test_logconf syslog:server=127.0.0.1:514", + "/etc/nginx/waf/nac-logconfs/test_logconf2", + }, + AppProtectLogEnable: "on", + AppProtectDosEnable: "on", + AppProtectDosPolicyFile: "/test/policy.json", + AppProtectDosLogConfFile: "/test/logConf.json", + AppProtectDosLogEnable: true, + AppProtectDosMonitorURI: "/path/to/monitor", + AppProtectDosMonitorProtocol: "http1", + AppProtectDosMonitorTimeout: 30, + AppProtectDosName: "testdos", + AppProtectDosAccessLogDst: "/var/log/dos", + AppProtectDosAllowListPath: "/etc/nginx/dos/allowlist/default_test.example.com", + }, + }, + Upstreams: []Upstream{testUpstream}, + Keepalive: "16", + Ingress: Ingress{ + Name: "cafe-ingress", + Namespace: "default", + }, + } + // Ingress Config example with path-regex annotation value "case_sensitive" ingressCfgWithRegExAnnotationCaseSensitive = IngressNginxConfig{ Servers: []Server{ diff --git a/internal/k8s/validation.go b/internal/k8s/validation.go index a6669932a7..b7ee366774 100644 --- a/internal/k8s/validation.go +++ b/internal/k8s/validation.go @@ -34,6 +34,7 @@ const ( proxyPassHeadersAnnotation = "nginx.org/proxy-pass-headers" // #nosec G101 proxySetHeadersAnnotation = "nginx.org/proxy-set-headers" clientMaxBodySizeAnnotation = "nginx.org/client-max-body-size" + clientBodyBufferSizeAnnotation = "nginx.org/client-body-buffer-size" redirectToHTTPSAnnotation = "nginx.org/redirect-to-https" sslRedirectAnnotation = "ingress.kubernetes.io/ssl-redirect" proxyBufferingAnnotation = "nginx.org/proxy-buffering" @@ -179,6 +180,10 @@ var ( validateRequiredAnnotation, validateOffsetAnnotation, }, + clientBodyBufferSizeAnnotation: { + validateRequiredAnnotation, + validateSizeAnnotation, + }, redirectToHTTPSAnnotation: { validateRequiredAnnotation, validateBoolAnnotation,