diff --git a/docs/content/configuration/global-configuration/configmap-resource.md b/docs/content/configuration/global-configuration/configmap-resource.md index 2461abbb57..a6709e309a 100644 --- a/docs/content/configuration/global-configuration/configmap-resource.md +++ b/docs/content/configuration/global-configuration/configmap-resource.md @@ -185,6 +185,7 @@ See the doc about [VirtualServer and VirtualServerRoute resources](/nginx-ingres |``app-protect-failure-mode-action`` | Sets the ``app_protect_failure_mode_action`` [global directive](/nginx-app-protect/configuration/#global-directives). | ``pass`` | | |``app-protect-cpu-thresholds`` | Sets the ``app_protect_cpu_thresholds`` [global directive](/nginx-app-protect/configuration/#global-directives). | ``high=100 low=100`` | | |``app-protect-physical-memory-util-thresholds`` | Sets the ``app_protect_physical_memory_util_thresholds`` [global directive](/nginx-app-protect/configuration/#global-directives). | ``high=100 low=100`` | | +|`app-protect-reconnect-period-seconds` | Sets the `app_protect_reconnect_period_seconds` [global directive](/nginx-app-protect/configuration/#global-directives). | `5` | | |``app-protect-dos-log-format`` | Sets the custom [log format](https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format) for Dos Access log traffic. For convenience, it is possible to define the log format across multiple lines (each line separated by ``\n``). In that case, the Ingress Controller will replace every ``\n`` character with a space character. All ``'`` characters must be escaped. | `, vs_name_al=$app_protect_dos_vs_name, ip=$remote_addr, tls_fp=$app_protect_dos_tls_fp, outcome=$app_protect_dos_outcome, reason=$app_protect_dos_outcome_reason, policy_name=$app_protect_dos_policy_name, dos_version=$app_protect_dos_version, ip_tls=$remote_addr:$app_protect_dos_tls_fp,` | | |``app-protect-dos-log-format-escaping`` | Sets the characters escaping for the variables of the stream log format. Supported values: ``json`` (JSON escaping), ``default`` (the default escaping) ``none`` (disables escaping). | ``default`` | | {{% /table %}} diff --git a/internal/configs/config_params.go b/internal/configs/config_params.go index b1f7705789..994fc0c6c2 100644 --- a/internal/configs/config_params.go +++ b/internal/configs/config_params.go @@ -53,6 +53,7 @@ type ConfigParams struct { MainAppProtectCookieSeed string MainAppProtectCPUThresholds string MainAppProtectPhysicalMemoryThresholds string + MainAppProtectReconnectPeriod string AppProtectDosResource string MainAppProtectDosLogFormat []string MainAppProtectDosLogFormatEscaping string diff --git a/internal/configs/configmaps.go b/internal/configs/configmaps.go index 2bac654c39..f752f5d57f 100644 --- a/internal/configs/configmaps.go +++ b/internal/configs/configmaps.go @@ -499,6 +499,14 @@ func ParseConfigMap(cfgm *v1.ConfigMap, nginxPlus bool, hasAppProtect bool, hasA glog.Error("ConfigMap Key 'app-protect-physical-memory-thresholds' must follow pattern: 'high=<0 - 100> low=<0 - 100>'. Ignoring.") } } + if appProtectReconnectPeriod, exists := cfgm.Data["app-protect-reconnect-period-seconds"]; exists { + period, err := ParseFloat64(appProtectReconnectPeriod) + if err == nil && period > 0 && period <= 60 { + cfgParams.MainAppProtectReconnectPeriod = appProtectReconnectPeriod + } else { + glog.Error("ConfigMap Key 'app-protect-reconnect-period-second' must have value between '0' and '60'. '0' is illegal. Ignoring.") + } + } } if hasAppProtectDos { @@ -579,6 +587,7 @@ func GenerateNginxMainConfig(staticCfgParams *StaticConfigParams, config *Config AppProtectCookieSeed: config.MainAppProtectCookieSeed, AppProtectCPUThresholds: config.MainAppProtectCPUThresholds, AppProtectPhysicalMemoryThresholds: config.MainAppProtectPhysicalMemoryThresholds, + AppProtectReconnectPeriod: config.MainAppProtectReconnectPeriod, AppProtectDosLogFormat: config.MainAppProtectDosLogFormat, AppProtectDosLogFormatEscaping: config.MainAppProtectDosLogFormatEscaping, InternalRouteServer: staticCfgParams.EnableInternalRoutes, diff --git a/internal/configs/configmaps_test.go b/internal/configs/configmaps_test.go index 98bb67a3f3..739e548df0 100644 --- a/internal/configs/configmaps_test.go +++ b/internal/configs/configmaps_test.go @@ -49,3 +49,71 @@ func TestParseConfigMapWithAppProtectCompressedRequestsAction(t *testing.T) { } } } + +func TestParseConfigMapWithAppProtectReconnectPeriod(t *testing.T) { + tests := []struct { + period string + expect string + msg string + }{ + { + period: "25", + expect: "25", + msg: "valid period 25", + }, + { + period: "13.875", + expect: "13.875", + msg: "valid period 13.875", + }, + { + period: "0.125", + expect: "0.125", + msg: "valid period 0.125", + }, + { + period: "60", + expect: "60", + msg: "valid period 60", + }, + { + period: "60.1", + expect: "", + msg: "invalid period 60.1", + }, + { + period: "100", + expect: "", + msg: "invalid period 100", + }, + { + period: "0", + expect: "", + msg: "invalid period 0", + }, + { + period: "-5", + expect: "", + msg: "invalid period -5", + }, + { + period: "", + expect: "", + msg: "empty period", + }, + } + nginxPlus := true + hasAppProtect := true + hasAppProtectDos := false + for _, test := range tests { + cm := &v1.ConfigMap{ + Data: map[string]string{ + "app-protect-reconnect-period-seconds": test.period, + }, + } + result := ParseConfigMap(cm, nginxPlus, hasAppProtect, hasAppProtectDos) + if result.MainAppProtectReconnectPeriod != test.expect { + t.Errorf("ParseConfigMap() returned %q but expected %q for the case %s", result.MainAppProtectReconnectPeriod, test.expect, test.msg) + } + } +} diff --git a/internal/configs/parsing_helpers.go b/internal/configs/parsing_helpers.go index f785486b11..95cde94a47 100644 --- a/internal/configs/parsing_helpers.go +++ b/internal/configs/parsing_helpers.go @@ -182,6 +182,11 @@ func ParseUint64(s string) (uint64, error) { return strconv.ParseUint(s, 10, 64) } +// ParseFloat64 ensures that the string value is a valid float64 +func ParseFloat64(s string) (float64, error) { + return strconv.ParseFloat(s, 64) +} + // timeRegexp http://nginx.org/en/docs/syntax.html var timeRegexp = regexp.MustCompile(`^(\d+y)??\s*(\d+M)??\s*(\d+w)??\s*(\d+d)??\s*(\d+h)??\s*(\d+m)??\s*(\d+s?)??\s*(\d+ms)??$`) diff --git a/internal/configs/parsing_helpers_test.go b/internal/configs/parsing_helpers_test.go index b3f4870f7b..e406208686 100644 --- a/internal/configs/parsing_helpers_test.go +++ b/internal/configs/parsing_helpers_test.go @@ -667,3 +667,46 @@ func TestParseUint64(t *testing.T) { } } } + +func TestParseFloat64(t *testing.T) { + testsWithValidInput := []struct { + input string + expected float64 + }{ + {"0", 0}, + {"1", 1}, + {"123.456", 123.456}, + {"-100", -100}, + {"-12345.6789", -12345.6789}, + {"123456789", 123456789}, + {"1.7E+308", 1.7e+308}, + {"-1.7E+308", -1.7e+308}, + } + + invalidInput := []string{ + "", + "blablah", + "100.15.12", + "1,000", + "1.8E+308", + "-1.8E+308", + } + + for _, test := range testsWithValidInput { + result, err := ParseFloat64(test.input) + if err != nil { + t.Errorf("TestParseFloat64(%q) returned an error for valid input", test.input) + } + + if result != test.expected { + t.Errorf("TestParseFloat64(%q) returned %e expected %e", test.input, result, test.expected) + } + } + + for _, input := range invalidInput { + _, err := ParseFloat64(input) + if err == nil { + t.Errorf("TestParseFloat64(%q) does not return an error for invalid input", input) + } + } +} diff --git a/internal/configs/version1/config.go b/internal/configs/version1/config.go index 469d55848e..b7d12d1f4f 100644 --- a/internal/configs/version1/config.go +++ b/internal/configs/version1/config.go @@ -206,6 +206,7 @@ type MainConfig struct { AppProtectCookieSeed string AppProtectCPUThresholds string AppProtectPhysicalMemoryThresholds string + AppProtectReconnectPeriod string AppProtectDosLoadModule bool AppProtectDosLogFormat []string AppProtectDosLogFormatEscaping string diff --git a/internal/configs/version1/nginx-plus.tmpl b/internal/configs/version1/nginx-plus.tmpl index 0167a1ac47..87967d6c9b 100644 --- a/internal/configs/version1/nginx-plus.tmpl +++ b/internal/configs/version1/nginx-plus.tmpl @@ -90,6 +90,7 @@ http { {{if .AppProtectCookieSeed}}app_protect_cookie_seed {{.AppProtectCookieSeed}};{{end}} {{if .AppProtectCPUThresholds}}app_protect_cpu_thresholds {{.AppProtectCPUThresholds}};{{end}} {{if .AppProtectPhysicalMemoryThresholds}}app_protect_physical_memory_util_thresholds {{.AppProtectPhysicalMemoryThresholds}};{{end}} + {{if .AppProtectReconnectPeriod}}app_protect_reconnect_period_seconds {{.AppProtectReconnectPeriod}};{{end}} include /etc/nginx/waf/nac-usersigs/index.conf; {{- end}}