From 36b3183b75e2cdd5248d2f586c8bd1c5ba033832 Mon Sep 17 00:00:00 2001 From: Ammar Bandukwala Date: Tue, 13 Oct 2020 23:25:09 +0000 Subject: [PATCH 01/25] Add Coder Cloud alpha sign up link --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index f126580b8..433413bb9 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,15 @@ The install script will print out how to run and start using code-server. Docs on the install script, manual installation and docker image are at [./doc/install.md](./doc/install.md). +### Alpha Program 🐣 +We're working on a cloud platform to make deploying and managing code-server easier. If you don't want to worry about + +* TLS +* Authentication +* Port Forwarding + +consider [joining our alpha program](https://codercom.typeform.com/to/U4IKyv0W). + ## FAQ See [./doc/FAQ.md](./doc/FAQ.md). From 4a3d2e5a9430756f68602deaa29b6349982b461b Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 21 Oct 2020 16:39:30 -0400 Subject: [PATCH 02/25] Remove unnecessary whoami Closes #2213 --- ci/release-image/Dockerfile | 1 + ci/release-image/entrypoint.sh | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/ci/release-image/Dockerfile b/ci/release-image/Dockerfile index 5c31ecbe7..a0b6aed71 100644 --- a/ci/release-image/Dockerfile +++ b/ci/release-image/Dockerfile @@ -43,5 +43,6 @@ EXPOSE 8080 # the uid will remain the same. note: only relevant if -u isn't passed to # docker-run. USER 1000 +ENV USER=coder WORKDIR /home/coder ENTRYPOINT ["/usr/bin/entrypoint.sh", "--bind-addr", "0.0.0.0:8080", "."] diff --git a/ci/release-image/entrypoint.sh b/ci/release-image/entrypoint.sh index b4343e7ed..abf9fddb3 100755 --- a/ci/release-image/entrypoint.sh +++ b/ci/release-image/entrypoint.sh @@ -1,10 +1,6 @@ #!/bin/sh set -eu -# This isn't set by default. -USER="$(whoami)" -export USER - if [ "${DOCKER_USER-}" ] && [ "$DOCKER_USER" != "$USER" ]; then echo "$DOCKER_USER ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/nopasswd > /dev/null # Unfortunately we cannot change $HOME as we cannot move any bind mounts From bca1bcfc03f5bfb0671b62b29766f6cbb271a27e Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 21 Oct 2020 16:45:53 -0400 Subject: [PATCH 03/25] Fix README formatting --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 433413bb9..8e68c3e16 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,12 @@ The install script will print out how to run and start using code-server. Docs on the install script, manual installation and docker image are at [./doc/install.md](./doc/install.md). ### Alpha Program 🐣 + We're working on a cloud platform to make deploying and managing code-server easier. If you don't want to worry about -* TLS -* Authentication -* Port Forwarding +- TLS +- Authentication +- Port Forwarding consider [joining our alpha program](https://codercom.typeform.com/to/U4IKyv0W). From 759a78d9d8cb372a8d7436eb88575b074a9c54ef Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 21 Oct 2020 17:00:20 -0400 Subject: [PATCH 04/25] install.sh: Rename SSH_FLAGS to RSH_FLAGS --- install.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/install.sh b/install.sh index 08e3a51e8..4865d4621 100755 --- a/install.sh +++ b/install.sh @@ -109,7 +109,7 @@ main() { VERSION \ OPTIONAL \ ALL_FLAGS \ - SSH_ARGS + RSH_ARGS ALL_FLAGS="" while [ "$#" -gt 0 ]; do @@ -152,7 +152,7 @@ main() { shift # We remove the -- added above. ALL_FLAGS="${ALL_FLAGS% --}" - SSH_ARGS="$*" + RSH_ARGS="$*" break ;; -*) @@ -161,7 +161,7 @@ main() { exit 1 ;; *) - SSH_ARGS="$*" + RSH_ARGS="$*" break ;; esac @@ -169,9 +169,9 @@ main() { shift done - if [ "${SSH_ARGS-}" ]; then - echoh "Installing remotely with ssh $SSH_ARGS" - curl -fsSL https://code-server.dev/install.sh | prefix "$SSH_ARGS" ssh "$SSH_ARGS" sh -s -- "$ALL_FLAGS" + if [ "${RSH_ARGS-}" ]; then + echoh "Installing remotely with ssh $RSH_ARGS" + curl -fsSL https://code-server.dev/install.sh | prefix "$RSH_ARGS" ssh "$RSH_ARGS" sh -s -- "$ALL_FLAGS" return fi From 30f30305309338217e9634c8fb99ccaf88a07383 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 21 Oct 2020 17:05:58 -0400 Subject: [PATCH 05/25] install.sh: Allow customizing remote shell with --rsh --- install.sh | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/install.sh b/install.sh index 4865d4621..2fd90d71b 100755 --- a/install.sh +++ b/install.sh @@ -24,7 +24,7 @@ ${not_curl_usage-} Usage: $arg0 [--dry-run] [--version X.X.X] [--method detect] \ - [--prefix ~/.local] [user@host] + [--prefix ~/.local] [--rsh ssh] [user@host] --dry-run Echo the commands for the install process without running them. @@ -45,6 +45,9 @@ Usage: and the binary symlinked into ~/.local/bin/code-server To install system wide pass ---prefix=/usr/local + --rsh + Specifies the remote shell for remote installation. Defaults to ssh. + - For Debian, Ubuntu and Raspbian it will install the latest deb package. - For Fedora, CentOS, RHEL and openSUSE it will install the latest rpm package. - For Arch Linux it will install the AUR package. @@ -109,7 +112,8 @@ main() { VERSION \ OPTIONAL \ ALL_FLAGS \ - RSH_ARGS + RSH_ARGS \ + RSH ALL_FLAGS="" while [ "$#" -gt 0 ]; do @@ -144,6 +148,13 @@ main() { --version=*) VERSION="$(parse_arg "$@")" ;; + --rsh) + RSH="$(parse_arg "$@")" + shift + ;; + --rsh=*) + RSH="$(parse_arg "$@")" + ;; -h | --h | -help | --help) usage exit 0 @@ -170,8 +181,8 @@ main() { done if [ "${RSH_ARGS-}" ]; then - echoh "Installing remotely with ssh $RSH_ARGS" - curl -fsSL https://code-server.dev/install.sh | prefix "$RSH_ARGS" ssh "$RSH_ARGS" sh -s -- "$ALL_FLAGS" + echoh "Installing remotely with $RSH $RSH_ARGS" + curl -fsSL https://code-server.dev/install.sh | prefix "$RSH_ARGS" "$RSH" "$RSH_ARGS" sh -s -- "$ALL_FLAGS" return fi From a0b7bf218042b5b1b8f10e73fa50f1809718ba55 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Thu, 22 Oct 2020 01:43:22 -0400 Subject: [PATCH 06/25] install.sh: Default $RSH to ssh --- install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install.sh b/install.sh index 2fd90d71b..2c0240297 100755 --- a/install.sh +++ b/install.sh @@ -181,6 +181,7 @@ main() { done if [ "${RSH_ARGS-}" ]; then + RSH="${RSH-ssh}" echoh "Installing remotely with $RSH $RSH_ARGS" curl -fsSL https://code-server.dev/install.sh | prefix "$RSH_ARGS" "$RSH" "$RSH_ARGS" sh -s -- "$ALL_FLAGS" return From 79443c14ff41f38d3f05e0a53e4aa592e3c0e418 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 23 Oct 2020 12:07:08 -0400 Subject: [PATCH 07/25] release-image: Remap UID within the image before handling $DOCKER_USER (#2223) If do not update the UID within the passwd database to match whatever uid the container is being ran as, then sudo will not work when renaming the user to match $DOCKER_USER as it will complain about the current user being non-existent. --- ci/release-image/entrypoint.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ci/release-image/entrypoint.sh b/ci/release-image/entrypoint.sh index abf9fddb3..4f2f7cfe2 100755 --- a/ci/release-image/entrypoint.sh +++ b/ci/release-image/entrypoint.sh @@ -1,7 +1,11 @@ #!/bin/sh set -eu -if [ "${DOCKER_USER-}" ] && [ "$DOCKER_USER" != "$USER" ]; then +# We do this first to ensure sudo works below when renaming the user. +# Otherwise the current container UID may not exist in the passwd database. +eval "$(fixuid -q)" + +if [ "${DOCKER_USER-}" ]; then echo "$DOCKER_USER ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/nopasswd > /dev/null # Unfortunately we cannot change $HOME as we cannot move any bind mounts # nor can we bind mount $HOME into a new home as that requires a privileged container. @@ -11,7 +15,6 @@ if [ "${DOCKER_USER-}" ] && [ "$DOCKER_USER" != "$USER" ]; then USER="$DOCKER_USER" sudo sed -i "/coder/d" /etc/sudoers.d/nopasswd - sudo sed -i "s/coder/$DOCKER_USER/g" /etc/fixuid/config.yml fi -dumb-init fixuid -q /usr/bin/code-server "$@" +dumb-init /usr/bin/code-server "$@" From 62735da69466a444561ab9b1115dc7c4d496d455 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 14 Oct 2020 17:06:42 -0500 Subject: [PATCH 08/25] v3.6.1 --- doc/install.md | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/install.md b/doc/install.md index b53a60675..c33299cf6 100644 --- a/doc/install.md +++ b/doc/install.md @@ -79,8 +79,8 @@ commands presented in the rest of this document. ## Debian, Ubuntu ```bash -curl -fOL https://github.com/cdr/code-server/releases/download/v3.6.0/code-server_3.6.0_amd64.deb -sudo dpkg -i code-server_3.6.0_amd64.deb +curl -fOL https://github.com/cdr/code-server/releases/download/v3.6.1/code-server_3.6.1_amd64.deb +sudo dpkg -i code-server_3.6.1_amd64.deb sudo systemctl enable --now code-server@$USER # Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml ``` @@ -88,8 +88,8 @@ sudo systemctl enable --now code-server@$USER ## Fedora, CentOS, RHEL, SUSE ```bash -curl -fOL https://github.com/cdr/code-server/releases/download/v3.6.0/code-server-3.6.0-amd64.rpm -sudo rpm -i code-server-3.6.0-amd64.rpm +curl -fOL https://github.com/cdr/code-server/releases/download/v3.6.1/code-server-3.6.1-amd64.rpm +sudo rpm -i code-server-3.6.1-amd64.rpm sudo systemctl enable --now code-server@$USER # Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml ``` @@ -158,10 +158,10 @@ Here is an example script for installing and using a standalone `code-server` re ```bash mkdir -p ~/.local/lib ~/.local/bin -curl -fL https://github.com/cdr/code-server/releases/download/v3.6.0/code-server-3.6.0-linux-amd64.tar.gz \ +curl -fL https://github.com/cdr/code-server/releases/download/v3.6.1/code-server-3.6.1-linux-amd64.tar.gz \ | tar -C ~/.local/lib -xz -mv ~/.local/lib/code-server-3.6.0-linux-amd64 ~/.local/lib/code-server-3.6.0 -ln -s ~/.local/lib/code-server-3.6.0/bin/code-server ~/.local/bin/code-server +mv ~/.local/lib/code-server-3.6.1-linux-amd64 ~/.local/lib/code-server-3.6.1 +ln -s ~/.local/lib/code-server-3.6.1/bin/code-server ~/.local/bin/code-server PATH="~/.local/bin:$PATH" code-server # Now visit http://127.0.0.1:8080. Your password is in ~/.config/code-server/config.yaml diff --git a/package.json b/package.json index cc3edd30a..6cb9dd17a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-server", "license": "MIT", - "version": "3.6.0", + "version": "3.6.1", "description": "Run VS Code on a remote server.", "homepage": "https://github.com/cdr/code-server", "bugs": { From 860c99e3b8b8c9f0b3627806a39e6b8b876a216e Mon Sep 17 00:00:00 2001 From: Katie Horne Date: Thu, 29 Oct 2020 16:21:47 -0500 Subject: [PATCH 09/25] Docs copyedits: README.md + CONTRIBUTING.md (#2242) * Edit README * Edit CONTRIBUTING * Format CONTRIBUTING.MD * Incorporate feedback * Revert movement of CONTRIBUTING.MD and format --- README.md | 46 ++++++---------- doc/CONTRIBUTING.md | 127 ++++++++++++++++++++++---------------------- 2 files changed, 79 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 8e68c3e16..ee0b8700b 100644 --- a/README.md +++ b/README.md @@ -6,72 +6,58 @@ Run [VS Code](https://github.com/Microsoft/vscode) on any machine anywhere and a ## Highlights -- **Code everywhere** - - Code on your Chromebook, tablet, and laptop with a consistent development environment. - - Develop on a Linux machine and pick up from any device with a web browser. -- **Server-powered** - - Take advantage of large cloud servers to speed up tests, compilations, downloads, and more. - - Preserve battery life when you're on the go as all intensive tasks run on your server. - - Make use of a spare computer you have lying around and turn it into a full development environment. +- Code on any device with a consistent development environment +- Use cloud servers to speed up tests, compilations, downloads, and more +- Preserve battery life when you're on the go; all intensive tasks run on your server ## Getting Started -For a full setup and walkthrough, please see [./doc/guide.md](./doc/guide.md). +There are two ways to get started: -### Quick Install +1. Using the [install script](./install.sh), which automates most of the process. The script uses the system package manager (if possible) +2. Manually installing code-server; see [Installation](./doc/install.md) for instructions applicable to most use cases -We have a [script](./install.sh) to install code-server for Linux, macOS and FreeBSD. - -It tries to use the system package manager if possible. - -First run to print out the install process: +If you choose to use the install script, you can preview what occurs during the install process: ```bash curl -fsSL https://code-server.dev/install.sh | sh -s -- --dry-run ``` -Now to actually install: +To install, run: ```bash curl -fsSL https://code-server.dev/install.sh | sh ``` -The install script will print out how to run and start using code-server. +When done, the install script prints out instructions for running and starting code-server. -### Manual Install - -Docs on the install script, manual installation and docker image are at [./doc/install.md](./doc/install.md). +We also have an in-depth [setup and configuration](./doc/guide.md) guide. ### Alpha Program 🐣 -We're working on a cloud platform to make deploying and managing code-server easier. If you don't want to worry about +We're working on a cloud platform that makes deploying and managing code-server easier. Consider [joining our alpha program](https://codercom.typeform.com/to/U4IKyv0W) if you don't want to worry about - TLS - Authentication - Port Forwarding -consider [joining our alpha program](https://codercom.typeform.com/to/U4IKyv0W). - ## FAQ See [./doc/FAQ.md](./doc/FAQ.md). -## Contributing +## Want to help? -See [./doc/CONTRIBUTING.md](./doc/CONTRIBUTING.md). +See [CONTRIBUTING](./doc/CONTRIBUTING.md) for details. ## Hiring -We ([@cdr](https://github.com/cdr)) are looking for engineers to help maintain -code-server, innovate on open source and streamline dev workflows. +We ([@cdr](https://github.com/cdr)) are looking for engineers to help [maintain +code-server](https://jobs.lever.co/coder/e40becde-2cbd-4885-9029-e5c7b0a734b8), innovate on open source, and streamline dev workflows. Our main office is in Austin, Texas. Remote is ok as long as you're in North America or Europe. -Please get in [touch](mailto:jobs@coder.com) with your resume/github if interested. - -We're also hiring someone specifically to help maintain code-server. -See the listing [here](https://jobs.lever.co/coder/e40becde-2cbd-4885-9029-e5c7b0a734b8). +Please get in [touch](mailto:jobs@coder.com) with your resume/GitHub if interested. ## For Organizations diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md index 62c20f915..52b4153a5 100644 --- a/doc/CONTRIBUTING.md +++ b/doc/CONTRIBUTING.md @@ -8,6 +8,7 @@ - [Build](#build) - [Structure](#structure) - [VS Code Patch](#vs-code-patch) + - [Currently Known Issues](#currently-known-issues) @@ -15,24 +16,26 @@ ## Pull Requests -Please link to the issue each PR solves. -If there is no existing issue, please first create one unless the fix is minor. +Please create a [GitHub Issue](https://github.com/cdr/code-server/issues) for each issue +you'd like to address unless the proposed fix is minor. -Please make sure the base of your PR is the master branch. We keep the GitHub -default branch the latest release branch to avoid confusion as the -documentation is on GitHub and we don't want users to see docs on unreleased -features. +In your Pull Requests (PR), link to the issue that the PR solves. + +Please ensure that the base of your PR is the **master** branch. (Note: The default +GitHub branch is the latest release branch, though you should point all of your changes to be merged into +master). ## Requirements -Please refer to [VS Code's prerequisites](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites). +The prerequisites for contributing to code-server are almost the same as those for +[VS Code](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#prerequisites). +There are several differences, however. You must: -Differences: +- Use Node.js version 12.x (or greater) +- Have [nfpm](https://github.com/goreleaser/nfpm) (which is used to build `.deb` and `.rpm` packages and [jq](https://stedolan.github.io/jq/) (used to build code-server releases) installed -- We require a minimum of node v12 but later versions should work. -- We use [nfpm](https://github.com/goreleaser/nfpm) to build `.deb` and `.rpm` packages. -- We use [jq](https://stedolan.github.io/jq/) to build code-server releases. -- The [CI container](../ci/images/debian10/Dockerfile) is a useful reference for all our dependencies. +The [CI container](../ci/images/debian8/Dockerfile) is a useful reference for all +of the dependencies code-server uses. ## Development Workflow @@ -40,10 +43,10 @@ Differences: yarn yarn vscode yarn watch -# Visit http://localhost:8080 once the build completed. +# Visit http://localhost:8080 once the build is completed. ``` -To develop inside of an isolated docker container: +To develop inside an isolated Docker container: ```shell ./ci/dev/image/run.sh yarn @@ -53,12 +56,12 @@ To develop inside of an isolated docker container: `yarn watch` will live reload changes to the source. -If changes are made to the patch and you've built previously you must manually -reset VS Code then run `yarn vscode:patch`. +If you introduce changes to the patch and you've previously built, you +must (1) manually reset VS Code and (2) run `yarn vscode:patch`. ## Build -You can build with: +You can build using: ```shell ./ci/dev/image/run.sh ./ci/steps/release.sh @@ -66,22 +69,22 @@ You can build with: Run your build with: -``` +```shell cd release yarn --production # Runs the built JavaScript with Node. node . ``` -Build release packages (make sure you run `./ci/steps/release.sh` first): +Build the release packages (make sure that you run `./ci/steps/release.sh` first): -``` +```shell IMAGE=centos7 ./ci/dev/image/run.sh ./ci/steps/release-packages.sh # The standalone release is in ./release-standalone # .deb, .rpm and the standalone archive are in ./release-packages ``` -The `release.sh` script is the equivalent of: +The `release.sh` script is equal to running: ```shell yarn @@ -91,73 +94,69 @@ yarn build:vscode yarn release ``` -And `release-packages.sh` is: +And `release-packages.sh` is equal to: -``` +```shell yarn release:standalone yarn test:standalone-release yarn package ``` -For a faster release build you can also run: +For a faster release build, you can run instead: -``` +```shell KEEP_MODULES=1 ./ci/steps/release.sh node ./release ``` ## Structure -The `code-server` script serves an HTTP API to login and start a remote VS Code process. +The `code-server` script serves an HTTP API for login and starting a remote VS Code process. The CLI code is in [./src/node](./src/node) and the HTTP routes are implemented in [./src/node/app](./src/node/app). -Most of the meaty parts are in our VS Code patch which is described next. +Most of the meaty parts are in the VS Code patch, which we described next. ### VS Code Patch -Back in v1 of code-server, we had an extensive patch of VS Code that split the codebase -into a frontend and server. The frontend consisted of all UI code and the server ran -the extensions and exposed an API to the frontend for file access and everything else -that the UI needed. +In v1 of code-server, we had a patch of VS Code that split the codebase into a front-end +and a server. The front-end consisted of all UI code, while the server ran the extensions +and exposed an API to the front-end for file access and all UI needs. -This worked but eventually Microsoft added support to VS Code to run it in the web. -They have open sourced the frontend but have kept the server closed source. - -So in interest of piggy backing off their work, v2 and beyond use the VS Code -web frontend and fill in the server. This is contained in our +Over time, Microsoft added support to VS Code to run it on the web. They have made +the front-end open source, but not the server. As such, code-server v2 (and later) uses +the VS Code front-end and implements the server. You can find this in [./ci/dev/vscode.patch](../ci/dev/vscode.patch) under the path `src/vs/server`. Other notable changes in our patch include: -- Add our own build file which includes our code and VS Code's web code. -- Allow multiple extension directories (both user and built-in). -- Modify the loader, websocket, webview, service worker, and asset requests to - use the URL of the page as a base (and TLS if necessary for the websocket). -- Send client-side telemetry through the server. -- Allow modification of the display language. -- Make it possible for us to load code on the client. -- Make extensions work in the browser. -- Make it possible to install extensions of any kind. -- Fix getting permanently disconnected when you sleep or hibernate for a while. -- Add connection type to web socket query parameters. +- Adding our build file, which includes our code and VS Code's web code +- Allowing multiple extension directories (both user and built-in) +- Modifying the loader, websocket, webview, service worker, and asset requests to + use the URL of the page as a base (and TLS, if necessary for the websocket) +- Sending client-side telemetry through the server +- Allowing modification of the display language +- Making it possible for us to load code on the client +- Making extensions work in the browser +- Making it possible to install extensions of any kind +- Fixing issue with getting disconnected when your machine sleeps or hibernates +- Adding connection type to web socket query parameters -Some known issues presently: - -- Creating custom VS Code extensions and debugging them doesn't work. -- Extension profiling and tips are currently disabled. - -As the web portion of VS Code matures, we'll be able to shrink and maybe even entirely -eliminate our patch. In the meantime, however, upgrading the VS Code version requires -ensuring that the patch still applies and has the intended effects. - -To generate a new patch run `yarn vscode:diff`. - -**note**: We have extension docs on the CI and build system at [./ci/README.md](../ci/README.md) - -If functionality doesn't depend on code from VS Code then it should be moved -into code-server otherwise it should be in the patch. - -In the future we'd like to run VS Code unit tests against our builds to ensure features +As the web portion of VS Code matures, we'll be able to shrink and possibly +eliminate our patch. In the meantime, upgrading the VS Code version requires +us to ensure that the patch is applied and works as intended. In the future, +we'd like to run VS Code unit tests against our builds to ensure that features work as expected. + +To generate a new patch, run `yarn vscode:diff` + +**Note**: We have [extension docs](../ci/README.md) on the CI and build system. + +If the functionality you're working on does NOT depend on code from VS Code, please +move it out and into code-server. + +### Currently Known Issues + +- Creating custom VS Code extensions and debugging them doesn't work +- Extension profiling and tips are currently disabled From 10b3028196192607dab139ea01b58b94a2336eae Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 30 Oct 2020 04:13:22 -0400 Subject: [PATCH 10/25] util: Generate self signed certificate into data directory Closes #1778 --- doc/FAQ.md | 3 +++ src/node/entry.ts | 2 +- src/node/util.ts | 18 ++++++++++-------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/doc/FAQ.md b/doc/FAQ.md index 370dd6660..694a07229 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -144,6 +144,9 @@ For HTTPS, you can use a self signed certificate by passing in just `--cert` or pass in an existing certificate by providing the path to `--cert` and the path to the key with `--cert-key`. +The self signed certificate will be generated into +`~/.local/share/code-server/self-signed.cert`. + If `code-server` has been passed a certificate it will also respond to HTTPS requests and will redirect all HTTP requests to HTTPS. diff --git a/src/node/entry.ts b/src/node/entry.ts index 96db046e2..3fbbb4cf1 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -209,7 +209,7 @@ const main = async (args: Args, configArgs: Args): Promise => { logger.info( args.cert && args.cert.value ? ` - Using provided certificate and key for HTTPS` - : ` - Using generated certificate and key for HTTPS`, + : ` - Using generated certificate and key for HTTPS: ${humanPath(options.cert)}`, ) } else { logger.info(" - Not serving HTTPS") diff --git a/src/node/util.ts b/src/node/util.ts index 75122fe76..ee1e85be9 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -55,11 +55,10 @@ export function humanPath(p?: string): string { } export const generateCertificate = async (): Promise<{ cert: string; certKey: string }> => { - const paths = { - cert: path.join(tmpdir, "self-signed.cert"), - certKey: path.join(tmpdir, "self-signed.key"), - } - const checks = await Promise.all([fs.pathExists(paths.cert), fs.pathExists(paths.certKey)]) + const certPath = path.join(paths.data, "self-signed.cert") + const certKeyPath = path.join(paths.data, "self-signed.key") + + const checks = await Promise.all([fs.pathExists(certPath), fs.pathExists(certKeyPath)]) if (!checks[0] || !checks[1]) { // Require on demand so openssl isn't required if you aren't going to // generate certificates. @@ -69,10 +68,13 @@ export const generateCertificate = async (): Promise<{ cert: string; certKey: st return error ? reject(error) : resolve(result) }) }) - await fs.mkdirp(tmpdir) - await Promise.all([fs.writeFile(paths.cert, certs.certificate), fs.writeFile(paths.certKey, certs.serviceKey)]) + await fs.mkdirp(paths.data) + await Promise.all([fs.writeFile(certPath, certs.certificate), fs.writeFile(certKeyPath, certs.serviceKey)]) + } + return { + cert: certPath, + certKey: certKeyPath, } - return paths } export const generatePassword = async (length = 24): Promise => { From 8b85006996847580b10092c4b57697f2c3eef5e4 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 30 Oct 2020 04:35:08 -0400 Subject: [PATCH 11/25] src/node/util.ts: Make certificate generation "modern" Now we add a subject alt name, set extendedKeyUsage and use the correct certificate extension. The above allow it to be properly trusted by iOS. See https://support.apple.com/en-us/HT210176 *.cert isn't a real extension for certificates, *.crt is correct for it to be recognized by e.g. keychain or when importing as a profile into iOS. Updates #1566 I've been able to successfully connect from my iPad Pro now to my code-server instance with a self signed certificate! Next commit will be docs. --- doc/FAQ.md | 2 +- src/node/util.ts | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/doc/FAQ.md b/doc/FAQ.md index 694a07229..5d1407d14 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -145,7 +145,7 @@ pass in an existing certificate by providing the path to `--cert` and the path t the key with `--cert-key`. The self signed certificate will be generated into -`~/.local/share/code-server/self-signed.cert`. +`~/.local/share/code-server/self-signed.crt`. If `code-server` has been passed a certificate it will also respond to HTTPS requests and will redirect all HTTP requests to HTTPS. diff --git a/src/node/util.ts b/src/node/util.ts index ee1e85be9..20880913b 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -55,7 +55,7 @@ export function humanPath(p?: string): string { } export const generateCertificate = async (): Promise<{ cert: string; certKey: string }> => { - const certPath = path.join(paths.data, "self-signed.cert") + const certPath = path.join(paths.data, "self-signed.crt") const certKeyPath = path.join(paths.data, "self-signed.key") const checks = await Promise.all([fs.pathExists(certPath), fs.pathExists(certKeyPath)]) @@ -64,9 +64,25 @@ export const generateCertificate = async (): Promise<{ cert: string; certKey: st // generate certificates. const pem = require("pem") as typeof import("pem") const certs = await new Promise((resolve, reject): void => { - pem.createCertificate({ selfSigned: true }, (error, result) => { - return error ? reject(error) : resolve(result) - }) + pem.createCertificate( + { + selfSigned: true, + config: ` +[req] +req_extensions = v3_req + +[ v3_req ] +extendedKeyUsage = serverAuth +subjectAltName = @alt_names + +[alt_names] +DNS.1 = localhost +`, + }, + (error, result) => { + return error ? reject(error) : resolve(result) + }, + ) }) await fs.mkdirp(paths.data) await Promise.all([fs.writeFile(certPath, certs.certificate), fs.writeFile(certKeyPath, certs.serviceKey)]) From bae28727bd00e02601b618fe3efe720a675a3e3a Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 30 Oct 2020 05:26:40 -0400 Subject: [PATCH 12/25] src/node/cli.ts: Add --cert-host to configure generated certificate hostname --- src/node/cli.ts | 7 ++++++- src/node/entry.ts | 2 +- src/node/util.ts | 9 +++++---- test/socket.test.ts | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/node/cli.ts b/src/node/cli.ts index 1403d8920..4ff35b445 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -26,6 +26,7 @@ export interface Args extends VsArgs { readonly auth?: AuthType readonly password?: string readonly cert?: OptionalString + readonly "cert-host"?: string readonly "cert-key"?: string readonly "disable-telemetry"?: boolean readonly help?: boolean @@ -101,7 +102,11 @@ const options: Options> = { cert: { type: OptionalString, path: true, - description: "Path to certificate. Generated if no path is provided.", + description: "Path to certificate. A self signed certificate is generated if none is provided.", + }, + "cert-host": { + type: "string", + description: "Hostname to use when generating a self signed certificate.", }, "cert-key": { type: "string", path: true, description: "Path to certificate key when using non-generated cert." }, "disable-telemetry": { type: "boolean", description: "Disable telemetry." }, diff --git a/src/node/entry.ts b/src/node/entry.ts index 3fbbb4cf1..5184c4344 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -160,7 +160,7 @@ const main = async (args: Args, configArgs: Args): Promise => { proxyDomains: args["proxy-domain"], socket: args.socket, ...(args.cert && !args.cert.value - ? await generateCertificate() + ? await generateCertificate(args["cert-host"] || "localhost") : { cert: args.cert && args.cert.value, certKey: args["cert-key"], diff --git a/src/node/util.ts b/src/node/util.ts index 20880913b..b4e175a3e 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -54,9 +54,9 @@ export function humanPath(p?: string): string { return p.replace(os.homedir(), "~") } -export const generateCertificate = async (): Promise<{ cert: string; certKey: string }> => { - const certPath = path.join(paths.data, "self-signed.crt") - const certKeyPath = path.join(paths.data, "self-signed.key") +export const generateCertificate = async (hostname: string): Promise<{ cert: string; certKey: string }> => { + const certPath = path.join(paths.data, `${hostname.replace(/\./g, "_")}.crt`) + const certKeyPath = path.join(paths.data, `${hostname.replace(/\./g, "_")}.key`) const checks = await Promise.all([fs.pathExists(certPath), fs.pathExists(certKeyPath)]) if (!checks[0] || !checks[1]) { @@ -67,6 +67,7 @@ export const generateCertificate = async (): Promise<{ cert: string; certKey: st pem.createCertificate( { selfSigned: true, + commonName: hostname, config: ` [req] req_extensions = v3_req @@ -76,7 +77,7 @@ extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] -DNS.1 = localhost +DNS.1 = ${hostname} `, }, (error, result) => { diff --git a/test/socket.test.ts b/test/socket.test.ts index 7d4de985f..b1e974ad0 100644 --- a/test/socket.test.ts +++ b/test/socket.test.ts @@ -45,7 +45,7 @@ describe("SocketProxyProvider", () => { } before(async () => { - const cert = await generateCertificate() + const cert = await generateCertificate("localhost") const options = { cert: fs.readFileSync(cert.cert), key: fs.readFileSync(cert.certKey), From a1b61d165935e647e16570c304feb7b3df801afd Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 30 Oct 2020 05:42:42 -0400 Subject: [PATCH 13/25] src/node/util.ts: Mark generated certificates as CA Required for access under iPad. --- src/node/util.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/util.ts b/src/node/util.ts index b4e175a3e..b50916b5c 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -73,6 +73,7 @@ export const generateCertificate = async (hostname: string): Promise<{ cert: str req_extensions = v3_req [ v3_req ] +basicConstraints = CA:true extendedKeyUsage = serverAuth subjectAltName = @alt_names From c63dc3a1ea8d4b339f970738ad773d37d13f79a7 Mon Sep 17 00:00:00 2001 From: Asher Date: Thu, 29 Oct 2020 14:48:01 -0500 Subject: [PATCH 14/25] Add more logging around connections --- ci/build/build-release.sh | 2 +- ci/dev/vscode.patch | 99 +++++++++++++++++++++++++-------------- 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/ci/build/build-release.sh b/ci/build/build-release.sh index 95579eb82..3b88ed7e9 100755 --- a/ci/build/build-release.sh +++ b/ci/build/build-release.sh @@ -67,7 +67,7 @@ EOF bundle_vscode() { mkdir -p "$VSCODE_OUT_PATH" rsync "$VSCODE_SRC_PATH/yarn.lock" "$VSCODE_OUT_PATH" - rsync "$VSCODE_SRC_PATH/out-vscode${MINIFY+-min}/" "$VSCODE_OUT_PATH/out" + rsync "$VSCODE_SRC_PATH/out-vscode${MINIFY:+-min}/" "$VSCODE_OUT_PATH/out" rsync "$VSCODE_SRC_PATH/.build/extensions/" "$VSCODE_OUT_PATH/extensions" if [ "$KEEP_MODULES" = 0 ]; then diff --git a/ci/dev/vscode.patch b/ci/dev/vscode.patch index b205aa83b..f1eaef04a 100644 --- a/ci/dev/vscode.patch +++ b/ci/dev/vscode.patch @@ -1225,10 +1225,10 @@ index 0000000000000000000000000000000000000000..4ea6d95d36aaac07dbd4d0e16ab3c1bb +} diff --git a/src/vs/server/entry.ts b/src/vs/server/entry.ts new file mode 100644 -index 0000000000000000000000000000000000000000..ab020fbb4e4ab3748cc807765ff9c362389faafa +index 0000000000000000000000000000000000000000..8482c48bae007ed6b39183001ae2cc6d140fcd50 --- /dev/null +++ b/src/vs/server/entry.ts -@@ -0,0 +1,78 @@ +@@ -0,0 +1,79 @@ +import { field } from '@coder/logger'; +import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; +import { CodeServerMessage, VscodeMessage } from 'vs/server/ipc'; @@ -1273,7 +1273,8 @@ index 0000000000000000000000000000000000000000..ab020fbb4e4ab3748cc807765ff9c362 +// Wait for the init message then start up VS Code. Subsequent messages will +// return new workbench options without starting a new instance. +process.on('message', async (message: CodeServerMessage, socket) => { -+ logger.debug('got message from code-server', field('message', message)); ++ logger.debug('got message from code-server', field('type', message.type)); ++ logger.trace('code-server message content', field('message', message)); + switch (message.type) { + case 'init': + try { @@ -1821,10 +1822,11 @@ index 0000000000000000000000000000000000000000..609c4d1cb43f52f92906b901c14c790f +} diff --git a/src/vs/server/node/connection.ts b/src/vs/server/node/connection.ts new file mode 100644 -index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759f396be18 +index 0000000000000000000000000000000000000000..93062cadc627c61e0829c27a72894b81e6a0e039 --- /dev/null +++ b/src/vs/server/node/connection.ts -@@ -0,0 +1,157 @@ +@@ -0,0 +1,171 @@ ++import { field, Logger, logger } from '@coder/logger'; +import * as cp from 'child_process'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { Emitter } from 'vs/base/common/event'; @@ -1832,10 +1834,8 @@ index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759 +import { ISocket } from 'vs/base/parts/ipc/common/ipc.net'; +import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -+import { ILogService } from 'vs/platform/log/common/log'; +import { getNlsConfiguration } from 'vs/server/node/nls'; +import { Protocol } from 'vs/server/node/protocol'; -+import { IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; + +export abstract class Connection { + private readonly _onClose = new Emitter(); @@ -1899,13 +1899,14 @@ index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759 + +export class ExtensionHostConnection extends Connection { + private process?: cp.ChildProcess; ++ private readonly logger: Logger; + + public constructor( + locale:string, protocol: Protocol, buffer: VSBuffer, token: string, -+ private readonly log: ILogService, + private readonly environment: INativeEnvironmentService, + ) { + super(protocol, token); ++ this.logger = logger.named("exthost", field("token", token)); + this.protocol.dispose(); + this.spawn(locale, buffer).then((p) => this.process = p); + this.protocol.getUnderlyingSocket().pause(); @@ -1928,6 +1929,7 @@ index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759 + private sendInitMessage(buffer: VSBuffer): void { + const socket = this.protocol.getUnderlyingSocket(); + socket.pause(); ++ this.logger.trace('Sending socket'); + this.process!.send({ // Process must be set at this point. + type: 'VSCODE_EXTHOST_IPC_SOCKET', + initialDataChunk: (buffer.buffer as Buffer).toString('base64'), @@ -1936,7 +1938,9 @@ index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759 + } + + private async spawn(locale: string, buffer: VSBuffer): Promise { ++ this.logger.trace('Getting NLS configuration...'); + const config = await getNlsConfiguration(locale, this.environment.userDataPath); ++ this.logger.trace('Spawning extension host...'); + const proc = cp.fork( + FileAccess.asFileUri('bootstrap-fork', require).fsPath, + [ '--type=extensionHost' ], @@ -1956,30 +1960,41 @@ index 0000000000000000000000000000000000000000..eec198c948d48b1539ff46510016f759 + }, + ); + -+ proc.on('error', () => this.dispose()); -+ proc.on('exit', () => this.dispose()); ++ proc.on('error', (error) => { ++ this.logger.error('Exited unexpectedly', field('error', error)); ++ this.dispose(); ++ }); ++ proc.on('exit', (code) => { ++ this.logger.trace('Exited', field('code', code)); ++ this.dispose(); ++ }); + if (proc.stdout && proc.stderr) { -+ proc.stdout.setEncoding('utf8').on('data', (d) => this.log.info('Extension host stdout', d)); -+ proc.stderr.setEncoding('utf8').on('data', (d) => this.log.error('Extension host stderr', d)); ++ proc.stdout.setEncoding('utf8').on('data', (d) => this.logger.info(d)); ++ proc.stderr.setEncoding('utf8').on('data', (d) => this.logger.error(d)); + } ++ + proc.on('message', (event) => { -+ if (event && event.type === '__$console') { -+ const severity = (this.log)[event.severity] ? event.severity : 'info'; -+ (this.log)[severity]('Extension host', event.arguments); -+ } -+ if (event && event.type === 'VSCODE_EXTHOST_DISCONNECTED') { -+ this.setOffline(); ++ switch (event && event.type) { ++ case '__$console': ++ const severity = (this.logger)[event.severity] || 'info'; ++ (this.logger)[severity]('console', field('arguments', event.arguments)); ++ break; ++ case 'VSCODE_EXTHOST_DISCONNECTED': ++ this.logger.trace('Going offline'); ++ this.setOffline(); ++ break; ++ case 'VSCODE_EXTHOST_IPC_READY': ++ this.logger.trace('Got ready message'); ++ this.sendInitMessage(buffer); ++ break; ++ default: ++ this.logger.error('Unexpected message', field("event", event)); ++ break; + } + }); + -+ const listen = (message: IExtHostReadyMessage) => { -+ if (message.type === 'VSCODE_EXTHOST_IPC_READY') { -+ proc.removeListener('message', listen); -+ this.sendInitMessage(buffer); -+ } -+ }; -+ -+ return proc.on('message', listen); ++ this.logger.trace('Waiting for handshake...'); ++ return proc; + } +} diff --git a/src/vs/server/node/insights.ts b/src/vs/server/node/insights.ts @@ -2463,15 +2478,17 @@ index 0000000000000000000000000000000000000000..3d428a57d31f29c40f9c3ce45f715b44 +}; diff --git a/src/vs/server/node/protocol.ts b/src/vs/server/node/protocol.ts new file mode 100644 -index 0000000000000000000000000000000000000000..3c74512192aec6220216bc8563b3127b9cfd5fbf +index 0000000000000000000000000000000000000000..523fcd3186d92799bc50e33a72832bd443b2945b --- /dev/null +++ b/src/vs/server/node/protocol.ts -@@ -0,0 +1,73 @@ +@@ -0,0 +1,80 @@ ++import { field } from '@coder/logger'; +import * as net from 'net'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; +import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { AuthRequest, ConnectionTypeRequest, HandshakeMessage } from 'vs/platform/remote/common/remoteAgentConnection'; ++import { logger } from 'vs/server/node/logger'; + +export interface SocketOptions { + readonly reconnectionToken: string; @@ -2499,16 +2516,21 @@ index 0000000000000000000000000000000000000000..3c74512192aec6220216bc8563b3127b + * Perform a handshake to get a connection request. + */ + public handshake(): Promise { ++ logger.trace('Protocol handshake', field('token', this.options.reconnectionToken)); + return new Promise((resolve, reject) => { + const handler = this.onControlMessage((rawMessage) => { + try { -+ const message = JSON.parse(rawMessage.toString()); ++ const raw = rawMessage.toString(); ++ logger.trace('Protocol message', field('token', this.options.reconnectionToken), field('message', raw)); ++ const message = JSON.parse(raw); + switch (message.type) { -+ case 'auth': return this.authenticate(message); ++ case 'auth': ++ return this.authenticate(message); + case 'connectionType': + handler.dispose(); + return resolve(message); -+ default: throw new Error('Unrecognized message type'); ++ default: ++ throw new Error('Unrecognized message type'); + } + } catch (error) { + handler.dispose(); @@ -2521,7 +2543,7 @@ index 0000000000000000000000000000000000000000..3c74512192aec6220216bc8563b3127b + /** + * TODO: This ignores the authentication process entirely for now. + */ -+ private authenticate(_message: AuthRequest): void { ++ private authenticate(_?: AuthRequest): void { + this.sendMessage({ type: 'sign', data: '' }); + } + @@ -2542,10 +2564,11 @@ index 0000000000000000000000000000000000000000..3c74512192aec6220216bc8563b3127b +} diff --git a/src/vs/server/node/server.ts b/src/vs/server/node/server.ts new file mode 100644 -index 0000000000000000000000000000000000000000..a1289865858f405f93d3d396f41c6a0aadffd5e5 +index 0000000000000000000000000000000000000000..45a7bf62a6c07d8771b0257e7c98fae095109eb1 --- /dev/null +++ b/src/vs/server/node/server.ts -@@ -0,0 +1,286 @@ +@@ -0,0 +1,291 @@ ++import { field } from '@coder/logger'; +import * as fs from 'fs'; +import * as net from 'net'; +import * as path from 'path'; @@ -2709,6 +2732,7 @@ index 0000000000000000000000000000000000000000..a1289865858f405f93d3d396f41c6a0a + ); + } + ++ logger.debug('New connection', field('token', token)); + protocol.sendMessage(await ok()); + + let connection: Connection; @@ -2727,12 +2751,14 @@ index 0000000000000000000000000000000000000000..a1289865858f405f93d3d396f41c6a0a + connection = new ExtensionHostConnection( + message.args ? message.args.language : 'en', + protocol, buffer, token, -+ this.services.get(ILogService) as ILogService, + this.services.get(IEnvironmentService) as INativeEnvironmentService, + ); + } + connections.set(token, connection); -+ connection.onClose(() => connections.delete(token)); ++ connection.onClose(() => { ++ logger.debug('Connection closed', field('token', token)); ++ connections.delete(token); ++ }); + this.disposeOldOfflineConnections(connections); + break; + case ConnectionType.Tunnel: return protocol.tunnel(); @@ -2744,6 +2770,7 @@ index 0000000000000000000000000000000000000000..a1289865858f405f93d3d396f41c6a0a + const offline = Array.from(connections.values()) + .filter((connection) => typeof connection.offline !== 'undefined'); + for (let i = 0, max = offline.length - this.maxExtraOfflineConnections; i < max; ++i) { ++ logger.debug('Disposing offline connection', field("token", offline[i].token)); + offline[i].dispose(); + } + } From 0b9af6ef67253e4db7eb128a78d6676097a59cb8 Mon Sep 17 00:00:00 2001 From: Asher Date: Thu, 29 Oct 2020 18:54:18 -0500 Subject: [PATCH 15/25] Initiate connection handshake from server This way the connection can be initiated by either side. It looks like sometimes the initial message from the client is lost (it never makes it into the onControlMessage callback) but I'm still not sure why or if that is preventable. Also added a timeout on the server end to clean things up in case the client never responds. --- ci/dev/vscode.patch | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ci/dev/vscode.patch b/ci/dev/vscode.patch index f1eaef04a..531bffe2a 100644 --- a/ci/dev/vscode.patch +++ b/ci/dev/vscode.patch @@ -2478,10 +2478,10 @@ index 0000000000000000000000000000000000000000..3d428a57d31f29c40f9c3ce45f715b44 +}; diff --git a/src/vs/server/node/protocol.ts b/src/vs/server/node/protocol.ts new file mode 100644 -index 0000000000000000000000000000000000000000..523fcd3186d92799bc50e33a72832bd443b2945b +index 0000000000000000000000000000000000000000..0d9310038c0ca378579652d89bc8ac84924213db --- /dev/null +++ b/src/vs/server/node/protocol.ts -@@ -0,0 +1,80 @@ +@@ -0,0 +1,91 @@ +import { field } from '@coder/logger'; +import * as net from 'net'; +import { VSBuffer } from 'vs/base/common/buffer'; @@ -2518,6 +2518,11 @@ index 0000000000000000000000000000000000000000..523fcd3186d92799bc50e33a72832bd4 + public handshake(): Promise { + logger.trace('Protocol handshake', field('token', this.options.reconnectionToken)); + return new Promise((resolve, reject) => { ++ const timeout = setTimeout(() => { ++ logger.error('Handshake timed out', field('token', this.options.reconnectionToken)); ++ reject(new Error("timed out")); ++ }, 10000); // Matches the client timeout. ++ + const handler = this.onControlMessage((rawMessage) => { + try { + const raw = rawMessage.toString(); @@ -2528,15 +2533,21 @@ index 0000000000000000000000000000000000000000..523fcd3186d92799bc50e33a72832bd4 + return this.authenticate(message); + case 'connectionType': + handler.dispose(); ++ clearTimeout(timeout); + return resolve(message); + default: + throw new Error('Unrecognized message type'); + } + } catch (error) { + handler.dispose(); ++ clearTimeout(timeout); + reject(error); + } + }); ++ ++ // Kick off the handshake in case we missed the client's opening shot. ++ // TODO: Investigate why that message seems to get lost. ++ this.authenticate(); + }); + } + From 07e7c38ea2ba601c11d1c4f40380e00e629855e3 Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 30 Oct 2020 13:38:13 -0500 Subject: [PATCH 16/25] Immediately pause web socket This will buffer any data sent to it until something is ready to listen on it. --- src/node/http.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/node/http.ts b/src/node/http.ts index c616c8837..c3635e073 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -738,6 +738,8 @@ export class HttpServer { } private onUpgrade = async (request: http.IncomingMessage, socket: net.Socket, head: Buffer): Promise => { + socket.pause() + try { this.heart.beat() socket.on("error", () => socket.destroy()) From 9ad7d0b7a3e78ff6cb111a263f20e078da140ef3 Mon Sep 17 00:00:00 2001 From: Asher Date: Fri, 30 Oct 2020 14:50:06 -0500 Subject: [PATCH 17/25] Fix potential 500 when loading in parallel --- src/node/app/vscode.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/node/app/vscode.ts b/src/node/app/vscode.ts index ed4f714e5..18c9d8a90 100644 --- a/src/node/app/vscode.ts +++ b/src/node/app/vscode.ts @@ -50,12 +50,15 @@ export class VscodeHttpProvider extends HttpProvider { logger.debug("setting up vs code...") return new Promise((resolve, reject) => { - vscode.once("message", (message: VscodeMessage) => { - logger.debug("got message from vs code", field("message", message)) - return message.type === "options" && message.id === id - ? resolve(message.options) - : reject(new Error("Unexpected response during initialization")) - }) + const onMessage = (message: VscodeMessage) => { + // There can be parallel initializations so wait for the right ID. + if (message.type === "options" && message.id === id) { + logger.trace("got message from vs code", field("message", message)) + vscode.off("message", onMessage) + resolve(message.options) + } + } + vscode.on("message", onMessage) vscode.once("error", reject) vscode.once("exit", (code) => reject(new Error(`VS Code exited unexpectedly with code ${code}`))) this.send({ type: "init", id, options }, vscode) @@ -77,7 +80,7 @@ export class VscodeHttpProvider extends HttpProvider { this._vscode = new Promise((resolve, reject) => { vscode.once("message", (message: VscodeMessage) => { - logger.debug("got message from vs code", field("message", message)) + logger.trace("got message from vs code", field("message", message)) return message.type === "ready" ? resolve(vscode) : reject(new Error("Unexpected response waiting for ready response")) From e07a59174599f1c39f04196d275c702795387be9 Mon Sep 17 00:00:00 2001 From: Asher Date: Mon, 2 Nov 2020 16:47:09 -0600 Subject: [PATCH 18/25] Catch cloud agent download failure - See #2251 and #2229. --- ci/build/npm-postinstall.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ci/build/npm-postinstall.sh b/ci/build/npm-postinstall.sh index bd7922d5c..cede01c4c 100755 --- a/ci/build/npm-postinstall.sh +++ b/ci/build/npm-postinstall.sh @@ -25,8 +25,11 @@ main() { esac OS="$(uname | tr '[:upper:]' '[:lower:]')" - curl -fsSL "https://storage.googleapis.com/coder-cloud-releases/agent/latest/$OS/cloud-agent" -o ./lib/coder-cloud-agent - chmod +x ./lib/coder-cloud-agent + if curl -fsSL "https://storage.googleapis.com/coder-cloud-releases/agent/latest/$OS/cloud-agent" -o ./lib/coder-cloud-agent ; then + chmod +x ./lib/coder-cloud-agent + else + echo "Failed to download cloud agent; --link will not work" + fi if ! vscode_yarn; then echo "You may not have the required dependencies to build the native modules." From 6fbbb1047f70af136e96065f73038d8bef12575b Mon Sep 17 00:00:00 2001 From: Asher Date: Mon, 2 Nov 2020 17:17:09 -0600 Subject: [PATCH 19/25] fmt --- ci/build/npm-postinstall.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/build/npm-postinstall.sh b/ci/build/npm-postinstall.sh index cede01c4c..743b549ff 100755 --- a/ci/build/npm-postinstall.sh +++ b/ci/build/npm-postinstall.sh @@ -25,7 +25,7 @@ main() { esac OS="$(uname | tr '[:upper:]' '[:lower:]')" - if curl -fsSL "https://storage.googleapis.com/coder-cloud-releases/agent/latest/$OS/cloud-agent" -o ./lib/coder-cloud-agent ; then + if curl -fsSL "https://storage.googleapis.com/coder-cloud-releases/agent/latest/$OS/cloud-agent" -o ./lib/coder-cloud-agent; then chmod +x ./lib/coder-cloud-agent else echo "Failed to download cloud agent; --link will not work" From 9bde62fbd611a7a91c5f327fa43e0d06f1379169 Mon Sep 17 00:00:00 2001 From: Asher Date: Mon, 2 Nov 2020 17:17:25 -0600 Subject: [PATCH 20/25] v3.6.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6cb9dd17a..75d2e0c9b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-server", "license": "MIT", - "version": "3.6.1", + "version": "3.6.2", "description": "Run VS Code on a remote server.", "homepage": "https://github.com/cdr/code-server", "bugs": { From 31306f7fddda269a35da2beef78468a92725cb24 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 30 Oct 2020 04:48:51 -0400 Subject: [PATCH 21/25] docs: Add iPad self signed certificate documentation Closes #1816 Closes #1566 --- ci/dev/fmt.sh | 1 + doc/FAQ.md | 5 +++++ doc/ipad.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 doc/ipad.md diff --git a/ci/dev/fmt.sh b/ci/dev/fmt.sh index d3bd41915..47911aadf 100755 --- a/ci/dev/fmt.sh +++ b/ci/dev/fmt.sh @@ -26,6 +26,7 @@ main() { doctoc --title '# Install' doc/install.md > /dev/null doctoc --title '# npm Install Requirements' doc/npm.md > /dev/null doctoc --title '# Contributing' doc/CONTRIBUTING.md > /dev/null + doctoc --title '# iPad' doc/ipad.md > /dev/null if [[ ${CI-} && $(git ls-files --other --modified --exclude-standard) ]]; then echo "Files need generation or are formatted incorrectly:" diff --git a/doc/FAQ.md b/doc/FAQ.md index 5d1407d14..25e7a13d1 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -3,6 +3,7 @@ # FAQ - [Questions?](#questions) +- [iPad Status?](#ipad-status) - [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration) - [Differences compared to VS Code?](#differences-compared-to-vs-code) - [How can I request a missing extension?](#how-can-i-request-a-missing-extension) @@ -33,6 +34,10 @@ Please file all questions and support requests at https://github.com/cdr/code-server/discussions. +## iPad Status? + +Please see [./ipad.md](./ipad.md). + ## How can I reuse my VS Code configuration? The very popular [Settings Sync](https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync) extension works. diff --git a/doc/ipad.md b/doc/ipad.md new file mode 100644 index 000000000..e190000e6 --- /dev/null +++ b/doc/ipad.md @@ -0,0 +1,48 @@ + + +# iPad + +- [iPad](#ipad) + - [How to access code-server with a self signed certificate on iPad](#how-to-access-code-server-with-a-self-signed-certificate-on-ipad) + + + +# iPad + +## How to access code-server with a self signed certificate on iPad + +Accessing a self signed certificate on iPad isn't as easy as accepting through all +the security warnings. Safari will prevent WebSocket connections unless the certificate +is installed as a profile on the device. + +The below assumes you are using the self signed certificate that code-server +generates for you. If not, that's fine but you'll have to make sure your certificate +abides by the following guidelines from Apple: https://support.apple.com/en-us/HT210176 + +**note**: Another undocumented requirement we noticed is that the certificate has to have `basicConstraints=CA:true`. + +The following instructions assume you have code-server installed and running +with a self signed certificate. If not, please first go through [./guide.md](./guide.md)! + +**warning**: Your iPad must access code-server via a domain name. It could be local +DNS like `mymacbookpro.local` but it must be a domain name. Otherwise Safari will +refuse to allow WebSockets to connect. + +1. Your certificate **must** have a subject alt name that matches the hostname + at which you will access code-server from your iPad. You can pass this to code-server + so that it generates the certificate correctly with `--cert-host`. +2. Share your self signed certificate with the iPad. + - code-server will print the location of the certificate it has generated in the logs. + +``` +[2020-10-30T08:55:45.139Z] info - Using generated certificate and key for HTTPS: ~/.local/share/code-server/mymbp_local.crt +``` + +- You can mail it to yourself or if you have a Mac, it's easiest to just Airdrop to the iPad. + +3. When opening the `*.crt` file, you'll be prompted to go into settings to install. +4. Go to `Settings -> General -> Profile`, select the profile and then hit `Install`. + - It should say the profile is verified. +5. Go to `Settings -> About -> Certificate Trust Settings` and enable full trust for + the certificate. +6. Now you can access code-server! 🍻 From c07296cce0e448803491aa72a1ceeb2219eee387 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 30 Oct 2020 06:08:42 -0400 Subject: [PATCH 22/25] docs: Add known issues to iPad docs and add more links to iPad docs Closes #1816 --- doc/FAQ.md | 10 ---------- doc/guide.md | 3 +-- doc/ipad.md | 12 ++++++++++-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/doc/FAQ.md b/doc/FAQ.md index 25e7a13d1..1a6a217d6 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -22,7 +22,6 @@ - [Heartbeat File](#heartbeat-file) - [Healthz endpoint](#healthz-endpoint) - [How does the config file work?](#how-does-the-config-file-work) -- [Blank screen on iPad?](#blank-screen-on-ipad) - [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure) - [How do I make my keyboard shortcuts work?](#how-do-i-make-my-keyboard-shortcuts-work) - [Differences compared to Theia?](#differences-compared-to-theia) @@ -287,15 +286,6 @@ The `--config` flag or `$CODE_SERVER_CONFIG` can be used to change the config fi The default location also respects `$XDG_CONFIG_HOME`. -## Blank screen on iPad? - -Unfortunately at the moment self signed certificates cause a blank screen on iPadOS - -There does seem to be a way to get it to work if you create your own CA and create a -certificate using the CA and then import the CA onto your iPad. - -See [#1566](https://github.com/cdr/code-server/issues/1566#issuecomment-623159434). - ## Isn't an install script piped into sh insecure? Please give diff --git a/doc/guide.md b/doc/guide.md index 8782f57f8..ce17a3614 100644 --- a/doc/guide.md +++ b/doc/guide.md @@ -251,8 +251,7 @@ Visit `https://` to access `code-server`. Congratulations! ### Self Signed Certificate -**note:** Self signed certificates do not work with iPad and will cause a blank page. You'll -have to use [Let's Encrypt](#lets-encrypt) instead. See the [FAQ](./FAQ.md#blank-screen-on-ipad). +**note:** Self signed certificates do not work with iPad normally. See [./ipad.md](./ipad.md) for details. Recommended reading: https://security.stackexchange.com/a/8112. diff --git a/doc/ipad.md b/doc/ipad.md index e190000e6..1bda0bd44 100644 --- a/doc/ipad.md +++ b/doc/ipad.md @@ -3,13 +3,21 @@ # iPad - [iPad](#ipad) - - [How to access code-server with a self signed certificate on iPad](#how-to-access-code-server-with-a-self-signed-certificate-on-ipad) + - [Known Issues](#known-issues) + - [How to access code-server with a self signed certificate on iPad?](#how-to-access-code-server-with-a-self-signed-certificate-on-ipad) # iPad -## How to access code-server with a self signed certificate on iPad +## Known Issues + +- Getting self signed certificates certificates to work is involved, see below. +- Keyboard may disappear sometimes [#1313](https://github.com/cdr/code-server/issues/1313), [#979](https://github.com/cdr/code-server/issues/979) +- Trackpad scrolling does not work [#1455](https://github.com/cdr/code-server/issues/1455) +- See [issues tagged with the iPad label](https://github.com/cdr/code-server/issues?q=is%3Aopen+is%3Aissue+label%3AiPad) for more. + +## How to access code-server with a self signed certificate on iPad? Accessing a self signed certificate on iPad isn't as easy as accepting through all the security warnings. Safari will prevent WebSocket connections unless the certificate From ddbac8dd78819b76f6fcc385f301ecf725fcec13 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 3 Nov 2020 23:23:41 +0000 Subject: [PATCH 23/25] Update README Alpha section to remove reference to typeform --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ee0b8700b..aa5673938 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,17 @@ We also have an in-depth [setup and configuration](./doc/guide.md) guide. ### Alpha Program 🐣 -We're working on a cloud platform that makes deploying and managing code-server easier. Consider [joining our alpha program](https://codercom.typeform.com/to/U4IKyv0W) if you don't want to worry about +We're working on a cloud platform that makes deploying and managing code-server easier. Consider running code-server with our experimental flag `--link` if you don't want to worry about - TLS - Authentication - Port Forwarding +```bash +$ code-server --link +Proxying code-server to Coder Cloud, you can access your IDE at https://valmar-jon.cdr.co +``` + ## FAQ See [./doc/FAQ.md](./doc/FAQ.md). From 1eebde56ab6618252dfabcd778715fc685a9d885 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 3 Nov 2020 18:03:28 -0600 Subject: [PATCH 24/25] Specify that Coder Alpha requires v3.6.2 (#2270) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa5673938..0e7750039 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ We also have an in-depth [setup and configuration](./doc/guide.md) guide. ### Alpha Program 🐣 -We're working on a cloud platform that makes deploying and managing code-server easier. Consider running code-server with our experimental flag `--link` if you don't want to worry about +We're working on a cloud platform that makes deploying and managing code-server easier. Consider [updating to 3.6.2](https://github.com/cdr/code-server/releases/tag/v3.6.2) and running code-server with our experimental flag `--link` if you don't want to worry about - TLS - Authentication From da6000b96f8666cd21e5da108e421a2d66b72e1f Mon Sep 17 00:00:00 2001 From: Ben Potter Date: Tue, 10 Nov 2020 14:00:05 -0500 Subject: [PATCH 25/25] Add Slack link to issue template (#2282) --- .github/ISSUE_TEMPLATE/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 9dc409dd7..2f567fce3 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,4 +2,7 @@ blank_issues_enabled: false contact_links: - name: Question url: https://github.com/cdr/code-server/discussions/new?category_id=22503114 - about: Ask the community for help + about: Ask the community for help on our GitHub Discussions board + - name: Chat + about: Need immediate help or just want to talk? Hop in our Slack + url: https://cdr.co/join-community