ssh tunnel WIP

This commit is contained in:
SPRINX0\prochazka
2025-01-03 16:44:57 +01:00
parent 59788faefd
commit aff282d31e
6 changed files with 29 additions and 8 deletions

View File

@@ -166,7 +166,9 @@ function start() {
if (time - lastPing > 40 * 1000) { if (time - lastPing > 40 * 1000) {
logger.info('Server connection not alive, exiting'); logger.info('Server connection not alive, exiting');
const driver = requireEngineDriver(storedConnection); const driver = requireEngineDriver(storedConnection);
await driver.close(dbhan); if (dbhan) {
await driver.close(dbhan);
}
process.exit(0); process.exit(0);
} }
}, 10 * 1000); }, 10 * 1000);

View File

@@ -7,7 +7,7 @@ const { getLogger, extractErrorLogData, extractErrorMessage } = require('dbgate-
const logger = getLogger('sshProcess'); const logger = getLogger('sshProcess');
async function getSshConnection(connection) { async function getSshConnection(connection, tunnelConfig) {
const sshConfig = { const sshConfig = {
endHost: connection.sshHost || '', endHost: connection.sshHost || '',
endPort: connection.sshPort || 22, endPort: connection.sshPort || 22,
@@ -23,6 +23,7 @@ async function getSshConnection(connection) {
: undefined, : undefined,
skipAutoPrivateKey: true, skipAutoPrivateKey: true,
noReadline: true, noReadline: true,
bindHost: tunnelConfig.fromHost,
}; };
const sshConn = new SSHConnection(sshConfig); const sshConn = new SSHConnection(sshConfig);
@@ -31,7 +32,7 @@ async function getSshConnection(connection) {
async function handleStart({ connection, tunnelConfig }) { async function handleStart({ connection, tunnelConfig }) {
try { try {
const sshConn = await getSshConnection(connection); const sshConn = await getSshConnection(connection, tunnelConfig);
await sshConn.forward(tunnelConfig); await sshConn.forward(tunnelConfig);
process.send({ process.send({

View File

@@ -130,7 +130,7 @@ class SSHConnection {
const connectionToBastion = await this.connect(bastionHost); const connectionToBastion = await this.connect(bastionHost);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
connectionToBastion.forwardOut( connectionToBastion.forwardOut(
'127.0.0.1', this.options.bindHost,
22, 22,
this.options.endHost, this.options.endHost,
this.options.endPort || 22, this.options.endPort || 22,
@@ -228,9 +228,9 @@ class SSHConnection {
options.toPort options.toPort
); );
connection.forwardOut( connection.forwardOut(
'127.0.0.1', this.options.bindHost,
options.fromPort, options.fromPort,
options.toHost || '127.0.0.1', options.toHost || this.options.bindHost,
options.toPort, options.toPort,
(error, stream) => { (error, stream) => {
if (error) { if (error) {
@@ -241,7 +241,7 @@ class SSHConnection {
} }
); );
}) })
.listen(options.fromPort, '127.0.0.1', () => { .listen(options.fromPort, this.options.bindHost, () => {
return resolve(); return resolve();
}); });
}); });

View File

@@ -63,7 +63,7 @@ async function connectUtility(driver, storedConnection, connectionMode, addition
throw new Error(tunnel.message); throw new Error(tunnel.message);
} }
connection.server = 'localhost'; connection.server = tunnel.localHost;
connection.port = tunnel.localPort; connection.port = tunnel.localPort;
} }

View File

@@ -61,15 +61,20 @@ function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
} }
async function getSshTunnel(connection) { async function getSshTunnel(connection) {
const config = require('../controllers/config');
const tunnelCacheKey = stableStringify(_.pick(connection, TUNNEL_FIELDS)); const tunnelCacheKey = stableStringify(_.pick(connection, TUNNEL_FIELDS));
const globalSettings = await config.getSettings();
return await lock.acquire(tunnelCacheKey, async () => { return await lock.acquire(tunnelCacheKey, async () => {
if (sshTunnelCache[tunnelCacheKey]) return sshTunnelCache[tunnelCacheKey]; if (sshTunnelCache[tunnelCacheKey]) return sshTunnelCache[tunnelCacheKey];
const localPort = await portfinder.getPortPromise({ port: 10000, stopPort: 60000 }); const localPort = await portfinder.getPortPromise({ port: 10000, stopPort: 60000 });
const localHost = globalSettings?.['connection.sshBindHost'] || '127.0.0.1';
// workaround for `getPortPromise` not releasing the port quickly enough // workaround for `getPortPromise` not releasing the port quickly enough
await new Promise(resolve => setTimeout(resolve, 500)); await new Promise(resolve => setTimeout(resolve, 500));
const tunnelConfig = { const tunnelConfig = {
fromPort: localPort, fromPort: localPort,
fromHost: localHost,
toPort: connection.port, toPort: connection.port,
toHost: connection.server, toHost: connection.server,
}; };
@@ -87,6 +92,7 @@ async function getSshTunnel(connection) {
sshTunnelCache[tunnelCacheKey] = { sshTunnelCache[tunnelCacheKey] = {
state: 'ok', state: 'ok',
localPort, localPort,
localHost,
subprocess, subprocess,
}; };
return sshTunnelCache[tunnelCacheKey]; return sshTunnelCache[tunnelCacheKey];

View File

@@ -202,6 +202,18 @@ ORDER BY
defaultValue="30" defaultValue="30"
disabled={values['connection.autoRefresh'] === false} disabled={values['connection.autoRefresh'] === false}
/> />
<FormSelectField
label="Local host address for SSH connections"
name="connection.sshBindHost"
isNative
defaultValue="127.0.0.1"
options={[
{ value: '127.0.0.1', label: '127.0.0.1 (IPv4)' },
{ value: '::1', label: '::1 (IPv6)' },
{ value: 'localhost', label: 'localhost (domain name)' },
{ value: 'testerror', label: 'testerror' },
]}
/>
<div class="heading">Query sessions</div> <div class="heading">Query sessions</div>
<FormCheckboxField <FormCheckboxField