Skip to content
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

[Bug]: Port forwarding with suburl makes the forwarded links corrupted . #5792

Closed
4 tasks done
a279437145 opened this issue Nov 18, 2022 · 40 comments
Closed
4 tasks done
Labels
waiting-for-info Waiting for more information from submitter

Comments

@a279437145
Copy link

a279437145 commented Nov 18, 2022

Is there an existing issue for this?

OS/Web Information

  • Web Browser: Chrome (Version 98.0.4758.102) & Firefox (Version 102.3.0esr )
  • Local OS: Debian
  • Remote OS: Ubuntu
  • Remote Architecture: arm64
  • code-server --version: 4.8.3 977b853 with Code 1.72.1

Steps to Reproduce

  1. Open code-server
  2. Run jupyter notebook in terminal , and forward the port (should be forwarded automatically)
  3. Open the forwarded url

Expected

Jupyter notebook shows up a correct UI and works without problems.

Actual

The jupyter notebook showed up with a unformatted UI (because css and js files are requested with a corrupted URL, only the https://192.168.0.114:28080/proxy/8888/login?next=%2Ftree is requested correctly)
if logging in to the notebook it redirected me to https://192.168.0.114:28080/?next=/tree , which is obviously wrong (code-server is serving there)
eg , for the logo.png , it requested for https://192.168.0.114:28080/static/base/images/logo.png?v=<a long string> , but it should request for https://192.168.0.114:28080/proxy/8888/static/base/images/logo.png?v=<a long string>

Logs

Logs
[IPC Library: Pty Host] DEBUG CommandDetectionCapability#handleCommandExecuted 0 1
[IPC Library: Pty Host] DEBUG CommandDetectionCapability#setCommandLine jupyter notebook
[IPC Library: Pty Host] TRACE IPty#onData [I 15:06:55.184 NotebookApp] Serving notebooks from local directory: /home/ubuntu/workspace

[IPC Library: Pty Host] TRACE IPty#onData [I 15:06:55.184 NotebookApp] Jupyter Notebook 6.5.2 is running at:
[I 15:06:55.184 NotebookApp] http://localhost:8888/?token=d5e6bd5bb2af22923a05edf7e2bb29171e26a5ea21988ac8
[I 15:06:55.184 NotebookApp]  or http://127.0.0.1:8888/?token=d5e6bd5bb2af22923a05edf7e2bb29171e26a5ea21988ac8
[I 15:06:55.184 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).

[IPC Library: Pty Host] TRACE IPty#onData [C 15:06:55.197 NotebookApp] 

    To access the notebook, open this file in a browser:
        file:///home/ubuntu/.local/share/jupyter/runtime/nbserver-13720-open.html
    Or copy and paste one of these URLs:
        http://localhost:8888/?token=d5e6bd5bb2af22923a05edf7e2bb29171e26a5ea21988ac8
     or http://127.0.0.1:8888/?token=d5e6bd5bb2af22923a05edf7e2bb29171e26a5ea21988ac8

[IPC Library: Pty Host] DEBUG ChildProcessMonitor: Has child processes changed true
[IPC Library: Pty Host] TRACE IPty#onData [I 15:07:03.317 NotebookApp] 302 GET / (127.0.0.1) 1.440000ms

[IPC Library: Pty Host] TRACE IPty#onData [I 15:07:03.407 NotebookApp] 302 GET /tree (127.0.0.1) 1.770000ms

[15:07:15] [<unknown>][29df24bf][ManagementConnection] The client has disconnected, will wait for reconnection 3h before disposing...
[15:07:17] [<unknown>][29df24bf][ManagementConnection] Another client has connected, will shorten the wait for reconnection 5m before disposing...
[15:07:17] [<unknown>][3473a0f6][ExtensionHostConnection] - startParams language: en
[15:07:17] [<unknown>][3473a0f6][ExtensionHostConnection] - startParams env: {"VSCODE_PROXY_URI":"https://192.168.0.114:28080/proxy/{{port}}/"}
[15:07:17] [<unknown>][3473a0f6][ExtensionHostConnection] The client has reconnected.
[15:07:17] [<unknown>][29df24bf][ManagementConnection] The client has reconnected.
[2022-11-18T15:07:21.474Z] debug 2 active connections
Trace: [2022-11-18T15:07:21.477Z] trace heartbeat
    at doLog (/home/ubuntu/code/code-server-4.8.3-linux-arm64/node_modules/@coder/logger/out/logger.js:57:28)
    at ServerFormatter.doWrite (/home/ubuntu/code/code-server-4.8.3-linux-arm64/node_modules/@coder/logger/out/logger.js:200:20)
    at ServerFormatter.write (/home/ubuntu/code/code-server-4.8.3-linux-arm64/node_modules/@coder/logger/out/logger.js:119:14)
    at Logger.handle (/home/ubuntu/code/code-server-4.8.3-linux-arm64/node_modules/@coder/logger/out/logger.js:339:25)
    at Logger.trace (/home/ubuntu/code/code-server-4.8.3-linux-arm64/node_modules/@coder/logger/out/logger.js:268:14)
    at Heart.<anonymous> (/home/ubuntu/code/code-server-4.8.3-linux-arm64/out/node/heart.js:41:29)
    at Generator.next (<anonymous>)
    at /home/ubuntu/code/code-server-4.8.3-linux-arm64/out/node/heart.js:8:71
    at new Promise (<anonymous>)
    at __awaiter (/home/ubuntu/code/code-server-4.8.3-linux-arm64/out/node/heart.js:4:12)
    at Heart.beat (/home/ubuntu/code/code-server-4.8.3-linux-arm64/out/node/heart.js:37:16)
    at /home/ubuntu/code/code-server-4.8.3-linux-arm64/out/node/heart.js:76:17
    at Generator.next (<anonymous>)
    at fulfilled (/home/ubuntu/code/code-server-4.8.3-linux-arm64/out/node/heart.js:5:58)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Screenshot/Video

Behavior

Run jupyter:
image
Forward port:
image
Access it:
image

Browser console output

image

Explaination

The correct URL:
image
The corrupted URL:
image

Does this issue happen in VS Code or GitHub Codespaces?

  • I cannot reproduce this in VS Code.
  • I cannot reproduce this in GitHub Codespaces.

Are you accessing code-server over HTTPS?

  • I am using HTTPS.

Notes

Code-server is running on a lxc container , it exposed the port 8888 to the server host , and I used iptables(iptables -t nat -A PREROUTING -p tcp --dport 28080 -j DNAT --to-destination 10.154.46.4:8080 , where 28080 is the server exposed port , and 10.154.46.4:8080 is the container exposed port).
It is using a self-signed certificate with cert and cert-key params in the config file (not serving through nginx/caddy)

Also , this bug makes the pdf viewer from LaTeX workshop not working properly.

Is it because of relative path ? But jupyter and LaTeX workshop aren't my project , i don't know where I can make changes.
Subdomains may work , but I don't have a domain for my server.

@a279437145 a279437145 added bug Something isn't working triage This issue needs to be triaged by a maintainer labels Nov 18, 2022
@frank8922
Copy link

frank8922 commented Nov 27, 2022

I'm having the same browser output when I try using jupyter-lab as well. I'm using code-server in a docker container behind a reverse proxy (caddy). I noticed when I prepend the path of the 404 requests with the path '/proxy/8888/' it fetches the desired resource successfully. In my case it looks like because code-server's proxy strips the '/proxy//' portion of the path it fails.

@jsjoeio
Copy link
Contributor

jsjoeio commented Nov 30, 2022

Hmm 🤔 Can this be fixed via the environment variable? Here are the notes from the release about this feature:

Support for the Ports panel which leverages code-server's built-in proxy. It
also uses VSCODE_PROXY_URI where {{port}} is replace when forwarding a port.
Example: VSCODE_PROXY_URI=https://{{port}}.kyle.dev would forward an
application running on localhost:3000 to https://3000.kyle.dev/

I don't work with Jupyter notebooks or jupyter-lab so I'm not sure what to expect here.

If you can provide some tests cases and what the expected URLs would be, I can help more.

@jsjoeio jsjoeio added waiting-for-info Waiting for more information from submitter and removed bug Something isn't working triage This issue needs to be triaged by a maintainer labels Nov 30, 2022
@a279437145
Copy link
Author

a279437145 commented Dec 1, 2022

I tried subdomain , I don't have a registered domain so I added 192.168.0.114 raspberrypi and 192.168.0.114 8888.raspberrypi to my /etc/hosts , and I launched with VSCODE_PROXY_URI=https://{{port}}.raspberrypi:28080 nohup ./code/code-server-4.8.3-linux-arm64/bin/code-server --verbose & (I've changed the code-server config to serve on port 28080)
Forwarded like this:

Screenshot

image

But when I try to access it , it shows the `code-server` , not the `jupyter`
Screenshot

image
Note : I forgot to add DNS.=.raspberrypi when I signed my certificate , so it shows a Not secure mark (Does this matter?)

Seems the server does know it's being accessed via `8888.raspberrypi:28080` , because when extension are scanned , they showed this in the `authority` block , eg
Logs
[IPC Library: Pty Host] TRACE ptyService#getLayoutInfo { workspaceId: '-392037db' }
[15:00:45] Scanned user extensions: 7
[15:00:45] Scanned system extensions: 88
[15:00:45] Scanned Extensions [
  {
    id: 'vscode.bat',
    identifier: b { value: 'vscode.bat', _lower: 'vscode.bat' },
    isBuiltin: true,
    isUserBuiltin: false,
    isUnderDevelopment: false,
    extensionLocation: {
      '$mid': 1,
      path: '/home/ubuntu/code/code-server-4.8.3-linux-arm64/lib/vscode/extensions/bat',
      scheme: 'vscode-remote',
      authority: '8888.raspberrypi:28080'
    },
    uuid: undefined,
    targetPlatform: 'undefined',
    name: 'bat',
    displayName: 'Windows Bat Language Basics',
    description: 'Provides snippets, syntax highlighting, bracket matching and folding in Windows batch files.',
    version: '1.0.0',
    publisher: 'vscode',
    license: 'MIT',
    engines: { vscode: '^1.52.0' },
    scripts: {
      'update-grammar': 'node ../node_modules/vscode-grammar-updater/bin mmims/language-batchfile grammars/batchfile.cson ./syntaxes/batchfile.tmLanguage.json'
    },
    contributes: { languages: [Array], grammars: [Array], snippets: [Array] },
    repository: { type: 'git', url: 'https://github.com/microsoft/vscode.git' }
  },

Does the logger logs access ?

Because I accessed code-server via iptables fort forwarding , I also tried to access it directly from the server's internal network
Got the same result 😢

Screenshot

image

Also , I prefer using subpath here , then I don't have to do modify the hosts file if I'm on a new device.

UPD :
I think that invalid certificate won't matter .(Not so sure)
I used wireshark to check , it did send server_name

Screenshot

image

@jsjoeio
Copy link
Contributor

jsjoeio commented Dec 1, 2022

But when I try to access it , it shows the code-server , not the jupyter

Woah that's weird. Okay let me summarize to make sure I'm understanding.

You're running code-server on https://raspberry:28080. You can access code-server just fine there ✅

You pass VSCODE_PROXY_URI=https://{{port}}.raspberrypi:28080 and when you start a process, the Ports panel shows the correct address ✅

When you access said process i.e. Jupyter running at https://8888.raspberrypi:28080 it shows code-server 🤔

Also , I prefer using subpath here , then I don't have to do modify the hosts file if I'm on a new device.

Agreed! So let's try this:

  1. start code-server without VSCODE_PROXY_URI set
  2. in the Integrated Terminal, run python3 -m http.server
  3. try accessing it via the ports panel, which should use the built-in /proxy/<port> path

If that works, then try running Jupyter on 8888 and try again.

At least that's what I'd do. I don't know if Jupyter needs to have some environment variable or something to run behind a subpath but we can check that next. I just want to make sure the port forwarding works with your setup first.

@a279437145
Copy link
Author

a279437145 commented Dec 2, 2022

@jsjoeio
Yes , correct .

And , I found this https://jupyter-notebook.readthedocs.io/en/stable/public_server.html#running-the-notebook-with-a-customized-url-prefix, and I followed it.
I added c.NotebookApp.base_url = '/proxy/8888/' to/home/ubuntu(my user name)/.jupyter/jupyter_notebook_config.py and launched it .
This time when I access it , the jupyter icon loaded successfully but the site returned a 404

Screenshot

image

And jupyter notebook output

Logs
ubuntu@UbuntuCode:~/workspace$ jupyter notebook
[I 12:38:22.275 NotebookApp] Serving notebooks from local directory: /home/ubuntu/workspace
[I 12:38:22.275 NotebookApp] Jupyter Notebook 6.5.2 is running at:
[I 12:38:22.275 NotebookApp] http://localhost:8888/proxy/8888/?token=f56b2a3c705274d2ea73102a64fd811057f09797df2d5617
[I 12:38:22.275 NotebookApp]  or http://127.0.0.1:8888/proxy/8888/?token=f56b2a3c705274d2ea73102a64fd811057f09797df2d5617
[I 12:38:22.275 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 12:38:22.287 NotebookApp] 
    
    To access the notebook, open this file in a browser:
        file:///home/ubuntu/.local/share/jupyter/runtime/nbserver-8769-open.html
    Or copy and paste one of these URLs:
        http://localhost:8888/proxy/8888/?token=f56b2a3c705274d2ea73102a64fd811057f09797df2d5617
     or http://127.0.0.1:8888/proxy/8888/?token=f56b2a3c705274d2ea73102a64fd811057f09797df2d5617
[W 12:38:25.247 NotebookApp] 404 GET / (127.0.0.1) 64.800000ms referer=https://192.168.0.114:28080/?folder=/home/ubuntu/workspace
[W 12:38:25.559 NotebookApp] 404 GET /static/style/style.min.css?v=e1ab1c38b672063a6541baf468c83345cd0f509729783ec9b7ccb64073004f5f056110c82c28aefbf3dbf32e0e040f05b8f0420bc411b669ed3d4f07511812ca (127.0.0.1) 8.280000ms referer=https://192.168.0.114:28080/proxy/8888/
[W 12:38:25.600 NotebookApp] 404 GET /custom/custom.css (127.0.0.1) 6.840000ms referer=https://192.168.0.114:28080/proxy/8888/
[W 12:38:25.610 NotebookApp] 404 GET /static/components/jquery-ui/dist/themes/smoothness/jquery-ui.min.css?v=32f9dcde0cd9843f2b66d34c1c9928b59a5d7ef007ba7a6a6a790b3e78f7857a698444d7a716dfaf8fa834c3b3175efd258bbc07cfc4aabb86769b07e5f358c3 (127.0.0.1) 4.900000ms referer=https://192.168.0.114:28080/proxy/8888/
[W 12:38:25.615 NotebookApp] 404 GET /static/components/es6-promise/promise.min.js?v=bea335d74136a63ae1b5130f5ac9a50c6256a5f435e6e09fef599491a84d834a8b0f011ca3eaaca3b4ab6a2da2d3e1191567a2f171e60da1d10e5b9d52f84184 (127.0.0.1) 3.910000ms referer=https://192.168.0.114:28080/proxy/8888/
[W 12:38:25.622 NotebookApp] 404 GET /static/components/jquery-typeahead/dist/jquery.typeahead.min.css?v=5edf53bf6bb9c3b1ddafd8594825a7e2ed621f19423e569c985162742f63911c09eba2c529f8fb47aebf27fafdfe287d563347f58c1126b278189a18871b6a9a (127.0.0.1) 4.220000ms referer=https://192.168.0.114:28080/proxy/8888/
[W 12:38:25.628 NotebookApp] 404 GET /static/components/react/react.production.min.js?v=9a0aaf84a316c8bedd6c2ff7d5b5e0a13f8f84ec02442346cba0b842c6c81a6bf6176e64f3675c2ebf357cb5bb048e0b527bd39377c95681d22468da3d5de735 (127.0.0.1) 4.240000ms referer=https://192.168.0.114:28080/proxy/8888/
[W 12:38:25.697 NotebookApp] 404 GET /static/components/react/react-dom.production.min.js?v=6fc58c1c4736868ff84f57bd8b85f2bdb985993a9392718f3b4af4bfa10fb4efba2b4ddd68644bd2a8daf0619a3844944c9c43f8528364a1aa6fc01ec1b8ae84 (127.0.0.1) 6.330000ms referer=https://192.168.0.114:28080/proxy/8888/
[W 12:38:25.723 NotebookApp] 404 GET /static/components/requirejs/require.js?v=d37b48bb2137faa0ab98157e240c084dd5b1b5e74911723aa1d1f04c928c2a03dedf922d049e4815f7e5a369faa2e6b6a1000aae958b7953b5cc60411154f593 (127.0.0.1) 4.920000ms referer=https://192.168.0.114:28080/proxy/8888/
[W 12:38:25.727 NotebookApp] 404 GET /static/components/create-react-class/index.js?v=894ad57246e682b4cfbe7cd5e408dcd6b38d06af4de4f3425991e2676fdc2ef1732cbd19903104198878ae77de12a1996de3e7da3a467fb226bdda8f4618faec (127.0.0.1) 6.840000ms referer=https://192.168.0.114:28080/proxy/8888/
I'll look into the docs later to see if there are something more I should specify.

And for python3 -m http.server , it does work

Screenshots

image
image
image

UPD:
Figured out , seems jupyter messed the urls up.

Screenshots

image

accessing from the internal net : (not through code-server proxy)
Screenshot from 2022-12-02 21-25-09

The reverse proxy server used by code-server makes jupyter thinks that you are accessing path / while you are actually accessing /proxy/8888 , but jupyter only works when it thinks you are accessing /proxy/8888 , so in this case you have to double the /proxy/8888 . So accessing https://192.168.0.114:28080/proxy/8888/proxy/8888/login can show the login screen , but nothing else (like the logo or css files) loads because they were requested with https://192.168.0.114:28080/proxy/8888.
Pretty confusing 😭

@jsjoeio
Copy link
Contributor

jsjoeio commented Dec 2, 2022

c.NotebookApp.base_url = '/proxy/8888/'

Does this need to include the full url? i.e c.NotebookApp.base_url = 'https://192.168.0.114:28080/proxy/8888/?

Figured out , seems jupyter messed the urls up.

Nice work!!!

The reverse proxy server used by code-server makes jupyter thinks that you are accessing path / while you are actually accessing /proxy/8888 , but jupyter only works when it thinks you are accessing /proxy/8888 , so in this case you have to double the /proxy/8888 .

Argh :( that is very confusing indeed. When @code-asher is back, maybe he'll have some ideas about this and know if there's anything we can do to make it better from the code-server side. Sorry for all the trouble!

@a279437145
Copy link
Author

Does this need to include the full url? i.e c.NotebookApp.base_url = 'https://192.168.0.114:28080/proxy/8888/?

It doens't need to include the full url , there are descriptions in jupyter's docs

For example, if you prefer that the notebook dashboard be located with a sub-directory that contains other ipython files, e.g. http://localhost:8888/ipython/, you can do so with configuration options like the following (see above for instructions about modifying jupyter_notebook_config.py):
c.NotebookApp.base_url = '/ipython/'

@code-asher
Copy link
Member

code-asher commented Dec 5, 2022

Yeah many applications use absolute URLs unfortunately and default to / so they will need to be made aware of the base /proxy/{port}/ path, including Jupyter.

The sub-domain proxy method is superior since it does not have the same issue but it needs to be enabled via the --proxy-domain flag. I think it would look like this:

VSCODE_PROXY_URI=https://{{port}}.raspberrypi:28080 code-server --proxy-domain *.raspberrypi

(Alternatively the proxy could be implemented via a separate reverse proxy.)

@code-asher
Copy link
Member

code-asher commented Dec 5, 2022

I wonder if there is a way we can surface help around this, maybe via a tooltip or text underneath the forwarded port? Or something like a "help me" button that triggers a notification or opens documentation?

@jsjoeio
Copy link
Contributor

jsjoeio commented Dec 5, 2022

Or something like a "help me" button that triggers a notification or opens documentation?

I like that! We could add a doc and then in the message that pops up about the forwarded port, add a link below and link to the docs.

@a279437145
Copy link
Author

a279437145 commented Dec 6, 2022 via email

@jsjoeio
Copy link
Contributor

jsjoeio commented Dec 6, 2022

Hmm...that sounds a bit out of scope for now. We want to keep it as close to upstream (VS Code) as possible. Let me know if there's another way we can help you though.

@code-asher
Copy link
Member

Yeah I think for advanced proxy configuration it might be better to set up NGINX or Caddy instead of using code-server's built-in.

Also if all you are looking for is to avoid stripping the base path then we have another proxy endpoint /absproxy for that so you could set VSCODE_PROXY_URI=https://my-domain.com/absproxy/{{port}}. It would be cool to have a way to choose between /proxy and /absproxy from the UI.

@a279437145
Copy link
Author

a279437145 commented Dec 10, 2022

Thank you , the absproxy worked.
image

  • Note : need to add c.NotebookApp.base_url = '/absproxy/8888(jupyter port)/' to jupyter_notebook_config.py

But it can't solve extension's problems (For latex-workshop , I can't view pdf file with its built-in pdf viewer because some resources are requested with a bad URL)
And it needs to configure the forwarded site .

@a279437145
Copy link
Author

a279437145 commented Dec 10, 2022

I...tried nginx
This is the configuration file I wrote

Note:This configuration doesn't fully work , use the one below

Configuration
server {
    listen 28081 ssl;
    server_name 192.168.0.114;
    server_name raspberrypi;


    ssl_certificate "/path/to/mycert.crt";
    ssl_certificate_key "/path/to/mycert.key";

    client_max_body_size 50M;
    location ~* ^/proxy/(?<proxy_port>[^\n\r\/]+)(?<proxy_path>.*)$ {
        default_type text/html;
        if ($proxy_port !~* ^([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-5][0-9][0-9][0-9][0-9]|6[0-5][0-5][0-3][0-5])$) {
            add_header Content-Type 'text/html; charset=utf-8';
            return 501 'Illegal port ${proxy_port}';
        }
        if ($proxy_path = ''){
            set $proxy_path '/'; #force to use relative path
        }
        #proxy_ssl_server_name on;
        proxy_pass http://127.0.0.1:$proxy_port$proxy_path;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real_IP $remote_addr;
        proxy_set_header User-Agent $http_user_agent;
        proxy_set_header Accept-Encoding "";
        gzip off;
        proxy_buffering off;
        subs_filter_types text/css text/plain application/x-javascript application/javascript;
        subs_filter (.*)((<img|<script|<link)(.*)\ (src|href))\=(\"|')(?!cdn|https|https|\/\/)(\/)?(<\/script>|\>)?(.*) $1$2=$6/proxy/$proxy_port/$8$9 rg;
    }
}

The subs_filter regex comes from https://regex101.com/r/zY1lJ7/1

And with the subs_filter option almost all the resources are requested correctly

Screenshot

image

But from there you can see there's still main.min.js not loaded correctly so the page is not fully loaded.And I can't figure out where it's been requested . I checked the stack trace .

Screenshots

image
image

@a279437145
Copy link
Author

@code-asher @jsjoeio
Well , I tried a new way today . And , it worked !

Here is the nginx configuration.

Configuration
server {
    listen 28081 ssl;
    server_name 192.168.0.114;
    server_name raspberrypi;


    ssl_certificate "/path/to/mycert.crt";
    ssl_certificate_key "/path/to/mycert.key";

    client_max_body_size 50M;
    location ~* ^/proxy/(?<proxy_port>[^\n\r\/]+)(?<proxy_path>.*)$ {
	default_type text/html;
    	if ($proxy_port !~* ^([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-5][0-9][0-9][0-9][0-9]|6[0-5][0-5][0-3][0-5])$) {
	    add_header Content-Type 'text/html; charset=utf-8';
	    return 501 'Illegal port ${proxy_port}';
	}
	if ($proxy_path = ''){
	    set $proxy_path '/'; #force to use relative path
	}
	add_header Set-Cookie 'proxy_port=$proxy_port; Path=/';
	proxy_http_version 1.1;
        #proxy_ssl_server_name on;
        proxy_pass http://127.0.0.1:$proxy_port$proxy_path;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real_IP $remote_addr;
        proxy_set_header User-Agent $http_user_agent;
        proxy_set_header Accept-Encoding "";
        proxy_buffering off;
        #websocket
	set $to_upgrade "";
	set $to_connection "";
	if ($http_upgrade = "websocket"){
	    set $to_upgrade "websocket";
	    set $to_connection "Upgrade";
	}
	proxy_set_header Upgrade $to_upgrade;
	proxy_set_header Connection $to_connection;
	proxy_read_timeout 86400;
    }
    location / {
    	default_type text/html;
	if ($cookie_proxy_port = ''){
	    return 404 'Nothing here';
	}
	proxy_http_version 1.1;
	proxy_pass http://127.0.0.1:$cookie_proxy_port;
	proxy_set_header Host $http_host;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Real_IP $remote_addr;
	proxy_set_header User-Agent $http_user_agent;
	proxy_set_header Accept-Encoding "";
	proxy_buffering off;
	#websocket
	set $to_upgrade "";
	set $to_connection "";
	if ($http_upgrade = "websocket"){
	    set $to_upgrade "websocket";
	    set $to_connection "Upgrade";
	}
	proxy_set_header Upgrade $to_upgrade;
	proxy_set_header Connection $to_connection;
	proxy_read_timeout 86400;
    }
}

When accessing https://server-host.com/proxy/{port} nginx will set proxy_port={port} to the client cookies.
And if the client requests for https://server-host.com/path/to/the/resource with the cookie (proxy_port={port}), nginx will proxy_pass it to http://127.0.0.1:{port}/path/to/the/resource so the resource can be loaded correctly.
Also nginx will check if the connection is a websocket , and if it is nginx will automatically configure it . (So it's able to connect to jupyter kernel through nginx)

What I have tested :

  • Jupyter notebook . (Work)
  • LaTeX-workshop view pdf in browser . (Work)
Screenshots

image
image

@jsjoeio
Copy link
Contributor

jsjoeio commented Dec 12, 2022

Ahhh wow, nice debugging and thanks for posting your solution! I guess for now this is where we point people if they run into this.

@qianchd
Copy link

qianchd commented Dec 13, 2022

@a279437145, It is a nice solution. But what if I have already set the code-server through the Nginx server? e.g. https://www.example.com/code-server.
It seems that now there is a two-layer forward issue...

@a279437145
Copy link
Author

a279437145 commented Dec 13, 2022 via email

@a279437145
Copy link
Author

@qianchd Just tested.
Here's my nginx configuration

Configuration
server {
    listen 28081 ssl;
    server_name 192.168.0.114;
    server_name raspberrypi;


    ssl_certificate "/path/to/certificate.crt";
    ssl_certificate_key "/path/to/certificate.key";

    client_max_body_size 50M;
    location /code-server/ {
    	proxy_pass http://localhost:8080/;
	proxy_set_header Host $host;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection upgrade;
	proxy_set_header Accept-Encoding gzip;

    }
    location ~* ^/proxy/(?<proxy_port>[^\n\r\/]+)(?<proxy_path>.*)$ {
	default_type text/html;
    	if ($proxy_port !~* ^([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-5][0-9][0-9][0-9][0-9]|6[0-5][0-5][0-3][0-5])$) {
	    add_header Content-Type 'text/html; charset=utf-8';
	    return 501 'Illegal port ${proxy_port}';
	}
	if ($proxy_path = ''){
	    set $proxy_path '/'; #force to use relative path
	}
	add_header Set-Cookie 'proxy_port=$proxy_port; Path=/';
	proxy_http_version 1.1;
        #proxy_ssl_server_name on;
        proxy_pass http://127.0.0.1:$proxy_port$proxy_path;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real_IP $remote_addr;
        proxy_set_header User-Agent $http_user_agent;
        proxy_set_header Accept-Encoding "";
        proxy_buffering off;
        #websocket
	set $to_upgrade "";
	set $to_connection "";
	if ($http_upgrade = "websocket"){
	    set $to_upgrade "websocket";
	    set $to_connection "Upgrade";
	}
	proxy_set_header Upgrade $to_upgrade;
	proxy_set_header Connection $to_connection;
	proxy_read_timeout 86400;
    }
    location / {
    	default_type text/html;
	if ($cookie_proxy_port = ''){
	    return 404 'Nothing here';
	}
	proxy_http_version 1.1;
	proxy_pass http://127.0.0.1:$cookie_proxy_port;
	proxy_set_header Host $http_host;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Real_IP $remote_addr;
	proxy_set_header User-Agent $http_user_agent;
	proxy_set_header Accept-Encoding "";
	proxy_buffering off;
	#websocket
	set $to_upgrade "";
	set $to_connection "";
	if ($http_upgrade = "websocket"){
	    set $to_upgrade "websocket";
	    set $to_connection "Upgrade";
	}
	proxy_set_header Upgrade $to_upgrade;
	proxy_set_header Connection $to_connection;
	proxy_read_timeout 86400;
    }
}

Remember to change the listen 28081 to your port , server name to your domain (or ip) , the certificate path to your certificate file.

Screenshot

image

It works normally . And the reverse proxy still works , no conflicts.

Actually , since nginx is already forwarding ports at /proxy/{port} , you can directly access code-server with https://host.com/proxy/8080/ (while 8080 is the port code-server is using)

  • Note : If the browser reported The site redirected you too many times , you can directly access https://host.com/ after accessing https://host.com/proxy/{port}/.

@kamskanagi
Copy link

kamskanagi commented Jan 9, 2023

I am having the same issue I am running a code-server on docker(image: lscr.io/linuxserver/code-server).

I am building a react application with vite dev server on the code-server.

when i npm run dev, the application will be redirect or forward to https://{ip_address}:8443/proxy/8081/. If base:'/proxy/8081/' or base:'/proxy/{port}/'' is not define in my vite.config.js, it will shows a blank page.

if base:'/proxy/8081' is define in vite.config.js, it will show the content of the url and can navigate through menus. However, if click on sublink, the /proxy/8081/ will disappear and shows "page not found".

I try to use ngnix but it ask for authorization. as shown in the attach.

ng

How can i solve this problems? need your help
Thank you in advance

@jsjoeio
Copy link
Contributor

jsjoeio commented Jan 9, 2023

Have you looked through Discussions? We've helped people with Vite in the past. Maybe this has your answer: #4541

@kamskanagi
Copy link

kamskanagi commented Jan 10, 2023

I have looked https://github.com/coder/code-server/discussions/4541 but does not solve the stripping of /absproxy/{port]/ when clicked on a sublink or suburl.

expectation
when click on a suburl, i expect the url to be like this http://{ vm_ip}:8444/absproxy/8081/out-of-home-barcode/detail?id=2,

Reality
But what i get is this http://{ vm_ip}:8444/out-of-home-barcode/detail?id=2, with page not found error. so i have to manually add the /absproxy/8081/ for it to work.

My second question is how to remove the authorization access on the code-server. because i edit the ~/.config/code-server/config.yaml file as shown below, but still ask for authorization access.

--bind-addr: 127.0.0.1:8080
--auth: none
password: 5*******f5
cert: false

the code-server is running on http://{vm_ip}:8444/?folder=/config/workspace/project

@a279437145
Copy link
Author

a279437145 commented Jan 10, 2023

401 is from code-server , not from nginx or vite .
If you want to use nginx to correct the url ,run nginx inside docker container with the configuration above (remember to remove the ssl as you are using http) and forward the nginx port and access http://your-ip:{nginx-port}/proxy/{react-app-port}
nginx is a seperate process from code-server.
The traffic doesn't go through code-server while using nginx to forward ports.

@jsjoeio
Copy link
Contributor

jsjoeio commented Jan 10, 2023

Reality

Sounds like you need to set a base path or base URL for your application. That's where I'd look at least.

You'll need to pass --auth "none" to code-server or remove the password from your config.yaml and set auth: none I believe.

@kamskanagi
Copy link

@jsjoeio thanks for the reply.

I did set the base as base: "/absproxy/{port}/", in the vite.config.js. at the moment i manually add the /absproxy/{port}/ into the url for it to work.

For the authorization, i try your suggestion.

@kamskanagi
Copy link

@a279437145

Yes i know the 401 is from code-server not the nginx. Thats why i was thinking of removing or disabling the authorization access in code-server.

@a279437145
Copy link
Author

a279437145 commented Jan 12, 2023 via email

@a279437145
Copy link
Author

a279437145 commented Jan 17, 2023 via email

@a279437145
Copy link
Author

a279437145 commented Jan 26, 2023

I'm not an experienced nginx user , so this isn't a perfect solution .
But anyways , it can work !
So I'm satisfied ^_^

1.Install nginx

It's the same as normal nginx installation , and nginx from the package manager should work , simply install with : (on Debian/Ubuntu)

sudo apt install nginx

Or you can install with any way you like.

2.Configure

Create a .conf file under /etc/nginx/sites-enabled , and copy the following configuration into it.

You can also download it via curl https://gist.githubusercontent.com/a279437145/d8c1565e49e5eda7577e68ec519660bc/raw/1086df501003368582fd59c23ef25f7e3ffc5481/proxy.conf -o proxy.conf

Configuration

The same as https://gist.github.com/a279437145/d8c1565e49e5eda7577e68ec519660bc

 server {
    listen {{PORT}} ssl;
    server_name {{IP_ADDRESS/DOMAIN}};


    ssl_certificate "{{PATH_TO_SSL_CERTIFICATION_FILE}}";
    ssl_certificate_key "{{PATH_TO_SSL_CERTIFICATION_KEY}}";

    client_max_body_size 50M;
    location ~* ^/proxy/(?<proxy_port>[^\n\r\/]+)(?<proxy_path>.*)$ {
	default_type text/html;
    	if ($proxy_port !~* ^([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-5][0-9][0-9][0-9][0-9]|6[0-5][0-5][0-3][0-5])$) {
	    add_header Content-Type 'text/html; charset=utf-8';
	    return 501 'Illegal port ${proxy_port}';
	}
	if ($proxy_path = ''){
	    set $proxy_path '/'; #force to use relative path
	}
	add_header Set-Cookie 'proxy_port=$proxy_port; Path=/';
	proxy_http_version 1.1;
        #proxy_ssl_server_name on;
        proxy_pass http://127.0.0.1:$proxy_port$proxy_path;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real_IP $remote_addr;
        proxy_set_header User-Agent $http_user_agent;
        proxy_set_header Accept-Encoding "";
        proxy_buffering off;
        #websocket
	set $to_upgrade "";
	set $to_connection "";
	if ($http_upgrade = "websocket"){
	    set $to_upgrade "websocket";
	    set $to_connection "Upgrade";
	}
	proxy_set_header Upgrade $to_upgrade;
	proxy_set_header Connection $to_connection;
	proxy_read_timeout 86400;
    }
    location / {
    	default_type text/html;
	if ($cookie_proxy_port = ''){
	    return 404 'Nothing here';
	}
	proxy_http_version 1.1;
	proxy_pass http://127.0.0.1:$cookie_proxy_port;
	proxy_set_header Host $http_host;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Real_IP $remote_addr;
	proxy_set_header User-Agent $http_user_agent;
	proxy_set_header Accept-Encoding "";
	proxy_buffering off;
	#websocket
	set $to_upgrade "";
	set $to_connection "";
	if ($http_upgrade = "websocket"){
	    set $to_upgrade "websocket";
	    set $to_connection "Upgrade";
	}
	proxy_set_header Upgrade $to_upgrade;
	proxy_set_header Connection $to_connection;
	proxy_read_timeout 86400;
    }
}

Then replace the double braces surrounded contents with yours. (Remember to use a different port than code-server)

Note :

  • 1 . If you don't use https to access the proxy , remove ssl on listen line , and remove ssl_certificate ssl_certificate_key .

  • 2 . If your backend is using https , change http on proxy_pass line to https.

3.Usage

Access https://{IP}:{NGINX_PORT}/proxy/{SERVICE_PORT}

Note : If the browser reported The site redirected you too many times , you can directly access https://{IP}/ after accessing https://{IP}:{NGINX_PORT}/proxy/{SERVICE_PORT}.

Notice

1.This uses / location , so better use a port without other services running .

2.This solution uses cookie , so don't use private mode in browser or disable cookie.

3.The default nginx.conf ( /etc/nginx/nginx.conf ) may contain a server config using port 80 , if you don't need it you can comment it to prevent conflicts with other services running on the same server.

@jxfruit
Copy link

jxfruit commented Apr 26, 2023

I'm not an experienced nginx user , so this isn't a perfect solution . But anyways , it can work ! So I'm satisfied ^_^

1.Install nginx

It's the same as normal nginx installation , and nginx from the package manager should work , simply install with : (on Debian/Ubuntu)

sudo apt install nginx

Or you can install with any way you like.

2.Configure

Create a .conf file under /etc/nginx/sites-enabled , and copy the following configuration into it.

You can also download it via curl https://gist.githubusercontent.com/a279437145/d8c1565e49e5eda7577e68ec519660bc/raw/1086df501003368582fd59c23ef25f7e3ffc5481/proxy.conf -o proxy.conf

Configuration
The same as https://gist.github.com/a279437145/d8c1565e49e5eda7577e68ec519660bc

 server {
    listen {{PORT}} ssl;
    server_name {{IP_ADDRESS/DOMAIN}};


    ssl_certificate "{{PATH_TO_SSL_CERTIFICATION_FILE}}";
    ssl_certificate_key "{{PATH_TO_SSL_CERTIFICATION_KEY}}";

    client_max_body_size 50M;
    location ~* ^/proxy/(?<proxy_port>[^\n\r\/]+)(?<proxy_path>.*)$ {
	default_type text/html;
    	if ($proxy_port !~* ^([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-5][0-9][0-9][0-9][0-9]|6[0-5][0-5][0-3][0-5])$) {
	    add_header Content-Type 'text/html; charset=utf-8';
	    return 501 'Illegal port ${proxy_port}';
	}
	if ($proxy_path = ''){
	    set $proxy_path '/'; #force to use relative path
	}
	add_header Set-Cookie 'proxy_port=$proxy_port; Path=/';
	proxy_http_version 1.1;
        #proxy_ssl_server_name on;
        proxy_pass http://127.0.0.1:$proxy_port$proxy_path;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real_IP $remote_addr;
        proxy_set_header User-Agent $http_user_agent;
        proxy_set_header Accept-Encoding "";
        proxy_buffering off;
        #websocket
	set $to_upgrade "";
	set $to_connection "";
	if ($http_upgrade = "websocket"){
	    set $to_upgrade "websocket";
	    set $to_connection "Upgrade";
	}
	proxy_set_header Upgrade $to_upgrade;
	proxy_set_header Connection $to_connection;
	proxy_read_timeout 86400;
    }
    location / {
    	default_type text/html;
	if ($cookie_proxy_port = ''){
	    return 404 'Nothing here';
	}
	proxy_http_version 1.1;
	proxy_pass http://127.0.0.1:$cookie_proxy_port;
	proxy_set_header Host $http_host;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	proxy_set_header X-Real_IP $remote_addr;
	proxy_set_header User-Agent $http_user_agent;
	proxy_set_header Accept-Encoding "";
	proxy_buffering off;
	#websocket
	set $to_upgrade "";
	set $to_connection "";
	if ($http_upgrade = "websocket"){
	    set $to_upgrade "websocket";
	    set $to_connection "Upgrade";
	}
	proxy_set_header Upgrade $to_upgrade;
	proxy_set_header Connection $to_connection;
	proxy_read_timeout 86400;
    }
}

Then replace the double braces surrounded contents with yours. (Remember to use a different port than code-server)

Note :

  • 1 . If you don't use https to access the proxy , remove ssl on listen line , and remove ssl_certificate ssl_certificate_key .
  • 2 . If your backend is using https , change http on proxy_pass line to https.

3.Usage

Access https://{IP}:{NGINX_PORT}/proxy/{SERVICE_PORT}

Note : If the browser reported The site redirected you too many times , you can directly access https://{IP}/ after accessing https://{IP}:{NGINX_PORT}/proxy/{SERVICE_PORT}.

Notice

1.This uses / location , so better use a port without other services running .

2.This solution uses cookie , so don't use private mode in browser or disable cookie.

3.The default nginx.conf ( /etc/nginx/nginx.conf ) may contain a server config using port 80 , if you don't need it you can comment it to prevent conflicts with other services running on the same server.

I am curious about the ip address. The address "https://{IP}:{NGINX_PORT}/proxy/{SERVICE_PORT}" only can be visited in container?

@a279437145
Copy link
Author

a279437145 commented May 13, 2023 via email

@thuzhf
Copy link

thuzhf commented Apr 24, 2024

@code-asher Is there a way for code-server's built-in port-forwarding to forward the full url path to backend server?
For example, if my server is running at https://somehost/code-server-prefix/proxy/8080/ , and I visit https://somehost/code-server-prefix/proxy/8080/path1/path2, in the current version of code-server, my backend server will receive a request of /path1/path2, but I hope my backend server receive a request of /code-server-prefix/proxy/8080/path1/path2. That's to say, I do not want code-server remove the path prefix in the request url, but pass the full url to the backend server.
Is there a way to achieve this effect?

@code-asher
Copy link
Member

Yup, use absproxy instead of proxy: https://somehost/code-server-prefix/absproxy/8080/.

It would be nice if in the port forwarding panel we had an option to choose which one you want, since right now the only way to even know about absproxy is to read through the docs.

@code-asher
Copy link
Member

code-asher commented Apr 24, 2024

One caveat though, code-server does not know about its own prefix, so this will result in /absproxy/8080/path1/path2 to your backend (minus the /code-server-prefix). There is not a way around this currently, so if you need this you might want to stand up a standalone proxy like Caddy or NGINX instead of using code-server's built-in (you can still tie it into the ports panel if necessary).

@thuzhf
Copy link

thuzhf commented Apr 25, 2024

One caveat though, code-server does not know about its own prefix, so this will result in /absproxy/8080/path1/path2 to your backend (minus the /code-server-prefix). There is not a way around this currently, so if you need this you might want to stand up a standalone proxy like Caddy or NGINX instead of using code-server's built-in (you can still tie it into the ports panel if necessary).

Oh, that's a bit unexpected...
You said "there is not a way around this currently". Does this mean code-server doesn't support any params like base_url such as in jupyter (e.g., you can start a jupyter server by running: jupyter lab --ServerApp.base_url=/jupyter-prefix/ so that in nginx config you can pass full request url to the backend jupyter server)?

@thuzhf
Copy link

thuzhf commented Apr 25, 2024

@code-asher In addition, what I want to claim is that even though code-server itself doesn't need such a param like base_url because it probably uses relative URLs instead of absolute URLs, however, because users have a massive demand to set up others servers/apps inside the code-server (maybe that's a big reason why you develop the automatic port forwarding tools which I think is very useful) and many famous apps (including jupyter server, dash server, etc) do not use relative URLs (which itself is probably not a good implementation IMHO), thus these servers cannot be properly port-forwarded by the code-server's built-in port-forwarding functionality, which can do large damage to its ease of use and bothers me a lot.
I hope code-server can also support base_url besides using relative URLs. In this way, code-server can both doesn't depend on the prefix path it is running on and also pass the base_url to the servers/apps running inside it for some of these servers/apps to be port-forwarded properly.
That's my humble opinion, and if there is already a way to achieve this effect, please let know. Thanks!

@code-asher
Copy link
Member

Does this mean code-server doesn't support any params like base_url [...]
because it probably uses relative URLs instead of absolute URLs

Yup, exactly this.

Are you able to host at a subdomain instead? Something like code-server.domain.tld.

many famous apps (including jupyter server, dash server, etc) do not use relative URLs (which itself is probably not a good implementation IMHO)

100% agree.

I think it would be reasonable to add a --base-path flag that will be prepended to all absproxy requests.

@code-asher
Copy link
Member

I opened an issue: #6770

Not sure I can get to it any time soon, but happy to take pull requests.

@thuzhf
Copy link

thuzhf commented Apr 26, 2024

@code-asher I see. Thank you for your clear explanation and understanding and support!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting-for-info Waiting for more information from submitter
Projects
None yet
Development

No branches or pull requests

8 participants