code-server
code-server copied to clipboard
[Bug]: Proxying using {{port}}-{{host}} does not work
Is there an existing issue for this?
- [X] I have searched the existing issues
OS/Web Information
- Web Browser: Firefox
- Local OS: MacOS
- Remote OS: Ubuntu 22.04
- Remote Architecture: amd64
-
code-server --version
: 4.15.0 daac46b3cf5901466410ec7df4eabfb0d9ab384a with Code 1.80.1
Steps to Reproduce
To avoid re-creating certs for different domains and sub-domains, I've tried to prepend the port to the URL instead of using it as a sub-domain.
-
Using
VSCODE_PROXY_URI=https://{{port}}-{{host}}
, if code-server is running oncode.example.com
, then port 9000 should work on9000-code.example.com
. This is similar to how Gitpod does it. -
Setup nginx to read
9000-code.example.com
and proxy_pass it to port 9000, NOT the port code-server is running on. If you proxy_pass it to the code_server port, it will just open code-server. -
Open
python -m http.server 9000
inside code-server. You will see the popup with the proxy URL.
The proxy URL will work but it's only because of nginx, not because it's being proxied through code-server.
Expected
It should be proxied through code-server and require cookies and stuff, by proxying to the code-server port (and not port 9000 as seen above).
Actual
Proxying to the code-server port simply opens code-server again instead of the proxied request.
Proxying to the actual port creates a public URL which forwards the requests to the correct port but WITHOUT actually going through code-server. On the running Python server, I also see a 404 response from GET /_static/out/browser/serviceWorker.js
but I don't know why this happens.
Logs
No response
Screenshot/Video
No response
Does this issue happen in VS Code or GitHub Codespaces?
- [X] I cannot reproduce this in VS Code.
- [X] I cannot reproduce this in GitHub Codespaces.
Are you accessing code-server over HTTPS?
- [X] I am using HTTPS.
Notes
While this currently works for my setup, it looks like a bug and I'm not sure if the "fix" is stable long term. My only goal is to use {{ port }}-{{ host }}
instead of {{ port }}.{{ host }}
so if anyone has suggestions, I'm open to them.
You aren't activating the internal reverse proxy functionality, therefore it does not work.
You can start code-server like this: ./code-server --proxy-domain {{port}}-{{host}}
, then it should work as you expected.
OH! That's right, it worked! I was using --proxy-domain example
.
Can you tell me how the two (setting VSCODE_PROXY_URI
and passing --proxy-domain
) are different? . I've tried reading the code but I don't work with Typescript. :/
In the docs I see the following example, perhaps this needs to be fixed?
You can either set up a wildcard DNS entry for
*.<domain>
if your domain name registrar supports it, or you can create one for every port you want to access (3000.<domain>
,8080.<domain>
, etc).
Now from the above I assumed <domain>
is example.com
.
And then it says:
To set your domain, start code-server with the
--proxy-domain
flag:code-server --proxy-domain <domain>
I hope this explains why I did what I did.
Additionally, I still see the following requests being made:
127.0.0.1 - - [27/Jul/2023 01:20:16] "GET /_static/out/browser/serviceWorker.js HTTP/1.1" 404 -
And these throw a 401 Unauthorized when I try to login at https://9000-example.com/login in incognito:
https://9000-example.com/_static/src/browser/pages/global.css
https://9000-example.com/_static/src/browser/pages/login.css
https://9000-exmaple.com/_static/src/browser/media/pwa-icon-512.png
What's the reason for this?
Thank you for your help! :-D
Can you tell me how the two (setting
VSCODE_PROXY_URI
and passing--proxy-domain
) are different?
VSCODE_PROXY_URI
sets the domain to use in the ports panel and
--proxy-domain
enables code-server's built-in proxy (and will
also set VSCODE_PROXY_URI
if it has not already been set).
They are separate because some folks run their own proxy instead
of code-server's built-in and want that proxy to show in the ports
panel, so for them they can set VSCODE_PROXY_URI
.
If you are using code-server's built-in proxy then there is no
need to manually set VSCODE_PROXY_URI
.
Now from the above I assumed
is example.com.
Yeah we should update that section to say you can use {{port}}
and {{host}}
. If you do not include {{port}}
then code-server
prepends it for you. So --domain-proxy example.com
will become
{{port}}.example.com
.
If you check code-server's output it will show you what it ended up using for the proxy and the ports tab:
[2023-07-28T18:13:19.664Z] info - Proxying the following domain:
[2023-07-28T18:13:19.664Z] info - {{port}}.example.com
[2023-07-28T18:13:19.664Z] info Using proxy URI in PORTS tab: //{{port}}.example.com
And these throw a 401 Unauthorized when I try to login.
Good question. My guess is we have a bug where those resources are being blocked through the domain proxy but we should let them through. Not sure about the 404 though.
Good question. My guess is we have a bug where those resources are being blocked through the domain proxy but we should let them through. Not sure about the 404 though.
At first glance, I think this is correct behavior. We are reverse proxying the target port, so these files are not available except the specific application also provides these files. But even then it would not work, because at that particular moment you are not authenticated (401).
I'm not sure if the path is generic enough to not collide with the forwarded application, so instead I think the password prompt page should either load the resources from the main domain or embed the required resources. And I think the serviceWorker is not required at all.
Looks like this needs to change to _static
; I think someone
renamed the endpoint a while ago because VS Code was using
static
but never updated this:
https://github.com/coder/code-server/blob/e37b35278d4b529fd774469c3e1807ec8f4d1eb0/src/node/routes/domainProxy.ts#L67-L70
Is there a risk of collision? The collision that comes to my mind is if the browser caches our assets and then uses them instead of the application's but I believe our assets use etags for caching so I think that would not be possible.
I think the serviceWorker is not required at all.
Good point, we should just get rid of it on proxied addresess.
Ah, I did not look at the code. Yes, you are right, as it is only served if unauthenticated and has etags it should be fine.
I think letting the request through would cause a bit of confusion. Wouldn't it be better to redirect unauthenticated requests to the main domain (without the port)? - 9000.code.example.com/*
redirects to code.example.com/login
.
Once logged in, the user can be allowed to access the proxied domains.
On a similar note, I believe we use a cookie called coder-session-cookie (I don't remember what it is exactly), but nginx doesn't play nice with cookies with hyphens in them. You have to pull the cookie from $http_cookie instead of being able to access it directly with $cookie_coder_session_cookie. Can we rename this to coder_session_cookie?
this would require a more complex flow, because of general cookie policies.
- 9000.code.example.com/[path]
- redirect to code.example.com/login
- user authenticates (or is already authenticated)
- redirect to 9000.code.example.com/code with an authorization code
- /code must validate that code and set a cookie accordingly
- redirect back to 9000.code.example.com/[path]
Benefit: For the user, the authentication process can be skipped in most cases as he is already authenticated on the coder-server editor. So no password input required. But I'm not sure if it's worth the effort.
On a similar note, I believe we use a cookie called coder-session-cookie (I don't remember what it is exactly), but nginx doesn't play nice with cookies with hyphens in them. You have to pull the cookie from $http_cookie instead of being able to access it directly with $cookie_coder_session_cookie. Can we rename this to coder_session_cookie?
why do you tamper with the cookies at all?
I'm not sure I understand the workflow you've pointed out.
- Why is there a /code endpoint on a proxied domain?
- Why does it redirect to 9000.code.example.com with an auth code? Why doesn't it just check the cookie set on the parent domain?
why do you tamper with the cookies at all?
To try what I suggested - if 9000.code.example.com doesn't have a auth cookie, redirect to code.example.com for it to be authenticated. Once the user's logged in, they can visit 9000.code.example.com again view the proxied data.
As I said, general cookie policies. You can only set cookies downwards: code.example.com can set cookies that are accessible from 9000.code.example.com. But e.g. 9000-code.example.com cannot access any cookie from code.example.com and code.example.com cannot set cookies for 9000-code.example.com.
This is fixable - if I use varunchopra.code.example.com, 9000-varunchopra.code.example.com, I can set the cookie on .code.example.com and it will work. We only need to specify what the cookie domain should be.
And like you said, it should already work for the child domains.
FWIW I think it's fine as it is - I'm all up for your initial suggestion.
Well multiple issues:
- The proxy domain can be completly different from the domain where the code-server runs. The Authentication should work in the same way everywhere.
- Normally, it is not good practice and a potential security risk to set "Site" cookies that can be accessed by ALL subdomains. Are you really in control of all subdomains all the time? (But it seems that it is currently done this way https://github.com/coder/code-server/blob/main/src/node/http.ts#L326) @code-asher Is there a reason why "domain" is set here?
- Especially if you think about code-{{port}}.example.com. What about code2-{{port}} etc. They will be able to capture your cookie.
Yes, you're right. While I'm okay with having the same cookie - I suspect most people won't and we shouldn't add anything insecure by default.
You are right, that is exactly why the domain is set there, so the cookie can be accessed by all subdomains. If I recall correctly it takes the domain, finds the highest level --proxy-domain
that matches, and uses that as the cookie domain value so I think we actually broke it since the proxy domain always contains {{port}}
now and will never match. :laughing:
But you raise a really good point about it being a security risk, so I think it would be prudent to remove this or fix it and make it opt-in.
Another thought: we might need to think about logout logic now that it might not all be under the same domain.
I think we solved the original issue so I will close this and open others for the various points that were raised here.