Code-server: how to run code-server behind cloudflare and nginx

Created on 27 Apr 2020  Â·  30Comments  Â·  Source: cdr/code-server

I can't find any documentation on how to run code-server behind an https url without using ssl on code-server.

I can't figure out for the life of me how to have code-server answering to a certain https domain with the ssl certification handled by another system. Probably I'm not experienced enough and there is a very simple way to achieve this, but I fail no matter what I try. (is there a code-server documentation effort somewhere?)

Here is my scenario:

  • I want to run code-server on c.begiter.net.
  • I have code-server installed (not docker) on my server.
  • begiter.net DNS, and HTTPS is managed by CloudFlare.
  • I have nginx installed on my server, with multiple sub-domains registered. None of them employ ssl since that part is handled by CloudFlare.
  • c.begiter.net is proxied to localhost:9999, which runs code-server.

If I access with IP (and self-signed certificate - https://IP:9999), everything works fine on the desktop. On my iPad, after the password screen, websocket upgrades fail with 1006. I tried with Safari, Firefox, Chrome, Yandex, and Aloha browsers. I got Chrome to dump errors to Javascript console and the gist of it is _http upgrade fails with code 1006_ (which apparently is a browser-related error code, not something you can fix on the server).

Accessing with c.begiter.net fails with a white screen of death, with nginx reporting "upstream error". Meaning, code-server on localhost:9999 does not respond to requests - my guess, since they are to https://c.begiter.net but CloudFlare sends http:// requests to my server.

I can't figure out what command line parameters to use for code-server. Here is my current command line:
/home/necmettin/editor/code-server --port 9999 --user-data-dir /home/necmettin/editor/user-data --extensions-dir /home/necmettin/editor/extension-data --auth password --disable-ssh --host 213.159.29.172 --cert /home/necmettin/editor/cert/editor.crt --cert-key /home/necmettin/editor/cert/editor.key

And here is my nginx proxy setup (for Location / or c.begiter.net):

proxy_pass http://unix:/tmp/editor.sock;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Accept-Encoding gzip;
proxy_set_header Host $host;

What am I doing wrong? What is the correct way to do this? Please advise.

Most helpful comment

We’re making it easier to access your code-server instance securely from any device. We’ve eliminated the need for configuring TLS, domain registration, DNS, DoS protect, and authentication. To gain pre-release access, please consider joining our alpha program.

cc @necmettin @TomaruDev @HPaulson

All 30 comments

A few points that might help:

  • With Cloudflare handling TLS you won't need the --cert and --cert-key arguments to code-server unless you have a requirement to encrypt internally between the proxy and code-server.
  • Where is that socket on the proxy_pass line coming from? You might want proxy_pass http://[code-server host]:9999. Or if you remove --host then you can proxy to http://localhost:9999.
  • From what we've seen the iPad doesn't like self-signed certificates for websockets and recently Chrome appears to be doing the same thing.

Something like this should work, for example:

server {
  listen 80;
  listen [::]:80;
  server_name code.example.com code.example.org;
  location / {
      proxy_pass http://localhost:9999/;
      proxy_set_header Host $host;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection upgrade;
      proxy_set_header Accept-Encoding gzip;
  }
}
code-server --port 9999

First, thank you very much for your time and help.

* With Cloudflare handling TLS you won't need the `--cert` and `--cert-key` arguments to code-server unless you have a requirement to encrypt internally between the proxy and code-server.

I was using those only because I was accessing code-server directly by IP without nginx proxy. I removed them to access code-server through nginx proxy.

* Where is that socket on the `proxy_pass` line coming from? You might want `proxy_pass http://[code-server host]:9999`. Or if you remove `--host` then you can proxy to `http://localhost:9999`.

I created a systemd service to have code-server start automatically on boot. I was using the linux socket for that service. I changed to nginx config as you suggested.

* From what we've seen the iPad doesn't like self-signed certificates for websockets and recently Chrome appears to be doing the same thing.

That's why I'm trying to run it behind CloudFlare - nginx. But I can't get it to work on c.begiter.net either :(

Something like this should work, for example:

server {
  listen 80;
  listen [::]:80;
  server_name code.example.com code.example.org;
  location / {
      proxy_pass http://localhost:9999/;
      proxy_set_header Host $host;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection upgrade;
      proxy_set_header Accept-Encoding gzip;
  }
}
code-server --port 9999

Attached is the Firefox console log when I do as you suggest.

firefox-console-log-begiter.txt

WebSocket 1006 error again.

Nothing changes when I add --proxy-domain c.begiter.net.

Adding --host c.begiter.net causes a completely different error:

info code-server 3.1.1 28e91ba70cd70fa9adf3f2e3e3b87631b5667ecf error listen EADDRNOTAVAIL: address not available 104.24.110.195:9999

Which is to say, code-server checks if the fqdn I provided belongs to that IP. And it doesn't. 104.24.110.195 belongs to CloudFlare, but my begiter.net is on a different IP (that's how CloudFlare works after all).

I do not know why code-server checks if the IP matches the domain, there must be a reason of course but it makes having code-server run behind a web application firewall (all WAFs I know mask the origin IP this way) impossible.

Still looking for a way to achieve this.

I suspect

Yeah if you provide a domain name to --host the only thing code-server can do is look up the IP and that's what the DNS returns. code-server needs to listen on a local address or socket but the domain name resolves to an external address so it'll just fail.

So it looks like the proxy is connecting to code-server properly, it's just the websockets that aren't connecting, right?

Is it possible to connect directly to your server and bypass Cloudflare just to see if nginx and code-server are configured properly? It seems unlikely to me that the problem is on Cloudflare's end but it couldn't hurt to double-check (maybe there's a setting that needs to be enabled to support websockets; I'm not super familiar with Cloudflare unfortunately).

Yeah if you provide a domain name to --host the only thing code-server can do is look up the IP and that's what the DNS returns. code-server needs to listen on a local address or socket but the domain name resolves to an external address so it'll just fail.

So maybe it should be possible to set interface?

So it looks like the proxy is connecting to code-server properly, it's just the websockets that aren't connecting, right?

Yes, it is the websockets. Password screen and initial login works fine. When the page tries websocketing, error 1006.

Is it possible to connect directly to your server and bypass Cloudflare just to see if nginx and code-server are configured properly? It seems unlikely to me that the problem is on Cloudflare's end but it couldn't hurt to double-check (maybe there's a setting that needs to be enabled to support websockets; I'm not super familiar with Cloudflare unfortunately).

CloudFlare works fine with websockets out-of-the-box.

After many trials, I gave up on this. I decided to go local SSL way; created local ssl certificates using letsencrypt and everything is fine behind nginx now. I hope someone more experienced can get this working someday.

Thank you very much for your support.

You cannot proxy to localhost:9999. localhost is only accessible locally, you need to make code-server listen on your servers public IP and proxy Cloudflare to that.

Ah sorry I see you're using nginx to expose code-server. You seem to be using /tmp/editor.sock in your nginx config instead of localhost:9999?

Ah sorry I see you're using nginx to expose code-server. You seem to be using /tmp/editor.sock in your nginx config instead of localhost:9999?

That was the previous setup but changed that to listen to localhost:9999 instead of a unix socket. Then used nginx to reverse-proxy requests on my url to localhost:9999, and put cloudflare before that. After two days of misery, I removed CloudFlare from the equation and I am now using a letsencrypt certificate with nginx.

I got code-server to run on my domain this way, which I guess is what most people do. Now all that’s left is to somehow create an iOS app specifically to serve my code-server installation :)

Glad you got it all working.

Well I didn’t really get it working. I just gave up on the CloudFlare part :)

Thanks for all you guys’ time and effort though. Much appreciated.

@necmettin Heyo! Did you ever get this working correctly? I'm experiencing the same issue using Cloudflare & Nginx, and would prefer to not give up on Cloudflare all together... If you've gotten it working correctly between April and Now, please let me know!

Thanks,
~ Hunter

Did you ever get this working correctly?

Unfortunately, no :/ I will probably turn back to this a few months from now, but no solutions yet.

Unfortunately, no :/ I will probably turn back to this a few months from now, but no solutions yet.

Good to know! I tried setting it up for HOURS last night & also had no success. Hopefully someone more experienced than I will find this issue, although closed, and be able to provide a solution. Thanks for the response!

As far as I can tell there's no issue here to be solved. code-server works fine o Cloudflare and behind nginx. There's something wrong with how you guys are setting up things.

Can you provide complete details on how you set things up @HPaulson and I'll try to help you debug.

@nhooyr I'm reviving the exact same errors OP was in the initial post. All my other reverse proxy's though Nginx and proxied through Cloudflare work just fine. I'm pretty sure it has something to do with SSL issues. I don't serve any SSL certs from my server, and don't use end to end encryption. I only use Cloudflare Flexable Encryption.

I don't serve any SSL certs from my server, and don't use end to end encryption. I only use Cloudflare Flexable Encryption.

That's ok and should work fine.

If you forward the code-server port locally, can you access code-server?

What happens if you replace code-server with python3 -m http.server? Can you access that over Cloudflare?

@nhooyr I'd be happy to do some more testing with this later today for sure. I tried using code-server the same as any other process on my VPS:

  • I installed code server using the script provided
  • I setup an nginx reverse proxy (The config I use is ver similar to the one provided in self hosting guide, but I tested this exact one too)
  • I reset Nginx using systemctl
  • Changed password & Port in config, also set cert to false
  • I ran code server
  • Added proxied dns A record on Cloudflare
  • Received a white screen with a ton of errors, most notably 1006 as noted by OP

When running on local host on my machine without Nginx/Cloudflare everything works just fine.

I'll be happy to checkout this py cmd to see if I'm able to reverse proxy it's server correctly!

After a quick test, can confirm the python webserver is working as expected. When running python3 -m http.server [PORT], and using the same port I have setup for code-server (That's also in my nginx proxy) I was easily able to open it up on my subdomain which is proxied through couldflare.

When again running code-server, and logging in, I'm receiving the same errors as previously. Here's a full log from chrome:

serviceWorker.ts:4 [Service Worker] install
register.ts:12 [Service Worker] registered
indexedDBLogProvider.ts:54 Uncaught (in promise) DOMException: Failed to execute 'transaction' on 'IDBDatabase': A version change transaction is running.
    at https://REDACTED/static/REDACTED/lib/vscode/out/vs/workbench/workbench.web.api.js:4095:63
browserSocketFactory.ts:162 WebSocket connection to 'wss://REDACTED.com/?type=Management&reconnectionToken=REDACTED&reconnection=false&skipWebSocketFrames=false' failed: Error during WebSocket handshake: Unexpected response code: 200
create @ browserSocketFactory.ts:162
log.ts:197   ERR [remote-connection][Management   ][bd7dc…][initial][REDACTED.com:80] socketFactory.connect() failed. Error:
log.ts:197   ERR Error: WebSocket close with status code 1006
    at WebSocket.<anonymous> (browserSocketFactory.ts:129)
log.ts:197   ERR [remote-connection] An error occurred in the very first connect attempt, it will be treated as a permanent error! Error:
2log.ts:197   ERR Error: WebSocket close with status code 1006
    at WebSocket.<anonymous> (browserSocketFactory.ts:129)
3(index):1 Uncaught (in promise) Error: WebSocket close with status code 1006
    at WebSocket.<anonymous> (browserSocketFactory.ts:129)
types.ts:86 Uncaught (in promise) Error: Assertion Failed: argument is undefined or null
    at Object.t.assertIsDefined (types.ts:86)
    at e.getStorage (storageService.ts:117)
    at e.getBoolean (storageService.ts:99)
    at Q.open (web.main.ts:70)
    at async Object.t.create (workbench.web.api.ts:318)
errors.ts:26 Uncaught Error: WebSocket close with status code 1006

Error: WebSocket close with status code 1006
    at WebSocket.<anonymous> (browserSocketFactory.ts:129)
    at errors.ts:26
(index):1 Uncaught (in promise) Error: WebSocket close with status code 1006
    at WebSocket.<anonymous> (browserSocketFactory.ts:129)
errors.ts:26 Uncaught Error: Unable to read file 'vscode-userdata:/User/settings.json' (Error: WebSocket close with status code 1006)

Error: Unable to read file 'vscode-userdata:/User/settings.json' (Error: WebSocket close with status code 1006)
    at e.doReadAsFileStream (fileService.ts:457)
    at async e.readFile (fileService.ts:386)
    at /async https://REDACTED.com/static/REDACTED/lib/vscode/out/vs/workbench/workbench.web.api.js:3367
    at async Promise.all (/index 0)
    at async Promise.all (/index 0)
    at async f.loadConfiguration (configuration.ts:120)
    at async m.reload (configuration.ts:60)
    at errors.ts:26
errors.ts:26 Uncaught Error: Unable to read file 'vscode-userdata:/User/tasks.json' (Error: WebSocket close with status code 1006)

Error: Unable to read file 'vscode-userdata:/User/tasks.json' (Error: WebSocket close with status code 1006)
    at e.doReadAsFileStream (fileService.ts:457)
    at async e.readFile (fileService.ts:386)
    at /async https://REDACTED.com/static/REDACTED/lib/vscode/out/vs/workbench/workbench.web.api.js:3367
    at async Promise.all (/index 0)
    at async Promise.all (/index 1)
    at async f.loadConfiguration (configuration.ts:120)
    at async m.reload (configuration.ts:60)
    at errors.ts:26

Since the WebSocket request is being tried, that means the HTML/CSS/JS loads fine lol. So for whatever reason Cloudflare and nginx aren't playing well with WebSocket.

Are you sure you configured nginx correctly?

This sounds like you guys forgot these lines from the guide:

      proxy_set_header Host $host;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection upgrade;

https://github.com/cdr/code-server/blob/master/doc/guide.md#nginx

I know you said you tried the exact config but there doesn't seem to be anything else I can think of that could cause this. Can you give the exact config one more shot?

And can you try accessing code-server directly against the nginx IP address? If that fails as well, very likely it's the config problem.

@nhooyr Actually, you were totally correct! It was definitely a config issue- However, the config provided in the guide still was not working as expected for me. When replacing my proxy headers with the ones you provided, however, it works just fine. The only noticeable difference I can spot with the confgs is

    listen 80;
-    listen [::]:80;
    server_name mydomain.com;

My config just has listen 80;. I really appreciate all your help! Also gonna tag OP @necmettin as he mentioned here that he hadn't figured this one out yet.

For anyone who has this problem in the future and finds this issue, I'm gonna leave this here:

  1. Ensure cloudflare proxy (orange cloud) is turned on
  2. Ensure in your code server config, cert is set as false
  3. Have the following nginx config:
server {
    listen 80;
    server_name my.website.com;
    location / {
        proxy_pass         http://127.0.0.1:8080;
      proxy_set_header Host $host;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection upgrade;
    }
}

Awesome, glad you got things working!

That extra line shouldn't cause any problems. If anyone has any more issues, I'll reopen.

Cheers 🍻

Hello !
I have exactly the same problem with Apache, and I'm not very comfortable transforming NGINX code to Apache.
Could someone help me?
Thanks in advance ^^

I haven't used apache with code-server but I think something like this should work:

<VirtualHost *:80>
  ServerName code.example.com

  RewriteEngine On
  RewriteCond %{HTTP:Upgrade} =websocket [NC]
  RewriteRule /(.*)           ws://localhost:8080/$1 [P,L]
  RewriteCond %{HTTP:Upgrade} !=websocket [NC]
  RewriteRule /(.*)           http://localhost:8080/$1 [P,L]

  ProxyRequests off
  ProxyPass        / http://localhost:8080/ nocanon
  ProxyPassReverse / http://localhost:8080/
</VirtualHost>

If it's still not working please feel free to open a new discussion under the help category!

Thank you very much!
I'll try that right now :)

He redirects me to my localhost
(I use port 8000 and my domain name is in .dev, it must be in HTTPS because of that)

I'll try to tinker a little
If I have other problems I will create an issue :)

@TomaruDev This is kinda unrelated to your actual answer, but what's the issue moving to nginx? It's actually much more secure than Nginx, and Apache2 is pretty much getting old and is a thing of the 2000's. Apache2 was made to work great with php, and php in 2020 is a yikes. This is quite unrelated, but I figured I'd toss in my 2 cents!

Hope you figure out the answer none the less!

I know that Apache is old, but the problem is that I don't know anything about NGINX and it's a friend who made the apache proxy.
However, no problem with PHP, Apache is only in reverse-proxy, my main server is coded in NodeJS.
I totally agree with the fact that Apache is old and outdated, but since I didn't do the reverse-proxy I couldn't ask him to do it under NGINX (and he doesn't know anything about it either!).
That's it!
For the moment I'm still on apache, one day I'll upgrade all this :)
I will create an issue for my problem with apache.
Thanks for everything !

That's it, I created a discussion: https://github.com/cdr/code-server/discussions/2104

Good!
I'm determined to put ALL my virtualhosts on NGINX.
It's easier than it seems :)
So I used the solution for NGINX from the other issue, and everything works perfectly !
Thank you very much for your help :)

We’re making it easier to access your code-server instance securely from any device. We’ve eliminated the need for configuring TLS, domain registration, DNS, DoS protect, and authentication. To gain pre-release access, please consider joining our alpha program.

cc @necmettin @TomaruDev @HPaulson

Was this page helpful?
0 / 5 - 0 ratings