diff --git a/docs/user-guide/ingress-path-matching.md b/docs/user-guide/ingress-path-matching.md index 7d326682d..c7c360562 100644 --- a/docs/user-guide/ingress-path-matching.md +++ b/docs/user-guide/ingress-path-matching.md @@ -2,7 +2,7 @@ ## Regular Expression Support -The ingress controller supports **case insensitive** regular expressions in the `spec.rules.http.paths.path` field. __Currently curly braces `{}` cannot be used in regular expressions due to a [known issue](https://github.com/kubernetes/ingress-nginx/issues/3155).__ +The ingress controller supports **case insensitive** regular expressions in the `spec.rules.http.paths.path` field. See the [description](./nginx-configuration/annotations.md#use-regex) of the `use-regex` annotation for more details. diff --git a/internal/ingress/controller/template/template.go b/internal/ingress/controller/template/template.go index 8bee8589f..2d5e5522c 100644 --- a/internal/ingress/controller/template/template.go +++ b/internal/ingress/controller/template/template.go @@ -320,8 +320,6 @@ func enforceRegexModifier(input interface{}) bool { // buildLocation produces the location string, if the ingress has redirects // (specified through the nginx.ingress.kubernetes.io/rewrite-target annotation) -// TODO: return quotes around returned location path to prevent regex from breaking under certain conditions, see: -// https://github.com/kubernetes/ingress-nginx/issues/3155 func buildLocation(input interface{}, enforceRegex bool) string { location, ok := input.(*ingress.Location) if !ok { @@ -340,11 +338,11 @@ func buildLocation(input interface{}, enforceRegex bool) string { // Not treat the slash after "location path" as a part of baseuri baseuri = fmt.Sprintf(`\/?%s`, baseuri) } - return fmt.Sprintf(`~* ^%s%s`, path, baseuri) + return fmt.Sprintf(`~* "^%s%s"`, path, baseuri) } if enforceRegex { - return fmt.Sprintf(`~* ^%s`, path) + return fmt.Sprintf(`~* "^%s"`, path) } return path } @@ -528,15 +526,15 @@ subs_filter '%v' '$1' ro; // special case redirect to / // ie /something to / return fmt.Sprintf(` -rewrite (?i)%s(.*) /$1 break; -rewrite (?i)%s$ / break; +rewrite "(?i)%s(.*)" /$1 break; +rewrite "(?i)%s$" / break; %v%v %s%s; %v`, path, location.Path, xForwardedPrefix, proxyPass, proto, upstreamName, abu) } return fmt.Sprintf(` -rewrite (?i)%s(.*) %s/$1 break; -rewrite (?i)%s$ %s/ break; +rewrite "(?i)%s(.*)" %s/$1 break; +rewrite "(?i)%s$" %s/ break; %v%v %s%s; %v`, path, location.Rewrite.Target, location.Path, location.Rewrite.Target, xForwardedPrefix, proxyPass, proto, upstreamName, abu) } diff --git a/internal/ingress/controller/template/template_test.go b/internal/ingress/controller/template/template_test.go index 534964f84..d8622e2f6 100644 --- a/internal/ingress/controller/template/template_test.go +++ b/internal/ingress/controller/template/template_test.go @@ -129,8 +129,8 @@ var ( "/jenkins", "~* ^/", ` -rewrite (?i)/(.*) /jenkins/$1 break; -rewrite (?i)/$ /jenkins/ break; +rewrite "(?i)/(.*)" /jenkins/$1 break; +rewrite "(?i)/$" /jenkins/ break; proxy_pass http://upstream-name; `, false, @@ -143,10 +143,10 @@ proxy_pass http://upstream-name; "redirect /something to /": { "/something", "/", - `~* ^/something\/?(?.*)`, + `~* "^/something\/?(?.*)"`, ` -rewrite (?i)/something/(.*) /$1 break; -rewrite (?i)/something$ / break; +rewrite "(?i)/something/(.*)" /$1 break; +rewrite "(?i)/something$" / break; proxy_pass http://upstream-name; `, false, @@ -159,10 +159,10 @@ proxy_pass http://upstream-name; "redirect /end-with-slash/ to /not-root": { "/end-with-slash/", "/not-root", - "~* ^/end-with-slash/(?.*)", + `~* "^/end-with-slash/(?.*)"`, ` -rewrite (?i)/end-with-slash/(.*) /not-root/$1 break; -rewrite (?i)/end-with-slash/$ /not-root/ break; +rewrite "(?i)/end-with-slash/(.*)" /not-root/$1 break; +rewrite "(?i)/end-with-slash/$" /not-root/ break; proxy_pass http://upstream-name; `, false, @@ -175,10 +175,10 @@ proxy_pass http://upstream-name; "redirect /something-complex to /not-root": { "/something-complex", "/not-root", - `~* ^/something-complex\/?(?.*)`, + `~* "^/something-complex\/?(?.*)"`, ` -rewrite (?i)/something-complex/(.*) /not-root/$1 break; -rewrite (?i)/something-complex$ /not-root/ break; +rewrite "(?i)/something-complex/(.*)" /not-root/$1 break; +rewrite "(?i)/something-complex$" /not-root/ break; proxy_pass http://upstream-name; `, false, @@ -193,8 +193,8 @@ proxy_pass http://upstream-name; "/jenkins", "~* ^/", ` -rewrite (?i)/(.*) /jenkins/$1 break; -rewrite (?i)/$ /jenkins/ break; +rewrite "(?i)/(.*)" /jenkins/$1 break; +rewrite "(?i)/$" /jenkins/ break; proxy_pass http://upstream-name; set_escape_uri $escaped_base_uri $baseuri; @@ -210,10 +210,10 @@ subs_filter '(<(?:H|h)(?:E|e)(?:A|a)(?:D|d)(?:[^">]|"[^"]*")*>)' '$1.*)`, + `~* "^/something\/?(?.*)"`, ` -rewrite (?i)/something/(.*) /$1 break; -rewrite (?i)/something$ / break; +rewrite "(?i)/something/(.*)" /$1 break; +rewrite "(?i)/something$" / break; proxy_pass http://upstream-name; set_escape_uri $escaped_base_uri $baseuri; @@ -229,10 +229,10 @@ subs_filter '(<(?:H|h)(?:E|e)(?:A|a)(?:D|d)(?:[^">]|"[^"]*")*>)' '$1.*)`, + `~* "^/end-with-slash/(?.*)"`, ` -rewrite (?i)/end-with-slash/(.*) /not-root/$1 break; -rewrite (?i)/end-with-slash/$ /not-root/ break; +rewrite "(?i)/end-with-slash/(.*)" /not-root/$1 break; +rewrite "(?i)/end-with-slash/$" /not-root/ break; proxy_pass http://upstream-name; set_escape_uri $escaped_base_uri $baseuri; @@ -248,10 +248,10 @@ subs_filter '(<(?:H|h)(?:E|e)(?:A|a)(?:D|d)(?:[^">]|"[^"]*")*>)' '$1.*)`, + `~* "^/something-complex\/?(?.*)"`, ` -rewrite (?i)/something-complex/(.*) /not-root/$1 break; -rewrite (?i)/something-complex$ /not-root/ break; +rewrite "(?i)/something-complex/(.*)" /not-root/$1 break; +rewrite "(?i)/something-complex$" /not-root/ break; proxy_pass http://upstream-name; set_escape_uri $escaped_base_uri $baseuri; @@ -267,10 +267,10 @@ subs_filter '(<(?:H|h)(?:E|e)(?:A|a)(?:D|d)(?:[^">]|"[^"]*")*>)' '$1.*)`, + `~* "^/something\/?(?.*)"`, ` -rewrite (?i)/something/(.*) /$1 break; -rewrite (?i)/something$ / break; +rewrite "(?i)/something/(.*)" /$1 break; +rewrite "(?i)/something$" / break; proxy_pass http://upstream-name; set_escape_uri $escaped_base_uri $baseuri; @@ -288,8 +288,8 @@ subs_filter '(<(?:H|h)(?:E|e)(?:A|a)(?:D|d)(?:[^">]|"[^"]*")*>)' '$1.*)`, + `~* "^/there\/?(?.*)"`, ` -rewrite (?i)/there/(.*) /something/$1 break; -rewrite (?i)/there$ /something/ break; +rewrite "(?i)/there/(.*)" /something/$1 break; +rewrite "(?i)/there$" /something/ break; proxy_set_header X-Forwarded-Prefix "/there/"; proxy_pass http://sticky-upstream-name; `, @@ -335,7 +335,7 @@ proxy_pass http://sticky-upstream-name; "use ~* location modifier when ingress does not use rewrite/regex target but at least one other ingress does": { "/something", "/something", - "~* ^/something", + `~* "^/something"`, "proxy_pass http://upstream-name;", false, "", diff --git a/test/e2e/annotations/rewrite.go b/test/e2e/annotations/rewrite.go index 5844f4dd9..70c89db58 100644 --- a/test/e2e/annotations/rewrite.go +++ b/test/e2e/annotations/rewrite.go @@ -55,8 +55,8 @@ var _ = framework.IngressNginxDescribe("Annotations - Rewrite", func() { err = f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "rewrite (?i)/something/(.*) /$1 break;") && - strings.Contains(server, "rewrite (?i)/something$ / break;") + return strings.Contains(server, `rewrite "(?i)/something/(.*)" /$1 break;`) && + strings.Contains(server, `rewrite "(?i)/something$" / break;`) }) Expect(err).NotTo(HaveOccurred()) @@ -154,7 +154,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Rewrite", func() { err = f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "location ~* ^/ {") && strings.Contains(server, "location ~* ^/.well-known/acme/challenge {") + return strings.Contains(server, "location ~* ^/ {") && strings.Contains(server, `location ~* "^/.well-known/acme/challenge" {`) }) Expect(err).NotTo(HaveOccurred()) @@ -195,7 +195,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Rewrite", func() { err = f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "location ~* ^/foo {") && strings.Contains(server, `location ~* ^/foo.+\/?(?.*) {`) + return strings.Contains(server, `location ~* "^/foo" {`) && strings.Contains(server, `location ~* "^/foo.+\/?(?.*)" {`) }) Expect(err).NotTo(HaveOccurred()) @@ -234,14 +234,14 @@ var _ = framework.IngressNginxDescribe("Annotations - Rewrite", func() { "nginx.ingress.kubernetes.io/use-regex": "true", "nginx.ingress.kubernetes.io/rewrite-target": "/new/backend", } - ing = framework.NewSingleIngress("regex", "/foo/bar/[a-z][a-z][a-z]", host, f.IngressController.Namespace, "http-svc", 80, &annotations) + ing = framework.NewSingleIngress("regex", "/foo/bar/[a-z]{3}", host, f.IngressController.Namespace, "http-svc", 80, &annotations) _, err = f.EnsureIngress(ing) Expect(err).NotTo(HaveOccurred()) Expect(ing).NotTo(BeNil()) err = f.WaitForNginxServer(host, func(server string) bool { - return strings.Contains(server, "location ~* ^/foo/bar/bar {") && strings.Contains(server, `location ~* ^/foo/bar/[a-z][a-z][a-z]\/?(?.*) {`) + return strings.Contains(server, `location ~* "^/foo/bar/bar" {`) && strings.Contains(server, `location ~* "^/foo/bar/[a-z]{3}\/?(?.*)" {`) }) Expect(err).NotTo(HaveOccurred())