diff --git a/examples/customization/README.md b/examples/customization/README.md index 425b2d1537..2eb2ec1a9b 100644 --- a/examples/customization/README.md +++ b/examples/customization/README.md @@ -18,6 +18,7 @@ The table below summarizes some of the options. More options (extensions) are av | N/A | `server-names-hash-bucket-size` | Sets the value of the [server_names_hash_max_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#server_names_hash_max_size) directive. | Depends on the size of the processor’s cache line. | | N/A | `server-names-hash-max-size` | Sets the value of the [server_names_hash_bucket_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#server_names_hash_bucket_size) directive. | `512` | | `nginx.org/http2` | `http2` | Enables HTTP/2 in servers with SSL enabled. To support HTTP/2 for Chrome users, use the provided controller image based on the alpine Linux. It includes OpenSSL with ALPN support, [necessary for Chrome users](https://www.nginx.com/blog/supporting-http2-google-chrome-users/). | `False` | +| `nginx.org/redirect-to-https` | `redirect-to-https` | Sets the 301 redirect rule based on the value of the `http_x_forwarded_proto` header on the server block to force incoming traffic to be over HTTPS. Useful when terminating SSL in a load balancer in front of the Ingress controller — see [115](https://github.com/nginxinc/kubernetes-ingress/issues/115) | `False` | | N/A | `log-format` | Sets the custom [log format](http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format). | See the [template file](../../nginx-controller/nginx/nginx.conf.tmpl). | | `nginx.org/hsts` | `hsts` | Enables [HTTP Strict Transport Security (HSTS)](https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/): the HSTS header is added to the responses from backends. The `preload` directive is included in the header. | `False` | | `nginx.org/hsts-max-age` | `hsts-max-age` | Sets the value of the `max-age` directive of the HSTS header. | `2592000` (1 month) | diff --git a/nginx-controller/controller/controller.go b/nginx-controller/controller/controller.go index a5dfe260d2..10b7c296f2 100644 --- a/nginx-controller/controller/controller.go +++ b/nginx-controller/controller/controller.go @@ -405,6 +405,13 @@ func (lbc *LoadBalancerController) syncCfgm(key string) { cfg.HTTP2 = HTTP2 } } + if redirectToHTTPS, exists,err := nginx.GetMapKeyAsBool(cfgm.Data, "redirect-to-https", cfgm); exists { + if err != nil { + glog.Error(err) + } else { + cfg.RedirectToHTTPS = redirectToHTTPS + } + } // HSTS block if hsts, exists, err := nginx.GetMapKeyAsBool(cfgm.Data, "hsts", cfgm); exists { diff --git a/nginx-controller/nginx/config.go b/nginx-controller/nginx/config.go index 66c8d39380..e25daed8bd 100644 --- a/nginx-controller/nginx/config.go +++ b/nginx-controller/nginx/config.go @@ -7,6 +7,7 @@ type Config struct { ProxyReadTimeout string ClientMaxBodySize string HTTP2 bool + RedirectToHTTPS bool MainServerNamesHashBucketSize string MainServerNamesHashMaxSize string MainLogFormat string diff --git a/nginx-controller/nginx/configurator.go b/nginx-controller/nginx/configurator.go index 08e54073d4..e0f20222ba 100644 --- a/nginx-controller/nginx/configurator.go +++ b/nginx-controller/nginx/configurator.go @@ -111,6 +111,7 @@ func (cnf *Configurator) generateNginxCfg(ingEx *IngressEx, pems map[string]stri Name: serverName, ServerTokens: ingCfg.ServerTokens, HTTP2: ingCfg.HTTP2, + RedirectToHTTPS: ingCfg.RedirectToHTTPS, ProxyProtocol: ingCfg.ProxyProtocol, HSTS: ingCfg.HSTS, HSTSMaxAge: ingCfg.HSTSMaxAge, @@ -162,6 +163,7 @@ func (cnf *Configurator) generateNginxCfg(ingEx *IngressEx, pems map[string]stri Name: emptyHost, ServerTokens: ingCfg.ServerTokens, HTTP2: ingCfg.HTTP2, + RedirectToHTTPS: ingCfg.RedirectToHTTPS, ProxyProtocol: ingCfg.ProxyProtocol, HSTS: ingCfg.HSTS, HSTSMaxAge: ingCfg.HSTSMaxAge, @@ -232,6 +234,13 @@ func (cnf *Configurator) createConfig(ingEx *IngressEx) Config { ingCfg.HTTP2 = HTTP2 } } + if redirectToHTTPS, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/redirect-to-https", ingEx.Ingress); exists { + if err != nil { + glog.Error(err) + } else { + ingCfg.RedirectToHTTPS = redirectToHTTPS + } + } if proxyBuffering, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/proxy-buffering", ingEx.Ingress); exists { if err != nil { glog.Error(err) diff --git a/nginx-controller/nginx/ingress.tmpl b/nginx-controller/nginx/ingress.tmpl index b9d1cbc274..8e5846d707 100644 --- a/nginx-controller/nginx/ingress.tmpl +++ b/nginx-controller/nginx/ingress.tmpl @@ -34,6 +34,11 @@ server { proxy_hide_header Strict-Transport-Security; add_header Strict-Transport-Security "max-age={{$server.HSTSMaxAge}}; {{if $server.HSTSIncludeSubdomains}}includeSubDomains; {{end}}preload" always;{{end}} {{- end}} + {{- if $server.RedirectToHTTPS}} + if ($http_x_forwarded_proto = 'http') { + return 301 https://$host$request_uri; + } + {{- end}} {{range $location := $server.Locations}} location {{$location.Path}} { @@ -50,7 +55,7 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; - proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Proto {{if $server.RedirectToHTTPS}}https{{else}}$scheme{{end}}; proxy_buffering {{if $location.ProxyBuffering}}on{{else}}off{{end}}; {{- if $location.ProxyBuffers}} diff --git a/nginx-controller/nginx/nginx.go b/nginx-controller/nginx/nginx.go index ac76c0f2ee..aa7a2cd61b 100644 --- a/nginx-controller/nginx/nginx.go +++ b/nginx-controller/nginx/nginx.go @@ -47,6 +47,7 @@ type Server struct { SSLCertificate string SSLCertificateKey string HTTP2 bool + RedirectToHTTPS bool ProxyProtocol bool HSTS bool HSTSMaxAge int64 diff --git a/nginx-plus-controller/controller/controller.go b/nginx-plus-controller/controller/controller.go index 5400ac6c1b..00a2a6f904 100644 --- a/nginx-plus-controller/controller/controller.go +++ b/nginx-plus-controller/controller/controller.go @@ -409,6 +409,13 @@ func (lbc *LoadBalancerController) syncCfgm(key string) { cfg.HTTP2 = HTTP2 } } + if redirectToHTTPS, exists,err := nginx.GetMapKeyAsBool(cfgm.Data, "redirect-to-https", cfgm); exists { + if err != nil { + glog.Error(err) + } else { + cfg.RedirectToHTTPS = redirectToHTTPS + } + } // HSTS block if hsts, exists, err := nginx.GetMapKeyAsBool(cfgm.Data, "hsts", cfgm); exists { diff --git a/nginx-plus-controller/nginx/config.go b/nginx-plus-controller/nginx/config.go index aa8874a07f..ee41d41434 100644 --- a/nginx-plus-controller/nginx/config.go +++ b/nginx-plus-controller/nginx/config.go @@ -7,6 +7,7 @@ type Config struct { ProxyReadTimeout string ClientMaxBodySize string HTTP2 bool + RedirectToHTTPS bool MainServerNamesHashBucketSize string MainServerNamesHashMaxSize string MainLogFormat string diff --git a/nginx-plus-controller/nginx/configurator.go b/nginx-plus-controller/nginx/configurator.go index 0a5360548d..31a8fc246d 100644 --- a/nginx-plus-controller/nginx/configurator.go +++ b/nginx-plus-controller/nginx/configurator.go @@ -120,6 +120,7 @@ func (cnf *Configurator) generateNginxCfg(ingEx *IngressEx, pems map[string]stri Name: serverName, ServerTokens: ingCfg.ServerTokens, HTTP2: ingCfg.HTTP2, + RedirectToHTTPS: ingCfg.RedirectToHTTPS, ProxyProtocol: ingCfg.ProxyProtocol, HSTS: ingCfg.HSTS, HSTSMaxAge: ingCfg.HSTSMaxAge, @@ -176,6 +177,7 @@ func (cnf *Configurator) generateNginxCfg(ingEx *IngressEx, pems map[string]stri Name: serverName, ServerTokens: ingCfg.ServerTokens, HTTP2: ingCfg.HTTP2, + RedirectToHTTPS: ingCfg.RedirectToHTTPS, ProxyProtocol: ingCfg.ProxyProtocol, HSTS: ingCfg.HSTS, HSTSMaxAge: ingCfg.HSTSMaxAge, @@ -251,6 +253,13 @@ func (cnf *Configurator) createConfig(ingEx *IngressEx) Config { ingCfg.HTTP2 = HTTP2 } } + if redirectToHTTPS, exists,err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/redirect-to-https", ingEx.Ingress); exists { + if err != nil { + glog.Error(err) + } else { + ingCfg.RedirectToHTTPS = redirectToHTTPS + } + } if proxyBuffering, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/proxy-buffering", ingEx.Ingress); exists { if err != nil { glog.Error(err) diff --git a/nginx-plus-controller/nginx/ingress.tmpl b/nginx-plus-controller/nginx/ingress.tmpl index 615daed1d7..18110d3254 100644 --- a/nginx-plus-controller/nginx/ingress.tmpl +++ b/nginx-plus-controller/nginx/ingress.tmpl @@ -40,6 +40,11 @@ server { {{- if $server.HSTS}} add_header Strict-Transport-Security "max-age={{$server.HSTSMaxAge}}; {{if $server.HSTSIncludeSubdomains}}includeSubDomains; {{end}}preload" always;{{end}} {{- end}} + {{- if $server.RedirectToHTTPS}} + if ($http_x_forwarded_proto = 'http') { + return 301 https://$host$request_uri; + } + {{- end}} {{range $location := $server.Locations}} location {{$location.Path}} { @@ -56,7 +61,7 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; - proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Proto {{if $server.RedirectToHTTPS}}https{{else}}$scheme{{end}}; proxy_buffering {{if $location.ProxyBuffering}}on{{else}}off{{end}}; {{- if $location.ProxyBuffers}} proxy_buffers {{$location.ProxyBuffers}}; diff --git a/nginx-plus-controller/nginx/nginx.go b/nginx-plus-controller/nginx/nginx.go index 577fab2e4a..71f580788f 100644 --- a/nginx-plus-controller/nginx/nginx.go +++ b/nginx-plus-controller/nginx/nginx.go @@ -49,6 +49,7 @@ type Server struct { SSLCertificateKey string StatusZone string HTTP2 bool + RedirectToHTTPS bool ProxyProtocol bool HSTS bool HSTSMaxAge int64