-
Notifications
You must be signed in to change notification settings - Fork 504
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GEP 1767: CORS Filter #3435
base: main
Are you sure you want to change the base?
GEP 1767: CORS Filter #3435
Conversation
Hi @lianglli. Thanks for your PR. I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. 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-sigs/prow repository. |
Thanks for the energy here @liangli, but the correct process here is for you to add some discussion about this feature to the proposed set of Experimental changes in #3403, and then, if this CORS GEP is selected for an Experimental slot by the community's voting, this PR will be able to move forward. This is a useful feature that already has a GEP, has been discussed before, and is not too big, so it's reasonably likely that it will be included if we can free up enough Experimental slots by moving things to Standard. I note that you added this to the 1.2 Experimental discussion as well, so it's fine to just reuse the same description in the v1.3 scoping discussion there so that any new folks will have the context to vote. Until then, sadly, this PR will need to be on hold. /hold |
Thanks for writing up this GEP @lianglli! This made the cut for v1.3 release scoping, it's great to already have a GEP PR ready for review, will take a look shortly. /ok-to-test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all your work on this @lianglli! This is incredibly detailed and well written. I took a first pass and left some initial comments.
geps/gep-1767/index.md
Outdated
// When responding to a credentialed requests, the gateway must specify | ||
// one or more HTTP headers in the value of the Access-Control-Allow-Headers response header, | ||
// instead of specifying the * wildcard. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if a user has configured *
here? Should the Gateway implementation reject the request? Or do we just need to add CEL validation to prevent allowCredentials
being set to true
at the same time as this is set to *
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you may be mixing up the user HTTPRoute config and the server's expected response. a user can legitimately set *
in the config certainly -- this seems to be saying they cannot set it in the response header.
However, I am not sure why? The spec does not seem to reject *
? https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers#syntax
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We had someone in ingress-nginx, request that we updated CORS to accept * and null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that Envoy can't return *
directly because it treats *
specially:
https://github.com/envoyproxy/envoy/blob/6445d0dcdd33413e47454221fa80b3cfabea7d4e/source/extensions/filters/http/cors/cors_filter.cc#L159-L163.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if a user has configured
*
here? Should the Gateway implementation reject the request? Or do we just need to add CEL validation to preventallowCredentials
being set totrue
at the same time as this is set to*
?
When responding to a credentialed requests, the response headers Access-Control-Allow-Origin
, Access-Control-Expose-Headers
, Access-Control-Allow-Methods
and Access-Control-Allow-Headers
can NOT use *
as value.
However, if a HTTPCORSFilter sets AllowCredentials
as true and configures * for AllowHeaders
, the gateway will return HTTP response header Access-Control-Allow-Credentials: true
and Access-Control-Allow-Headers: *
to the "preflight" request based on the HTTPCORSFilter specifically.
Then, clients will NOT be able to send actual cross-origin request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that Envoy can't return
*
directly because it treats*
specially: https://github.com/envoyproxy/envoy/blob/6445d0dcdd33413e47454221fa80b3cfabea7d4e/source/extensions/filters/http/cors/cors_filter.cc#L159-L163.
Most gateways support the *
wildcard as the value of a HTTP header.
Moreover, *
wildcard is a valid character of header value based on rfc7230.
Hence, the envoy should support it in future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This field (and this spec in general) is pretty unclear as to which "user" we're talking about.
I suggest using different names for the HTTPRoute owner (maybe "user" is okay here), and the client, (should probably use that word instead).
So, I think what @robscott was saying is "What happens if the HTTPRoute owner has configured *
here?"
The text says:
// When responding to a credentialed requests, the gateway must specify
// one or more HTTP headers in the value of the Access-Control-Allow-Headers response header,
// instead of specifying the * wildcard.
What headers should the implementation respond with? Below, @spacewander suggests responding with whatever was sent in the Access-Control-Request-Headers
field in the OPTIONS request. Whatever we want to do MUST be defined in this field, as what to do in the other cases:
*
is configured in this field, andAccess-Control-Request-Headers
is not sent- One or more values are configured in this field and
Access-Control-Request-Headers
is sent - One or more values are configured in this field and
Access-Control-Request-Headers
is not sent
What we are aiming for is that someone who reads the spec should be able to understand enough to write conformance tests for the spec, ideally without looking at any other documents.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// A wildcard indicates that the requests with all HTTP headers are allowed.
// The Access-Control-Allow-Headers response header can only use `*` wildcard
// as value when the field AllowCredentials is unspecified.
//
// Input:
// Access-Control-Request-Headers: Content-Type, Cache-Control
//
// Config:
// allowHeaders: ["*"]
//
// Output:
// Access-Control-Allow-Headers: *
//
// When the field AllowCredentials is specified, the gateway must specify
// one or more HTTP headers in the value of the Access-Control-Allow-Headers
// response header, instead of specifying the `*` wildcard. The value of the
// header Access-Control-Allow-Headers same as the Access-Control-Request-Headers
// header provided by the client.
//
// Input:
// Access-Control-Request-Headers: Content-Type, Cache-Control
//
// Config:
// allowHeaders: ["*"]
// allowCredentials: true
//
// Output:
// Access-Control-Allow-Headers: Content-Type, Cache-Control
// Access-Control-Allow-Credentials: true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- One or more values are configured in this field and Access-Control-Request-Headers is sent
- One or more values are configured in this field and Access-Control-Request-Headers is not sent
// Multiple header names in the value of the Access-Control-Allow-Headers
// response header are separated by a comma (",").
//
// Config:
// allowHeaders: ["DNT", "Keep-Alive", "User-Agent", "X-Requested-With", "If-Modified-Since", "Cache-Control", "Content-Type", "Range", "Authorization"]
//
// Output:
// Access-Control-Allow-Headers: DNT, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization
If one or more values are configured in this field AllowHeaders
, the gateway always return response header 'Access-Control-Allow-Headers: $allowHeaders' , regardless of whether the request includes the header Access-Control-Request-Headers.
geps/gep-1767/index.md
Outdated
// MaxAge indicates the duration (in seconds) for the client to cache | ||
// the results of a "preflight" request. | ||
// | ||
// The default value of header Access-Control-Max-Age is 5 (seconds). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have a default here at all? This seems like it could be surprising to users unless it's common for implementations to have a default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on Fetch Living Standard, the default value of Access-Control-Max-Age is 5 (seconds).
If a HTTPCORSFilter does NOT set 'MaxAge' specifically, the gateway will return the header "Access-Control-Max-Age: 5" to clients by default.
For example, the default value of Access-Control-Max-Age for Ingress-NGINX Enable CORS
is 1728000 seconds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The question I have: if unset, should the server respond Access-Control-Max-Age: 5
, or should the server not include anything and let the client decide (where it will likely default to 5s)?
Envoy prior art is "not include"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the default value for MDN is 5 seconds, I think it's good to set an explicit default here: in case the gateway does not include such a header, it's up to the client such a decision, which is never a good thing in my opinion. Setting a default here makes the whole thing explicit and always defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the default is 'unset', we support all cases: if a user wants it to be unset (and let client decide) they can, and if a user wants it set they can explicitly set it.
Not sure how valid this is though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the GEP -- top notch details!
geps/gep-1767/index.md
Outdated
// When responding to a credentialed requests, the gateway must specify | ||
// one or more HTTP headers in the value of the Access-Control-Allow-Headers response header, | ||
// instead of specifying the * wildcard. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you may be mixing up the user HTTPRoute config and the server's expected response. a user can legitimately set *
in the config certainly -- this seems to be saying they cannot set it in the response header.
However, I am not sure why? The spec does not seem to reject *
? https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers#syntax
geps/gep-1767/index.md
Outdated
// MaxAge indicates the duration (in seconds) for the client to cache | ||
// the results of a "preflight" request. | ||
// | ||
// The default value of header Access-Control-Max-Age is 5 (seconds). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The question I have: if unset, should the server respond Access-Control-Max-Age: 5
, or should the server not include anything and let the client decide (where it will likely default to 5s)?
Envoy prior art is "not include"
geps/gep-1767/index.md
Outdated
// When responding to a credentialed requests, the gateway must specify | ||
// one or more HTTP headers in the value of the Access-Control-Allow-Headers response header, | ||
// instead of specifying the * wildcard. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that Envoy can't return *
directly because it treats *
specially:
https://github.com/envoyproxy/envoy/blob/6445d0dcdd33413e47454221fa80b3cfabea7d4e/source/extensions/filters/http/cors/cors_filter.cc#L159-L163.
geps/gep-1767/index.md
Outdated
// MaxAge indicates the duration (in seconds) for the client to cache | ||
// the results of a "preflight" request. | ||
// | ||
// The default value of header Access-Control-Max-Age is 5 (seconds). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the default value for MDN is 5 seconds, I think it's good to set an explicit default here: in case the gateway does not include such a header, it's up to the client such a decision, which is never a good thing in my opinion. Setting a default here makes the whole thing explicit and always defined.
CORS GEP updates based on the review comments. Co-authored-by: Mattia Lavacca <[email protected]>
…ontrol-Allow-Methods, Access-Control-Allow-Headers and Access-Control-Expose-Headers are separated by a comma
…ds, Access-Control-Allow-Headers and Access-Control-Expose-Headers
Thanks for all the work on this @lianglli! /lgtm Although I reopened a couple comment threads that I think could use a bit more discussion, they're clearly small enough to cover when we get into the next phase of the enhancement process with additions to API types. |
// | ||
// If any header name in the `Access-Control-Request-Headers` request header | ||
// is not included in the list of header names specified by the response header | ||
// `Access-Control-Allow-Methods`, it will present an error on the client side. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and next paragraph, I think the Access-Control-Allow-Headers and Access-Control-Allow_methods headers are mixed up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the work on this, @lianglli!
/approve
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: lianglli, mlavacca, robscott The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Thanks for all the work on this @lianglli! I'll hold off on merging this until the most recent comment has been resolved, but IMO this is sufficiently close to keep it in the v1.3 milestone and on to the next phase. |
What type of PR is this?
/kind gep
What this PR does / why we need it:
This GEP proposes to add a new field
HTTPCORSFilter
toHTTPRouteFilter
.Which issue(s) this PR fixes:
Fixes #1767
Does this PR introduce a user-facing change?: