Ingress-nginx: Path is not threated as regex if ingress.kubernetes.io/rewrite-target isn't ending with /

Created on 29 Aug 2017  路  13Comments  路  Source: kubernetes/ingress-nginx

Given the example ingress rule blow
I need the path to be handled as regex for several reasons, one is to support CaMelCase in requests. Another is to get my desired combinations of hosts and paths in the ingress rules to function correctly. Currently we have around 5 hosts and more than 200 paths split in about 50 ingress rules, that all needs to resolve to the correct upstream service.

The problem is that I have to add a trailing / to the ingress.kubernetes.io/rewrite-target, to get the path ending up as regex in the location section of the ingress nginx.conf file.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: foo
  labels:
    name: foo
  annotations:
    kubernetes.io/ingress.class: "nginx"
    ingress.kubernetes.io/ssl-redirect: "false" 
    ingress.kubernetes.io/secure-backends: "true"    
    ingress.kubernetes.io/rewrite-target: /arcgis/rest/services/Geocache_UTM33_EUREF89/GeocacheBasis/  
spec:
  tls:
  - hosts:
    - foo.geodataonline.no
    secretName: star-gdo-tls-chain
  rules:
  - host: foo.geodataonline.no 
    http: 
      paths:
      - path: /arcgis/rest/services/Geocache_UTM33_EUREF89/GeocacheBasis
        backend:
          serviceName: foo
          servicePort: 6443
 ```

Resulting location segment of the nginx.conf file
    location ~* ^/arcgis/rest/services/Geocache_UTM33_EUREF89/GeocacheBasis\/?(?<baseuri>.*) {
        set $proxy_upstream_name "default-geocachebasisutm33euref89-6443";
        port_in_redirect off;
        client_max_body_size                    "1m";
        proxy_set_header Host                   $best_http_host;
        # Pass the extracted client certificate to the backend
        # Allow websocket connections
        proxy_set_header                        Upgrade           $http_upgrade;
        proxy_set_header                        Connection        $connection_upgrade;
        proxy_set_header X-Real-IP              $the_real_ip;
        proxy_set_header X-Forwarded-For        $the_real_ip;
        proxy_set_header X-Forwarded-Host       $best_http_host;
        proxy_set_header X-Forwarded-Port       $pass_port;
        proxy_set_header X-Forwarded-Proto      $pass_access_scheme;
        proxy_set_header X-Original-URI         $request_uri;
        proxy_set_header X-Scheme               $pass_access_scheme;
        # mitigate HTTPoxy Vulnerability
        # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
        proxy_set_header Proxy                  "";
        # Custom headers to proxied server
        proxy_connect_timeout                   5s;
        proxy_send_timeout                      60s;
        proxy_read_timeout                      60s;
        proxy_redirect                          off;
        proxy_buffering                         off;
        proxy_buffer_size                       "4k";
        proxy_buffers                           4 "4k";
        proxy_http_version                      1.1;
        proxy_cookie_domain                     off;
        proxy_cookie_path                       off;
        # In case of errors try the next upstream server before returning an error
        proxy_next_upstream                     error timeout invalid_header http_502 http_503 http_504;
rewrite /arcgis/rest/services/Geocache_UTM33_EUREF89/GeocacheBasis/(.*) /arcgis/rest/services/Geocache_UTM33_EUREF89/GeocacheBasis//$1 break;
proxy_pass https://default-geocachebasisutm33euref89-6443;

    }
The problem is that a request like 
`https://foo.geodataonline.no/arcgis/rest/services/Geocache_UTM33_EUREF89/GeocacheBasis/foo/bar?query=string`
are passed to my upstream service as
`/arcgis/rest/services/Geocache_UTM33_EUREF89/GeocacheBasis//foo/bar?query=string`
notice the // fefore foo

An ideal situation would be to have an option to force the ingress path to be threaded as regex, and to be able to redirect to whatever I want (even without a trailing /). 

Desired ingress config

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: foo
labels:
name: foo
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/ssl-redirect: "false"
ingress.kubernetes.io/secure-backends: "true"
ingress.kubernetes.io/paths-as-regex: "true"
ingress.kubernetes.io/rewrite-target: /foo/bar
spec:
tls:

  • hosts:

    • my.host.no

      secretName: star-gdo-tls-chain

      rules:

  • host: my.host.no
    http:
    paths:

    • path: /foo/bar

      backend:

      serviceName: foo

      servicePort: 6443

      ```

Which then ideally should result in the following being passed to the upstream service

my.host.no/foo/bar -> upstream/foo/bar
my.host.no/fOO/bAr/baz -> upstream/foo/bar/baz
my.host.no/foo/barBaz -> upstream/foo/barBaz
my.host.no/foO/barbecue?request=dinner&location=home -> upstream/foo/barbecue?request=dinner&location=home
lifecyclrotten nginx

Most helpful comment

This is still certainly an issue, even if people seem to have gone silent on it.

Being unable to use regex paths - as the docs state should work - is quite a pain, especially since there doesn't seem to be any notice anywhere about this.

All 13 comments

I use 0.9.0-beta.12 , and without a trailing / to the ingress.kubernetes.io/rewrite-target. And it works well, But add trailing / to ingress.kubernetes.io/rewrite-target lead to rewrite /xxx/(.*) /xxx//$1 break;

I'm using the same version. Rewrite works for me as well without the trailing /. But the problem is that the spec.rules.http.paths.path is not interpreted as regex if the rewrite doesn't end with a /.

It should not be, please see func buildProxyPass

I think the problem might be in the buildLocation function (&& location.Rewrite.Target != path), not the buildProxyPass. If the path and the ingress.kubernetes.io/rewrite-target are equal (which they are in my case if not adding a trailing / to the ingress.kubernetes.io/rewrite-targe), the nginx Location is not prefixed with ~* and handled as regex.

    if len(location.Rewrite.Target) > 0 && location.Rewrite.Target != path {
        if path == slash {
            return fmt.Sprintf("~* %s", path)
        }
        // baseuri regex will parse basename from the given location
        baseuri := `(?<baseuri>.*)`
        if !strings.HasSuffix(path, slash) {
            // 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 path

I would suggest a directive to specify that the spec.rules.http.paths.path should end up as a regex Location, regardless of what ingress.kubernetes.io/rewrite-target might be (even if it not specified). currently the only way to get the Location to be threatened as regex (added ~*) is to define a ingress.kubernetes.io/rewrite-target that is different from spec.rules.http.paths.path. This is the key issue in my case. I'm actually not even after the rewrite functionality in many cases, but just to get at regex Location.

After digging around in the issue tracker I can see several related issues. It turns out I'm not the only one who have found the current behavior a little confusing and limiting.

https://github.com/kubernetes/ingress/issues/619
https://github.com/kubernetes/ingress/issues/646
https://github.com/kubernetes/contrib/issues/1884

Are there any plans on how to take this further?

@paalkr before this we need proper e2e tests

@aledbf , so is that something I can contribute with? Or does it require developer skills?

Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale.
Stale issues rot after an additional 30d of inactivity and eventually close.

Prevent issues from auto-closing with an /lifecycle frozen comment.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or @fejta.
/lifecycle stale

Stale issues rot after 30d of inactivity.
Mark the issue as fresh with /remove-lifecycle rotten.
Rotten issues close after an additional 30d of inactivity.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/lifecycle rotten
/remove-lifecycle stale

This is still certainly an issue, even if people seem to have gone silent on it.

Being unable to use regex paths - as the docs state should work - is quite a pain, especially since there doesn't seem to be any notice anywhere about this.

This is still certainly an issue, even if people seem to have gone silent on it.

+

Rotten issues close after 30d of inactivity.
Reopen the issue with /reopen.
Mark the issue as fresh with /remove-lifecycle rotten.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/close

+

Was this page helpful?
0 / 5 - 0 ratings