diff --git a/internal/configs/config_params.go b/internal/configs/config_params.go index 7dd9c51ae0..1410fd45f6 100644 --- a/internal/configs/config_params.go +++ b/internal/configs/config_params.go @@ -68,6 +68,7 @@ type ConfigParams struct { MainAppProtectDosLogFormat []string MainAppProtectDosLogFormatEscaping string MainAppProtectDosArbFqdn string + OIDC OIDC ProxyBuffering bool ProxyBuffers string ProxyBufferSize string @@ -192,6 +193,15 @@ type ZoneSync struct { ResolverIPV6 *bool } +// OIDC holds OIDC configuration parameters. +type OIDC struct { + PKCETimeout string + IDTokenTimeout string + AccessTimeout string + RefreshTimeout string + SIDSTimeout string +} + // MGMTSecrets holds mgmt block secret names type MGMTSecrets struct { License string @@ -258,6 +268,13 @@ func NewDefaultConfigParams(ctx context.Context, isPlus bool) *ConfigParams { LimitReqZoneSize: "10m", LimitReqLogLevel: "error", LimitReqRejectCode: 429, + OIDC: OIDC{ + PKCETimeout: "90s", + IDTokenTimeout: "1h", + AccessTimeout: "1h", + RefreshTimeout: "8h", + SIDSTimeout: "8h", + }, } } diff --git a/internal/configs/configmaps.go b/internal/configs/configmaps.go index ed4d6dc272..c4e514e431 100644 --- a/internal/configs/configmaps.go +++ b/internal/configs/configmaps.go @@ -459,6 +459,11 @@ func ParseConfigMap(ctx context.Context, cfgm *v1.ConfigMap, nginxPlus bool, has configOk = false } + err = parseConfigMapOIDC(l, cfgm, cfgParams, eventLog) + if err != nil { + configOk = false + } + if upstreamZoneSize, exists := cfgm.Data["upstream-zone-size"]; exists { cfgParams.UpstreamZoneSize = upstreamZoneSize } @@ -694,6 +699,61 @@ func ParseConfigMap(ctx context.Context, cfgm *v1.ConfigMap, nginxPlus bool, has return cfgParams, configOk } +// parseConfigMapOIDC parses OIDC timeout configuration from ConfigMap. +func parseConfigMapOIDC(l *slog.Logger, cfgm *v1.ConfigMap, cfgParams *ConfigParams, eventLog record.EventRecorder) error { + if oidcPKCETimeout, exists := cfgm.Data["oidc-pkce-timeout"]; exists { + pkceTimeout, err := ParseTime(oidcPKCETimeout) + if err != nil { + errorText := fmt.Sprintf("ConfigMap %s/%s: invalid value for 'oidc-pkce-timeout': %q, must be a valid nginx time (e.g. '90s', '5m', '1h')", cfgm.Namespace, cfgm.Name, oidcPKCETimeout) + nl.Warn(l, errorText) + eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, errorText) + return err + } + cfgParams.OIDC.PKCETimeout = pkceTimeout + } + if oidcIDTokensTimeout, exists := cfgm.Data["oidc-id-tokens-timeout"]; exists { + idTokensTimeout, err := ParseTime(oidcIDTokensTimeout) + if err != nil { + errorText := fmt.Sprintf("ConfigMap %s/%s: invalid value for 'oidc-id-tokens-timeout': %q, must be a valid nginx time (e.g. '1h', '30m', '2h')", cfgm.Namespace, cfgm.Name, oidcIDTokensTimeout) + nl.Warn(l, errorText) + eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, errorText) + return err + } + cfgParams.OIDC.IDTokenTimeout = idTokensTimeout + } + if oidcAccessTokensTimeout, exists := cfgm.Data["oidc-access-tokens-timeout"]; exists { + accessTokensTimeout, err := ParseTime(oidcAccessTokensTimeout) + if err != nil { + errorText := fmt.Sprintf("ConfigMap %s/%s: invalid value for 'oidc-access-tokens-timeout': %q, must be a valid nginx time (e.g. '1h', '30m', '2h')", cfgm.Namespace, cfgm.Name, oidcAccessTokensTimeout) + nl.Warn(l, errorText) + eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, errorText) + return err + } + cfgParams.OIDC.AccessTimeout = accessTokensTimeout + } + if oidcRefreshTokensTimeout, exists := cfgm.Data["oidc-refresh-tokens-timeout"]; exists { + refreshTokensTimeout, err := ParseTime(oidcRefreshTokensTimeout) + if err != nil { + errorText := fmt.Sprintf("ConfigMap %s/%s: invalid value for 'oidc-refresh-tokens-timeout': %q, must be a valid nginx time (e.g. '8h', '12h', '24h')", cfgm.Namespace, cfgm.Name, oidcRefreshTokensTimeout) + nl.Warn(l, errorText) + eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, errorText) + return err + } + cfgParams.OIDC.RefreshTimeout = refreshTokensTimeout + } + if oidcSIDSTimeout, exists := cfgm.Data["oidc-sids-timeout"]; exists { + sidsTimeout, err := ParseTime(oidcSIDSTimeout) + if err != nil { + errorText := fmt.Sprintf("ConfigMap %s/%s: invalid value for 'oidc-sids-timeout': %q, must be a valid nginx time (e.g. '8h', '12h', '24h')", cfgm.Namespace, cfgm.Name, oidcSIDSTimeout) + nl.Warn(l, errorText) + eventLog.Event(cfgm, v1.EventTypeWarning, nl.EventReasonInvalidValue, errorText) + return err + } + cfgParams.OIDC.SIDSTimeout = sidsTimeout + } + return nil +} + //nolint:gocyclo func parseConfigMapZoneSync(l *slog.Logger, cfgm *v1.ConfigMap, cfgParams *ConfigParams, eventLog record.EventRecorder, nginxPlus bool) (*ZoneSync, error) { if zoneSync, exists, err := GetMapKeyAsBool(cfgm.Data, "zone-sync", cfgm); exists { @@ -1121,11 +1181,18 @@ func GenerateNginxMainConfig(staticCfgParams *StaticConfigParams, config *Config InternalRouteServer: staticCfgParams.EnableInternalRoutes, InternalRouteServerName: staticCfgParams.InternalRouteServerName, LatencyMetrics: staticCfgParams.EnableLatencyMetrics, - OIDC: staticCfgParams.EnableOIDC, - ZoneSyncConfig: zoneSyncConfig, - DynamicSSLReloadEnabled: staticCfgParams.DynamicSSLReload, - StaticSSLPath: staticCfgParams.StaticSSLPath, - NginxVersion: staticCfgParams.NginxVersion, + OIDC: version1.OIDCConfig{ + Enable: staticCfgParams.EnableOIDC, + PKCETimeout: config.OIDC.PKCETimeout, + IDTokenTimeout: config.OIDC.IDTokenTimeout, + AccessTimeout: config.OIDC.AccessTimeout, + RefreshTimeout: config.OIDC.RefreshTimeout, + SIDSTimeout: config.OIDC.SIDSTimeout, + }, + ZoneSyncConfig: zoneSyncConfig, + DynamicSSLReloadEnabled: staticCfgParams.DynamicSSLReload, + StaticSSLPath: staticCfgParams.StaticSSLPath, + NginxVersion: staticCfgParams.NginxVersion, } return nginxCfg } diff --git a/internal/configs/configmaps_test.go b/internal/configs/configmaps_test.go index 5039340298..30e054f3be 100644 --- a/internal/configs/configmaps_test.go +++ b/internal/configs/configmaps_test.go @@ -298,6 +298,246 @@ func TestParseConfigMapAccessLogDefault(t *testing.T) { } } +func TestParseConfigMapOIDC(t *testing.T) { + t.Parallel() + tests := []struct { + configMap *v1.ConfigMap + want *OIDC + msg string + }{ + { + configMap: &v1.ConfigMap{ + Data: map[string]string{}, + }, + want: &OIDC{ + PKCETimeout: "90s", + IDTokenTimeout: "1h", + AccessTimeout: "1h", + RefreshTimeout: "8h", + SIDSTimeout: "8h", + }, + msg: "default OIDC values", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-pkce-timeout": "5m", + "oidc-id-tokens-timeout": "2h", + "oidc-access-tokens-timeout": "3h", + "oidc-refresh-tokens-timeout": "48h", + "oidc-sids-timeout": "72h", + }, + }, + want: &OIDC{ + PKCETimeout: "5m", + IDTokenTimeout: "2h", + AccessTimeout: "3h", + RefreshTimeout: "48h", + SIDSTimeout: "72h", + }, + msg: "all timeout values custom", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-pkce-timeout": "15m", + }, + }, + want: &OIDC{ + PKCETimeout: "15m", + }, + msg: "custom PKCE timeout only", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-id-tokens-timeout": "90m", + }, + }, + want: &OIDC{ + IDTokenTimeout: "90m", + }, + msg: "custom ID token timeout only", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-access-tokens-timeout": "4h", + }, + }, + want: &OIDC{ + AccessTimeout: "4h", + }, + msg: "custom access token timeout only", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-refresh-tokens-timeout": "16h", + }, + }, + want: &OIDC{ + RefreshTimeout: "16h", + }, + msg: "custom refresh token timeout only", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-sids-timeout": "12h", + }, + }, + want: &OIDC{ + SIDSTimeout: "12h", + }, + msg: "custom SIDS timeout only", + }, + } + + nginxPlus := true + hasAppProtect := false + hasAppProtectDos := false + hasTLSPassthrough := false + directiveAutoadjustEnabled := false + + for _, test := range tests { + t.Run(test.msg, func(t *testing.T) { + result, configOk := ParseConfigMap(context.Background(), test.configMap, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough, directiveAutoadjustEnabled, makeEventLogger()) + if !configOk { + t.Error("want configOk true, got configOk false") + } + + // Check only the specific fields that are set in the test expectation + if test.want.PKCETimeout != "" { + assert.Equal(t, test.want.PKCETimeout, result.OIDC.PKCETimeout) + } + if test.want.IDTokenTimeout != "" { + assert.Equal(t, test.want.IDTokenTimeout, result.OIDC.IDTokenTimeout) + } + if test.want.AccessTimeout != "" { + assert.Equal(t, test.want.AccessTimeout, result.OIDC.AccessTimeout) + } + if test.want.RefreshTimeout != "" { + assert.Equal(t, test.want.RefreshTimeout, result.OIDC.RefreshTimeout) + } + if test.want.SIDSTimeout != "" { + assert.Equal(t, test.want.SIDSTimeout, result.OIDC.SIDSTimeout) + } + }) + } +} + +func TestParseConfigMapOIDCErrors(t *testing.T) { + t.Parallel() + tests := []struct { + configMap *v1.ConfigMap + expectedErr bool + msg string + }{ + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-pkce-timeout": "invalid-time", + }, + }, + expectedErr: true, + msg: "invalid PKCE timeout format", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-id-tokens-timeout": "abc123", + }, + }, + expectedErr: true, + msg: "invalid ID token timeout format", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-access-tokens-timeout": "5x", + }, + }, + expectedErr: true, + msg: "invalid access token timeout format", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-refresh-tokens-timeout": "", + }, + }, + expectedErr: true, + msg: "empty refresh token timeout", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-sids-timeout": " ", + }, + }, + expectedErr: true, + msg: "whitespace-only SIDS timeout", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-pkce-timeout": "-5m", + }, + }, + expectedErr: true, + msg: "negative PKCE timeout", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-id-tokens-timeout": "1.5h", + }, + }, + expectedErr: true, + msg: "decimal in ID token timeout", + }, + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-access-tokens-timeout": "5minutes", + }, + }, + expectedErr: true, + msg: "invalid time unit format", + }, + + { + configMap: &v1.ConfigMap{ + Data: map[string]string{ + "oidc-sids-timeout": "5s 10m", + }, + }, + expectedErr: true, + msg: "multiple time values without proper format", + }, + } + + nginxPlus := true + hasAppProtect := false + hasAppProtectDos := false + hasTLSPassthrough := false + directiveAutoadjustEnabled := false + + for _, test := range tests { + t.Run(test.msg, func(t *testing.T) { + _, configOk := ParseConfigMap(context.Background(), test.configMap, nginxPlus, hasAppProtect, hasAppProtectDos, hasTLSPassthrough, directiveAutoadjustEnabled, makeEventLogger()) + + if test.expectedErr && configOk { + t.Errorf("want configOk false, got configOk true for %s", test.msg) + } + if !test.expectedErr && !configOk { + t.Errorf("want configOk true, got configOk false for %s", test.msg) + } + }) + } +} + func TestParseMGMTConfigMapError(t *testing.T) { t.Parallel() tests := []struct { diff --git a/internal/configs/oidc/oidc_common.conf b/internal/configs/oidc/oidc_common.conf index 7b14bf961c..208162bb71 100644 --- a/internal/configs/oidc/oidc_common.conf +++ b/internal/configs/oidc/oidc_common.conf @@ -16,12 +16,6 @@ map $http_x_forwarded_proto $proto { # JWK Set will be fetched from $oidc_jwks_uri and cached here - ensure writable by nginx user proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:64k max_size=1m; -# Change timeout values to at least the validity period of each token type -keyval_zone zone=oidc_id_tokens:1M timeout=1h sync; -keyval_zone zone=oidc_access_tokens:1M timeout=1h sync; -keyval_zone zone=refresh_tokens:1M timeout=8h sync; -keyval_zone zone=oidc_sids:1M timeout=8h sync; - keyval $cookie_auth_token $session_jwt zone=oidc_id_tokens; # Exchange cookie for ID token(JWT) keyval $cookie_auth_token $access_token zone=oidc_access_tokens; # Exchange cookie for access token keyval $cookie_auth_token $refresh_token zone=refresh_tokens; # Exchange cookie for refresh token diff --git a/internal/configs/oidc/oidc_pkce_supplements.conf b/internal/configs/oidc/oidc_pkce_supplements.conf deleted file mode 100644 index 668b817f46..0000000000 --- a/internal/configs/oidc/oidc_pkce_supplements.conf +++ /dev/null @@ -1,8 +0,0 @@ -# https://nginx.org/en/docs/http/ngx_http_keyval_module.html -# context: http - -# keyval_zone keyval_zone zone=name:size [state=file] [timeout=time] [type=string|ip|prefix] [sync]; -keyval_zone zone=oidc_pkce:128K timeout=90s sync; # Temporary storage for PKCE code verifier. - -# keyval key $variable zone -keyval $pkce_id $pkce_code_verifier zone=oidc_pkce; diff --git a/internal/configs/version1/__snapshots__/template_test.snap b/internal/configs/version1/__snapshots__/template_test.snap index 20e332277b..c3982d5369 100644 --- a/internal/configs/version1/__snapshots__/template_test.snap +++ b/internal/configs/version1/__snapshots__/template_test.snap @@ -3251,6 +3251,294 @@ mgmt { --- +[TestExecuteTemplate_ForMainForNGINXPlusWithOIDCTimeoutCustom - 1] +worker_processes ; + +daemon off; + +error_log stderr ; +pid /var/lib/nginx/nginx.pid; +load_module modules/ngx_fips_check_module.so; + +load_module modules/ngx_http_js_module.so; + +events { + worker_connections ; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + map_hash_max_size ; + map_hash_bucket_size ; + + js_import /etc/nginx/njs/apikey_auth.js; + js_set $apikey_auth_hash apikey_auth.hash; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + map $upstream_trailer_grpc_status $grpc_status { + default $upstream_trailer_grpc_status; + '' $sent_http_grpc_status; + } + + access_log ; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout ; + keepalive_requests 0; + + #gzip on; + + server_names_hash_max_size ; + + + variables_hash_bucket_size 0; + variables_hash_max_size 0; + + map $request_uri $request_uri_no_args { + "~^(?P[^?]*)(\?.*)?$" $path; + } + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + map $http_upgrade $vs_connection_header { + default upgrade; + '' $default_connection_header; + } + + + + keyval_zone zone=oidc_pkce:128K timeout=2m sync; + keyval_zone zone=oidc_id_tokens:1M timeout=2h sync; + keyval_zone zone=oidc_access_tokens:1M timeout=30m sync; + keyval_zone zone=refresh_tokens:1M timeout=1h sync; + keyval_zone zone=oidc_sids:1M timeout=120s sync; + include oidc/oidc_common.conf; + + server { + # required to support the Websocket protocol in VirtualServer/VirtualServerRoutes + set $default_connection_header ""; + set $resource_type ""; + set $resource_name ""; + set $resource_namespace ""; + set $service ""; + + listen 0 default_server;listen [::]:0 default_server; + listen 0 ssl default_server; + listen [::]:0 ssl default_server; + ssl_certificate /etc/nginx/secrets/default; + ssl_certificate_key /etc/nginx/secrets/default; + + server_name _; + server_tokens ""; + + location / { + return ; + } + } + + # NGINX Plus API over unix socket + server { + listen unix:/var/lib/nginx/nginx-plus-api.sock; + access_log off; + + # $config_version_mismatch is defined in /etc/nginx/config-version.conf + location /configVersionCheck { + if ($config_version_mismatch) { + return 503; + } + return 200; + } + + location /api { + api write=on; + } + } + + include /etc/nginx/config-version.conf; + include /etc/nginx/conf.d/*.conf; + + server { + listen unix:/var/lib/nginx/nginx-418-server.sock; + access_log off; + + return 418; + } +} + +stream { + log_format stream-main '$remote_addr [$time_local] ' + '$protocol $status $bytes_sent $bytes_received ' + '$session_time "$ssl_preread_server_name"'; + + access_log /dev/stdout stream-main; + + + + map_hash_max_size ; + + include /etc/nginx/stream-conf.d/*.conf; +} + +mgmt { + license_token /license.jwt; + enforce_initial_report off; + deployment_context /etc/nginx/reporting/tracking.info; +} + +--- + +[TestExecuteTemplate_ForMainForNGINXPlusWithOIDCTimeoutDefault - 1] +worker_processes ; + +daemon off; + +error_log stderr ; +pid /var/lib/nginx/nginx.pid; +load_module modules/ngx_fips_check_module.so; + +load_module modules/ngx_http_js_module.so; + +events { + worker_connections ; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + map_hash_max_size ; + map_hash_bucket_size ; + + js_import /etc/nginx/njs/apikey_auth.js; + js_set $apikey_auth_hash apikey_auth.hash; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + map $upstream_trailer_grpc_status $grpc_status { + default $upstream_trailer_grpc_status; + '' $sent_http_grpc_status; + } + + access_log ; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout ; + keepalive_requests 0; + + #gzip on; + + server_names_hash_max_size ; + + + variables_hash_bucket_size 0; + variables_hash_max_size 0; + + map $request_uri $request_uri_no_args { + "~^(?P[^?]*)(\?.*)?$" $path; + } + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + map $http_upgrade $vs_connection_header { + default upgrade; + '' $default_connection_header; + } + + + + keyval_zone zone=oidc_pkce:128K timeout=90s sync; + keyval_zone zone=oidc_id_tokens:1M timeout=1h sync; + keyval_zone zone=oidc_access_tokens:1M timeout=1h sync; + keyval_zone zone=refresh_tokens:1M timeout=8h sync; + keyval_zone zone=oidc_sids:1M timeout=8h sync; + include oidc/oidc_common.conf; + + server { + # required to support the Websocket protocol in VirtualServer/VirtualServerRoutes + set $default_connection_header ""; + set $resource_type ""; + set $resource_name ""; + set $resource_namespace ""; + set $service ""; + + listen 0 default_server;listen [::]:0 default_server; + listen 0 ssl default_server; + listen [::]:0 ssl default_server; + ssl_certificate /etc/nginx/secrets/default; + ssl_certificate_key /etc/nginx/secrets/default; + + server_name _; + server_tokens ""; + + location / { + return ; + } + } + + # NGINX Plus API over unix socket + server { + listen unix:/var/lib/nginx/nginx-plus-api.sock; + access_log off; + + # $config_version_mismatch is defined in /etc/nginx/config-version.conf + location /configVersionCheck { + if ($config_version_mismatch) { + return 503; + } + return 200; + } + + location /api { + api write=on; + } + } + + include /etc/nginx/config-version.conf; + include /etc/nginx/conf.d/*.conf; + + server { + listen unix:/var/lib/nginx/nginx-418-server.sock; + access_log off; + + return 418; + } +} + +stream { + log_format stream-main '$remote_addr [$time_local] ' + '$protocol $status $bytes_sent $bytes_received ' + '$session_time "$ssl_preread_server_name"'; + + access_log /dev/stdout stream-main; + + + + map_hash_max_size ; + + include /etc/nginx/stream-conf.d/*.conf; +} + +mgmt { + license_token /license.jwt; + enforce_initial_report off; + deployment_context /etc/nginx/reporting/tracking.info; +} + +--- + [TestExecuteTemplate_ForMainForNGINXPlusWithoutCustomDefaultHTTPAndHTTPSListenerPorts - 1] worker_processes auto; worker_rlimit_nofile 65536; diff --git a/internal/configs/version1/config.go b/internal/configs/version1/config.go index d492697df6..30cea86864 100644 --- a/internal/configs/version1/config.go +++ b/internal/configs/version1/config.go @@ -204,6 +204,16 @@ type ZoneSyncConfig struct { ResolverIPV6 *bool } +// OIDCConfig allows to configure OIDC parameters. +type OIDCConfig struct { + Enable bool + PKCETimeout string + IDTokenTimeout string + AccessTimeout string + RefreshTimeout string + SIDSTimeout string +} + // MGMTConfig is tbe configuration for the MGMT block. type MGMTConfig struct { SSLVerify *bool @@ -297,7 +307,7 @@ type MainConfig struct { InternalRouteServerName string LatencyMetrics bool ZoneSyncConfig ZoneSyncConfig - OIDC bool + OIDC OIDCConfig DynamicSSLReloadEnabled bool StaticSSLPath string NginxVersion nginx.Version diff --git a/internal/configs/version1/nginx-plus.tmpl b/internal/configs/version1/nginx-plus.tmpl index 8ce5b4f000..79bb9b0158 100644 --- a/internal/configs/version1/nginx-plus.tmpl +++ b/internal/configs/version1/nginx-plus.tmpl @@ -162,7 +162,12 @@ http { {{ makeResolver .ResolverAddresses .ResolverValid $resolverIPV6HTTPBool }} {{if .ResolverTimeout}}resolver_timeout {{.ResolverTimeout}};{{end}} - {{- if .OIDC}} + {{- if .OIDC.Enable}} + keyval_zone zone=oidc_pkce:128K timeout={{.OIDC.PKCETimeout}} sync; + keyval_zone zone=oidc_id_tokens:1M timeout={{.OIDC.IDTokenTimeout}} sync; + keyval_zone zone=oidc_access_tokens:1M timeout={{.OIDC.AccessTimeout}} sync; + keyval_zone zone=refresh_tokens:1M timeout={{.OIDC.RefreshTimeout}} sync; + keyval_zone zone=oidc_sids:1M timeout={{.OIDC.SIDSTimeout}} sync; include oidc/oidc_common.conf; {{- end}} diff --git a/internal/configs/version1/template_test.go b/internal/configs/version1/template_test.go index 4a4ed277f0..efc8f015c8 100644 --- a/internal/configs/version1/template_test.go +++ b/internal/configs/version1/template_test.go @@ -790,6 +790,68 @@ func TestExecuteTemplate_ForMainForNGINXPlusWithCustomDefaultHTTPSListenerPort(t snaps.MatchSnapshot(t, buf.String()) } +func TestExecuteTemplate_ForMainForNGINXPlusWithOIDCTimeoutDefault(t *testing.T) { + t.Parallel() + + tmpl := newNGINXPlusMainTmpl(t) + buf := &bytes.Buffer{} + + err := tmpl.Execute(buf, mainCfgWithOIDCTimeoutDefault) + if err != nil { + t.Fatalf("Failed to write template %v", err) + } + + mainConf := buf.String() + t.Log(mainConf) + + expectedDirectives := []string{ + "keyval_zone zone=oidc_pkce:128K timeout=90s sync;", + "keyval_zone zone=oidc_id_tokens:1M timeout=1h sync;", + "keyval_zone zone=oidc_access_tokens:1M timeout=1h sync;", + "keyval_zone zone=refresh_tokens:1M timeout=8h sync;", + "keyval_zone zone=oidc_sids:1M timeout=8h sync;", + "include oidc/oidc_common.conf;", + } + + for _, directive := range expectedDirectives { + if !strings.Contains(mainConf, directive) { + t.Errorf("oidc timeout directive not found: %s", directive) + } + } + snaps.MatchSnapshot(t, buf.String()) +} + +func TestExecuteTemplate_ForMainForNGINXPlusWithOIDCTimeoutCustom(t *testing.T) { + t.Parallel() + + tmpl := newNGINXPlusMainTmpl(t) + buf := &bytes.Buffer{} + + err := tmpl.Execute(buf, mainCfgWithOIDCTimeoutCustom) + if err != nil { + t.Fatalf("Failed to write template %v", err) + } + + mainConf := buf.String() + t.Log(mainConf) + + expectedDirectives := []string{ + "keyval_zone zone=oidc_pkce:128K timeout=2m sync;", + "keyval_zone zone=oidc_id_tokens:1M timeout=2h sync;", + "keyval_zone zone=oidc_access_tokens:1M timeout=30m sync;", + "keyval_zone zone=refresh_tokens:1M timeout=1h sync;", + "keyval_zone zone=oidc_sids:1M timeout=120s sync;", + "include oidc/oidc_common.conf;", + } + + for _, directive := range expectedDirectives { + if !strings.Contains(mainConf, directive) { + t.Errorf("oidc timeout directive not found: %s", directive) + } + } + snaps.MatchSnapshot(t, buf.String()) +} + func TestExecuteTemplate_ForMainForNGINXWithHTTP2On(t *testing.T) { t.Parallel() @@ -2628,6 +2690,28 @@ var ( MainOtelServiceName: "nginx-ingress-controller:nginx", } + mainCfgWithOIDCTimeoutDefault = MainConfig{ + OIDC: OIDCConfig{ + Enable: true, + PKCETimeout: "90s", + IDTokenTimeout: "1h", + AccessTimeout: "1h", + RefreshTimeout: "8h", + SIDSTimeout: "8h", + }, + } + + mainCfgWithOIDCTimeoutCustom = MainConfig{ + OIDC: OIDCConfig{ + Enable: true, + PKCETimeout: "2m", + IDTokenTimeout: "2h", + AccessTimeout: "30m", + RefreshTimeout: "1h", + SIDSTimeout: "120s", + }, + } + // Vars for Mergable Ingress Master - Minion tests coffeeUpstreamNginxPlus = Upstream{ diff --git a/internal/configs/version2/__snapshots__/templates_test.snap b/internal/configs/version2/__snapshots__/templates_test.snap index 77f4e238f3..50f2fe69b4 100644 --- a/internal/configs/version2/__snapshots__/templates_test.snap +++ b/internal/configs/version2/__snapshots__/templates_test.snap @@ -1471,7 +1471,7 @@ server { [TestExecuteVirtualServerTemplateWithNGINXDebugLevelDebug - 1] keyval $idp_sid $client_sid zone=oidc_sids; -include oidc/oidc_pkce_supplements.conf; +keyval $pkce_id $pkce_code_verifier zone=oidc_pkce; server { listen 80 proxy_protocol; @@ -1521,7 +1521,7 @@ server { [TestExecuteVirtualServerTemplateWithNGINXDebugLevelNotDebug - 1] keyval $idp_sid $client_sid zone=oidc_sids; -include oidc/oidc_pkce_supplements.conf; +keyval $pkce_id $pkce_code_verifier zone=oidc_pkce; server { listen 80 proxy_protocol; @@ -1570,7 +1570,7 @@ server { [TestExecuteVirtualServerTemplateWithOIDCAndPKCEPolicyNGINXPlus - 1] keyval $idp_sid $client_sid zone=oidc_sids; -include oidc/oidc_pkce_supplements.conf; +keyval $pkce_id $pkce_code_verifier zone=oidc_pkce; server { listen 80 proxy_protocol; diff --git a/internal/configs/version2/nginx-plus.virtualserver.tmpl b/internal/configs/version2/nginx-plus.virtualserver.tmpl index 91b652c4d9..4886cc2463 100644 --- a/internal/configs/version2/nginx-plus.virtualserver.tmpl +++ b/internal/configs/version2/nginx-plus.virtualserver.tmpl @@ -118,7 +118,7 @@ map $request_method $cache_purge_{{ replaceAll $l.Cache.ZoneName "-" "_" }} { {{- if $s.OIDC }} keyval $idp_sid $client_sid zone=oidc_sids; {{- if $s.OIDC.PKCEEnable }} -include oidc/oidc_pkce_supplements.conf; +keyval $pkce_id $pkce_code_verifier zone=oidc_pkce; {{- end }} {{- end }} diff --git a/internal/configs/version2/templates_test.go b/internal/configs/version2/templates_test.go index 3f565a9e04..7b5253f92a 100644 --- a/internal/configs/version2/templates_test.go +++ b/internal/configs/version2/templates_test.go @@ -848,7 +848,7 @@ func TestExecuteVirtualServerTemplateWithOIDCAndPKCEPolicyNGINXPlus(t *testing.T t.Error(err) } - want := "include oidc/oidc_pkce_supplements.conf" + want := "keyval $pkce_id $pkce_code_verifier zone=oidc_pkce;" want2 := "include oidc/oidc.conf;" if !bytes.Contains(got, []byte(want)) {