From 8a55801cc087e259493dc8ff787f3a37432c11a6 Mon Sep 17 00:00:00 2001 From: Alex Zhang Date: Mon, 24 May 2021 00:13:39 +0800 Subject: [PATCH] feat: multiple-cors-allow-origin support (#7134) --- .../nginx-configuration/annotations.md | 6 +++--- internal/ingress/annotations/cors/main.go | 10 +++++++++- internal/ingress/annotations/cors/main_test.go | 6 +++--- test/e2e/annotations/cors.go | 16 ++++++++++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/docs/user-guide/nginx-configuration/annotations.md b/docs/user-guide/nginx-configuration/annotations.md index add5c5595e..86a6e28e88 100755 --- a/docs/user-guide/nginx-configuration/annotations.md +++ b/docs/user-guide/nginx-configuration/annotations.md @@ -348,10 +348,10 @@ CORS can be controlled with the following annotations: - Example: `nginx.ingress.kubernetes.io/cors-expose-headers: "*, X-CustomResponseHeader"` * `nginx.ingress.kubernetes.io/cors-allow-origin` - controls what's the accepted Origin for CORS. - This is a single field value, with the following format: `http(s)://origin-site.com` or `http(s)://origin-site.com:port` + controls which Origin(s) are allowed for CORS. + This is a multi-valued field, separated by ',', each value should obey the following format: `http(s)://origin-site.com` or `http(s)://origin-site.com:port` - Default: `*` - - Example: `nginx.ingress.kubernetes.io/cors-allow-origin: "https://origin-site.com:4443"` + - Example: `nginx.ingress.kubernetes.io/cors-allow-origin: "https://origin-site.com:4443, https://origin-site-2.com"` * `nginx.ingress.kubernetes.io/cors-allow-credentials` controls if credentials can be passed during CORS operations. diff --git a/internal/ingress/annotations/cors/main.go b/internal/ingress/annotations/cors/main.go index 2f1a0a37b5..716d624fab 100644 --- a/internal/ingress/annotations/cors/main.go +++ b/internal/ingress/annotations/cors/main.go @@ -18,6 +18,7 @@ package cors import ( "regexp" + "strings" networking "k8s.io/api/networking/v1beta1" @@ -113,7 +114,14 @@ func (c cors) Parse(ing *networking.Ingress) (interface{}, error) { } config.CorsAllowOrigin, err = parser.GetStringAnnotation("cors-allow-origin", ing) - if err != nil || !corsOriginRegex.MatchString(config.CorsAllowOrigin) { + if err == nil { + for _, origin := range strings.Split(config.CorsAllowOrigin, ",") { + if !corsOriginRegex.MatchString(strings.TrimSpace(origin)) { + config.CorsAllowOrigin = "*" + break + } + } + } else { config.CorsAllowOrigin = "*" } diff --git a/internal/ingress/annotations/cors/main_test.go b/internal/ingress/annotations/cors/main_test.go index 6f75ce6a76..5715925ed1 100644 --- a/internal/ingress/annotations/cors/main_test.go +++ b/internal/ingress/annotations/cors/main_test.go @@ -72,7 +72,7 @@ func TestIngressCorsConfigValid(t *testing.T) { data[parser.GetAnnotationWithPrefix("cors-allow-headers")] = "DNT,X-CustomHeader, Keep-Alive,User-Agent" data[parser.GetAnnotationWithPrefix("cors-allow-credentials")] = "false" data[parser.GetAnnotationWithPrefix("cors-allow-methods")] = "GET, PATCH" - data[parser.GetAnnotationWithPrefix("cors-allow-origin")] = "https://origin123.test.com:4443" + data[parser.GetAnnotationWithPrefix("cors-allow-origin")] = "https://origin123.test.com:4443, https://origin456.test.com" data[parser.GetAnnotationWithPrefix("cors-expose-headers")] = "*, X-CustomResponseHeader" data[parser.GetAnnotationWithPrefix("cors-max-age")] = "600" ing.SetAnnotations(data) @@ -103,7 +103,7 @@ func TestIngressCorsConfigValid(t *testing.T) { t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("cors-allow-methods")], nginxCors.CorsAllowMethods) } - if nginxCors.CorsAllowOrigin != "https://origin123.test.com:4443" { + if nginxCors.CorsAllowOrigin != "https://origin123.test.com:4443, https://origin456.test.com" { t.Errorf("expected %v but returned %v", data[parser.GetAnnotationWithPrefix("cors-allow-origin")], nginxCors.CorsAllowOrigin) } @@ -126,7 +126,7 @@ func TestIngressCorsConfigInvalid(t *testing.T) { data[parser.GetAnnotationWithPrefix("cors-allow-headers")] = "@alright, #ingress" data[parser.GetAnnotationWithPrefix("cors-allow-credentials")] = "no" data[parser.GetAnnotationWithPrefix("cors-allow-methods")] = "GET, PATCH, $nginx" - data[parser.GetAnnotationWithPrefix("cors-allow-origin")] = "origin123.test.com:4443" + data[parser.GetAnnotationWithPrefix("cors-allow-origin")] = "https://origin.test.com, origin123.test.com:4443" data[parser.GetAnnotationWithPrefix("cors-expose-headers")] = "@alright, #ingress" data[parser.GetAnnotationWithPrefix("cors-max-age")] = "abcd" ing.SetAnnotations(data) diff --git a/test/e2e/annotations/cors.go b/test/e2e/annotations/cors.go index e4cdd32b4c..5267395390 100644 --- a/test/e2e/annotations/cors.go +++ b/test/e2e/annotations/cors.go @@ -121,6 +121,22 @@ var _ = framework.DescribeAnnotation("cors-*", func() { }) }) + ginkgo.It("should allow multiple origins for cors", func() { + host := "cors.foo.com" + annotations := map[string]string{ + "nginx.ingress.kubernetes.io/enable-cors": "true", + "nginx.ingress.kubernetes.io/cors-allow-origin": "https://origin.cors.com:8080, https://origin2.cors.com", + } + + ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations) + f.EnsureIngress(ing) + + f.WaitForNginxServer(host, + func(server string) bool { + return strings.Contains(server, "more_set_headers 'Access-Control-Allow-Origin: https://origin.cors.com:8080, https://origin2.cors.com';") + }) + }) + ginkgo.It("should allow headers for cors", func() { host := "cors.foo.com" annotations := map[string]string{