Proxy path fixes (#4548)
* Fix issue where HTTP error status codes are not read. * Fix issues surrounding sessions when accessed from a proxy. - Updated vscode args to match latest upstream. - Fixed issues surrounding trailing slashes affecting base paths. - Updated cookie names to better match upstream's usage, debuggability. * Bump vendor. * Update tests. * Fix issue where tests lack cookie key. Co-authored-by: Asher <ash@coder.com>
This commit is contained in:
@ -3,14 +3,11 @@ import { promises as fs } from "fs"
|
||||
import { RateLimiter as Limiter } from "limiter"
|
||||
import * as os from "os"
|
||||
import * as path from "path"
|
||||
import { CookieKeys } from "../../common/http"
|
||||
import { rootPath } from "../constants"
|
||||
import { authenticated, getCookieDomain, redirect, replaceTemplates } from "../http"
|
||||
import { getPasswordMethod, handlePasswordValidation, humanPath, sanitizeString, escapeHtml } from "../util"
|
||||
|
||||
export enum Cookie {
|
||||
Key = "key",
|
||||
}
|
||||
|
||||
// RateLimiter wraps around the limiter library for logins.
|
||||
// It allows 2 logins every minute plus 12 logins every hour.
|
||||
export class RateLimiter {
|
||||
@ -62,7 +59,7 @@ router.get("/", async (req, res) => {
|
||||
res.send(await getRoot(req))
|
||||
})
|
||||
|
||||
router.post("/", async (req, res) => {
|
||||
router.post<{}, string, { password: string; base?: string }, { to?: string }>("/", async (req, res) => {
|
||||
const password = sanitizeString(req.body.password)
|
||||
const hashedPasswordFromArgs = req.args["hashed-password"]
|
||||
|
||||
@ -87,13 +84,13 @@ router.post("/", async (req, res) => {
|
||||
if (isPasswordValid) {
|
||||
// The hash does not add any actual security but we do it for
|
||||
// obfuscation purposes (and as a side effect it handles escaping).
|
||||
res.cookie(Cookie.Key, hashedPassword, {
|
||||
res.cookie(CookieKeys.Session, hashedPassword, {
|
||||
domain: getCookieDomain(req.headers.host || "", req.args["proxy-domain"]),
|
||||
// Browsers do not appear to allow cookies to be set relatively so we
|
||||
// need to get the root path from the browser since the proxy rewrites
|
||||
// it out of the path. Otherwise code-server instances hosted on
|
||||
// separate sub-paths will clobber each other.
|
||||
path: req.body.base ? path.posix.join(req.body.base, "..") : "/",
|
||||
path: req.body.base ? path.posix.join(req.body.base, "..", "/") : "/",
|
||||
sameSite: "lax",
|
||||
})
|
||||
|
||||
|
@ -1,17 +1,21 @@
|
||||
import { Router } from "express"
|
||||
import { CookieKeys } from "../../common/http"
|
||||
import { getCookieDomain, redirect } from "../http"
|
||||
import { Cookie } from "./login"
|
||||
|
||||
import { sanitizeString } from "../util"
|
||||
|
||||
export const router = Router()
|
||||
|
||||
router.get("/", async (req, res) => {
|
||||
router.get<{}, undefined, undefined, { base?: string; to?: string }>("/", async (req, res) => {
|
||||
const path = sanitizeString(req.query.base) || "/"
|
||||
const to = sanitizeString(req.query.to) || "/"
|
||||
|
||||
// Must use the *identical* properties used to set the cookie.
|
||||
res.clearCookie(Cookie.Key, {
|
||||
res.clearCookie(CookieKeys.Session, {
|
||||
domain: getCookieDomain(req.headers.host || "", req.args["proxy-domain"]),
|
||||
path: req.query.base || "/",
|
||||
path: decodeURIComponent(path),
|
||||
sameSite: "lax",
|
||||
})
|
||||
|
||||
const to = (typeof req.query.to === "string" && req.query.to) || "/"
|
||||
return redirect(req, res, to, { to: undefined, base: undefined })
|
||||
})
|
||||
|
@ -24,7 +24,7 @@ export class CodeServerRouteWrapper {
|
||||
const isAuthenticated = await authenticated(req)
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return redirect(req, res, "login", {
|
||||
return redirect(req, res, "login/", {
|
||||
// req.baseUrl can be blank if already at the root.
|
||||
to: req.baseUrl && req.baseUrl !== "/" ? req.baseUrl : undefined,
|
||||
})
|
||||
@ -88,9 +88,12 @@ export class CodeServerRouteWrapper {
|
||||
|
||||
try {
|
||||
this._codeServerMain = await createVSServer(null, {
|
||||
connectionToken: "0000",
|
||||
"connection-token": "0000",
|
||||
"accept-server-license-terms": true,
|
||||
...args,
|
||||
// For some reason VS Code takes the port as a string.
|
||||
/** Type casting. */
|
||||
help: !!args.help,
|
||||
version: !!args.version,
|
||||
port: args.port?.toString(),
|
||||
})
|
||||
} catch (createServerError) {
|
||||
|
Reference in New Issue
Block a user