mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-07-15 04:43:36 +00:00
Fix OIDC redirect forwarded port handling (#1007)
This commit is contained in:
@@ -433,8 +433,8 @@ http {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
|
||||
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
|
||||
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
@@ -676,8 +676,8 @@ http {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
|
||||
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
|
||||
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
|
||||
+4
-4
@@ -434,8 +434,8 @@ http {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
|
||||
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
|
||||
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
@@ -677,8 +677,8 @@ http {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
|
||||
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
|
||||
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 86400s;
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
getRequestBasePath,
|
||||
getRequestBaseUrl,
|
||||
getRequestBaseUrlWithForceHTTPS,
|
||||
getRequestOrigin,
|
||||
normalizeBasePath,
|
||||
} from "./request-origin.js";
|
||||
|
||||
@@ -106,3 +107,52 @@ describe("getRequestBasePath", () => {
|
||||
).toBe("https://example.com/termix");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRequestOrigin", () => {
|
||||
it("ignores non-numeric forwarded ports", () => {
|
||||
expect(
|
||||
getRequestOrigin(
|
||||
request({
|
||||
"x-forwarded-proto": "https",
|
||||
"x-forwarded-host": "termix.test.de",
|
||||
"x-forwarded-port": "{server_port}",
|
||||
}),
|
||||
),
|
||||
).toBe("https://termix.test.de");
|
||||
});
|
||||
|
||||
it("drops invalid ports embedded in forwarded hosts", () => {
|
||||
expect(
|
||||
getRequestOrigin(
|
||||
request({
|
||||
"x-forwarded-proto": "https",
|
||||
"x-forwarded-host": "termix.test.de:{server_port}",
|
||||
}),
|
||||
),
|
||||
).toBe("https://termix.test.de");
|
||||
});
|
||||
|
||||
it("keeps valid non-default forwarded ports", () => {
|
||||
expect(
|
||||
getRequestOrigin(
|
||||
request({
|
||||
"x-forwarded-proto": "https",
|
||||
"x-forwarded-host": "termix.test.de",
|
||||
"x-forwarded-port": "8443",
|
||||
}),
|
||||
),
|
||||
).toBe("https://termix.test.de:8443");
|
||||
});
|
||||
|
||||
it("omits default forwarded ports", () => {
|
||||
expect(
|
||||
getRequestOrigin(
|
||||
request({
|
||||
"x-forwarded-proto": "https",
|
||||
"x-forwarded-host": "termix.test.de",
|
||||
"x-forwarded-port": "443",
|
||||
}),
|
||||
),
|
||||
).toBe("https://termix.test.de");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,44 @@ function firstHeaderValue(value: string | string[] | undefined): string {
|
||||
return raw.split(",")[0].trim();
|
||||
}
|
||||
|
||||
function normalizePort(value: string | string[] | undefined): string {
|
||||
const raw = firstHeaderValue(value);
|
||||
if (!/^\d+$/.test(raw)) return "";
|
||||
|
||||
const port = Number(raw);
|
||||
if (!Number.isInteger(port) || port < 1 || port > 65535) return "";
|
||||
|
||||
return String(port);
|
||||
}
|
||||
|
||||
function splitHostHeader(value: string | string[] | undefined): {
|
||||
host: string;
|
||||
port: string;
|
||||
} {
|
||||
const raw = firstHeaderValue(value) || "localhost";
|
||||
|
||||
if (raw.startsWith("[")) {
|
||||
const match = raw.match(/^(\[[^\]]+\])(?::(.+))?$/);
|
||||
return {
|
||||
host: match?.[1] || raw,
|
||||
port: normalizePort(match?.[2]),
|
||||
};
|
||||
}
|
||||
|
||||
const parts = raw.split(":");
|
||||
if (parts.length === 2) {
|
||||
return {
|
||||
host: parts[0] || "localhost",
|
||||
port: normalizePort(parts[1]),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
host: raw,
|
||||
port: "",
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeBasePath(value: unknown): string {
|
||||
if (typeof value !== "string") return "";
|
||||
let basePath = value.split(",")[0].trim();
|
||||
@@ -45,38 +83,23 @@ export function getRequestOrigin(req: Request | IncomingMessage): string {
|
||||
: "http";
|
||||
}
|
||||
|
||||
const portHeader = req.headers["x-forwarded-port"];
|
||||
let port: string | undefined =
|
||||
typeof portHeader === "string"
|
||||
? portHeader.split(",")[0].trim()
|
||||
: undefined;
|
||||
let port = normalizePort(req.headers["x-forwarded-port"]);
|
||||
const { host, port: hostPort } = splitHostHeader(
|
||||
req.headers["x-forwarded-host"] || req.headers.host,
|
||||
);
|
||||
port ||= hostPort;
|
||||
|
||||
const hostHeaderRaw =
|
||||
req.headers["x-forwarded-host"] || req.headers.host || "localhost";
|
||||
const hostHeader =
|
||||
typeof hostHeaderRaw === "string"
|
||||
? hostHeaderRaw.split(",")[0].trim()
|
||||
: String(hostHeaderRaw);
|
||||
|
||||
if (!port && hostHeader.includes(":")) {
|
||||
const parts = hostHeader.split(":");
|
||||
if (parts.length === 2 && !parts[0].includes("[")) {
|
||||
port = parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
const hostWithoutPort = hostHeader.split(":")[0];
|
||||
if (port) {
|
||||
const isDefaultPort =
|
||||
(protocol === "http" && port === "80") ||
|
||||
(protocol === "https" && port === "443");
|
||||
|
||||
return isDefaultPort
|
||||
? `${protocol}://${hostWithoutPort}`
|
||||
: `${protocol}://${hostWithoutPort}:${port}`;
|
||||
? `${protocol}://${host}`
|
||||
: `${protocol}://${host}:${port}`;
|
||||
}
|
||||
|
||||
return `${protocol}://${hostWithoutPort}`;
|
||||
return `${protocol}://${host}`;
|
||||
}
|
||||
|
||||
export function getRequestOriginWithForceHTTPS(
|
||||
|
||||
Reference in New Issue
Block a user