diff --git a/internal/ingress/controller/template/crossplane/config.go b/internal/ingress/controller/template/crossplane/config.go index 2dc1211c9..322a7e8ad 100644 --- a/internal/ingress/controller/template/crossplane/config.go +++ b/internal/ingress/controller/template/crossplane/config.go @@ -20,7 +20,7 @@ import ( ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" ) -func (c *CrossplaneTemplate) buildConfig() { +func (c *Template) buildConfig() { // Write basic directives config := &ngx_crossplane.Config{ Parsed: ngx_crossplane.Directives{ diff --git a/internal/ingress/controller/template/crossplane/crossplane.go b/internal/ingress/controller/template/crossplane/crossplane.go index f3636db13..1307de2f7 100644 --- a/internal/ingress/controller/template/crossplane/crossplane.go +++ b/internal/ingress/controller/template/crossplane/crossplane.go @@ -34,16 +34,16 @@ Unsupported directives: // On this case we will try to use the go ngx_crossplane to write the template instead of the template renderer -type CrossplaneTemplate struct { +type Template struct { options *ngx_crossplane.BuildOptions config *ngx_crossplane.Config tplConfig *config.TemplateConfig mimeFile string } -func NewCrossplaneTemplate() *CrossplaneTemplate { +func NewTemplate() *Template { lua := ngx_crossplane.Lua{} - return &CrossplaneTemplate{ + return &Template{ mimeFile: "/etc/nginx/mime.types", options: &ngx_crossplane.BuildOptions{ Builders: []ngx_crossplane.RegisterBuilder{ @@ -53,11 +53,11 @@ func NewCrossplaneTemplate() *CrossplaneTemplate { } } -func (c *CrossplaneTemplate) SetMimeFile(file string) { +func (c *Template) SetMimeFile(file string) { c.mimeFile = file } -func (c *CrossplaneTemplate) Write(conf *config.TemplateConfig) ([]byte, error) { +func (c *Template) Write(conf *config.TemplateConfig) ([]byte, error) { c.tplConfig = conf // build root directives diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_test.go index 6479b197e..b8bc3258d 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_internal_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_test.go @@ -48,7 +48,7 @@ func Test_Internal_buildEvents(t *testing.T) { }, } - cplane := NewCrossplaneTemplate() + cplane := NewTemplate() cplane.config = &c cplane.tplConfig = tplConfig cplane.buildEvents() @@ -81,7 +81,7 @@ func Test_Internal_buildEvents(t *testing.T) { }, } - cplane := NewCrossplaneTemplate() + cplane := NewTemplate() cplane.config = &c cplane.tplConfig = tplConfig cplane.buildEvents() diff --git a/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go index e9ac96691..f31701781 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_internal_utils_test.go @@ -19,6 +19,7 @@ package crossplane import ( "testing" + ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" "github.com/stretchr/testify/require" "k8s.io/ingress-nginx/internal/ingress/controller/config" ) @@ -36,6 +37,16 @@ func Test_Internal_buildDirectives(t *testing.T) { }) } +func Test_Internal_buildMapDirectives(t *testing.T) { + t.Run("should be able to run build a map directive with empty block", func(t *testing.T) { + directive := buildMapDirective("somedirective", "bla", ngx_crossplane.Directives{buildDirective("something", "otherstuff")}) + require.Equal(t, directive.Directive, "map") + require.Equal(t, directive.Args, []string{"somedirective", "bla"}) + require.Equal(t, directive.Block[0].Directive, "something") + require.Equal(t, directive.Block[0].Args, []string{"otherstuff"}) + }) +} + func Test_Internal_boolToStr(t *testing.T) { require.Equal(t, boolToStr(true), "on") require.Equal(t, boolToStr(false), "off") diff --git a/internal/ingress/controller/template/crossplane/crossplane_test.go b/internal/ingress/controller/template/crossplane/crossplane_test.go index ccb534b7d..0e88ad4b1 100644 --- a/internal/ingress/controller/template/crossplane/crossplane_test.go +++ b/internal/ingress/controller/template/crossplane/crossplane_test.go @@ -36,7 +36,7 @@ types { } ` -// TestCrossplaneTemplate should be a roundtrip test. +// TestTemplate should be a roundtrip test. // We should initialize the scenarios based on the template configuration // Then Parse and write a crossplane configuration, and roundtrip/parse back to check // if the directives matches @@ -69,7 +69,7 @@ func TestCrossplaneTemplate(t *testing.T) { } tplConfig.Cfg.DefaultSSLCertificate = defaultCertificate - tpl := crossplane.NewCrossplaneTemplate() + tpl := crossplane.NewTemplate() tpl.SetMimeFile(mimeFile.Name()) content, err := tpl.Write(tplConfig) require.NoError(t, err) diff --git a/internal/ingress/controller/template/crossplane/events.go b/internal/ingress/controller/template/crossplane/events.go index 877d44e01..fa0c599e5 100644 --- a/internal/ingress/controller/template/crossplane/events.go +++ b/internal/ingress/controller/template/crossplane/events.go @@ -20,7 +20,7 @@ import ( ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" ) -func (c *CrossplaneTemplate) buildEvents() { +func (c *Template) buildEvents() { events := &ngx_crossplane.Directive{ Directive: "events", Block: ngx_crossplane.Directives{ diff --git a/internal/ingress/controller/template/crossplane/http.go b/internal/ingress/controller/template/crossplane/http.go index b0707b9a3..8a9e61b8b 100644 --- a/internal/ingress/controller/template/crossplane/http.go +++ b/internal/ingress/controller/template/crossplane/http.go @@ -24,7 +24,7 @@ import ( ngx_crossplane "github.com/nginxinc/nginx-go-crossplane" ) -func (c *CrossplaneTemplate) initHTTPDirectives() ngx_crossplane.Directives { +func (c *Template) initHTTPDirectives() ngx_crossplane.Directives { cfg := c.tplConfig.Cfg httpBlock := ngx_crossplane.Directives{ buildDirective("lua_package_path", "/etc/nginx/lua/?.lua;;"), @@ -81,7 +81,7 @@ func (c *CrossplaneTemplate) initHTTPDirectives() ngx_crossplane.Directives { return httpBlock } -func (c *CrossplaneTemplate) buildHTTP() { +func (c *Template) buildHTTP() { cfg := c.tplConfig.Cfg httpBlock := c.initHTTPDirectives() httpBlock = append(httpBlock, buildLuaSharedDictionaries(&c.tplConfig.Cfg)...) @@ -147,18 +147,12 @@ func (c *CrossplaneTemplate) buildHTTP() { httpBlock = append(httpBlock, buildDirective("log_format", "upstreaminfo", escape, cfg.LogFormatUpstream)) - // buildMap directive - mapLogDirective := &ngx_crossplane.Directive{ - Directive: "map", - Args: []string{"$request_uri", "$loggable"}, - Block: make(ngx_crossplane.Directives, 0), - } + loggableMap := make(ngx_crossplane.Directives, 0) for k := range cfg.SkipAccessLogURLs { - mapLogDirective.Block = append(mapLogDirective.Block, buildDirective(cfg.SkipAccessLogURLs[k], "0")) + loggableMap = append(loggableMap, buildDirective(cfg.SkipAccessLogURLs[k], "0")) } - mapLogDirective.Block = append(mapLogDirective.Block, buildDirective("default", "1")) - httpBlock = append(httpBlock, mapLogDirective) - // end of build mapLog + loggableMap = append(loggableMap, buildDirective("default", "1")) + httpBlock = append(httpBlock, buildMapDirective("$request_uri", "$loggable", loggableMap)) if cfg.DisableAccessLog || cfg.DisableHTTPAccessLog { httpBlock = append(httpBlock, buildDirective("access_log", "off")) @@ -181,6 +175,60 @@ func (c *CrossplaneTemplate) buildHTTP() { httpBlock = append(httpBlock, buildDirective("error_log", cfg.ErrorLogPath, cfg.ErrorLogLevel)) } + if cfg.SSLSessionCache { + httpBlock = append(httpBlock, + buildDirective("ssl_session_cache", fmt.Sprintf("shared:SSL:%s", cfg.SSLSessionCacheSize)), + buildDirective("ssl_session_timeout", cfg.SSLSessionTimeout), + ) + } + + if cfg.SSLSessionTicketKey != "" { + httpBlock = append(httpBlock, buildDirective("ssl_session_ticket_key", "/etc/ingress-controller/tickets.key")) + } + + if cfg.SSLCiphers != "" { + httpBlock = append(httpBlock, + buildDirective("ssl_ciphers", cfg.SSLCiphers), + buildDirective("ssl_prefer_server_ciphers", "on"), + ) + } + + if cfg.SSLDHParam != "" { + httpBlock = append(httpBlock, buildDirective("ssl_dhparam", cfg.SSLDHParam)) + } + + if len(cfg.CustomHTTPErrors) > 0 && !cfg.DisableProxyInterceptErrors { + httpBlock = append(httpBlock, buildDirective("proxy_intercept_errors", "on")) + } + + httpUpgradeMap := ngx_crossplane.Directives{buildDirective("default", "upgrade")} + if cfg.UpstreamKeepaliveConnections < 1 { + httpUpgradeMap = append(httpUpgradeMap, buildDirective("", "close")) + } + httpBlock = append(httpBlock, buildMapDirective("$http_upgrade", "$connection_upgrade", httpUpgradeMap)) + + reqIDMap := ngx_crossplane.Directives{buildDirective("default", "$http_x_request_id")} + if cfg.GenerateRequestID { + reqIDMap = append(reqIDMap, buildDirective("", "$request_id")) + } + httpBlock = append(httpBlock, buildMapDirective("$http_x_request_id", "$req_id", reqIDMap)) + + if cfg.UseForwardedHeaders && cfg.ComputeFullForwardedFor { + forwardForMap := make(ngx_crossplane.Directives, 0) + if cfg.UseProxyProtocol { + forwardForMap = append(forwardForMap, + buildDirective("default", "$http_x_forwarded_for, $proxy_protocol_addr"), + buildDirective("", "$http_x_forwarded_for, $proxy_protocol_addr"), + ) + } else { + forwardForMap = append(forwardForMap, + buildDirective("default", "$http_x_forwarded_for, $realip_remote_addr"), + buildDirective("", "$realip_remote_addr"), + ) + } + httpBlock = append(httpBlock, buildMapDirective("$http_x_forwarded_for", "$full_x_forwarded_for", forwardForMap)) + } + c.config.Parsed = append(c.config.Parsed, &ngx_crossplane.Directive{ Directive: "http", Block: httpBlock, diff --git a/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl b/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl index 102d9776f..d4a256ec8 100644 --- a/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl +++ b/internal/ingress/controller/template/crossplane/testdata/nginx.tmpl @@ -10,6 +10,7 @@ # MIGRATED pid {{ .PID }}; +# MODULES ARE NOT MIGRATED YET! {{ if $cfg.EnableBrotli }} load_module /etc/nginx/modules/ngx_http_brotli_filter_module.so; load_module /etc/nginx/modules/ngx_http_brotli_static_module.so; @@ -23,10 +24,9 @@ load_module /etc/nginx/modules/ngx_http_auth_digest_module.so; load_module /etc/nginx/modules/otel_ngx_module.so; {{ end }} -# MIGRATED +# MIGRATED 1 daemon off; -# MIGRATED 1 worker_processes {{ $cfg.WorkerProcesses }}; {{ if gt (len $cfg.WorkerCPUAffinity) 0 }} worker_cpu_affinity {{ $cfg.WorkerCPUAffinity }}; @@ -38,9 +38,6 @@ worker_rlimit_nofile {{ $cfg.MaxWorkerOpenFiles }}; {{/* avoid waiting too long during a reload */}} worker_shutdown_timeout {{ $cfg.WorkerShutdownTimeout }} ; -# END MIGRATED 1 - -# MIGRATED EVENTS events { multi_accept {{ if $cfg.EnableMultiAccept }}on{{ else }}off{{ end }}; worker_connections {{ $cfg.MaxWorkerConnections }}; @@ -49,7 +46,8 @@ events { debug_connection {{ $v }}; {{ end }} } -# END MIGRATED EVENTS + +# END MIGRATED 1 http { {{ if (shouldLoadOpentelemetryModule $cfg $servers) }} @@ -129,7 +127,7 @@ http { plugins.run() } - # MIGRATED REALIP + # MIGRATED VARIOUS 1 {{/* Enable the real_ip module only if we use either X-Forwarded headers or Proxy Protocol. */}} {{/* we use the value of the real IP for the geo_ip module */}} {{ if or (or $cfg.UseForwardedHeaders $cfg.UseProxyProtocol) $cfg.EnableRealIP }} @@ -144,9 +142,7 @@ http { set_real_ip_from {{ $trusted_ip }}; {{ end }} {{ end }} - # END MIGRATED REAL IP - # MIGRATED VARIOUS 1 aio threads; {{ if $cfg.EnableAioWrite }} @@ -287,17 +283,6 @@ http { proxy_ssl_session_reuse on; - # END MIGRATED VARIOUS 1 - - {{ buildOpentelemetry $cfg $servers }} - - {{ if $cfg.EnableBrotli }} - brotli on; - brotli_comp_level {{ $cfg.BrotliLevel }}; - brotli_min_length {{ $cfg.BrotliMinLength }}; - brotli_types {{ $cfg.BrotliTypes }}; - {{ end }} - # See https://www.nginx.com/blog/websocket-nginx map $http_upgrade $connection_upgrade { default upgrade; @@ -318,6 +303,9 @@ http { {{ end }} } + # Cache for internal auth checks + proxy_cache_path /tmp/nginx/nginx-cache-auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=30m use_temp_path=off; + {{ if and $cfg.UseForwardedHeaders $cfg.ComputeFullForwardedFor }} # We can't use $proxy_add_x_forwarded_for because the realip module # replaces the remote_addr too soon @@ -333,13 +321,7 @@ http { {{ end }} - # Create a variable that contains the literal $ character. - # This works because the geo module will not resolve variables. - geo $literal_dollar { - default "$"; - } - - # turn on session caching to drastically improve performance + # turn on session caching to drastically improve performance {{ if $cfg.SSLSessionCache }} ssl_session_cache shared:SSL:{{ $cfg.SSLSessionCacheSize }}; ssl_session_timeout {{ $cfg.SSLSessionTimeout }}; @@ -364,6 +346,23 @@ http { proxy_intercept_errors on; {{ end }} + # END MIGRATED VARIOUS 1 + + {{ buildOpentelemetry $cfg $servers }} + + {{ if $cfg.EnableBrotli }} + brotli on; + brotli_comp_level {{ $cfg.BrotliLevel }}; + brotli_min_length {{ $cfg.BrotliMinLength }}; + brotli_types {{ $cfg.BrotliTypes }}; + {{ end }} + + # Create a variable that contains the literal $ character. + # This works because the geo module will not resolve variables. + geo $literal_dollar { + default "$"; + } + {{ range $errCode := $cfg.CustomHTTPErrors }} error_page {{ $errCode }} = @custom_upstream-default-backend_{{ $errCode }};{{ end }} @@ -410,9 +409,6 @@ http { {{ $zone }} {{ end }} - # Cache for internal auth checks - proxy_cache_path /tmp/nginx/nginx-cache-auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=30m use_temp_path=off; - # Global filters {{ range $ip := $cfg.BlockCIDRs }}deny {{ trimSpace $ip }}; {{ end }} diff --git a/internal/ingress/controller/template/crossplane/utils.go b/internal/ingress/controller/template/crossplane/utils.go index 8c07b61c2..437a59ef5 100644 --- a/internal/ingress/controller/template/crossplane/utils.go +++ b/internal/ingress/controller/template/crossplane/utils.go @@ -85,6 +85,15 @@ func buildResolversInternal(res []net.IP, disableIpv6 bool) []string { return r } +// buildMapDirective is used to build a map directive +func buildMapDirective(name, variable string, block ngx_crossplane.Directives) *ngx_crossplane.Directive { + return &ngx_crossplane.Directive{ + Directive: "map", + Args: []string{name, variable}, + Block: block, + } +} + func boolToStr(b bool) string { if b { return "on"