Fix tunnels

This commit is contained in:
LukeGus
2025-09-30 23:54:27 -05:00
parent a7a62bad21
commit ccfe866b8f
2 changed files with 126 additions and 113 deletions

View File

@@ -62,17 +62,26 @@ router.get("/db/host/internal", async (req: Request, res: Response) => {
.select() .select()
.from(sshData) .from(sshData)
.where( .where(
or( and(
isNotNull(sshData.autostartPassword), eq(sshData.enableTunnel, true),
isNotNull(sshData.autostartKey), isNotNull(sshData.tunnelConnections),
), ),
); );
const result = autostartHosts.map((host) => { const result = autostartHosts
.map((host) => {
const tunnelConnections = host.tunnelConnections const tunnelConnections = host.tunnelConnections
? JSON.parse(host.tunnelConnections) ? JSON.parse(host.tunnelConnections)
: []; : [];
const hasAutoStartTunnels = tunnelConnections.some(
(tunnel: any) => tunnel.autoStart,
);
if (!hasAutoStartTunnels) {
return null;
}
return { return {
id: host.id, id: host.id,
userId: host.userId, userId: host.userId,
@@ -87,16 +96,19 @@ router.get("/db/host/internal", async (req: Request, res: Response) => {
autostartKey: host.autostartKey, autostartKey: host.autostartKey,
autostartKeyPassword: host.autostartKeyPassword, autostartKeyPassword: host.autostartKeyPassword,
authType: host.authType, authType: host.authType,
keyType: host.keyType,
credentialId: host.credentialId,
enableTunnel: true, enableTunnel: true,
tunnelConnections: tunnelConnections.filter( tunnelConnections: tunnelConnections.filter(
(tunnel: any) => tunnel.autoStart, (tunnel: any) => tunnel.autoStart,
), ),
pin: false, pin: !!host.pin,
enableTerminal: false, enableTerminal: !!host.enableTerminal,
enableFileManager: false, enableFileManager: !!host.enableFileManager,
tags: ["autostart"], tags: ["autostart"],
}; };
}); })
.filter(Boolean);
res.json(result); res.json(result);
} catch (err) { } catch (err) {

View File

@@ -18,6 +18,7 @@ import { CONNECTION_STATES } from "../../types/index.js";
import { tunnelLogger } from "../utils/logger.js"; import { tunnelLogger } from "../utils/logger.js";
import { SystemCrypto } from "../utils/system-crypto.js"; import { SystemCrypto } from "../utils/system-crypto.js";
import { SimpleDBOps } from "../utils/simple-db-ops.js"; import { SimpleDBOps } from "../utils/simple-db-ops.js";
import { DataCrypto } from "../utils/data-crypto.js";
const app = express(); const app = express();
app.use( app.use(
@@ -491,6 +492,8 @@ async function connectSSHTunnel(
if (tunnelConfig.sourceCredentialId && tunnelConfig.sourceUserId) { if (tunnelConfig.sourceCredentialId && tunnelConfig.sourceUserId) {
try { try {
const userDataKey = DataCrypto.getUserDataKey(tunnelConfig.sourceUserId);
if (userDataKey) {
const credentials = await SimpleDBOps.select( const credentials = await SimpleDBOps.select(
getDb() getDb()
.select() .select()
@@ -515,11 +518,8 @@ async function connectSSHTunnel(
authMethod: credential.authType, authMethod: credential.authType,
}; };
} else { } else {
tunnelLogger.warn("No source credentials found in database", { }
operation: "tunnel_connect", } else {
tunnelName,
credentialId: tunnelConfig.sourceCredentialId,
});
} }
} catch (error) { } catch (error) {
tunnelLogger.warn("Failed to resolve source credentials from database", { tunnelLogger.warn("Failed to resolve source credentials from database", {
@@ -569,6 +569,8 @@ async function connectSSHTunnel(
if (tunnelConfig.endpointCredentialId && tunnelConfig.endpointUserId) { if (tunnelConfig.endpointCredentialId && tunnelConfig.endpointUserId) {
try { try {
const userDataKey = DataCrypto.getUserDataKey(tunnelConfig.endpointUserId);
if (userDataKey) {
const credentials = await SimpleDBOps.select( const credentials = await SimpleDBOps.select(
getDb() getDb()
.select() .select()
@@ -599,6 +601,13 @@ async function connectSSHTunnel(
credentialId: tunnelConfig.endpointCredentialId, credentialId: tunnelConfig.endpointCredentialId,
}); });
} }
} else {
tunnelLogger.warn("User data key not available for endpoint credentials, using tunnel config credentials", {
operation: "tunnel_connect",
tunnelName,
credentialId: tunnelConfig.endpointCredentialId,
});
}
} catch (error) { } catch (error) {
tunnelLogger.warn( tunnelLogger.warn(
`Failed to resolve endpoint credentials for tunnel ${tunnelName}: ${error instanceof Error ? error.message : "Unknown error"}`, `Failed to resolve endpoint credentials for tunnel ${tunnelName}: ${error instanceof Error ? error.message : "Unknown error"}`,
@@ -710,9 +719,9 @@ async function connectSSHTunnel(
resolvedEndpointCredentials.sshKey resolvedEndpointCredentials.sshKey
) { ) {
const keyFilePath = `/tmp/tunnel_key_${tunnelName.replace(/[^a-zA-Z0-9]/g, "_")}`; const keyFilePath = `/tmp/tunnel_key_${tunnelName.replace(/[^a-zA-Z0-9]/g, "_")}`;
tunnelCmd = `echo '${resolvedEndpointCredentials.sshKey}' > ${keyFilePath} && chmod 600 ${keyFilePath} && exec -a "${tunnelMarker}" ssh -i ${keyFilePath} -v -N -o StrictHostKeyChecking=no -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o GatewayPorts=yes -R ${tunnelConfig.endpointPort}:localhost:${tunnelConfig.sourcePort} ${tunnelConfig.endpointUsername}@${tunnelConfig.endpointIP} && rm -f ${keyFilePath}`; tunnelCmd = `echo '${resolvedEndpointCredentials.sshKey}' > ${keyFilePath} && chmod 600 ${keyFilePath} && exec -a "${tunnelMarker}" ssh -i ${keyFilePath} -N -o StrictHostKeyChecking=no -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o GatewayPorts=yes -R ${tunnelConfig.endpointPort}:localhost:${tunnelConfig.sourcePort} ${tunnelConfig.endpointUsername}@${tunnelConfig.endpointIP} && rm -f ${keyFilePath}`;
} else { } else {
tunnelCmd = `exec -a "${tunnelMarker}" sshpass -p '${resolvedEndpointCredentials.password || ""}' ssh -v -N -o StrictHostKeyChecking=no -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o GatewayPorts=yes -R ${tunnelConfig.endpointPort}:localhost:${tunnelConfig.sourcePort} ${tunnelConfig.endpointUsername}@${tunnelConfig.endpointIP}`; tunnelCmd = `exec -a "${tunnelMarker}" sshpass -p '${resolvedEndpointCredentials.password || ""}' ssh -N -o StrictHostKeyChecking=no -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o GatewayPorts=yes -R ${tunnelConfig.endpointPort}:localhost:${tunnelConfig.sourcePort} ${tunnelConfig.endpointUsername}@${tunnelConfig.endpointIP}`;
} }
conn.exec(tunnelCmd, (err, stream) => { conn.exec(tunnelCmd, (err, stream) => {
@@ -999,6 +1008,8 @@ async function killRemoteTunnelByMarker(
if (tunnelConfig.sourceCredentialId && tunnelConfig.sourceUserId) { if (tunnelConfig.sourceCredentialId && tunnelConfig.sourceUserId) {
try { try {
const userDataKey = DataCrypto.getUserDataKey(tunnelConfig.sourceUserId);
if (userDataKey) {
const credentials = await SimpleDBOps.select( const credentials = await SimpleDBOps.select(
getDb() getDb()
.select() .select()
@@ -1023,6 +1034,8 @@ async function killRemoteTunnelByMarker(
authMethod: credential.authType, authMethod: credential.authType,
}; };
} }
} else {
}
} catch (error) { } catch (error) {
tunnelLogger.warn("Failed to resolve source credentials for cleanup", { tunnelLogger.warn("Failed to resolve source credentials for cleanup", {
tunnelName, tunnelName,
@@ -1418,18 +1431,6 @@ async function initializeAutoStartTunnels(): Promise<void> {
const hasEndpointKey = const hasEndpointKey =
tunnelConnection.endpointKey || endpointHost.autostartKey; tunnelConnection.endpointKey || endpointHost.autostartKey;
if (!hasSourcePassword && !hasSourceKey) {
tunnelLogger.warn(
`Tunnel '${tunnelConfig.name}' may fail: source host '${host.name || `${host.username}@${host.ip}`}' has no plaintext credentials. Enable autostart for this host to use unattended tunneling.`,
);
}
if (!hasEndpointPassword && !hasEndpointKey) {
tunnelLogger.warn(
`Tunnel '${tunnelConfig.name}' may fail: endpoint host '${endpointHost.name || `${endpointHost.username}@${endpointHost.ip}`}' has no plaintext credentials. Consider enabling autostart for this host or configuring credentials in tunnel connection.`,
);
}
autoStartTunnels.push(tunnelConfig); autoStartTunnels.push(tunnelConfig);
} else { } else {
tunnelLogger.error( tunnelLogger.error(