-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
add dynamic check of origin for CORS #7904
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
Conversation
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.
Cool! Nice approach that can further streamline the CORS UX. 🎉
Was able to replicate with the following steps:
- create a CloudFront distribution, as per our docs:
$ awslocal s3 mb s3://test
$ echo 'Hello World' > /tmp/hello.txt
$ awslocal s3 cp /tmp/hello.txt s3://test/hello.txt --acl public-read
$ domain=$(awslocal cloudfront create-distribution --origin-domain-name test.s3.amazonaws.com | jq -r '.Distribution.DomainName')
$ echo $domain
ce6554a5.cloudfront.localhost.localstack.cloud
- create a simple
index.html
file that connects to the enpoint:
<html>
<script>
fetch('http://ce6554a5.cloudfront.localhost.localstack.cloud:4566/hello.txt');
</script>
</html>
- upload the file to a bucket and expose it as an S3 website:
awslocal s3 cp /tmp/index.html s3://test/index.html --content-type text/html
awslocal s3api put-bucket-website --bucket test --website-configuration 'ErrorDocument={Key=index.html},IndexDocument={Suffix=index.html}'
- open http://test.s3-website.localhost.localstack.cloud:4566/index.html in the browser, observe the Network console (request to
http://ce6554a5.cloudfront.localhost.localstack.cloud:4566/hello.txt
)
Result:
- returns a
403
error on current master 🟥 - returns
200
and expected file content with the changes in this PR ✅
To me, this achieves a decent balance between improving convenience (works out of the box without extra configs), while not sacrificing security (requests still only allowed from *.localhost.localstack.cloud
pages in the browser). 👍 As usual with CORS or security-critical configurations, would be good to get a second pair of eyes on it, though.. 👀 /cc @dfangl @alexrashed @thrau
for protocol in {"http", "https"}: | ||
for port in _ports: | ||
for port in _get_allowed_cors_ports(): |
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.
nit: we could also use _ALLOWED_INTERNAL_PORTS
here, for consistency. (although the performance impact is probably negligible).
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.
That's what I thought and did at first, but then it would be generated only once, and in your other tests where you monkey patch ALLOWED_CORS_ORIGINS
, you'd have to monkey patch _ALLOWED_INTERNAL_PORTS
before. I thought it'd make your tests a bit more complicated.
localstack/tests/unit/test_cors.py
Lines 18 to 21 in be7178d
# test allowed origins for extended config (HTTPS edge port 443, HTTP edge port 4566) | |
monkeypatch.setattr(config, "EDGE_PORT", 443) | |
monkeypatch.setattr(config, "EDGE_PORT_HTTP", 4566) | |
origins = cors._get_allowed_cors_origins() |
Thanks @whummer for creating a reproducible sample! Just a note:
The requests are only allowed from |
a693385
to
c87decd
Compare
c87decd
to
f61cd88
Compare
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 this is fantastic 🎉 I don't see a big issue right now 👍
localstack/aws/handlers/cors.py
Outdated
@@ -166,11 +196,25 @@ def is_cors_origin_allowed(headers: Headers) -> bool: | |||
return True | |||
|
|||
@staticmethod | |||
@log_duration(min_ms=0) |
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.
nit: could we remove this again?
localstack/aws/handlers/cors.py
Outdated
from ...constants import LOCALHOST, LOCALHOST_HOSTNAME, PATH_USER_REQUEST | ||
from ...utils.bootstrap import log_duration |
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.
nit: ideally we don't use relative imports of modules that are outside the scope of the component we are building. basically: relative imports are used when things belong together in a component, absolute imports are used when importing code that represents a dependency to another component
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.
Right, I'm not sure why there are dynamic imports here, I can change them 👍
This is a follow up from #7554, from an idea proposed by @whummer (in this comment) , with dynamic handling of CORS origin for web resource served by LocalStack.
This is not mergeable as is, as it still has some logging regarding performance, but I'd like to have some comments / opinions.
Scenario:
LocalStack is serving a resource meant to be accessed from the browser (CloudFront or S3 website).
But this resource needs to access LocalStack resources as well. Right now, the only way is to set the environment variables of LocalStack to allow the Origin (let’s say,
http://<bucket>.s3-website.localhost.localstack.cloud:4566
).Fix
This PR adds a dynamic check of the origin, to validate if it follows some regex matching 2 service domains known for serving content accessible from a user browser (thus enforcing CORS). We can then add a regex for every resource subdomain in that situation.
The performance right now is around 4µs on my local machine when a request would be either rejected or matching
s3-website
orcloudfront
. The performance would not be affected for most requests, as we either don't match CORS because of noOrigin
header, or the request is normally allowed.\cc @whummer @dfangl @alexrashed