From edb37038f523573d9da3a947d443d9982b5a1fd9 Mon Sep 17 00:00:00 2001 From: Luke Seelenbinder Date: Sat, 7 Oct 2017 13:56:11 -0400 Subject: [PATCH] Add support for Kubernetes ssl-redirect annotation. --- examples/customization/README.md | 3 ++- nginx-controller/controller/controller.go | 7 +++++++ nginx-controller/nginx/config.go | 2 ++ nginx-controller/nginx/configurator.go | 8 ++++++++ nginx-controller/nginx/nginx.go | 1 + .../nginx/templates/nginx-plus.ingress.tmpl | 2 ++ .../nginx/templates/nginx.ingress.tmpl | 2 ++ .../nginx/templates/templates_test.go | 19 ++++++++++++------- 8 files changed, 36 insertions(+), 8 deletions(-) diff --git a/examples/customization/README.md b/examples/customization/README.md index 8a53561b81..e010c5b787 100644 --- a/examples/customization/README.md +++ b/examples/customization/README.md @@ -19,7 +19,8 @@ The table below summarizes some of the options. More options (extensions) are av | N/A | `server-names-hash-max-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. | `512` | | N/A | `http2` | Enables HTTP/2 in servers with SSL enabled. | `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). | +| `ingress.kubernetes.io/ssl-redirect` | `ssl-redirect` | Sets an unconditional 301 redirect rule for all incoming HTTP traffic to force incoming traffic over HTTPS. | `True` | +| 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) | | `nginx.org/hsts-include-subdomains` | `hsts-include-subdomains` | Adds the `includeSubDomains` directive to the HSTS header. | `False`| diff --git a/nginx-controller/controller/controller.go b/nginx-controller/controller/controller.go index 29d4ee3142..2ce6ee4828 100644 --- a/nginx-controller/controller/controller.go +++ b/nginx-controller/controller/controller.go @@ -440,6 +440,13 @@ func (lbc *LoadBalancerController) syncCfgm(task Task) { cfg.RedirectToHTTPS = redirectToHTTPS } } + if sslRedirect, exists, err := nginx.GetMapKeyAsBool(cfgm.Data, "ssl-redirect", cfgm); exists { + if err != nil { + glog.Error(err) + } else { + cfg.SSLRedirect = sslRedirect + } + } // 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 4c879291db..ff270e2f01 100644 --- a/nginx-controller/nginx/config.go +++ b/nginx-controller/nginx/config.go @@ -10,6 +10,7 @@ type Config struct { ClientMaxBodySize string HTTP2 bool RedirectToHTTPS bool + SSLRedirect bool MainHTTPSnippets []string MainServerNamesHashBucketSize string MainServerNamesHashMaxSize string @@ -56,6 +57,7 @@ func NewDefaultConfig() *Config { ProxyConnectTimeout: "60s", ProxyReadTimeout: "60s", ClientMaxBodySize: "1m", + SSLRedirect: true, MainServerNamesHashMaxSize: "512", ProxyBuffering: true, MainWorkerProcesses: "auto", diff --git a/nginx-controller/nginx/configurator.go b/nginx-controller/nginx/configurator.go index 4e254ea672..1b6893fbfc 100644 --- a/nginx-controller/nginx/configurator.go +++ b/nginx-controller/nginx/configurator.go @@ -122,6 +122,7 @@ func (cnf *Configurator) generateNginxCfg(ingEx *IngressEx, pems map[string]stri ServerTokens: ingCfg.ServerTokens, HTTP2: ingCfg.HTTP2, RedirectToHTTPS: ingCfg.RedirectToHTTPS, + SSLRedirect: ingCfg.SSLRedirect, ProxyProtocol: ingCfg.ProxyProtocol, HSTS: ingCfg.HSTS, HSTSMaxAge: ingCfg.HSTSMaxAge, @@ -259,6 +260,13 @@ func (cnf *Configurator) createConfig(ingEx *IngressEx) Config { ingCfg.RedirectToHTTPS = redirectToHTTPS } } + if sslRedirect, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "ingress.kubernetes.io/ssl-redirect", ingEx.Ingress); exists { + if err != nil { + glog.Error(err) + } else { + ingCfg.SSLRedirect = sslRedirect + } + } 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/nginx.go b/nginx-controller/nginx/nginx.go index aaea3632ab..003d0b05b9 100644 --- a/nginx-controller/nginx/nginx.go +++ b/nginx-controller/nginx/nginx.go @@ -61,6 +61,7 @@ type Server struct { StatusZone string HTTP2 bool RedirectToHTTPS bool + SSLRedirect bool ProxyProtocol bool HSTS bool HSTSMaxAge int64 diff --git a/nginx-controller/nginx/templates/nginx-plus.ingress.tmpl b/nginx-controller/nginx/templates/nginx-plus.ingress.tmpl index 9ec357f1e2..47b34d92c0 100644 --- a/nginx-controller/nginx/templates/nginx-plus.ingress.tmpl +++ b/nginx-controller/nginx/templates/nginx-plus.ingress.tmpl @@ -39,9 +39,11 @@ server { proxy_pass_header {{$proxyPassHeader}};{{end}} {{if $server.SSL}} + {{- if $server.SSLRedirect}} if ($scheme = http) { return 301 https://$host:{{index $server.SSLPorts 0}}$request_uri; } + {{- end}} {{- if $server.HSTS}} add_header Strict-Transport-Security "max-age={{$server.HSTSMaxAge}}; {{if $server.HSTSIncludeSubdomains}}includeSubDomains; {{end}}preload" always;{{end}} {{- end}} diff --git a/nginx-controller/nginx/templates/nginx.ingress.tmpl b/nginx-controller/nginx/templates/nginx.ingress.tmpl index 0a9de62830..d6baa76682 100644 --- a/nginx-controller/nginx/templates/nginx.ingress.tmpl +++ b/nginx-controller/nginx/templates/nginx.ingress.tmpl @@ -32,9 +32,11 @@ server { {{range $proxyPassHeader := $server.ProxyPassHeaders}} proxy_pass_header {{$proxyPassHeader}};{{end}} {{if $server.SSL}} + {{- if $server.SSLRedirect}} if ($scheme = http) { return 301 https://$host:{{index $server.SSLPorts 0}}$request_uri; } + {{- end}} {{- if $server.HSTS}} proxy_hide_header Strict-Transport-Security; add_header Strict-Transport-Security "max-age={{$server.HSTSMaxAge}}; {{if $server.HSTSIncludeSubdomains}}includeSubDomains; {{end}}preload" always;{{end}} diff --git a/nginx-controller/nginx/templates/templates_test.go b/nginx-controller/nginx/templates/templates_test.go index ceb76c5447..44427e46eb 100644 --- a/nginx-controller/nginx/templates/templates_test.go +++ b/nginx-controller/nginx/templates/templates_test.go @@ -24,13 +24,18 @@ var ingCfg = nginx.IngressNginxConfig{ Servers: []nginx.Server{ nginx.Server{ - Name: "test.example.com", - ServerTokens: "off", - StatusZone: "test.example.com", - JWTKey: "/etc/nginx/secrets/key.jwk", - JWTRealm: "closed site", - JWTToken: "$cookie_auth_token", - JWTLoginURL: "https://test.example.com/login", + Name: "test.example.com", + ServerTokens: "off", + StatusZone: "test.example.com", + JWTKey: "/etc/nginx/secrets/key.jwk", + JWTRealm: "closed site", + JWTToken: "$cookie_auth_token", + JWTLoginURL: "https://test.example.com/login", + SSL: true, + SSLCertificate: "secret.pem", + SSLCertificateKey: "secret.pem", + SSLPorts: []int{443}, + SSLRedirect: true, Locations: []nginx.Location{ nginx.Location{ Path: "/",