Skip to content

Add --disable-proxy option #6349

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- [Are there community projects involving code-server?](#are-there-community-projects-involving-code-server)
- [How do I change the port?](#how-do-i-change-the-port)
- [How do I hide the coder/coder promotion in Help: Getting Started?](#how-do-i-hide-the-codercoder-promotion-in-help-getting-started)
- [How do I disable the proxy?](#how-do-i-disable-the-proxy)
- [How do I disable file download?](#how-do-i-disable-file-download)

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

## How do I disable the proxy?

You can pass the flag `--disable-proxy` to `code-server` or
you can set the environment variable `CS_DISABLE_PROXY=1` or
`CS_DISABLE_PROXY=true`.

Note, this option currently only disables the proxy routes to forwarded ports, including
the domain and path proxy routes over HTTP and WebSocket; however, it does not
disable the automatic port forwarding in the VS Code workbench itself. In other words,
user will still see the Ports tab and notifications, but will not be able to actually
use access the ports. It is recommended to set `remote.autoForwardPorts` to `false`
when using the option.

## How do I disable file download?

You can pass the flag `--disable-file-downloads` to `code-server`
9 changes: 9 additions & 0 deletions src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export interface UserProvidedCodeArgs {
"disable-file-downloads"?: boolean
"disable-workspace-trust"?: boolean
"disable-getting-started-override"?: boolean
"disable-proxy"?: boolean
"session-socket"?: string
}

Expand Down Expand Up @@ -177,6 +178,10 @@ export const options: Options<Required<UserProvidedArgs>> = {
type: "boolean",
description: "Disable the coder/coder override in the Help: Getting Started page.",
},
"disable-proxy": {
type: "boolean",
description: "Disable domain and path proxy routes.",
},
// --enable can be used to enable experimental features. These features
// provide no guarantees.
enable: { type: "string[]" },
Expand Down Expand Up @@ -558,6 +563,10 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args["disable-getting-started-override"] = true
}

if (process.env.CS_DISABLE_PROXY?.match(/^(1|true)$/)) {
args["disable-proxy"] = true
}

const usingEnvHashedPassword = !!process.env.HASHED_PASSWORD
if (process.env.HASHED_PASSWORD) {
args["hashed-password"] = process.env.HASHED_PASSWORD
Expand Down
19 changes: 19 additions & 0 deletions src/node/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,25 @@ export const replaceTemplates = <T extends object>(
.replace("{{OPTIONS}}", () => escapeJSON(serverOptions))
}

/**
* Throw an error if proxy is not enabled. Call `next` if provided.
*/
export const ensureProxyEnabled = (req: express.Request, _?: express.Response, next?: express.NextFunction): void => {
if (!proxyEnabled(req)) {
throw new HttpError("Forbidden", HttpCode.Forbidden)
}
if (next) {
next()
}
}

/**
* Return true if proxy is enabled.
*/
export const proxyEnabled = (req: express.Request): boolean => {
return !req.args["disable-proxy"]
}

/**
* Throw an error if not authorized. Call `next` if provided.
*/
Expand Down
6 changes: 5 additions & 1 deletion src/node/routes/domainProxy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Request, Router } from "express"
import { HttpCode, HttpError } from "../../common/http"
import { getHost, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
import { getHost, ensureProxyEnabled, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
import { proxy } from "../proxy"
import { Router as WsRouter } from "../wsRouter"

Expand Down Expand Up @@ -59,6 +59,8 @@ router.all("*", async (req, res, next) => {
return next()
}

ensureProxyEnabled(req)

// Must be authenticated to use the proxy.
const isAuthenticated = await authenticated(req)
if (!isAuthenticated) {
Expand Down Expand Up @@ -100,6 +102,8 @@ wsRouter.ws("*", async (req, _, next) => {
if (!port) {
return next()
}

ensureProxyEnabled(req)
ensureOrigin(req)
await ensureAuthenticated(req)
proxy.ws(req, req.ws, req.head, {
Expand Down
5 changes: 4 additions & 1 deletion src/node/routes/pathProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as path from "path"
import * as qs from "qs"
import * as pluginapi from "../../../typings/pluginapi"
import { HttpCode, HttpError } from "../../common/http"
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
import { ensureProxyEnabled, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
import { proxy as _proxy } from "../proxy"

const getProxyTarget = (req: Request, passthroughPath?: boolean): string => {
Expand All @@ -21,6 +21,8 @@ export async function proxy(
passthroughPath?: boolean
},
): Promise<void> {
ensureProxyEnabled(req)

if (!(await authenticated(req))) {
// If visiting the root (/:port only) redirect to the login page.
if (!req.params[0] || req.params[0] === "/") {
Expand Down Expand Up @@ -50,6 +52,7 @@ export async function wsProxy(
passthroughPath?: boolean
},
): Promise<void> {
ensureProxyEnabled(req)
ensureOrigin(req)
await ensureAuthenticated(req)
_proxy.ws(req, req.ws, req.head, {
Expand Down
28 changes: 28 additions & 0 deletions test/unit/node/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ describe("parser", () => {
delete process.env.CS_DISABLE_FILE_DOWNLOADS
delete process.env.CS_DISABLE_GETTING_STARTED_OVERRIDE
delete process.env.VSCODE_PROXY_URI
delete process.env.CS_DISABLE_PROXY
console.log = jest.fn()
})

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

"--disable-getting-started-override",

"--disable-proxy",

["--session-socket", "/tmp/override-code-server-ipc-socket"],

["--host", "0.0.0.0"],
Expand All @@ -123,6 +126,7 @@ describe("parser", () => {
},
"disable-file-downloads": true,
"disable-getting-started-override": true,
"disable-proxy": true,
enable: ["feature1", "feature2"],
help: true,
host: "0.0.0.0",
Expand Down Expand Up @@ -392,6 +396,30 @@ describe("parser", () => {
})
})

it("should use env var CS_DISABLE_PROXY", async () => {
process.env.CS_DISABLE_PROXY = "1"
const args = parse([])
expect(args).toEqual({})

const defaultArgs = await setDefaults(args)
expect(defaultArgs).toEqual({
...defaults,
"disable-proxy": true,
})
})

it("should use env var CS_DISABLE_PROXY set to true", async () => {
process.env.CS_DISABLE_PROXY = "true"
const args = parse([])
expect(args).toEqual({})

const defaultArgs = await setDefaults(args)
expect(defaultArgs).toEqual({
...defaults,
"disable-proxy": true,
})
})

it("should error if password passed in", () => {
expect(() => parse(["--password", "supersecret123"])).toThrowError(
"--password can only be set in the config file or passed in via $PASSWORD",
Expand Down
11 changes: 11 additions & 0 deletions test/unit/node/proxy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ describe("proxy", () => {
jest.clearAllMocks()
})

it("should return 403 Forbidden if proxy is disabled", async () => {
e.get("/wsup", (req, res) => {
res.json("you cannot see this")
})
codeServer = await integration.setup(["--auth=none", "--disable-proxy"], "")
const resp = await codeServer.fetch(proxyPath)
expect(resp.status).toBe(403)
const json = await resp.json()
expect(json).toEqual({ error: "Forbidden" })
})

it("should rewrite the base path", async () => {
e.get("/wsup", (req, res) => {
res.json("asher is the best")
Expand Down