diff --git a/controllers/nginx/pkg/cmd/controller/nginx.go b/controllers/nginx/pkg/cmd/controller/nginx.go index e299b0bb39..1e415544e3 100644 --- a/controllers/nginx/pkg/cmd/controller/nginx.go +++ b/controllers/nginx/pkg/cmd/controller/nginx.go @@ -87,47 +87,8 @@ func newNGINXController() ingress.Controller { configmap: &api_v1.ConfigMap{}, isIPV6Enabled: isIPv6Enabled(), resolver: h, - proxy: &proxy{ - Default: &server{ - Hostname: "localhost", - IP: "127.0.0.1", - Port: 442, - ProxyProtocol: true, - }, - }, } - listener, err := net.Listen("tcp", ":443") - if err != nil { - glog.Fatalf("%v", err) - } - - proxyList := &proxyproto.Listener{Listener: listener} - - // start goroutine that accepts tcp connections in port 443 - go func() { - for { - var conn net.Conn - var err error - - if n.isProxyProtocolEnabled { - // we need to wrap the listener in order to decode - // proxy protocol before handling the connection - conn, err = proxyList.Accept() - } else { - conn, err = listener.Accept() - } - - if err != nil { - glog.Warningf("unexpected error accepting tcp connection: %v", err) - continue - } - - glog.V(3).Infof("remote address %s to local %s", conn.RemoteAddr(), conn.LocalAddr()) - go n.proxy.Handle(conn) - } - }() - fcgiListener, err := net.Listen("unix", fastCGISocket) if err != nil { glog.Fatalf("%v", err) @@ -197,6 +158,8 @@ type NGINXController struct { // returns true if proxy protocol es enabled isProxyProtocolEnabled bool + isSSLPassthroughEnabled bool + proxy *proxy } @@ -320,6 +283,7 @@ func (n NGINXController) Info() *ingress.BackendInfo { // ConfigureFlags allow to configure more flags before the parsing of // command line arguments func (n *NGINXController) ConfigureFlags(flags *pflag.FlagSet) { + flags.BoolVar(&n.isSSLPassthroughEnabled, "enable-ssl-passthrough", false, `Enable SSL passthrough feature. Default is disabled`) } // OverrideFlags customize NGINX controller flags @@ -337,6 +301,49 @@ func (n *NGINXController) OverrideFlags(flags *pflag.FlagSet) { flags.Set("ingress-class", ic) n.stats = newStatsCollector(wc, ic, n.binary) + + if n.isSSLPassthroughEnabled { + glog.Info("starting TLS proxy for SSL passthrough") + n.proxy = &proxy{ + Default: &server{ + Hostname: "localhost", + IP: "127.0.0.1", + Port: 442, + ProxyProtocol: true, + }, + } + + listener, err := net.Listen("tcp", ":443") + if err != nil { + glog.Fatalf("%v", err) + } + + proxyList := &proxyproto.Listener{Listener: listener} + + // start goroutine that accepts tcp connections in port 443 + go func() { + for { + var conn net.Conn + var err error + + if n.isProxyProtocolEnabled { + // we need to wrap the listener in order to decode + // proxy protocol before handling the connection + conn, err = proxyList.Accept() + } else { + conn, err = listener.Accept() + } + + if err != nil { + glog.Warningf("unexpected error accepting tcp connection: %v", err) + continue + } + + glog.V(3).Infof("remote address %s to local %s", conn.RemoteAddr(), conn.LocalAddr()) + go n.proxy.Handle(conn) + } + }() + } } // DefaultIngressClass just return the default ingress class @@ -449,7 +456,9 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error { }) } - n.proxy.ServerList = servers + if n.isSSLPassthroughEnabled { + n.proxy.ServerList = servers + } // we need to check if the status module configuration changed if cfg.EnableVtsStatus { @@ -570,20 +579,21 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) error { cfg.SSLDHParam = sslDHParam tc := config.TemplateConfig{ - ProxySetHeaders: setHeaders, - AddHeaders: addHeaders, - MaxOpenFiles: maxOpenFiles, - BacklogSize: sysctlSomaxconn(), - Backends: ingressCfg.Backends, - PassthroughBackends: ingressCfg.PassthroughBackends, - Servers: ingressCfg.Servers, - TCPBackends: ingressCfg.TCPEndpoints, - UDPBackends: ingressCfg.UDPEndpoints, - HealthzURI: ngxHealthPath, - CustomErrors: len(cfg.CustomHTTPErrors) > 0, - Cfg: cfg, - IsIPV6Enabled: n.isIPV6Enabled && !cfg.DisableIpv6, - RedirectServers: redirectServers, + ProxySetHeaders: setHeaders, + AddHeaders: addHeaders, + MaxOpenFiles: maxOpenFiles, + BacklogSize: sysctlSomaxconn(), + Backends: ingressCfg.Backends, + PassthroughBackends: ingressCfg.PassthroughBackends, + Servers: ingressCfg.Servers, + TCPBackends: ingressCfg.TCPEndpoints, + UDPBackends: ingressCfg.UDPEndpoints, + HealthzURI: ngxHealthPath, + CustomErrors: len(cfg.CustomHTTPErrors) > 0, + Cfg: cfg, + IsIPV6Enabled: n.isIPV6Enabled && !cfg.DisableIpv6, + RedirectServers: redirectServers, + IsSSLPassthroughEnabled: n.isSSLPassthroughEnabled, } // We need to extract the endpoints to be used in the fastcgi error handler diff --git a/controllers/nginx/pkg/config/config.go b/controllers/nginx/pkg/config/config.go index 6154b4aa1c..8a8247e8a4 100644 --- a/controllers/nginx/pkg/config/config.go +++ b/controllers/nginx/pkg/config/config.go @@ -428,5 +428,6 @@ type TemplateConfig struct { CustomErrors bool Cfg Configuration IsIPV6Enabled bool + IsSSLPassthroughEnabled bool RedirectServers map[string]string } diff --git a/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl b/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl index 65c61c7e70..4fe9a91cea 100644 --- a/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl +++ b/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl @@ -157,11 +157,18 @@ http { } {{ end }} + {{ if $all.IsSSLPassthroughEnabled }} # map port 442 to 443 for header X-Forwarded-Port map $pass_server_port $pass_port { 442 443; default $pass_server_port; } + {{ else }} + map $pass_server_port $pass_port { + 443 443; + default $pass_server_port; + } + {{ end }} # Map a response error watching the header Content-Type map $http_accept $httpAccept { @@ -294,11 +301,11 @@ http { {{ range $hostname, $to := .RedirectServers }} server { listen 80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}; - listen 442{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }} ssl; + listen {{ if $all.IsSSLPassthroughEnabled }}442 proxy_protocol{{ else }}443{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} ssl; {{ if $IsIPV6Enabled }} listen [::]:80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}; - listen [::]:442{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}; - {{ end }} + listen {{ if $all.IsSSLPassthroughEnabled }}[::]:442 proxy_protocol{{ else }}[::]:443{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }}; + {{ end }} server_name {{ $hostname }}; return 301 $scheme://{{ $to }}$request_uri; } @@ -407,7 +414,7 @@ stream { server { listen {{ $tcpServer.Port }}{{ if $tcpServer.Backend.UseProxyProtocol }} proxy_protocol{{ end }}; {{ if $IsIPV6Enabled }}listen [::]:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.UseProxyProtocol }} proxy_protocol{{ end }};{{ end }} - proxy_timeout {{ $cfg.ProxyStreamTimeout }}; + proxy_timeout {{ $cfg.ProxyStreamTimeout }}; proxy_pass tcp-{{ $tcpServer.Port }}-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }}; } @@ -425,7 +432,7 @@ stream { listen {{ $udpServer.Port }} udp; {{ if $IsIPV6Enabled }}listen [::]:{{ $udpServer.Port }} udp;{{ end }} proxy_responses 1; - proxy_timeout {{ $cfg.ProxyStreamTimeout }}; + proxy_timeout {{ $cfg.ProxyStreamTimeout }}; proxy_pass udp-{{ $udpServer.Port }}-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }}; } {{ end }} @@ -499,9 +506,8 @@ stream { {{/* Listen on 442 because port 443 is used in the TLS sni server */}} {{/* This listener must always have proxy_protocol enabled, because the SNI listener forwards on source IP info in it. */}} - {{ if not (empty $server.SSLCertificate) }}listen 442 proxy_protocol{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }}; - {{ if $all.IsIPV6Enabled }}{{ if not (empty $server.SSLCertificate) }}listen [::]:442 proxy_protocol{{ end }} {{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};{{ end }} - {{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}} + {{ if not (empty $server.SSLCertificate) }}listen {{ if $all.IsSSLPassthroughEnabled }}442 proxy_protocol {{ else }}443{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }}; + {{ if $all.IsIPV6Enabled }}{{ if not (empty $server.SSLCertificate) }}listen {{ if $all.IsSSLPassthroughEnabled }}[::]:442 proxy_protocol{{ else }}[::]:443{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};{{ end }} {{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}} # PEM sha: {{ $server.SSLPemChecksum }} ssl_certificate {{ $server.SSLCertificate }}; ssl_certificate_key {{ $server.SSLCertificate }};