diff --git a/controllers/nginx/pkg/config/config.go b/controllers/nginx/pkg/config/config.go index 8a8247e8a..eb45991ca 100644 --- a/controllers/nginx/pkg/config/config.go +++ b/controllers/nginx/pkg/config/config.go @@ -324,12 +324,19 @@ type Configuration struct { // If no data is transmitted within this time, the connection is closed. // http://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_timeout ProxyStreamTimeout string `json:"proxy-stream-timeout,omitempty"` + + // Sets the ipv4 addresses on which the server will accept requests. + BindAddressIpv4 []string `json:"bind-address-ipv4,omitempty"` + + // Sets the ipv6 addresses on which the server will accept requests. + BindAddressIpv6 []string `json:"bind-address-ipv4,omitempty"` } // NewDefault returns the default nginx configuration func NewDefault() Configuration { defIPCIDR := make([]string, 0) defIPCIDR = append(defIPCIDR, "0.0.0.0/0") + defBindAddress := make([]string, 0) cfg := Configuration{ AllowBackendServerHeader: false, ClientHeaderBufferSize: "1k", @@ -392,6 +399,8 @@ func NewDefault() Configuration { }, UpstreamKeepaliveConnections: 0, LimitConnZoneVariable: defaultLimitConnZoneVariable, + BindAddressIpv4: defBindAddress, + BindAddressIpv6: defBindAddress, } if glog.V(5) { diff --git a/controllers/nginx/pkg/template/configmap.go b/controllers/nginx/pkg/template/configmap.go index 2b5401e42..65544f2d6 100644 --- a/controllers/nginx/pkg/template/configmap.go +++ b/controllers/nginx/pkg/template/configmap.go @@ -17,6 +17,8 @@ limitations under the License. package template import ( + "fmt" + "net" "strconv" "strings" @@ -24,6 +26,7 @@ import ( "github.com/mitchellh/mapstructure" "k8s.io/ingress/controllers/nginx/pkg/config" + ing_net "k8s.io/ingress/core/pkg/net" ) const ( @@ -31,6 +34,7 @@ const ( skipAccessLogUrls = "skip-access-log-urls" whitelistSourceRange = "whitelist-source-range" proxyRealIPCIDR = "proxy-real-ip-cidr" + bindAddress = "bind-address" ) // ReadConfig obtains the configuration defined by the user merged with the defaults. @@ -47,6 +51,8 @@ func ReadConfig(src map[string]string) config.Configuration { skipUrls := make([]string, 0) whitelist := make([]string, 0) proxylist := make([]string, 0) + bindAddressIpv4List := make([]string, 0) + bindAddressIpv6List := make([]string, 0) if val, ok := conf[customHTTPErrors]; ok { delete(conf, customHTTPErrors) @@ -73,12 +79,32 @@ func ReadConfig(src map[string]string) config.Configuration { } else { proxylist = append(proxylist, "0.0.0.0/0") } + if val, ok := conf[bindAddress]; ok { + delete(conf, bindAddress) + glog.Infof("bindAddress: %v", conf[bindAddress]) + for _, i := range strings.Split(val, ",") { + ns := net.ParseIP(i) + if ns != nil { + if ing_net.IsIPV6(ns) { + glog.Infof("Add bindAddress ipv6: %v", ns) + bindAddressIpv6List = append(bindAddressIpv6List, fmt.Sprintf("[%v]", ns)) + } else { + glog.Infof("Add bindAddress ipv4: %v", ns) + bindAddressIpv4List = append(bindAddressIpv4List, fmt.Sprintf("%v", ns)) + } + } + } + } to := config.NewDefault() to.CustomHTTPErrors = filterErrors(errors) to.SkipAccessLogURLs = skipUrls to.WhitelistSourceRange = whitelist to.ProxyRealIPCIDR = proxylist + to.BindAddressIpv4 = bindAddressIpv4List + to.BindAddressIpv6 = bindAddressIpv6List + glog.Infof("bindAddress ipv4: %v", to.BindAddressIpv4) + glog.Infof("bindAddress ipv6: %v", to.BindAddressIpv6) config := &mapstructure.DecoderConfig{ Metadata: nil, diff --git a/controllers/nginx/pkg/template/configmap_test.go b/controllers/nginx/pkg/template/configmap_test.go index b7aab61ac..09be6caa0 100644 --- a/controllers/nginx/pkg/template/configmap_test.go +++ b/controllers/nginx/pkg/template/configmap_test.go @@ -43,6 +43,7 @@ func TestMergeConfigMapToStruct(t *testing.T) { "enable-dynamic-tls-records": "false", "gzip-types": "text/html", "proxy-real-ip-cidr": "1.1.1.1/8,2.2.2.2/24", + "bind-address": "1.1.1.1,2.2.2.2,2001:db8:a0b:12f0::1,3731:54:65fe:2::a7", } def := config.NewDefault() def.CustomHTTPErrors = []int{300, 400} @@ -54,6 +55,8 @@ func TestMergeConfigMapToStruct(t *testing.T) { def.UseProxyProtocol = true def.GzipTypes = "text/html" def.ProxyRealIPCIDR = []string{"1.1.1.1/8", "2.2.2.2/24"} + def.BindAddressIpv4 = []string{"1.1.1.1", "2.2.2.2"} + def.BindAddressIpv6 = []string{"[2001:db8:a0b:12f0::1]", "[3731:54:65fe:2::a7]"} to := ReadConfig(conf) if diff := pretty.Compare(to, def); diff != "" { diff --git a/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl b/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl index 058aaa83d..6217f3981 100644 --- a/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl +++ b/controllers/nginx/rootfs/etc/nginx/template/nginx.tmpl @@ -300,12 +300,22 @@ http { {{/* Build server redirects (from/to www) */}} {{ range $hostname, $to := .RedirectServers }} server { + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ $address }}:80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}; + listen {{ if $all.IsSSLPassthroughEnabled }}{{ $address }}:442 proxy_protocol{{ else }}{{ $address }}:443{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} ssl; + {{ else }} listen 80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}; listen {{ if $all.IsSSLPassthroughEnabled }}442 proxy_protocol{{ else }}443{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} ssl; + {{ end }} {{ if $IsIPV6Enabled }} + {{ range $address := $all.Cfg.BindAddressIpv6 }} + listen {{ $address }}:80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}; + listen {{ if $all.IsSSLPassthroughEnabled }}{{ $address }}:442 proxy_protocol{{ else }}{{ $address }}:443{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }}; + {{ else }} listen [::]:80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}; listen {{ if $all.IsSSLPassthroughEnabled }}[::]:442 proxy_protocol{{ else }}[::]:443{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }}; {{ end }} + {{ end }} server_name {{ $hostname }}; return 301 $scheme://{{ $to }}$request_uri; } @@ -332,8 +342,18 @@ http { # Use the port 18080 (random value just to avoid known ports) as default port for nginx. # Changing this value requires a change in: # https://github.com/kubernetes/ingress/blob/master/controllers/nginx/pkg/cmd/controller/nginx.go - listen 18080 default_server reuseport backlog={{ .BacklogSize }}; - {{ if $IsIPV6Enabled }}listen [::]:18080 default_server reuseport backlog={{ .BacklogSize }};{{ end }} + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ $address }}:18080 default_server reuseport backlog={{ $all.BacklogSize }}; + {{ else }} + listen 18080 default_server reuseport backlog={{ $all.BacklogSize }}; + {{ end }} + {{ if $IsIPV6Enabled }} + {{ range $address := $all.Cfg.BindAddressIpv6 }} + listen {{ $address }}:18080 default_server reuseport backlog={{ $all.BacklogSize }}; + {{ else }} + listen [::]:18080 default_server reuseport backlog={{ $all.BacklogSize }}; + {{ end }} + {{ end }} set $proxy_upstream_name "-"; location {{ $healthzURI }} { @@ -376,7 +396,11 @@ http { # default server for services without endpoints server { + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ $address }}:8181; + {{ else }} listen 8181; + {{ end }} set $proxy_upstream_name "-"; location / { @@ -412,8 +436,18 @@ stream { {{ end }} } server { + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.UseProxyProtocol }} proxy_protocol{{ end }}; + {{ else }} listen {{ $tcpServer.Port }}{{ if $tcpServer.Backend.UseProxyProtocol }} proxy_protocol{{ end }}; - {{ if $IsIPV6Enabled }}listen [::]:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.UseProxyProtocol }} proxy_protocol{{ end }};{{ end }} + {{ end }} + {{ if $IsIPV6Enabled }} + {{ range $address := $all.Cfg.BindAddressIpv6 }} + listen {{ $address }}:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.UseProxyProtocol }} proxy_protocol{{ end }}; + {{ else }} + listen [::]:{{ $tcpServer.Port }}{{ if $tcpServer.Backend.UseProxyProtocol }} proxy_protocol{{ end }}; + {{ end }} + {{ end }} proxy_timeout {{ $cfg.ProxyStreamTimeout }}; proxy_pass tcp-{{ $tcpServer.Port }}-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }}; } @@ -429,8 +463,18 @@ stream { } server { + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ $address }}:{{ $udpServer.Port }} udp; + {{ else }} listen {{ $udpServer.Port }} udp; - {{ if $IsIPV6Enabled }}listen [::]:{{ $udpServer.Port }} udp;{{ end }} + {{ end }} + {{ if $IsIPV6Enabled }} + {{ range $address := $all.Cfg.BindAddressIpv6 }} + listen {{ $address }}:{{ $udpServer.Port }} udp; + {{ else }} + listen [::]:{{ $udpServer.Port }} udp; + {{ end }} + {{ end }} proxy_responses 1; proxy_timeout {{ $cfg.ProxyStreamTimeout }}; proxy_pass udp-{{ $udpServer.Port }}-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }}; @@ -500,14 +544,36 @@ stream { {{ define "SERVER" }} {{ $all := .First }} {{ $server := .Second }} + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ $address }}:80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}}; + {{ else }} listen 80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{end}}; - {{ if $all.IsIPV6Enabled }}listen [::]:80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{ end }};{{ end }} + {{ end }} + {{ if $all.IsIPV6Enabled }} + {{ range $address := $all.Cfg.BindAddressIpv6 }} + listen {{ $address }}:80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{ end }}; + {{ else }} + listen [::]:80{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $all.BacklogSize }}{{ end }}; + {{ end }} + {{ end }} set $proxy_upstream_name "-"; {{/* 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 {{ 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 */}} + {{ if not (empty $server.SSLCertificate) }} + {{ range $address := $all.Cfg.BindAddressIpv4 }} + listen {{ if $all.IsSSLPassthroughEnabled }}{{ $address }}:442 proxy_protocol {{ else }}{{ $address }}: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 }}; + {{ else }} + 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 }}; + {{ end }} + {{ if $all.IsIPV6Enabled }} + {{ range $address := $all.Cfg.BindAddressIpv6 }} + {{ if not (empty $server.SSLCertificate) }}listen {{ if $all.IsSSLPassthroughEnabled }}{{ $address }}:442 proxy_protocol{{ else }}{{ $address }}: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 }}; + {{ else }} + {{ 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 }} + {{ 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 }};