Skip to content

Commit 74da516

Browse files
authored
Add --disable-proxy option (#6349)
1 parent daac46b commit 74da516

File tree

7 files changed

+90
-2
lines changed

7 files changed

+90
-2
lines changed

docs/FAQ.md

+14
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
- [Are there community projects involving code-server?](#are-there-community-projects-involving-code-server)
3535
- [How do I change the port?](#how-do-i-change-the-port)
3636
- [How do I hide the coder/coder promotion in Help: Getting Started?](#how-do-i-hide-the-codercoder-promotion-in-help-getting-started)
37+
- [How do I disable the proxy?](#how-do-i-disable-the-proxy)
3738
- [How do I disable file download?](#how-do-i-disable-file-download)
3839

3940
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -453,6 +454,19 @@ You can pass the flag `--disable-getting-started-override` to `code-server` or
453454
you can set the environment variable `CS_DISABLE_GETTING_STARTED_OVERRIDE=1` or
454455
`CS_DISABLE_GETTING_STARTED_OVERRIDE=true`.
455456

457+
## How do I disable the proxy?
458+
459+
You can pass the flag `--disable-proxy` to `code-server` or
460+
you can set the environment variable `CS_DISABLE_PROXY=1` or
461+
`CS_DISABLE_PROXY=true`.
462+
463+
Note, this option currently only disables the proxy routes to forwarded ports, including
464+
the domain and path proxy routes over HTTP and WebSocket; however, it does not
465+
disable the automatic port forwarding in the VS Code workbench itself. In other words,
466+
user will still see the Ports tab and notifications, but will not be able to actually
467+
use access the ports. It is recommended to set `remote.autoForwardPorts` to `false`
468+
when using the option.
469+
456470
## How do I disable file download?
457471

458472
You can pass the flag `--disable-file-downloads` to `code-server`

src/node/cli.ts

+9
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export interface UserProvidedCodeArgs {
5151
"disable-file-downloads"?: boolean
5252
"disable-workspace-trust"?: boolean
5353
"disable-getting-started-override"?: boolean
54+
"disable-proxy"?: boolean
5455
"session-socket"?: string
5556
}
5657

@@ -178,6 +179,10 @@ export const options: Options<Required<UserProvidedArgs>> = {
178179
type: "boolean",
179180
description: "Disable the coder/coder override in the Help: Getting Started page.",
180181
},
182+
"disable-proxy": {
183+
type: "boolean",
184+
description: "Disable domain and path proxy routes.",
185+
},
181186
// --enable can be used to enable experimental features. These features
182187
// provide no guarantees.
183188
enable: { type: "string[]" },
@@ -564,6 +569,10 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
564569
args["disable-getting-started-override"] = true
565570
}
566571

572+
if (process.env.CS_DISABLE_PROXY?.match(/^(1|true)$/)) {
573+
args["disable-proxy"] = true
574+
}
575+
567576
const usingEnvHashedPassword = !!process.env.HASHED_PASSWORD
568577
if (process.env.HASHED_PASSWORD) {
569578
args["hashed-password"] = process.env.HASHED_PASSWORD

src/node/http.ts

+19
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,25 @@ export const replaceTemplates = <T extends object>(
7575
.replace("{{OPTIONS}}", () => escapeJSON(serverOptions))
7676
}
7777

78+
/**
79+
* Throw an error if proxy is not enabled. Call `next` if provided.
80+
*/
81+
export const ensureProxyEnabled = (req: express.Request, _?: express.Response, next?: express.NextFunction): void => {
82+
if (!proxyEnabled(req)) {
83+
throw new HttpError("Forbidden", HttpCode.Forbidden)
84+
}
85+
if (next) {
86+
next()
87+
}
88+
}
89+
90+
/**
91+
* Return true if proxy is enabled.
92+
*/
93+
export const proxyEnabled = (req: express.Request): boolean => {
94+
return !req.args["disable-proxy"]
95+
}
96+
7897
/**
7998
* Throw an error if not authorized. Call `next` if provided.
8099
*/

src/node/routes/domainProxy.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Request, Router } from "express"
22
import { HttpCode, HttpError } from "../../common/http"
3-
import { getHost, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
3+
import { getHost, ensureProxyEnabled, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
44
import { proxy } from "../proxy"
55
import { Router as WsRouter } from "../wsRouter"
66

@@ -59,6 +59,8 @@ router.all("*", async (req, res, next) => {
5959
return next()
6060
}
6161

62+
ensureProxyEnabled(req)
63+
6264
// Must be authenticated to use the proxy.
6365
const isAuthenticated = await authenticated(req)
6466
if (!isAuthenticated) {
@@ -100,6 +102,8 @@ wsRouter.ws("*", async (req, _, next) => {
100102
if (!port) {
101103
return next()
102104
}
105+
106+
ensureProxyEnabled(req)
103107
ensureOrigin(req)
104108
await ensureAuthenticated(req)
105109
proxy.ws(req, req.ws, req.head, {

src/node/routes/pathProxy.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as path from "path"
33
import * as qs from "qs"
44
import * as pluginapi from "../../../typings/pluginapi"
55
import { HttpCode, HttpError } from "../../common/http"
6-
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
6+
import { ensureProxyEnabled, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
77
import { proxy as _proxy } from "../proxy"
88

99
const getProxyTarget = (req: Request, passthroughPath?: boolean): string => {
@@ -21,6 +21,8 @@ export async function proxy(
2121
passthroughPath?: boolean
2222
},
2323
): Promise<void> {
24+
ensureProxyEnabled(req)
25+
2426
if (!(await authenticated(req))) {
2527
// If visiting the root (/:port only) redirect to the login page.
2628
if (!req.params[0] || req.params[0] === "/") {
@@ -50,6 +52,7 @@ export async function wsProxy(
5052
passthroughPath?: boolean
5153
},
5254
): Promise<void> {
55+
ensureProxyEnabled(req)
5356
ensureOrigin(req)
5457
await ensureAuthenticated(req)
5558
_proxy.ws(req, req.ws, req.head, {

test/unit/node/cli.test.ts

+28
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ describe("parser", () => {
4747
delete process.env.CS_DISABLE_FILE_DOWNLOADS
4848
delete process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE
4949
delete process.env.VSCODE_PROXY_URI
50+
delete process.env.CS_DISABLE_PROXY
5051
console.log = jest.fn()
5152
})
5253

@@ -103,6 +104,8 @@ describe("parser", () => {
103104

104105
"--disable-getting-started-override",
105106

107+
"--disable-proxy",
108+
106109
["--session-socket", "/tmp/override-code-server-ipc-socket"],
107110

108111
["--host", "0.0.0.0"],
@@ -123,6 +126,7 @@ describe("parser", () => {
123126
},
124127
"disable-file-downloads": true,
125128
"disable-getting-started-override": true,
129+
"disable-proxy": true,
126130
enable: ["feature1", "feature2"],
127131
help: true,
128132
host: "0.0.0.0",
@@ -392,6 +396,30 @@ describe("parser", () => {
392396
})
393397
})
394398

399+
it("should use env var CS_DISABLE_PROXY", async () => {
400+
process.env.CS_DISABLE_PROXY = "1"
401+
const args = parse([])
402+
expect(args).toEqual({})
403+
404+
const defaultArgs = await setDefaults(args)
405+
expect(defaultArgs).toEqual({
406+
...defaults,
407+
"disable-proxy": true,
408+
})
409+
})
410+
411+
it("should use env var CS_DISABLE_PROXY set to true", async () => {
412+
process.env.CS_DISABLE_PROXY = "true"
413+
const args = parse([])
414+
expect(args).toEqual({})
415+
416+
const defaultArgs = await setDefaults(args)
417+
expect(defaultArgs).toEqual({
418+
...defaults,
419+
"disable-proxy": true,
420+
})
421+
})
422+
395423
it("should error if password passed in", () => {
396424
expect(() => parse(["--password", "supersecret123"])).toThrowError(
397425
"--password can only be set in the config file or passed in via $PASSWORD",

test/unit/node/proxy.test.ts

+11
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ describe("proxy", () => {
4545
jest.clearAllMocks()
4646
})
4747

48+
it("should return 403 Forbidden if proxy is disabled", async () => {
49+
e.get("/wsup", (req, res) => {
50+
res.json("you cannot see this")
51+
})
52+
codeServer = await integration.setup(["--auth=none", "--disable-proxy"], "")
53+
const resp = await codeServer.fetch(proxyPath)
54+
expect(resp.status).toBe(403)
55+
const json = await resp.json()
56+
expect(json).toEqual({ error: "Forbidden" })
57+
})
58+
4859
it("should rewrite the base path", async () => {
4960
e.get("/wsup", (req, res) => {
5061
res.json("asher is the best")

0 commit comments

Comments
 (0)