-
Notifications
You must be signed in to change notification settings - Fork 803
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
fix: ensure we do not rewrite external Origin headers in wrangler dev #4950
Conversation
🦋 Changeset detectedLatest commit: ded262e The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
A wrangler prerelease is available for testing. You can install this latest build in your project with: npm install --save-dev https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/runs/7843359492/npm-package-wrangler-4950 You can reference the automatically updated head of this PR with: npm install --save-dev https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/prs/4950/npm-package-wrangler-4950 Or you can use npx https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/runs/7843359492/npm-package-wrangler-4950 dev path/to/script.js Additional artifacts:npx https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/runs/7843359492/npm-package-create-cloudflare-4950 --no-auto-update npm install https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/runs/7843359492/npm-package-miniflare-4950 npm install https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/runs/7843359492/npm-package-cloudflare-pages-shared-4950 Note that these links will no longer work once the GitHub Actions artifact expires.
Please ensure constraints are pinned, and |
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #4950 +/- ##
==========================================
+ Coverage 70.43% 70.52% +0.08%
==========================================
Files 295 295
Lines 15407 15419 +12
Branches 3948 3953 +5
==========================================
+ Hits 10852 10874 +22
+ Misses 4555 4545 -10
|
headers: { | ||
"cf-workers-preview-token": workerPreviewToken.value, | ||
Cookie: accessToken && `CF_Authorization=${accessToken}`, | ||
...(accessToken ? { Cookie: `CF_Authorization=${accessToken}` } : {}), |
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 noted that we had to make the headers
record allow undefined
values just to accommodate this Cookie
assignment. But in reality we don't actually want to assign undefined
to Cookie, since the code later one would result in the Cookie looking like: previous-cookies;undefined
.
So I fixed this up here, removed the undefined
from the type and was able to avoid a !
operator later on.
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 is fair enough. And is probably the "correct" solution. But this type will eventually be exposed to users to write themselves, and I believe allowing undefined
will be more ergonomic and subtly improve DX
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'm not convinced that allowing undefined
here adds to user DX. If anything it creates a mental overhead because the user has to think about what setting to undefined
actually means (when it actually means ignore this header and not remove the original header).
hostname: props.host, | ||
port: props.port.toString(), |
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.
Adding these here also fixes the remote dev mode Origin header rewriting!
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 might be a bit side-effecty... This will add a MF-Original-URL header which won't be interpreted and removed by miniflare in remote mode.
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.
Yeah I was a bit worried about that. Should we create new override properties that are specifically for header rewrites?
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.
Actually, as far as I can tell MF-Original-URL
and MF-Disable-Pretty-Error
are always set on every proxy worker request whether in local or remote mode. So I don't think that this change actually introduces any new side effect.
Whether those headers should be added in remote mode is a question for another PR I think.
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.
Yeah let's follow up in another PR if needed.
I think in remote mode, the inner url overrides aren't set so the header wouldn't have been set?
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.
e904944
to
3c37d20
Compare
hostname: props.host, | ||
port: props.port.toString(), |
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 might be a bit side-effecty... This will add a MF-Original-URL header which won't be interpreted and removed by miniflare in remote mode.
headers: { | ||
"cf-workers-preview-token": workerPreviewToken.value, | ||
Cookie: accessToken && `CF_Authorization=${accessToken}`, | ||
...(accessToken ? { Cookie: `CF_Authorization=${accessToken}` } : {}), |
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 is fair enough. And is probably the "correct" solution. But this type will eventually be exposed to users to write themselves, and I believe allowing undefined
will be more ergonomic and subtly improve DX
/** | ||
* Rewrite references to URLs in request headers. | ||
* | ||
* This function is used to map the URLs in headers like Origin and Access-Control-Allow-Origin |
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.
Access-Control-Allow-Origin is a response header so I assume you expect this function to be used in both directions?
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'll tweak the description.
@@ -127,6 +130,8 @@ export class ProxyWorker implements DurableObject { | |||
// if we decide to await, we should include a timeout (~100ms) in case the user worker has long-running/parellel requests | |||
void fetch(userWorkerUrl, new Request(request, { headers })) | |||
.then((res) => { | |||
rewriteUrlRelatedHeaders(headers, innerUrl, outerUrl); |
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.
Ah yes both directions! This is clever. Now the browser will have no idea that the URL was faked inside the worker either 😄
Small bug/typo:
rewriteUrlRelatedHeaders(headers, innerUrl, outerUrl); | |
rewriteUrlRelatedHeaders(res.headers, innerUrl, outerUrl); |
But you might not be able to mutate response headers without:
rewriteUrlRelatedHeaders(headers, innerUrl, outerUrl); | |
res = new Response(res); | |
rewriteUrlRelatedHeaders(res.headers, innerUrl, outerUrl); |
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 not a typo! It's a bona fide mistake! Thanks for spotting that.
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 also think this is a "new"-ish feature that should get its own changeset and documentation. It's really cool that a dev could write Response.redirect(`${url.origin}/redirect/path`)
and that would "just work"™️ in wrangler dev [--remote]
– we should shout from the rooftops!
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.
Strictly this was already part of the old proxy:
function rewriteRemoteHostToLocalHostInHeaders( |
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.
Fixed this problem in a fixup commit and added a test to prove it.
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 review
headers: { | ||
"cf-workers-preview-token": workerPreviewToken.value, | ||
Cookie: accessToken && `CF_Authorization=${accessToken}`, | ||
...(accessToken ? { Cookie: `CF_Authorization=${accessToken}` } : {}), |
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'm not convinced that allowing undefined
here adds to user DX. If anything it creates a mental overhead because the user has to think about what setting to undefined
actually means (when it actually means ignore this header and not remove the original header).
hostname: props.host, | ||
port: props.port.toString(), |
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.
Yeah I was a bit worried about that. Should we create new override properties that are specifically for header rewrites?
@@ -127,6 +130,8 @@ export class ProxyWorker implements DurableObject { | |||
// if we decide to await, we should include a timeout (~100ms) in case the user worker has long-running/parellel requests | |||
void fetch(userWorkerUrl, new Request(request, { headers })) | |||
.then((res) => { | |||
rewriteUrlRelatedHeaders(headers, innerUrl, outerUrl); |
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 not a typo! It's a bona fide mistake! Thanks for spotting that.
/** | ||
* Rewrite references to URLs in request headers. | ||
* | ||
* This function is used to map the URLs in headers like Origin and Access-Control-Allow-Origin |
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'll tweak the description.
In #4812 we tried to fix the Origin headers to match the Host header but were overzealous and rewrote Origin headers for external origins (outside of the proxy server's origin). This is now fixed, and moreover we rewrite any headers that refer to the proxy server on the request with the configured host and vice versa on the response. This should ensure that CORS is not broken in browsers when a different host is being simulated based on routes in the Wrangler configuration.
3c37d20
to
0e5e856
Compare
0e5e856
to
7db6025
Compare
6eb14e7
to
ded262e
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.
Happy to merge. Might want to hold off merging since I believe we want to get a hot fix out today.
One remaining point (you decide whether to address or not): should we print something to the console to let the user know the origin/host is being rewritten across all request/response headers inc Set-Cookie (and not the body) and let them know they can opt-out by...?
Yeah good question... we have not previously had a message for rewriting the
|
c621c64
to
ded262e
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.
The edge-preview-authenticated-proxy
stuff here looks good!
In remote mode, yeah no, but locally workerd logs now reach the log file (not the terminal but that's what you were going for anyway) |
Hmm I tried adding I think we can leave this to another time, or if someone gets confused by this rewriting. |
@petebacondarwin Can you please take a look at I think this PR is related, I had couple of notes: |
In #4812 we tried to fix the Origin headers to match the Host header but were overzealous and rewrote Origin headers for external origins (outside of the proxy server's origin).
This is now fixed, and moreover we rewrite any headers that refer to the proxy server on the request with the configured host and vice versa on the response.
This should ensure that CORS is not broken in browsers when a different host is being simulated based on routes in the Wrangler configuration.
Author has addressed the following: