emissary-ingress / emissary

open source Kubernetes-native API gateway for microservices built on the Envoy Proxy
https://www.getambassador.io
Apache License 2.0
4.37k stars 687 forks source link

cors mapping doesnt work with filters #2962

Open Yavin opened 4 years ago

Yavin commented 4 years ago

Describe the bug When filter is specified then CORS specified for mapping doesn't work. As I understand maybe filters are applied before mapping, but I didn't found any way for filter to be aplied after mapping or to pass OPTIONS request.

To Reproduce I have mapping and OIDC filter specified together for a service

apiVersion: getambassador.io/v2
kind: FilterPolicy
metadata:
  name: api-filter-policy
spec:
  rules:
    - host: api.example.com
      path: "*"
      filters:
        - name: api-filter
apiVersion: getambassador.io/v2
kind: Filter
metadata:
  name: api-filter
spec:
  OAuth2:
    authorizationURL: "https://keycloak.example.com/auth/realms/api"
    grantType: "AuthorizationCode"
    protectedOrigins:
      - origin: "https://api.example.com"
        internalOrigin: "*://*"
    audience: api-client
    clientID: api-client
    secret: "secretString"
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
  name: api-mapping
spec:
  host: api.example.com
  prefix: /
  service: api-service:8080
  cors:
    origins: '*'
    methods: '*'
    headers: '*'

Expected behavior A way for CORS to work with filters

Versions (please complete the following information):

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

havenotfear commented 3 years ago

Any update on this?

havenotfear commented 3 years ago

I did find a slight work around to this. If you skip the filter on the Access-Control-Request-Method header it should bypass the options request. The second filters block will pass the call through since the onDeny has the continue option. Good luck

  - name: my-filter
    arguments:
      insteadOfRedirect:
        ifRequestHeader:
          name: "Access-Control-Request-Method"
        filters:
          - name: my-filter
            onDeny: "continue"
michael4screen commented 3 years ago

Hi @havenotfear ,

I came accross because i have the same issue right now. Your solution might work but you will allow any request to bypass your auth filter only by setting the Access-Control-Request-Method header. If i understand it correctly, your solution is not restricted to allow OPTIONS requests only. This is a security issue then.

Maybe you have found another solution already ?

havenotfear commented 3 years ago

That's a good point. Our downstream services do token validation so I was not concerned about security issues with this approach. This is definitely worth mentioning in the case where your downstream services don't validate JWTs.

paulwitt commented 3 years ago

We're having the same issue. When the Filter and FilterPolicy aren't in place the CORS mapping works just as expected. @havenotfear 's comment is an issue for us as our JWT Filter is the only token validation we have in place. If there was a way to link mappings to filterpolicies then we might be able to work around this using the method definition of the mapping. But as far as I can tell, the only thing linking filters/filterpolicies to mappings is the host definition.

(EDIT) We're still testing it but I think this will work around the issue:


kind: FilterPolicy
metadata:
  name: my-service
  namespace: my-service
spec:
  rules:
  - host: my-service.my-domain.com
    path: "*"
    filters:
    - name: my-service
      namespace: my-service
      ifRequestHeader:
        name: ":method"
        value: "OPTIONS"
        negate: true
      arguments:
        ...```
matthiasdg commented 3 years ago

@paulwitt how did you find out you can use the ifRequestHeader for methods like that? I've seen people ask this question before and I've never found an answer. It's indeed working! (My solution was to create an External Filter linked to a service that returns a 200 for OPTIONS and 401 for everything else and put that in front of the OAuth2 filter we're using in the FilterPolicy, with the onDeny/onAllow directives)

edit: looked around a bit and found indeed https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route_components.proto#envoy-api-msg-route-headermatcher in the envoy docs. Still amazing that you found this! (didn't know about HTTP/2 psuedo-header fields either)

paulwitt commented 3 years ago

I had the developer record the network details from his browser and that was one of the headers. Tested that I couldn't force that header through curl or postman and then tested it in the filterpolicy.

Coincidentally, an Ambassador QA person (on their slack) got the same info out of one of their engineers less than 5 minutes after I found it. If I'd have been more patient I could've saved myself two days of pulling my hair out and had the same result.

cortopy commented 3 years ago

I don't know what I'm missing but neither ifRequestHeader nor insteadOfRedirect workarounds worked for me. OPTIONS requests still go to the filter instead of getting the configured Emissary's response

discointheair commented 1 year ago

Any experience with this bug for major versions 2 and 3? Does it still exists there? I also run into the problem that http redirects (307,303) (maybe after auth expiry) to keykloak do not include CORS Headers. With valid auth session CORS is included in the responses like defined in that mapping. Other question would be if ambassador is able to refresh the auth token instead of redirecting. Used version: 1.14.4 Ref #3279