From 3efd515cdfd9b660bf193484caa7fc06efa69836 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 18 Dec 2020 04:17:18 -0500 Subject: [PATCH 1/5] proxy_agent: Fix very embarrassing mistake --- src/node/proxy_agent.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/node/proxy_agent.ts b/src/node/proxy_agent.ts index 172feafcf..dd6a2b059 100644 --- a/src/node/proxy_agent.ts +++ b/src/node/proxy_agent.ts @@ -32,7 +32,7 @@ export function monkeyPatch(vscode: boolean): void { return } - logger.debug(`using $HTTP_PROXY ${process.env.HTTP_PROXY}`) + logger.debug(`using $HTTP_PROXY ${proxyURL}`) let pa: http.Agent // The reasoning for this split is that VS Code's build process does not have @@ -42,9 +42,9 @@ export function monkeyPatch(vscode: boolean): void { // I can't enable esModuleInterop in VS Code's build process as it breaks and spits out // a huge number of errors. if (vscode) { - pa = new (proxyagent as any)(process.env.HTTP_PROXY) + pa = new (proxyagent as any)(proxyURL) } else { - pa = new (proxyagent as any).default(process.env.HTTP_PROXY) + pa = new (proxyagent as any).default(proxyURL) } /** From ecb4d0736427c2d919a8eb8fff62a56b9acd12fe Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 18 Dec 2020 05:30:40 -0500 Subject: [PATCH 2/5] proxy_agent: Improve documentation --- src/node/proxy_agent.ts | 45 ++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/node/proxy_agent.ts b/src/node/proxy_agent.ts index dd6a2b059..8f407ab60 100644 --- a/src/node/proxy_agent.ts +++ b/src/node/proxy_agent.ts @@ -15,18 +15,25 @@ import * as proxyagent from "proxy-agent" */ /** - * monkeyPatch patches the node HTTP/HTTPS library to route all requests through our - * custom agent from the proxyAgent package. + * monkeyPatch patches the node http and https modules to route all requests through the + * agent we get from the proxy-agent package. + * + * We do not support $HTTPS_PROXY here as it's equivalent in proxy-agent. + * See the mapping at https://www.npmjs.com/package/proxy-agent + * + * I guess with most proxies support both HTTP and HTTPS proxying on the same port and + * so two variables aren't required anymore. And there's plenty of SOCKS proxies too where + * it wouldn't make sense to have two variables. + * + * It's the most performant/secure setup as using a HTTP proxy for HTTP requests allows + * for caching but then using a HTTPS proxy for HTTPS requests gives full end to end + * security. + * + * See https://stackoverflow.com/a/10442767/4283659 for HTTP vs HTTPS proxy. + * To be clear, both support HTTP/HTTPS resources, the difference is in how they fetch + * them. */ export function monkeyPatch(vscode: boolean): void { - // We do not support HTTPS_PROXY here to avoid confusion. proxy-agent will automatically - // use the correct protocol to connect to the proxy depending on the requested URL. - // - // We could implement support ourselves to allow people to configure the proxy used for - // HTTPS vs HTTP but there doesn't seem to be much value in that. - // - // At least of right now, it'd just be plain confusing to support HTTPS_PROXY when proxy-agent - // will still use HTTP to hit it for HTTP requests. const proxyURL = process.env.HTTP_PROXY || process.env.http_proxy if (!proxyURL) { return @@ -47,14 +54,16 @@ export function monkeyPatch(vscode: boolean): void { pa = new (proxyagent as any).default(proxyURL) } - /** - * None of our code ever passes in a explicit agent to the http modules but VS Code's - * does sometimes but only when a user sets the http.proxy configuration. - * See https://code.visualstudio.com/docs/setup/network#_legacy-proxy-server-support - * - * Even if they do, it's probably the same proxy so we should be fine! And those are - * deprecated anyway. - */ + // None of our code ever passes in a explicit agent to the http modules but VS Code's + // does sometimes but only when a user sets the http.proxy configuration. + // See https://code.visualstudio.com/docs/setup/network#_legacy-proxy-server-support + // + // Even if they do, it's probably the same proxy so we should be fine! And those are + // deprecated anyway. In fact, they implemented it incorrectly as they won't retrieve + // HTTPS resources over a HTTP proxy which is perfectly valid! Both HTTP and HTTPS proxies + // support HTTP/HTTPS resources. + // + // See https://stackoverflow.com/a/10442767/4283659 const http = require("http") const https = require("https") http.globalAgent = pa From 94b4ba131ef4fbf98cbc5c3ef9052243f951d747 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 18 Dec 2020 09:09:07 -0500 Subject: [PATCH 3/5] proxy_agent: Implement $HTTPS_PROXY Just convention, see https://golang.org/pkg/net/http/#ProxyFromEnvironment --- src/node/proxy_agent.ts | 75 +++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/src/node/proxy_agent.ts b/src/node/proxy_agent.ts index 8f407ab60..26026900b 100644 --- a/src/node/proxy_agent.ts +++ b/src/node/proxy_agent.ts @@ -1,10 +1,11 @@ import { logger } from "@coder/logger" import * as http from "http" +import * as url from "url" import * as proxyagent from "proxy-agent" /** - * This file does not have anything to do with the code-server proxy. - * It's for $HTTP_PROXY support! + * This file has nothing to do with the code-server proxy. + * It is for $HTTP_PROXY and $HTTPS_PROXY support. * - https://github.com/cdr/code-server/issues/124 * - https://www.npmjs.com/package/proxy-agent * @@ -15,57 +16,51 @@ import * as proxyagent from "proxy-agent" */ /** - * monkeyPatch patches the node http and https modules to route all requests through the - * agent we get from the proxy-agent package. + * monkeyPatch patches the node http,https modules to route all requests through the + * agents we get from the proxy-agent package. * - * We do not support $HTTPS_PROXY here as it's equivalent in proxy-agent. - * See the mapping at https://www.npmjs.com/package/proxy-agent + * This approach only works if there is no code specifying an explicit agent when making + * a request. * - * I guess with most proxies support both HTTP and HTTPS proxying on the same port and - * so two variables aren't required anymore. And there's plenty of SOCKS proxies too where - * it wouldn't make sense to have two variables. + * None of our code ever passes in a explicit agent to the http,https modules. + * VS Code's does sometimes but only when a user sets the http.proxy configuration. + * See https://code.visualstudio.com/docs/setup/network#_legacy-proxy-server-support * - * It's the most performant/secure setup as using a HTTP proxy for HTTP requests allows - * for caching but then using a HTTPS proxy for HTTPS requests gives full end to end - * security. + * Even if they do, it's probably the same proxy so we should be fine! And those knobs + * are deprecated anyway. * - * See https://stackoverflow.com/a/10442767/4283659 for HTTP vs HTTPS proxy. - * To be clear, both support HTTP/HTTPS resources, the difference is in how they fetch - * them. + * We use $HTTP_PROXY for all HTTP resources via a normal HTTP proxy. + * We use $HTTPS_PROXY for all HTTPS resources via HTTP connect. + * See https://stackoverflow.com/a/10442767/4283659 */ -export function monkeyPatch(vscode: boolean): void { - const proxyURL = process.env.HTTP_PROXY || process.env.http_proxy - if (!proxyURL) { - return +export function monkeyPatch(inVSCode: boolean): void { + const http = require("http") + const https = require("https") + + const httpProxyURL = process.env.HTTP_PROXY || process.env.http_proxy + if (httpProxyURL) { + logger.debug(`using $HTTP_PROXY ${httpProxyURL}`) + http.globalAgent = newProxyAgent(inVSCode, httpProxyURL) } - logger.debug(`using $HTTP_PROXY ${proxyURL}`) + const httpsProxyURL = process.env.HTTPS_PROXY || process.env.https_proxy + if (httpsProxyURL) { + logger.debug(`using $HTTPS_PROXY ${httpsProxyURL}`) + https.globalAgent = newProxyAgent(inVSCode, httpsProxyURL) + } +} - let pa: http.Agent +function newProxyAgent(inVSCode: boolean, for: "http" | "https", proxyURL: string): http.Agent { // The reasoning for this split is that VS Code's build process does not have // esModuleInterop enabled but the code-server one does. As a result depending on where // we execute, we either have a default attribute or we don't. // // I can't enable esModuleInterop in VS Code's build process as it breaks and spits out - // a huge number of errors. - if (vscode) { - pa = new (proxyagent as any)(proxyURL) + // a huge number of errors. And we can't use require as otherwise the modules won't be + // included in the final product. + if (inVSCode) { + return new (proxyagent as any)(opts) } else { - pa = new (proxyagent as any).default(proxyURL) + return new (proxyagent as any).default(opts) } - - // None of our code ever passes in a explicit agent to the http modules but VS Code's - // does sometimes but only when a user sets the http.proxy configuration. - // See https://code.visualstudio.com/docs/setup/network#_legacy-proxy-server-support - // - // Even if they do, it's probably the same proxy so we should be fine! And those are - // deprecated anyway. In fact, they implemented it incorrectly as they won't retrieve - // HTTPS resources over a HTTP proxy which is perfectly valid! Both HTTP and HTTPS proxies - // support HTTP/HTTPS resources. - // - // See https://stackoverflow.com/a/10442767/4283659 - const http = require("http") - const https = require("https") - http.globalAgent = pa - https.globalAgent = pa } From ae902b9dd1a91a634c8f30c79c8dad5157b1da10 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 18 Dec 2020 10:38:00 -0500 Subject: [PATCH 4/5] proxy_agent: Use proxy-from-env for robustness Now we support pretty much every variable under the sun along with $NO_PROXY all correctly and with minimal code on our end. --- lib/vscode/package.json | 2 ++ lib/vscode/yarn.lock | 7 +++++ package.json | 2 ++ src/node/proxy_agent.ts | 61 +++++++++++++++++++++++++---------------- yarn.lock | 9 +++++- 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/lib/vscode/package.json b/lib/vscode/package.json index b5b0612dc..97ad6b922 100644 --- a/lib/vscode/package.json +++ b/lib/vscode/package.json @@ -65,6 +65,7 @@ "native-watchdog": "1.3.0", "node-pty": "0.10.0-beta17", "proxy-agent": "^4.0.0", + "proxy-from-env": "^1.1.0", "rimraf": "^2.2.8", "spdlog": "^0.11.1", "sudo-prompt": "9.1.1", @@ -95,6 +96,7 @@ "@types/minimist": "^1.2.0", "@types/mocha": "2.2.39", "@types/node": "^12.11.7", + "@types/proxy-from-env": "^1.0.1", "@types/sinon": "^1.16.36", "@types/trusted-types": "^1.0.6", "@types/vscode-windows-registry": "^1.0.0", diff --git a/lib/vscode/yarn.lock b/lib/vscode/yarn.lock index c73be6d8e..b0befe0e3 100644 --- a/lib/vscode/yarn.lock +++ b/lib/vscode/yarn.lock @@ -302,6 +302,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.28.tgz#b6d0628b0371d6c629d729c98322de314b640219" integrity sha512-EM/qFeRH8ZCD+TlsaIPULyyFm9vOhFIvgskY2JmHbEsWsOPgN+rtjSXrcHGgJpob4Nu17VfO95FKewr0XY7iOQ== +"@types/proxy-from-env@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/proxy-from-env/-/proxy-from-env-1.0.1.tgz#b5f3e99230ca4518af196c18267055fc51f892b7" + integrity sha512-luG++TFHyS61eKcfkR1CVV6a1GMNXDjtqEQIIfaSHax75xp0HU3SlezjOi1yqubJwrG8e9DeW59n6wTblIDwFg== + dependencies: + "@types/node" "*" + "@types/semver@^5.4.0", "@types/semver@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" diff --git a/package.json b/package.json index bb25d9da7..d0cb4e33d 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@types/node": "^12.12.7", "@types/parcel-bundler": "^1.12.1", "@types/pem": "^1.9.5", + "@types/proxy-from-env": "^1.0.1", "@types/safe-compare": "^1.1.0", "@types/semver": "^7.1.0", "@types/split2": "^2.1.6", @@ -82,6 +83,7 @@ "limiter": "^1.1.5", "pem": "^1.14.2", "proxy-agent": "^4.0.0", + "proxy-from-env": "^1.1.0", "qs": "6.7.0", "rotating-file-stream": "^2.1.1", "safe-buffer": "^5.1.1", diff --git a/src/node/proxy_agent.ts b/src/node/proxy_agent.ts index 26026900b..416a97868 100644 --- a/src/node/proxy_agent.ts +++ b/src/node/proxy_agent.ts @@ -1,13 +1,15 @@ import { logger } from "@coder/logger" import * as http from "http" -import * as url from "url" -import * as proxyagent from "proxy-agent" +import * as proxyAgent from "proxy-agent" +import * as proxyFromEnv from "proxy-from-env" /** * This file has nothing to do with the code-server proxy. - * It is for $HTTP_PROXY and $HTTPS_PROXY support. + * It is to support $HTTP_PROXY, $HTTPS_PROXY and $NO_PROXY. + * * - https://github.com/cdr/code-server/issues/124 * - https://www.npmjs.com/package/proxy-agent + * - https://www.npmjs.com/package/proxy-from-env * * This file exists in two locations: * - src/node/proxy_agent.ts @@ -17,7 +19,7 @@ import * as proxyagent from "proxy-agent" /** * monkeyPatch patches the node http,https modules to route all requests through the - * agents we get from the proxy-agent package. + * agent we get from the proxy-agent package. * * This approach only works if there is no code specifying an explicit agent when making * a request. @@ -28,29 +30,22 @@ import * as proxyagent from "proxy-agent" * * Even if they do, it's probably the same proxy so we should be fine! And those knobs * are deprecated anyway. - * - * We use $HTTP_PROXY for all HTTP resources via a normal HTTP proxy. - * We use $HTTPS_PROXY for all HTTPS resources via HTTP connect. - * See https://stackoverflow.com/a/10442767/4283659 */ export function monkeyPatch(inVSCode: boolean): void { - const http = require("http") - const https = require("https") + if (shouldEnableProxy()) { + const http = require("http") + const https = require("https") - const httpProxyURL = process.env.HTTP_PROXY || process.env.http_proxy - if (httpProxyURL) { - logger.debug(`using $HTTP_PROXY ${httpProxyURL}`) - http.globalAgent = newProxyAgent(inVSCode, httpProxyURL) - } - - const httpsProxyURL = process.env.HTTPS_PROXY || process.env.https_proxy - if (httpsProxyURL) { - logger.debug(`using $HTTPS_PROXY ${httpsProxyURL}`) - https.globalAgent = newProxyAgent(inVSCode, httpsProxyURL) + // If we do not pass in a proxy URL, proxy-agent will get the URL from the environment. + // See https://www.npmjs.com/package/proxy-from-env. + // Also see shouldEnableProxy. + const pa = newProxyAgent(inVSCode) + http.globalAgent = pa + https.globalAgent = pa } } -function newProxyAgent(inVSCode: boolean, for: "http" | "https", proxyURL: string): http.Agent { +function newProxyAgent(inVSCode: boolean): http.Agent { // The reasoning for this split is that VS Code's build process does not have // esModuleInterop enabled but the code-server one does. As a result depending on where // we execute, we either have a default attribute or we don't. @@ -59,8 +54,28 @@ function newProxyAgent(inVSCode: boolean, for: "http" | "https", proxyURL: strin // a huge number of errors. And we can't use require as otherwise the modules won't be // included in the final product. if (inVSCode) { - return new (proxyagent as any)(opts) + return new (proxyAgent as any)() } else { - return new (proxyagent as any).default(opts) + return new (proxyAgent as any).default() } } + +// If they have $NO_PROXY set to example.com then this check won't work! +// But that's drastically unlikely. +function shouldEnableProxy(): boolean { + let shouldEnable = false + + const httpProxy = proxyFromEnv.getProxyForUrl(`http://example.com`) + if (httpProxy) { + shouldEnable = true + logger.debug(`using $HTTP_PROXY ${httpProxy}`) + } + + const httpsProxy = proxyFromEnv.getProxyForUrl(`https://example.com`) + if (httpsProxy) { + shouldEnable = true + logger.debug(`using $HTTPS_PROXY ${httpsProxy}`) + } + + return shouldEnable +} diff --git a/yarn.lock b/yarn.lock index 5f0bdb5e7..e0e3fa62c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1137,6 +1137,13 @@ dependencies: "@types/node" "*" +"@types/proxy-from-env@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/proxy-from-env/-/proxy-from-env-1.0.1.tgz#b5f3e99230ca4518af196c18267055fc51f892b7" + integrity sha512-luG++TFHyS61eKcfkR1CVV6a1GMNXDjtqEQIIfaSHax75xp0HU3SlezjOi1yqubJwrG8e9DeW59n6wTblIDwFg== + dependencies: + "@types/node" "*" + "@types/q@^1.5.1": version "1.5.4" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" @@ -6333,7 +6340,7 @@ proxy-agent@^4.0.0: proxy-from-env "^1.0.0" socks-proxy-agent "^5.0.0" -proxy-from-env@^1.0.0: +proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== From cb72128448316d1398d137ab66c694ef6afc3b6b Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Fri, 18 Dec 2020 05:18:30 -0500 Subject: [PATCH 5/5] doc/FAQ: Document proxy environment variables --- doc/FAQ.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/FAQ.md b/doc/FAQ.md index edd337a90..ef8d1cb9a 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -27,6 +27,7 @@ - [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) +- [`$HTTP_PROXY`, `$HTTPS_PROXY`, `$NO_PROXY`](#http_proxy-https_proxy-no_proxy) - [Enterprise](#enterprise) @@ -338,6 +339,30 @@ You can't just use your VS Code config in Theia like you can with code-server. To summarize, code-server is a patched fork of VS Code to run in the browser whereas Theia takes some parts of VS Code but is an entirely different editor. +## `$HTTP_PROXY`, `$HTTPS_PROXY`, `$NO_PROXY` + +code-server supports the standard environment variables to allow directing +server side requests through a proxy. + +```sh +export HTTP_PROXY=https://134.8.5.4 +export HTTPS_PROXY=https://134.8.5.4 +# Now all of code-server's server side requests will go through +# https://134.8.5.4 first. +code-server +``` + +- See [proxy-from-env](https://www.npmjs.com/package/proxy-from-env#environment-variables) + for a detailed reference on the various environment variables and their syntax. + - code-server only uses the `http` and `https` protocols. +- See [proxy-agent](https://www.npmjs.com/package/proxy-agent) for the various supported + proxy protocols. + +**note**: Only server side requests will be proxied! This includes fetching extensions, +requests made from extensions etc. To proxy requests from your browser you need to +configure your browser separately. Browser requests would cover exploring the extension +marketplace. + ## Enterprise Visit [our enterprise page](https://coder.com) for more information about our