mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-05-29 21:01:01 +00:00
fix: monitoring defaults not saving and OIDC redirect issues
This commit is contained in:
@@ -100,6 +100,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 $proxy_x_forwarded_port;
|
||||
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
|
||||
}
|
||||
|
||||
location ~ ^/version(/.*)?$ {
|
||||
|
||||
@@ -89,6 +89,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 $proxy_x_forwarded_port;
|
||||
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
|
||||
}
|
||||
|
||||
location ~ ^/version(/.*)?$ {
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
generateDeviceFingerprint,
|
||||
} from "../../utils/user-agent-parser.js";
|
||||
import { loginRateLimiter } from "../../utils/login-rate-limiter.js";
|
||||
import { getRequestOriginWithForceHTTPS } from "../../utils/request-origin.js";
|
||||
|
||||
const authManager = AuthManager.getInstance();
|
||||
|
||||
@@ -798,6 +799,9 @@ router.get("/oidc-config/admin", requireAdmin, async (req, res) => {
|
||||
*/
|
||||
router.get("/oidc/authorize", async (req, res) => {
|
||||
try {
|
||||
const origin = getRequestOriginWithForceHTTPS(req);
|
||||
const backendCallbackUri = `${origin}/users/oidc/callback`;
|
||||
|
||||
authLogger.info("OIDC authorize request headers", {
|
||||
protocol: req.protocol,
|
||||
host: req.get("Host"),
|
||||
@@ -807,6 +811,8 @@ router.get("/oidc/authorize", async (req, res) => {
|
||||
"x-forwarded-host": req.get("X-Forwarded-Host"),
|
||||
"x-forwarded-port": req.get("X-Forwarded-Port"),
|
||||
secure: req.secure,
|
||||
calculatedOrigin: origin,
|
||||
backendCallbackUri: backendCallbackUri,
|
||||
});
|
||||
|
||||
const envConfig = getOIDCConfigFromEnv();
|
||||
@@ -826,15 +832,6 @@ router.get("/oidc/authorize", async (req, res) => {
|
||||
const state = nanoid();
|
||||
const nonce = nanoid();
|
||||
|
||||
const protocol =
|
||||
process.env.OIDC_FORCE_HTTPS === "true"
|
||||
? "https"
|
||||
: req.get("X-Forwarded-Proto") || req.protocol;
|
||||
|
||||
const host = req.get("Host");
|
||||
const origin = `${protocol}://${host}`;
|
||||
const backendCallbackUri = `${origin}/users/oidc/callback`;
|
||||
|
||||
const referer = req.get("Referer");
|
||||
let frontendOrigin;
|
||||
if (referer) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import { FieldCrypto } from "../utils/field-crypto.js";
|
||||
import { promises as fs } from "fs";
|
||||
import path from "path";
|
||||
import axios from "axios";
|
||||
import { getRequestOrigin } from "../utils/request-origin.js";
|
||||
|
||||
const AUTH_TIMEOUT = 60 * 1000;
|
||||
|
||||
@@ -48,43 +49,6 @@ interface OPKSSHAuthSession {
|
||||
const activeAuthSessions = new Map<string, OPKSSHAuthSession>();
|
||||
const cleanupInProgress = new Set<string>();
|
||||
|
||||
export function getRequestOrigin(req: IncomingMessage): string {
|
||||
const protoHeader =
|
||||
req.headers["x-forwarded-proto"] ||
|
||||
((req.socket as unknown as { encrypted?: boolean }).encrypted
|
||||
? "https"
|
||||
: "http");
|
||||
const proto =
|
||||
typeof protoHeader === "string"
|
||||
? protoHeader.split(",")[0].trim()
|
||||
: String(protoHeader);
|
||||
|
||||
const portHeader = req.headers["x-forwarded-port"];
|
||||
const port =
|
||||
typeof portHeader === "string"
|
||||
? portHeader.split(",")[0].trim()
|
||||
: undefined;
|
||||
|
||||
const hostHeaderRaw =
|
||||
req.headers["x-forwarded-host"] || req.headers.host || "localhost";
|
||||
const hostHeader =
|
||||
typeof hostHeaderRaw === "string"
|
||||
? hostHeaderRaw.split(",")[0].trim()
|
||||
: String(hostHeaderRaw);
|
||||
|
||||
if (port) {
|
||||
const hostWithoutPort = hostHeader.split(":")[0];
|
||||
const isDefaultPort =
|
||||
(proto === "http" && port === "80") ||
|
||||
(proto === "https" && port === "443");
|
||||
return isDefaultPort
|
||||
? `${proto}://${hostWithoutPort}`
|
||||
: `${proto}://${hostWithoutPort}:${port}`;
|
||||
}
|
||||
|
||||
return `${proto}://${hostHeader}`;
|
||||
}
|
||||
|
||||
function getOPKConfigPath(): string {
|
||||
const dataDir =
|
||||
process.env.DATA_DIR || path.join(process.cwd(), "db", "data");
|
||||
|
||||
@@ -1001,6 +1001,28 @@ class PollingManager {
|
||||
}
|
||||
}
|
||||
|
||||
refreshAllPolling(): void {
|
||||
const hostsToRefresh: Array<{
|
||||
host: SSHHostWithCredentials;
|
||||
viewerUserId?: string;
|
||||
}> = [];
|
||||
|
||||
for (const [hostId, config] of this.pollingConfigs.entries()) {
|
||||
hostsToRefresh.push({
|
||||
host: config.host,
|
||||
viewerUserId: config.viewerUserId,
|
||||
});
|
||||
}
|
||||
|
||||
for (const hostId of this.pollingConfigs.keys()) {
|
||||
this.stopPollingForHost(hostId, false);
|
||||
}
|
||||
|
||||
for (const { host, viewerUserId } of hostsToRefresh) {
|
||||
this.startPollingForHost(host, { statusOnly: true, viewerUserId });
|
||||
}
|
||||
}
|
||||
|
||||
registerViewer(hostId: number, sessionId: string, userId: string): void {
|
||||
if (!this.activeViewers.has(hostId)) {
|
||||
this.activeViewers.set(hostId, new Set());
|
||||
@@ -1259,9 +1281,8 @@ async function resolveHostCredentials(
|
||||
const isSharedHost = userId !== ownerId;
|
||||
|
||||
if (isSharedHost) {
|
||||
const { SharedCredentialManager } = await import(
|
||||
"../utils/shared-credential-manager.js"
|
||||
);
|
||||
const { SharedCredentialManager } =
|
||||
await import("../utils/shared-credential-manager.js");
|
||||
const sharedCredManager = SharedCredentialManager.getInstance();
|
||||
const sharedCred = await sharedCredManager.getSharedCredentialForUser(
|
||||
host.id as number,
|
||||
@@ -3185,6 +3206,9 @@ app.post("/global-settings", requireAdmin, async (req, res) => {
|
||||
.run(String(metricsInterval));
|
||||
}
|
||||
|
||||
// Refresh all active polling to apply new intervals immediately
|
||||
pollingManager.refreshAllPolling();
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
statsLogger.error("Failed to save global settings", {
|
||||
|
||||
@@ -808,9 +808,9 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
case "opkssh_start_auth": {
|
||||
const opksshData = data as { hostId: number };
|
||||
try {
|
||||
const { startOPKSSHAuth, getRequestOrigin } = await import(
|
||||
"./opkssh-auth.js"
|
||||
);
|
||||
const { startOPKSSHAuth } = await import("./opkssh-auth.js");
|
||||
const { getRequestOrigin } =
|
||||
await import("../utils/request-origin.js");
|
||||
const db = getDb();
|
||||
const hostRow = await db
|
||||
.select()
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { Request } from "express";
|
||||
import type { IncomingMessage } from "http";
|
||||
|
||||
export function getRequestOrigin(req: Request | IncomingMessage): string {
|
||||
let protocol: string;
|
||||
const protoHeader = req.headers["x-forwarded-proto"];
|
||||
|
||||
if (protoHeader) {
|
||||
protocol =
|
||||
typeof protoHeader === "string"
|
||||
? protoHeader.split(",")[0].trim()
|
||||
: protoHeader[0];
|
||||
} else if ("protocol" in req && req.protocol) {
|
||||
protocol = req.protocol;
|
||||
} else {
|
||||
protocol = (req.socket as unknown as { encrypted?: boolean }).encrypted
|
||||
? "https"
|
||||
: "http";
|
||||
}
|
||||
|
||||
const portHeader = req.headers["x-forwarded-port"];
|
||||
const port =
|
||||
typeof portHeader === "string"
|
||||
? portHeader.split(",")[0].trim()
|
||||
: undefined;
|
||||
|
||||
const hostHeaderRaw =
|
||||
req.headers["x-forwarded-host"] || req.headers.host || "localhost";
|
||||
const hostHeader =
|
||||
typeof hostHeaderRaw === "string"
|
||||
? hostHeaderRaw.split(",")[0].trim()
|
||||
: String(hostHeaderRaw);
|
||||
|
||||
if (port) {
|
||||
const hostWithoutPort = hostHeader.split(":")[0];
|
||||
const isDefaultPort =
|
||||
(protocol === "http" && port === "80") ||
|
||||
(protocol === "https" && port === "443");
|
||||
|
||||
return isDefaultPort
|
||||
? `${protocol}://${hostWithoutPort}`
|
||||
: `${protocol}://${hostWithoutPort}:${port}`;
|
||||
}
|
||||
|
||||
return `${protocol}://${hostHeader}`;
|
||||
}
|
||||
|
||||
export function getRequestOriginWithForceHTTPS(
|
||||
req: Request | IncomingMessage,
|
||||
): string {
|
||||
if (process.env.OIDC_FORCE_HTTPS === "true") {
|
||||
const origin = getRequestOrigin(req);
|
||||
return origin.replace(/^http:/, "https:");
|
||||
}
|
||||
return getRequestOrigin(req);
|
||||
}
|
||||
Reference in New Issue
Block a user