Ingress-nginx: FEATURE REQUEST: set cors access-control-allow-origin to incoming origin

Created on 17 May 2018  Â·  27Comments  Â·  Source: kubernetes/ingress-nginx

In many cases, a public api is desired with the ability to run commands with public credentials, but the default cors allow-origin is set to '*' which will not allow a request with credentials to be exposed by the browser.

Failed to load xxxx: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'xxxx' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

A knob in the annotations to allow this and copy the origin into the outgoing request would be super nice!

kinfeature lifecyclrotten

Most helpful comment

I didn't want to open a separate ticket, but I would welcome a solution where I could whitelist what Origin values are Allowed to be reflected back in ACAO.

Eg. I have An API that two web frontends are accessing, but I only want to allow that API to be accessed from those two frontends and nowhere else:
Eg. api.myapp.example.com should reflect the Origin header value only if Origin equals web1.myapp.example.com, or web2.myapp.example.com.

I would even go so far as to allow reflecting all subdomains of a given domain (seconding the request of @ahoulgrave)

Eg.:

nginx-ingress.kubernetes.io/cors-reflect-subdomains: myapp.example.com

Could simply reflect all Origins in ACAO if they are subdomains of myapp.example.com.

I can prepare a PR for this if its something more people would want.

All 27 comments

@aledbf yes - that

nginx.ingress.kubernetes.io/cors-allow-origin controls what's the accepted Origin for CORS and defaults to '*'. This is a single field value, with the following format: http(s)://origin-site.com or http(s)://origin-site.com:port Example: nginx.ingress.kubernetes.io/cors-allow-origin: "https://origin-site.com:4443"

if you send a fetch() request with credentials to an ingress rule setup with the default "*", the browser wont expose it to the running script because wildcards are not allowed on access-control-allow-credentials: true. It would be super nice to have a knob that copies the incoming Origin: host.com header to the outgoing allow-origin header.

@dgregoire
Did u solve the problem in another way?

upd:
Found the solution

    nginx.ingress.kubernetes.io/configuration-snippet: |
      add_header Access-Control-Allow-Origin $http_origin;
      add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
      add_header Access-Control-Allow-Credentials true;

@odinsy
Thanks, good idea!

@aledbf
How do you feel about having the controller handle that case?

@claudiuchis provided a workaround at https://github.com/kubernetes/ingress-nginx/issues/1171#issuecomment-391988766

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.

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

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

@fejta-bot: Closing this issue.

In response to this:

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

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

Would be nice for the ingress to handle *.domain.com out of the box. It's very ugly to add all these custom large snippets

@dgregoire
Did u solve the problem in another way?

upd:
Found the solution

    nginx.ingress.kubernetes.io/configuration-snippet: |
      add_header Access-Control-Allow-Origin $http_origin;
      add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
      add_header Access-Control-Allow-Credentials true;

If backend return page with error code, this headers missing, I think, this is better solution:

    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS"
    nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
    nginx.ingress.kubernetes.io/configuration-snippet: |
       more_set_headers "Access-Control-Allow-Origin: $http_origin";

I didn't want to open a separate ticket, but I would welcome a solution where I could whitelist what Origin values are Allowed to be reflected back in ACAO.

Eg. I have An API that two web frontends are accessing, but I only want to allow that API to be accessed from those two frontends and nowhere else:
Eg. api.myapp.example.com should reflect the Origin header value only if Origin equals web1.myapp.example.com, or web2.myapp.example.com.

I would even go so far as to allow reflecting all subdomains of a given domain (seconding the request of @ahoulgrave)

Eg.:

nginx-ingress.kubernetes.io/cors-reflect-subdomains: myapp.example.com

Could simply reflect all Origins in ACAO if they are subdomains of myapp.example.com.

I can prepare a PR for this if its something more people would want.

@dgregoire
Did u solve the problem in another way?
upd:
Found the solution

    nginx.ingress.kubernetes.io/configuration-snippet: |
      add_header Access-Control-Allow-Origin $http_origin;
      add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
      add_header Access-Control-Allow-Credentials true;

If backend return page with error code, this headers missing, I think, this is better solution:

    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS"
    nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
    nginx.ingress.kubernetes.io/configuration-snippet: |
       more_set_headers "Access-Control-Allow-Origin: $http_origin";

I tried a similar configuration, but observed the following with nginx-ingress 0.25.0:

➜ curl -I -X OPTIONS -H "Origin: https://frontend.domain" https://backend.domain
HTTP/2 204
server: openresty/1.15.8.1
date: Tue, 24 Sep 2019 00:04:37 GMT
content-type: text/html
content-length: 0
access-control-allow-origin: *

It seems as though when the nginx.ingress.kubernetes.io/enable-cors: "true" annotation is present, you cannot more_set_headers Access-Control-Allow-Origin on OPTIONS requests. For me it was always overridden to *

@aledbf can we get a response and a possible reopen of this ticket as an improvement based on @reegnz suggestion?

@jeffreybrowning I am sorry, but I am not actively working on this project since the last release (0.26.1). Please check https://github.com/kubernetes/ingress-nginx/issues/4404

Pull requests are always welcome

/reopen

@aledbf: Reopened this issue.

In response to this:

/reopen

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@btsuhako Hi!
You can do like that:
ingress.kubernetes.io/cors-allow-origin: "$http_origin"

@avchu thanks! adding only that annotation seems to get the response we're expecting. the problem is when we add the nginx.ingress.kubernetes.io/enable-cors: "true" annotation, which seems to override every OPTIONS request, and sets the access-control-allow-origin: * header to every response, instead of using the $http_origin

@btsuhako Yes, I know, I had this situation today. But there is no other solution because default of '*' value overrides by cors-allow-origin, and then configuration-snippet became redundant

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.

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

@avchu my biggest issue with cors-allow-origin is that it is limited to a single origin. I usually put multiple domains behind ingress-nginx, and I'd prefer a way to provide a list of domains that are allowed. That is only possible if you provide a way to reflect back the Host header of a request, and then whitelist what domains are allowed to be reflected back to the caller.

Right now I have a workaround with http-snippet and location-snippet in the ingress configmap:

http-snippet: |
      # CORS start
      map $http_origin $allow_origin {
        hostnames;
        default "";
        *.example.com "$http_origin";
        *.docker "$http_origin";
        *.invalid "$http_origin";
        *.localhost "$http_origin";
      }
      map $allow_origin $cors_allow_credentials {
        default "true";
        "" "";
      }
      map $request_method $cors_method {
        default "allowed";
        "OPTIONS" "preflight";
      }
      map $cors_method $cors_max_age {
        default "";
        "preflight" 900;
      }
      map $cors_method $cors_allow_methods {
        default "";
        "preflight" "GET, PUT, POST, DELETE, PATCH, OPTIONS";
      }
      map $cors_method $cors_allow_headers {
        default "";
        "preflight" "DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization";
      }
      # CORS end

location-snippet: |
      # CORS start
      more_set_headers 'Access-Control-Allow-Origin: $allow_origin';
      more_set_headers 'Access-Control-Allow-Credentials: $cors_allow_credentials';
      more_set_headers 'Access-Control-Allow-Methods: $cors_allow_methods';
      more_set_headers 'Access-Control-Allow-Headers: $cors_allow_headers';
      more_set_headers 'Access-Control-Max-Age: $cors_max_age';
      more_set_headers 'Vary: Origin';

      if ($request_method = 'OPTIONS') {
          more_set_headers 'Content-Length: 0';
          more_set_headers 'Content-Type: text/plain';
          return 204;
      }
      # CORS end

It's based on various solutions for CORS that I've found, but mainly these two:
https://gist.github.com/sbuzonas/6e2dbc1218a0be0d7ae2
https://gist.github.com/huandu/aa8de89ba12a8139f518eedfd07fb1a5

@reegnz Thanks for your example config. Why did you include the '.docker' and '.localhost' in the origin list?

@bastoker that's just in my case the domains I use locally on my dev machine.
On production you'd only include the domains you whitelist for cross-origin requests.

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

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

@fejta-bot: Closing this issue.

In response to this:

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

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cxj110 picture cxj110  Â·  3Comments

cabrinoob picture cabrinoob  Â·  3Comments

cehoffman picture cehoffman  Â·  3Comments

lachlancooper picture lachlancooper  Â·  3Comments

natemurthy picture natemurthy  Â·  3Comments