CORS headers are broken for me since 2.0.6

I'm already running an elasticsearch instance behind Traefik for some time, now that I wanted to upgrade Traefik from a version previous 2.0.5 the CORS headers aren't working anymore like expected. Whenrequesting the elasticsearch instance with the help of elasticsearch.js this results in a cURL request copied like this from the web developer tools:

curl 'https://search.example.com/' -X OPTIONS -H 'authority: search.example.com' -H 'access-control-request-method: HEAD' -H 'origin: https://doc.example.com' -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36' -H 'access-control-request-headers: authorization' -H 'accept: */*' -H 'sec-fetch-site: same-site' -H 'sec-fetch-mode: cors' -H 'referer: https://doc.example.com/' -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7' --compressed -v

With Traefik <= 2.0.5 it resulted in a response like this:

> OPTIONS / HTTP/2
> Host: search.example.com
> authority: search.example.com
> access-control-request-method: HEAD
> origin: https://doc.example.com
> user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36
> access-control-request-headers: authorization
> accept: */*
> sec-fetch-site: same-site
> sec-fetch-mode: cors
> referer: https://doc.example.com/
> accept-encoding: gzip, deflate, br
> accept-language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< access-control-allow-credentials: true
< access-control-allow-headers: Authorization,Origin,Content-Type,Accept
< access-control-allow-methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
< access-control-allow-origin: *
< access-control-max-age: 0
< content-length: 0
< date: Thu, 09 Jan 2020 12:57:09 GMT
<
* Connection #0 to host search.example.com left intact
* Closing connection 0

Since Traefik >= 2.0.6 (IMHO it's related to https://github.com/containous/traefik/pull/4857) the response changed:

> OPTIONS / HTTP/2
> Host: search.example.com
> authority: search.example.com
> access-control-request-method: HEAD
> origin: https://doc.example.com
> user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36
> access-control-request-headers: authorization
> accept: */*
> sec-fetch-site: same-site
> sec-fetch-mode: cors
> referer: https://doc.example.com/
> accept-encoding: gzip, deflate, br
> accept-language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 200
< content-length: 0
< date: Thu, 09 Jan 2020 12:24:32 GMT
<
* Connection #0 to host search.example.com left intact
* Closing connection 0

So it's quite obvious that my browser errors out with something like Access to XMLHttpRequest at 'https://search.example.com/_search?q=con' from origin 'https://doc.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

So far I have defined this middleware, even if it's quite much open, it simply worked:

  --label traefik.http.middlewares.elasticsearch.headers.accesscontrolalloworigin=* \
  --label traefik.http.middlewares.elasticsearch.headers.accesscontrolallowcredentials=true \
  --label traefik.http.middlewares.elasticsearch.headers.accesscontrolallowheaders=Authorization,Origin,Content-Type,Accept \
  --label traefik.http.middlewares.elasticsearch.headers.accesscontrolallowmethods=GET,POST,PUT,PATCH,DELETE,OPTIONS \
  --label traefik.http.middlewares.elasticsearch.headers.customresponseheaders.Access-Control-Allow-Headers=Authorization,Origin,Content-Type,Accept \
  --label traefik.http.middlewares.elasticsearch.headers.customresponseheaders.Access-Control-Allow-Methods=GET,POST,PUT,PATCH,DELETE,OPTIONS \

I'm not sure how to resolve the issue now, I can see that Traefik is loggin the request as an OPTIONS request, but it simply doesn't return the configured CORS headers. What can I do? Is this a regression?

I just realized that I even had to keep the version at 2.0.2, not 2.0.5 :frowning:

Hello,

The PR 4857 was merged in v2.0.0-beta1, so I think it's not related.

But it can be related to 5835 in v2.0.6

Could you provide a minimal reproducible example (docker-compose file if possible), thanks.

Have you try with v2.1.2?

I can say for sure that 2.0.2 is the last version where I get the CORS headers for the mentioned curl request. I will try to prepare a docker-compose config to demonstrate the behavior.

Looks like the ordering of the used middlewares had been the issue for that. Is this really an expected behavior? Or is it just a bug? I have defined the following static middlewares within the traefik config:

http:
  middlewares:
    https:
      redirectScheme:
        permanent: true
        scheme: https
    secure:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        customFrameOptionsValue: SAMEORIGIN
        forceSTSHeader: true
        referrerPolicy: strict-origin-when-cross-origin
        sslRedirect: true
        stsIncludeSubdomains: false
        stsPreload: true
        stsSeconds: 315360000
    errors:
      errors:
        query: /{status}.html
        service: errors@docker
        status:
        - 404

When I'm launching the service with the following definition I don't get the CORS headers sent for the mentioned requests:

  --label traefik.enable=true \
  --label traefik.docker.network=traefik \
  --label traefik.http.services.elasticsearch.loadbalancer.server.port=9200 \
  --label traefik.http.services.elasticsearch.loadbalancer.server.scheme=http \
  --label traefik.http.services.elasticsearch.loadbalancer.passhostheader=true \
  --label traefik.http.routers.elasticsearch.rule="Host(`search.example.com`)" \
  --label traefik.http.routers.elasticsearch.service=elasticsearch@docker \
  --label traefik.http.routers.elasticsearch.entrypoints=https \
  --label traefik.http.routers.elasticsearch.tls=true \
  --label traefik.http.routers.elasticsearch.middlewares=secure@file,errors@file,elasticsearch@docker \
  --label traefik.http.routers.elasticsearch-insecure.rule="Host(`search.example.com`)" \
  --label traefik.http.routers.elasticsearch-insecure.service=elasticsearch@docker \
  --label traefik.http.routers.elasticsearch-insecure.entrypoints=http \
  --label traefik.http.routers.elasticsearch-insecure.middlewares=https@file,errors@file,elasticsearch@docker \
  --label traefik.http.middlewares.elasticsearch.headers.accesscontrolalloworigin=* \
  --label traefik.http.middlewares.elasticsearch.headers.accesscontrolallowcredentials=true \
  --label traefik.http.middlewares.elasticsearch.headers.accesscontrolallowheaders=Authorization,Origin,Content-Type,Accept \
  --label traefik.http.middlewares.elasticsearch.headers.accesscontrolallowmethods=GET,POST,PUT,PATCH,DELETE,OPTIONS \
  --label traefik.http.middlewares.elasticsearch.headers.customresponseheaders.Access-Control-Allow-Headers=Authorization,Origin,Content-Type,Accept \
  --label traefik.http.middlewares.elasticsearch.headers.customresponseheaders.Access-Control-Allow-Methods=GET,POST,PUT,PATCH,DELETE,OPTIONS \

But if I change the ordering of the middlewares like secure@file,errors@file,elasticsearch@docker to elasticsearch@docker,secure@file,errors@file and https@file,errors@file,elasticsearch@docker to elasticsearch@docker,https@file,errors@file (simply put the cors headers middleware at the first place) it magically starts to work as I would expect. Really a strange issue to me...

1 Like