From c0629e92c26d0ce9d801f250918878959c8dd50d Mon Sep 17 00:00:00 2001 From: Zhongcheng Lao Date: Sat, 30 May 2020 23:35:20 +0800 Subject: [PATCH] Add proxy-ssl-server-name to enable passing SNI --- .../nginx-configuration/annotations.md | 3 +++ internal/ingress/annotations/proxyssl/main.go | 20 ++++++++++++---- .../ingress/annotations/proxyssl/main_test.go | 17 ++++++++++++- rootfs/etc/nginx/template/nginx.tmpl | 6 +++-- test/e2e/annotations/proxyssl.go | 24 +++++++++++-------- 5 files changed, 52 insertions(+), 18 deletions(-) diff --git a/docs/user-guide/nginx-configuration/annotations.md b/docs/user-guide/nginx-configuration/annotations.md index f3b32315a..9524ccf7c 100755 --- a/docs/user-guide/nginx-configuration/annotations.md +++ b/docs/user-guide/nginx-configuration/annotations.md @@ -77,6 +77,7 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz |[nginx.ingress.kubernetes.io/proxy-ssl-protocols](#backend-certificate-authentication)|string| |[nginx.ingress.kubernetes.io/proxy-ssl-verify](#backend-certificate-authentication)|string| |[nginx.ingress.kubernetes.io/proxy-ssl-verify-depth](#backend-certificate-authentication)|number| +|[nginx.ingress.kubernetes.io/proxy-ssl-server-name](#backend-certificate-authentication)|string| |[nginx.ingress.kubernetes.io/enable-rewrite-log](#enable-rewrite-log)|"true" or "false"| |[nginx.ingress.kubernetes.io/rewrite-target](#rewrite)|URI| |[nginx.ingress.kubernetes.io/satisfy](#satisfy)|string| @@ -273,6 +274,8 @@ It is possible to authenticate to a proxied HTTPS backend with certificate using Allows to set [proxy_ssl_name](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_name). This allows overriding the server name used to verify the certificate of the proxied HTTPS server. This value is also passed through SNI when a connection is established to the proxied HTTPS server. * `nginx.ingress.kubernetes.io/proxy-ssl-protocols`: Enables the specified [protocols](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_protocols) for requests to a proxied HTTPS server. +* `nginx.ingress.kubernetes.io/proxy-ssl-server-name`: + Enables passing of the server name through TLS Server Name Indication extension (SNI, RFC 6066) when establishing a connection with the proxied HTTPS server. ### Configuration snippet diff --git a/internal/ingress/annotations/proxyssl/main.go b/internal/ingress/annotations/proxyssl/main.go index 51fd1eff7..da3bbecc0 100644 --- a/internal/ingress/annotations/proxyssl/main.go +++ b/internal/ingress/annotations/proxyssl/main.go @@ -34,6 +34,7 @@ const ( defaultProxySSLProtocols = "TLSv1 TLSv1.1 TLSv1.2" defaultProxySSLVerify = "off" defaultProxySSLVerifyDepth = 1 + defaultProxySSLServerName = "off" ) var ( @@ -45,11 +46,12 @@ var ( // and the configured VerifyDepth type Config struct { resolver.AuthSSLCert - Ciphers string `json:"ciphers"` - Protocols string `json:"protocols"` - ProxySSLName string `json:"proxySSLName"` - Verify string `json:"verify"` - VerifyDepth int `json:"verifyDepth"` + Ciphers string `json:"ciphers"` + Protocols string `json:"protocols"` + ProxySSLName string `json:"proxySSLName"` + Verify string `json:"verify"` + VerifyDepth int `json:"verifyDepth"` + ProxySSLServerName string `json:"proxySSLServerName"` } // Equal tests for equality between two Config types @@ -75,6 +77,9 @@ func (pssl1 *Config) Equal(pssl2 *Config) bool { if pssl1.VerifyDepth != pssl2.VerifyDepth { return false } + if pssl1.ProxySSLServerName != pssl2.ProxySSLServerName { + return false + } return true } @@ -159,5 +164,10 @@ func (p proxySSL) Parse(ing *networking.Ingress) (interface{}, error) { config.VerifyDepth = defaultProxySSLVerifyDepth } + config.ProxySSLServerName, err = parser.GetStringAnnotation("proxy-ssl-server-name", ing) + if err != nil || !proxySSLOnOffRegex.MatchString(config.ProxySSLServerName) { + config.ProxySSLServerName = defaultProxySSLServerName + } + return config, nil } diff --git a/internal/ingress/annotations/proxyssl/main_test.go b/internal/ingress/annotations/proxyssl/main_test.go index 1c35ed683..daf54a377 100644 --- a/internal/ingress/annotations/proxyssl/main_test.go +++ b/internal/ingress/annotations/proxyssl/main_test.go @@ -90,7 +90,7 @@ func TestAnnotations(t *testing.T) { data[parser.GetAnnotationWithPrefix("proxy-ssl-ciphers")] = "HIGH:-SHA" data[parser.GetAnnotationWithPrefix("proxy-ssl-name")] = "$host" data[parser.GetAnnotationWithPrefix("proxy-ssl-protocols")] = "TLSv1.3 SSLv2 TLSv1 TLSv1.2" - data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = "off" + data[parser.GetAnnotationWithPrefix("proxy-ssl-server-name")] = "on" data[parser.GetAnnotationWithPrefix("proxy-ssl-session-reuse")] = "off" data[parser.GetAnnotationWithPrefix("proxy-ssl-verify")] = "on" data[parser.GetAnnotationWithPrefix("proxy-ssl-verify-depth")] = "3" @@ -131,6 +131,9 @@ func TestAnnotations(t *testing.T) { if u.ProxySSLName != "$host" { t.Errorf("expected %v but got %v", "$host", u.ProxySSLName) } + if u.ProxySSLServerName != "on" { + t.Errorf("expected %v but got %v", "on", u.ProxySSLServerName) + } } @@ -188,6 +191,9 @@ func TestInvalidAnnotations(t *testing.T) { if u.VerifyDepth != defaultProxySSLVerifyDepth { t.Errorf("expected %v but got %v", defaultProxySSLVerifyDepth, u.VerifyDepth) } + if u.ProxySSLServerName != defaultProxySSLServerName { + t.Errorf("expected %v but got %v", defaultProxySSLServerName, u.ProxySSLServerName) + } } func TestEquals(t *testing.T) { @@ -261,6 +267,15 @@ func TestEquals(t *testing.T) { } cfg2.VerifyDepth = 1 + // Different ProxySSLServerName + cfg1.ProxySSLServerName = "off" + cfg2.ProxySSLServerName = "on" + result = cfg1.Equal(cfg2) + if result != false { + t.Errorf("Expected false") + } + cfg2.ProxySSLServerName = "off" + // Equal Configs result = cfg1.Equal(cfg2) if result != true { diff --git a/rootfs/etc/nginx/template/nginx.tmpl b/rootfs/etc/nginx/template/nginx.tmpl index e95c0feec..017feff81 100755 --- a/rootfs/etc/nginx/template/nginx.tmpl +++ b/rootfs/etc/nginx/template/nginx.tmpl @@ -875,7 +875,8 @@ stream { proxy_ssl_verify {{ $server.ProxySSL.Verify }}; proxy_ssl_verify_depth {{ $server.ProxySSL.VerifyDepth }}; {{ if not (empty $server.ProxySSL.ProxySSLName) }} - proxy_ssl_name {{ $server.ProxySSL.ProxySSLName }}; + proxy_ssl_name {{ $server.ProxySSL.ProxySSLName }}; + proxy_ssl_server_name {{ $server.ProxySSL.ProxySSLServerName }}; {{ end }} {{ end }} @@ -1307,7 +1308,8 @@ stream { {{ end }} {{ if not (empty $location.ProxySSL.ProxySSLName) }} - proxy_ssl_name {{ $location.ProxySSL.ProxySSLName }}; + proxy_ssl_name {{ $location.ProxySSL.ProxySSLName }}; + proxy_ssl_server_name {{ $location.ProxySSL.ProxySSLServerName }}; {{ end }} {{ if not (empty $location.ProxySSL.PemFileName) }} diff --git a/test/e2e/annotations/proxyssl.go b/test/e2e/annotations/proxyssl.go index 93b3084fe..1ab772d2c 100644 --- a/test/e2e/annotations/proxyssl.go +++ b/test/e2e/annotations/proxyssl.go @@ -45,7 +45,7 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { ing := framework.NewSingleIngressWithTLS(host, "/", host, []string{host}, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) - assertProxySSL(f, host, "DEFAULT", "TLSv1 TLSv1.1 TLSv1.2", "off", 1) + assertProxySSL(f, host, "DEFAULT", "TLSv1 TLSv1.1 TLSv1.2", "off", 1, "on") f.HTTPTestClient(). GET("/"). @@ -61,12 +61,13 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { Status(http.StatusOK) }) - ginkgo.It("should set valid proxy-ssl-secret, proxy-ssl-verify to on, and proxy-ssl-verify-depth to 2", func() { + ginkgo.It("should set valid proxy-ssl-secret, proxy-ssl-verify to on, proxy-ssl-verify-depth to 2, and proxy-ssl-server-name to on", func() { host := "proxyssl.foo.com" annotations := make(map[string]string) annotations["nginx.ingress.kubernetes.io/proxy-ssl-secret"] = f.Namespace + "/" + host annotations["nginx.ingress.kubernetes.io/proxy-ssl-verify"] = "on" annotations["nginx.ingress.kubernetes.io/proxy-ssl-verify-depth"] = "2" + annotations["nginx.ingress.kubernetes.io/proxy-ssl-server-name"] = "on" tlsConfig, err := framework.CreateIngressMASecret(f.KubeClientSet, host, host, f.Namespace) assert.Nil(ginkgo.GinkgoT(), err) @@ -74,7 +75,7 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { ing := framework.NewSingleIngressWithTLS(host, "/", host, []string{host}, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) - assertProxySSL(f, host, "DEFAULT", "TLSv1 TLSv1.1 TLSv1.2", "on", 2) + assertProxySSL(f, host, "DEFAULT", "TLSv1 TLSv1.1 TLSv1.2", "on", 2, "on") f.HTTPTestClient(). GET("/"). @@ -102,7 +103,7 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { ing := framework.NewSingleIngressWithTLS(host, "/", host, []string{host}, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) - assertProxySSL(f, host, "HIGH:!AES", "TLSv1 TLSv1.1 TLSv1.2", "off", 1) + assertProxySSL(f, host, "HIGH:!AES", "TLSv1 TLSv1.1 TLSv1.2", "off", 1, "off") f.HTTPTestClient(). GET("/"). @@ -130,7 +131,7 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { ing := framework.NewSingleIngressWithTLS(host, "/", host, []string{host}, f.Namespace, framework.EchoService, 80, annotations) f.EnsureIngress(ing) - assertProxySSL(f, host, "DEFAULT", "TLSv1.2 TLSv1.3", "off", 1) + assertProxySSL(f, host, "DEFAULT", "TLSv1.2 TLSv1.3", "off", 1, "off") f.HTTPTestClient(). GET("/"). @@ -156,6 +157,7 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { annotations["nginx.ingress.kubernetes.io/proxy-ssl-secret"] = f.Namespace + "/" + secretName annotations["nginx.ingress.kubernetes.io/backend-protocol"] = "HTTPS" annotations["nginx.ingress.kubernetes.io/proxy-ssl-verify"] = "on" + annotations["nginx.ingress.kubernetes.io/proxy-ssl-server-name"] = "on" tlsConfig, err := framework.CreateIngressMASecret(f.KubeClientSet, host, secretName, f.Namespace) assert.Nil(ginkgo.GinkgoT(), err) @@ -167,7 +169,7 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { wlValue := "true" f.UpdateNginxConfigMapData(wlKey, wlValue) - assertProxySSLName(f, host, secretName, "DEFAULT", "TLSv1 TLSv1.1 TLSv1.2", "on", 1) + assertProxySSLName(f, host, secretName, "DEFAULT", "TLSv1 TLSv1.1 TLSv1.2", "on", 1, "on") f.WaitForNginxCustomConfiguration("## start server proxyssl.com", "location ", func(server string) bool { return (!strings.Contains(server, "proxy_ssl_trusted_certificate") && @@ -196,7 +198,7 @@ var _ = framework.DescribeAnnotation("proxy-ssl-*", func() { }) -func assertProxySSL(f *framework.Framework, host, ciphers, protocols, verify string, depth int) { +func assertProxySSL(f *framework.Framework, host, ciphers, protocols, verify string, depth int, proxySSLServerName string) { certFile := fmt.Sprintf("/etc/ingress-controller/ssl/%s-%s.pem", f.Namespace, host) f.WaitForNginxServer(host, func(server string) bool { @@ -206,11 +208,12 @@ func assertProxySSL(f *framework.Framework, host, ciphers, protocols, verify str strings.Contains(server, fmt.Sprintf("proxy_ssl_ciphers %s;", ciphers)) && strings.Contains(server, fmt.Sprintf("proxy_ssl_protocols %s;", protocols)) && strings.Contains(server, fmt.Sprintf("proxy_ssl_verify %s;", verify)) && - strings.Contains(server, fmt.Sprintf("proxy_ssl_verify_depth %d;", depth)) + strings.Contains(server, fmt.Sprintf("proxy_ssl_verify_depth %d;", depth)) && + strings.Contains(server, fmt.Sprintf("proxy_ssl_server_name %s;", proxySSLServerName)) }) } -func assertProxySSLName(f *framework.Framework, host, sslName, ciphers, protocols, verify string, depth int) { +func assertProxySSLName(f *framework.Framework, host, sslName, ciphers, protocols, verify string, depth int, proxySSLServerName string) { certFile := fmt.Sprintf("/etc/ingress-controller/ssl/%s-%s.pem", f.Namespace, sslName) f.WaitForNginxServer(host, func(server string) bool { @@ -220,6 +223,7 @@ func assertProxySSLName(f *framework.Framework, host, sslName, ciphers, protocol strings.Contains(server, fmt.Sprintf("proxy_ssl_ciphers %s;", ciphers)) && strings.Contains(server, fmt.Sprintf("proxy_ssl_protocols %s;", protocols)) && strings.Contains(server, fmt.Sprintf("proxy_ssl_verify %s;", verify)) && - strings.Contains(server, fmt.Sprintf("proxy_ssl_verify_depth %d;", depth)) + strings.Contains(server, fmt.Sprintf("proxy_ssl_verify_depth %d;", depth)) && + strings.Contains(server, fmt.Sprintf("proxy_ssl_server_name %s;", proxySSLServerName)) }) }