diff --git a/internal/ingress/controller/template/template.go b/internal/ingress/controller/template/template.go index 0e03007433..8668920b0f 100644 --- a/internal/ingress/controller/template/template.go +++ b/internal/ingress/controller/template/template.go @@ -526,14 +526,18 @@ func buildLocation(input interface{}, enforceRegex bool) string { } path := location.Path - if enforceRegex { - return fmt.Sprintf(`~* "^%s"`, path) - } if location.PathType != nil && *location.PathType == networkingv1.PathTypeExact { + if enforceRegex { + return fmt.Sprintf(`~* "^%s$"`, path) + } return fmt.Sprintf(`= %s`, path) } + if enforceRegex { + return fmt.Sprintf(`~* "^%s"`, path) + } + return path } diff --git a/internal/ingress/controller/template/template_test.go b/internal/ingress/controller/template/template_test.go index 6553f5daf9..a42c7be0b0 100644 --- a/internal/ingress/controller/template/template_test.go +++ b/internal/ingress/controller/template/template_test.go @@ -67,6 +67,7 @@ var ( XForwardedPrefix string SecureBackend bool enforceRegex bool + pathType networking.PathType }{ "when secure backend enabled": { "/", @@ -78,6 +79,7 @@ var ( "", true, false, + networking.PathTypePrefix, }, "when secure backend and dynamic config enabled": { "/", @@ -89,6 +91,7 @@ var ( "", true, false, + networking.PathTypePrefix, }, "when secure backend, stickiness and dynamic config enabled": { "/", @@ -100,6 +103,7 @@ var ( "", true, false, + networking.PathTypePrefix, }, "invalid redirect / to / with dynamic config enabled": { "/", @@ -111,6 +115,7 @@ var ( "", false, false, + networking.PathTypePrefix, }, "invalid redirect / to /": { "/", @@ -122,6 +127,7 @@ var ( "", false, false, + networking.PathTypePrefix, }, "redirect / to /jenkins": { "/", @@ -137,6 +143,7 @@ proxy_pass $scheme://upstream_balancer;`, "", false, true, + networking.PathTypePrefix, }, "redirect / to /something with sticky enabled": { "/", @@ -152,6 +159,7 @@ proxy_pass $scheme://upstream_balancer;`, "", false, true, + networking.PathTypePrefix, }, "redirect / to /something with sticky and dynamic config enabled": { "/", @@ -167,6 +175,7 @@ proxy_pass $scheme://upstream_balancer;`, "", false, true, + networking.PathTypePrefix, }, "add the X-Forwarded-Prefix header": { "/there", @@ -184,6 +193,7 @@ proxy_pass $scheme://upstream_balancer;`, "/there", false, true, + networking.PathTypePrefix, }, "use ~* location modifier when ingress does not use rewrite/regex target but at least one other ingress does": { "/something", @@ -195,6 +205,19 @@ proxy_pass $scheme://upstream_balancer;`, "", false, true, + networking.PathTypePrefix, + }, + "exact paths should remain exact when enforce regex is enabled": { + "/something", + "/something", + `~* "^/something$"`, + "proxy_pass http://upstream_balancer;", + "proxy_pass $scheme://upstream_balancer;", + false, + "", + false, + true, + networking.PathTypeExact, }, } ) @@ -319,7 +342,7 @@ func TestBuildLocation(t *testing.T) { for k, tc := range tmplFuncTestcases { loc := &ingress.Location{ Path: tc.Path, - PathType: &pathPrefix, + PathType: &tc.pathType, Rewrite: rewrite.Config{Target: tc.Target}, } diff --git a/test/e2e/annotations/rewrite.go b/test/e2e/annotations/rewrite.go index 173df29f00..4bccc339d6 100644 --- a/test/e2e/annotations/rewrite.go +++ b/test/e2e/annotations/rewrite.go @@ -98,7 +98,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { return strings.Contains(server, `location ~* "^/" {`) && - strings.Contains(server, `location ~* "^/.well-known/acme/challenge" {`) + strings.Contains(server, `location ~* "^/.well-known/acme/challenge$" {`) }) ginkgo.By("making a second request to the non-rewritten location") @@ -132,7 +132,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/foo" {`) && + return strings.Contains(server, `location ~* "^/foo$" {`) && strings.Contains(server, `location ~* "^/foo.+" {`) }) @@ -174,7 +174,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, `location ~* "^/foo/bar/bar" {`) && + return strings.Contains(server, `location ~* "^/foo/bar/bar$" {`) && strings.Contains(server, `location ~* "^/foo/bar/[a-z]{3}" {`) }) diff --git a/test/e2e/ingress/pathtype_exact.go b/test/e2e/ingress/pathtype_exact.go index 2660e32a45..4fcca50e08 100644 --- a/test/e2e/ingress/pathtype_exact.go +++ b/test/e2e/ingress/pathtype_exact.go @@ -113,4 +113,44 @@ var _ = framework.IngressNginxDescribe("[Ingress] [PathType] exact", func() { assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=exact") assert.NotContains(ginkgo.GinkgoT(), body, "duplicated=true") }) + + ginkgo.It("should respect exact type when using regex matching", func() { + disableSnippet := f.AllowSnippetConfiguration() + defer disableSnippet() + + host := "exact.path" + + annotations := map[string]string{ + "nginx.ingress.kubernetes.io/configuration-snippet": `more_set_input_headers "pathType: exact";`, + "nginx.ingress.kubernetes.io/use-regex": "true", + } + + exactPathType := networking.PathTypeExact + ing := framework.NewSingleIngress("exact", "/exact", host, f.Namespace, framework.EchoService, 80, annotations) + ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &exactPathType + f.EnsureIngress(ing) + + f.WaitForNginxServer(host, + func(server string) bool { + return strings.Contains(server, host) && + strings.Contains(server, `location ~* "^/exact$"`) + }) + + body := f.HTTPTestClient(). + GET("/exact"). + WithHeader("Host", host). + Expect(). + Status(http.StatusOK). + Body(). + Raw() + + assert.NotContains(ginkgo.GinkgoT(), body, "pathtype=prefix") + assert.Contains(ginkgo.GinkgoT(), body, "pathtype=exact") + + f.HTTPTestClient(). + GET("/exact/suffix"). + WithHeader("Host", host). + Expect(). + Status(http.StatusNotFound) + }) })