Merge pull request #57 from LukeGus/dev-1.01
Dev 1.01
This commit was merged in pull request #57.
This commit is contained in:
4
.github/workflows/docker-image.yml
vendored
4
.github/workflows/docker-image.yml
vendored
@@ -16,7 +16,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, linux]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -113,4 +113,4 @@ jobs:
|
|||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
docker image prune -af
|
docker image prune -af
|
||||||
docker system prune -af --volumes
|
docker system prune -af --volumes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useRef, useState, useImperativeHandle, forwardRef } from 'react';
|
import {useEffect, useRef, useState, useImperativeHandle, forwardRef} from 'react';
|
||||||
import { useXTerm } from 'react-xtermjs';
|
import {useXTerm} from 'react-xtermjs';
|
||||||
import { FitAddon } from '@xterm/addon-fit';
|
import {FitAddon} from '@xterm/addon-fit';
|
||||||
import { ClipboardAddon } from '@xterm/addon-clipboard';
|
import {ClipboardAddon} from '@xterm/addon-clipboard';
|
||||||
|
|
||||||
interface SSHTerminalProps {
|
interface SSHTerminalProps {
|
||||||
hostConfig: any;
|
hostConfig: any;
|
||||||
@@ -12,18 +12,23 @@ interface SSHTerminalProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTerminal(
|
export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTerminal(
|
||||||
{ hostConfig, isVisible, splitScreen = false },
|
{hostConfig, isVisible, splitScreen = false},
|
||||||
ref
|
ref
|
||||||
) {
|
) {
|
||||||
const { instance: terminal, ref: xtermRef } = useXTerm();
|
const {instance: terminal, ref: xtermRef} = useXTerm();
|
||||||
const fitAddonRef = useRef<FitAddon | null>(null);
|
const fitAddonRef = useRef<FitAddon | null>(null);
|
||||||
const webSocketRef = useRef<WebSocket | null>(null);
|
const webSocketRef = useRef<WebSocket | null>(null);
|
||||||
const resizeTimeout = useRef<NodeJS.Timeout | null>(null);
|
const resizeTimeout = useRef<NodeJS.Timeout | null>(null);
|
||||||
const wasDisconnectedBySSH = useRef(false);
|
const wasDisconnectedBySSH = useRef(false);
|
||||||
|
const pingIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
disconnect: () => {
|
disconnect: () => {
|
||||||
|
if (pingIntervalRef.current) {
|
||||||
|
clearInterval(pingIntervalRef.current);
|
||||||
|
pingIntervalRef.current = null;
|
||||||
|
}
|
||||||
webSocketRef.current?.close();
|
webSocketRef.current?.close();
|
||||||
},
|
},
|
||||||
fit: () => {
|
fit: () => {
|
||||||
@@ -31,7 +36,7 @@ export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTermina
|
|||||||
},
|
},
|
||||||
sendInput: (data: string) => {
|
sendInput: (data: string) => {
|
||||||
if (webSocketRef.current?.readyState === 1) {
|
if (webSocketRef.current?.readyState === 1) {
|
||||||
webSocketRef.current.send(JSON.stringify({ type: 'input', data }));
|
webSocketRef.current.send(JSON.stringify({type: 'input', data}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}), []);
|
}), []);
|
||||||
@@ -73,7 +78,7 @@ export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTermina
|
|||||||
fitAddonRef.current?.fit();
|
fitAddonRef.current?.fit();
|
||||||
const cols = terminal.cols + 1;
|
const cols = terminal.cols + 1;
|
||||||
const rows = terminal.rows;
|
const rows = terminal.rows;
|
||||||
webSocketRef.current?.send(JSON.stringify({ type: 'resize', data: { cols, rows } }));
|
webSocketRef.current?.send(JSON.stringify({type: 'resize', data: {cols, rows}}));
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -93,10 +98,16 @@ export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTermina
|
|||||||
wasDisconnectedBySSH.current = false;
|
wasDisconnectedBySSH.current = false;
|
||||||
|
|
||||||
ws.addEventListener('open', () => {
|
ws.addEventListener('open', () => {
|
||||||
ws.send(JSON.stringify({ type: 'connectToHost', data: { cols, rows, hostConfig } }));
|
ws.send(JSON.stringify({type: 'connectToHost', data: {cols, rows, hostConfig}}));
|
||||||
terminal.onData((data) => {
|
terminal.onData((data) => {
|
||||||
ws.send(JSON.stringify({ type: 'input', data }));
|
ws.send(JSON.stringify({type: 'input', data}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pingIntervalRef.current = setInterval(() => {
|
||||||
|
if (ws.readyState === WebSocket.OPEN) {
|
||||||
|
ws.send(JSON.stringify({type: 'ping'}));
|
||||||
|
}
|
||||||
|
}, 30000);
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.addEventListener('message', (event) => {
|
ws.addEventListener('message', (event) => {
|
||||||
@@ -104,12 +115,13 @@ export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTermina
|
|||||||
const msg = JSON.parse(event.data);
|
const msg = JSON.parse(event.data);
|
||||||
if (msg.type === 'data') terminal.write(msg.data);
|
if (msg.type === 'data') terminal.write(msg.data);
|
||||||
else if (msg.type === 'error') terminal.writeln(`\r\n[ERROR] ${msg.message}`);
|
else if (msg.type === 'error') terminal.writeln(`\r\n[ERROR] ${msg.message}`);
|
||||||
else if (msg.type === 'connected') {}
|
else if (msg.type === 'connected') {
|
||||||
else if (msg.type === 'disconnected') {
|
} else if (msg.type === 'disconnected') {
|
||||||
wasDisconnectedBySSH.current = true;
|
wasDisconnectedBySSH.current = true;
|
||||||
terminal.writeln(`\r\n[${msg.message || 'Disconnected'}]`);
|
terminal.writeln(`\r\n[${msg.message || 'Disconnected'}]`);
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.addEventListener('close', () => {
|
ws.addEventListener('close', () => {
|
||||||
@@ -126,6 +138,10 @@ export const SSHTerminal = forwardRef<any, SSHTerminalProps>(function SSHTermina
|
|||||||
return () => {
|
return () => {
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
if (resizeTimeout.current) clearTimeout(resizeTimeout.current);
|
if (resizeTimeout.current) clearTimeout(resizeTimeout.current);
|
||||||
|
if (pingIntervalRef.current) {
|
||||||
|
clearInterval(pingIntervalRef.current);
|
||||||
|
pingIntervalRef.current = null;
|
||||||
|
}
|
||||||
webSocketRef.current?.close();
|
webSocketRef.current?.close();
|
||||||
};
|
};
|
||||||
}, [xtermRef, terminal, hostConfig]);
|
}, [xtermRef, terminal, hostConfig]);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { WebSocketServer, WebSocket, type RawData } from 'ws';
|
import {WebSocketServer, WebSocket, type RawData} from 'ws';
|
||||||
import { Client, type ClientChannel, type PseudoTtyOptions } from 'ssh2';
|
import {Client, type ClientChannel, type PseudoTtyOptions} from 'ssh2';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
const wss = new WebSocketServer({ port: 8082 });
|
const wss = new WebSocketServer({port: 8082});
|
||||||
|
|
||||||
const sshIconSymbol = '🖥️';
|
const sshIconSymbol = '🖥️';
|
||||||
const getTimeStamp = (): string => chalk.gray(`[${new Date().toLocaleTimeString()}]`);
|
const getTimeStamp = (): string => chalk.gray(`[${new Date().toLocaleTimeString()}]`);
|
||||||
@@ -33,6 +33,7 @@ const logger = {
|
|||||||
wss.on('connection', (ws: WebSocket) => {
|
wss.on('connection', (ws: WebSocket) => {
|
||||||
let sshConn: Client | null = null;
|
let sshConn: Client | null = null;
|
||||||
let sshStream: ClientChannel | null = null;
|
let sshStream: ClientChannel | null = null;
|
||||||
|
let pingInterval: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
ws.on('close', () => {
|
ws.on('close', () => {
|
||||||
cleanupSSH();
|
cleanupSSH();
|
||||||
@@ -44,11 +45,11 @@ wss.on('connection', (ws: WebSocket) => {
|
|||||||
parsed = JSON.parse(msg.toString());
|
parsed = JSON.parse(msg.toString());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('Invalid JSON received: ' + msg.toString());
|
logger.error('Invalid JSON received: ' + msg.toString());
|
||||||
ws.send(JSON.stringify({ type: 'error', message: 'Invalid JSON' }));
|
ws.send(JSON.stringify({type: 'error', message: 'Invalid JSON'}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { type, data } = parsed;
|
const {type, data} = parsed;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'connectToHost':
|
case 'connectToHost':
|
||||||
@@ -67,6 +68,10 @@ wss.on('connection', (ws: WebSocket) => {
|
|||||||
if (sshStream) sshStream.write(data);
|
if (sshStream) sshStream.write(data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'ping':
|
||||||
|
ws.send(JSON.stringify({type: 'pong'}));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
logger.warn('Unknown message type: ' + type);
|
logger.warn('Unknown message type: ' + type);
|
||||||
}
|
}
|
||||||
@@ -86,30 +91,39 @@ wss.on('connection', (ws: WebSocket) => {
|
|||||||
authType?: string;
|
authType?: string;
|
||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
const { cols, rows, hostConfig } = data;
|
const {cols, rows, hostConfig} = data;
|
||||||
const { ip, port, username, password, key, keyPassword, keyType, authType } = hostConfig;
|
const {ip, port, username, password, key, keyPassword, keyType, authType} = hostConfig;
|
||||||
|
|
||||||
if (!username || typeof username !== 'string' || username.trim() === '') {
|
if (!username || typeof username !== 'string' || username.trim() === '') {
|
||||||
logger.error('Invalid username provided');
|
logger.error('Invalid username provided');
|
||||||
ws.send(JSON.stringify({ type: 'error', message: 'Invalid username provided' }));
|
ws.send(JSON.stringify({type: 'error', message: 'Invalid username provided'}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ip || typeof ip !== 'string' || ip.trim() === '') {
|
if (!ip || typeof ip !== 'string' || ip.trim() === '') {
|
||||||
logger.error('Invalid IP provided');
|
logger.error('Invalid IP provided');
|
||||||
ws.send(JSON.stringify({ type: 'error', message: 'Invalid IP provided' }));
|
ws.send(JSON.stringify({type: 'error', message: 'Invalid IP provided'}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!port || typeof port !== 'number' || port <= 0) {
|
if (!port || typeof port !== 'number' || port <= 0) {
|
||||||
logger.error('Invalid port provided');
|
logger.error('Invalid port provided');
|
||||||
ws.send(JSON.stringify({ type: 'error', message: 'Invalid port provided' }));
|
ws.send(JSON.stringify({type: 'error', message: 'Invalid port provided'}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sshConn = new Client();
|
sshConn = new Client();
|
||||||
|
|
||||||
|
const connectionTimeout = setTimeout(() => {
|
||||||
|
if (sshConn) {
|
||||||
|
logger.error('SSH connection timeout');
|
||||||
|
ws.send(JSON.stringify({type: 'error', message: 'SSH connection timeout'}));
|
||||||
|
cleanupSSH(connectionTimeout);
|
||||||
|
}
|
||||||
|
}, 15000);
|
||||||
|
|
||||||
sshConn.on('ready', () => {
|
sshConn.on('ready', () => {
|
||||||
|
clearTimeout(connectionTimeout);
|
||||||
const pseudoTtyOpts: PseudoTtyOptions = {
|
const pseudoTtyOpts: PseudoTtyOptions = {
|
||||||
term: 'xterm-256color',
|
term: 'xterm-256color',
|
||||||
cols,
|
cols,
|
||||||
@@ -124,30 +138,43 @@ wss.on('connection', (ws: WebSocket) => {
|
|||||||
sshConn!.shell(pseudoTtyOpts, (err, stream) => {
|
sshConn!.shell(pseudoTtyOpts, (err, stream) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error('Shell error: ' + err.message);
|
logger.error('Shell error: ' + err.message);
|
||||||
ws.send(JSON.stringify({ type: 'error', message: 'Shell error: ' + err.message }));
|
ws.send(JSON.stringify({type: 'error', message: 'Shell error: ' + err.message}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sshStream = stream;
|
sshStream = stream;
|
||||||
|
|
||||||
stream.on('data', (chunk: Buffer) => {
|
stream.on('data', (chunk: Buffer) => {
|
||||||
ws.send(JSON.stringify({ type: 'data', data: chunk.toString() }));
|
ws.send(JSON.stringify({type: 'data', data: chunk.toString()}));
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.on('close', () => {
|
stream.on('close', () => {
|
||||||
cleanupSSH();
|
cleanupSSH(connectionTimeout);
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.on('error', (err: Error) => {
|
stream.on('error', (err: Error) => {
|
||||||
logger.error('SSH stream error: ' + err.message);
|
logger.error('SSH stream error: ' + err.message);
|
||||||
ws.send(JSON.stringify({ type: 'error', message: 'SSH stream error: ' + err.message }));
|
|
||||||
|
const isConnectionError = err.message.includes('ECONNRESET') ||
|
||||||
|
err.message.includes('EPIPE') ||
|
||||||
|
err.message.includes('ENOTCONN') ||
|
||||||
|
err.message.includes('ETIMEDOUT');
|
||||||
|
|
||||||
|
if (isConnectionError) {
|
||||||
|
ws.send(JSON.stringify({type: 'disconnected', message: 'Connection lost'}));
|
||||||
|
} else {
|
||||||
|
ws.send(JSON.stringify({type: 'error', message: 'SSH stream error: ' + err.message}));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.send(JSON.stringify({ type: 'connected', message: 'SSH connected' }));
|
setupPingInterval();
|
||||||
|
|
||||||
|
ws.send(JSON.stringify({type: 'connected', message: 'SSH connected'}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
sshConn.on('error', (err: Error) => {
|
sshConn.on('error', (err: Error) => {
|
||||||
|
clearTimeout(connectionTimeout);
|
||||||
logger.error('SSH connection error: ' + err.message);
|
logger.error('SSH connection error: ' + err.message);
|
||||||
|
|
||||||
let errorMessage = 'SSH error: ' + err.message;
|
let errorMessage = 'SSH error: ' + err.message;
|
||||||
@@ -163,23 +190,30 @@ wss.on('connection', (ws: WebSocket) => {
|
|||||||
errorMessage = 'SSH error: Connection refused. The server may not be running or the port may be incorrect.';
|
errorMessage = 'SSH error: Connection refused. The server may not be running or the port may be incorrect.';
|
||||||
} else if (err.message.includes('ETIMEDOUT')) {
|
} else if (err.message.includes('ETIMEDOUT')) {
|
||||||
errorMessage = 'SSH error: Connection timed out. Check your network connection and server availability.';
|
errorMessage = 'SSH error: Connection timed out. Check your network connection and server availability.';
|
||||||
|
} else if (err.message.includes('ECONNRESET') || err.message.includes('EPIPE')) {
|
||||||
|
errorMessage = 'SSH error: Connection was reset. This may be due to network issues or server timeout.';
|
||||||
|
} else if (err.message.includes('authentication failed') || err.message.includes('Permission denied')) {
|
||||||
|
errorMessage = 'SSH error: Authentication failed. Please check your username and password/key.';
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.send(JSON.stringify({ type: 'error', message: errorMessage }));
|
ws.send(JSON.stringify({type: 'error', message: errorMessage}));
|
||||||
cleanupSSH();
|
cleanupSSH(connectionTimeout);
|
||||||
});
|
});
|
||||||
|
|
||||||
sshConn.on('close', () => {
|
sshConn.on('close', () => {
|
||||||
cleanupSSH();
|
clearTimeout(connectionTimeout);
|
||||||
|
cleanupSSH(connectionTimeout);
|
||||||
});
|
});
|
||||||
|
|
||||||
const connectConfig: any = {
|
const connectConfig: any = {
|
||||||
host: ip,
|
host: ip,
|
||||||
port,
|
port,
|
||||||
username,
|
username,
|
||||||
keepaliveInterval: 5000,
|
keepaliveInterval: 30000,
|
||||||
keepaliveCountMax: 10,
|
keepaliveCountMax: 3,
|
||||||
readyTimeout: 10000,
|
readyTimeout: 10000,
|
||||||
|
tcpKeepAlive: true,
|
||||||
|
tcpKeepAliveInitialDelay: 30000,
|
||||||
|
|
||||||
algorithms: {
|
algorithms: {
|
||||||
kex: [
|
kex: [
|
||||||
@@ -226,7 +260,7 @@ wss.on('connection', (ws: WebSocket) => {
|
|||||||
}
|
}
|
||||||
} else if (authType === 'key') {
|
} else if (authType === 'key') {
|
||||||
logger.error('SSH key authentication requested but no key provided');
|
logger.error('SSH key authentication requested but no key provided');
|
||||||
ws.send(JSON.stringify({ type: 'error', message: 'SSH key authentication requested but no key provided' }));
|
ws.send(JSON.stringify({type: 'error', message: 'SSH key authentication requested but no key provided'}));
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
connectConfig.password = password;
|
connectConfig.password = password;
|
||||||
@@ -238,11 +272,20 @@ wss.on('connection', (ws: WebSocket) => {
|
|||||||
function handleResize(data: { cols: number; rows: number }) {
|
function handleResize(data: { cols: number; rows: number }) {
|
||||||
if (sshStream && sshStream.setWindow) {
|
if (sshStream && sshStream.setWindow) {
|
||||||
sshStream.setWindow(data.rows, data.cols, data.rows, data.cols);
|
sshStream.setWindow(data.rows, data.cols, data.rows, data.cols);
|
||||||
ws.send(JSON.stringify({ type: 'resized', cols: data.cols, rows: data.rows }));
|
ws.send(JSON.stringify({type: 'resized', cols: data.cols, rows: data.rows}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupSSH() {
|
function cleanupSSH(timeoutId?: NodeJS.Timeout) {
|
||||||
|
if (timeoutId) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pingInterval) {
|
||||||
|
clearInterval(pingInterval);
|
||||||
|
pingInterval = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (sshStream) {
|
if (sshStream) {
|
||||||
try {
|
try {
|
||||||
sshStream.end();
|
sshStream.end();
|
||||||
@@ -261,4 +304,17 @@ wss.on('connection', (ws: WebSocket) => {
|
|||||||
sshConn = null;
|
sshConn = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
function setupPingInterval() {
|
||||||
|
pingInterval = setInterval(() => {
|
||||||
|
if (sshConn && sshStream) {
|
||||||
|
try {
|
||||||
|
sshStream.write('\x00');
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.error('SSH keepalive failed: ' + e.message);
|
||||||
|
cleanupSSH();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 60000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -336,7 +336,6 @@ function handleDisconnect(tunnelName: string, tunnelConfig: TunnelConfig | null,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (retryExhaustedTunnels.has(tunnelName)) {
|
if (retryExhaustedTunnels.has(tunnelName)) {
|
||||||
broadcastTunnelStatus(tunnelName, {
|
broadcastTunnelStatus(tunnelName, {
|
||||||
connected: false,
|
connected: false,
|
||||||
@@ -571,6 +570,10 @@ function verifyTunnelConnection(tunnelName: string, tunnelConfig: TunnelConfig,
|
|||||||
port: tunnelConfig.sourceSSHPort,
|
port: tunnelConfig.sourceSSHPort,
|
||||||
username: tunnelConfig.sourceUsername,
|
username: tunnelConfig.sourceUsername,
|
||||||
readyTimeout: 10000,
|
readyTimeout: 10000,
|
||||||
|
keepaliveInterval: 30000,
|
||||||
|
keepaliveCountMax: 3,
|
||||||
|
tcpKeepAlive: true,
|
||||||
|
tcpKeepAliveInitialDelay: 30000,
|
||||||
algorithms: {
|
algorithms: {
|
||||||
kex: [
|
kex: [
|
||||||
'diffie-hellman-group14-sha256',
|
'diffie-hellman-group14-sha256',
|
||||||
@@ -692,7 +695,7 @@ function setupPingInterval(tunnelName: string, tunnelConfig: TunnelConfig): void
|
|||||||
handleDisconnect(tunnelName, tunnelConfig, !manualDisconnects.has(tunnelName));
|
handleDisconnect(tunnelName, tunnelConfig, !manualDisconnects.has(tunnelName));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, 30000);
|
}, 60000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectSSHTunnel(tunnelConfig: TunnelConfig, retryAttempt = 0): void {
|
function connectSSHTunnel(tunnelConfig: TunnelConfig, retryAttempt = 0): void {
|
||||||
@@ -891,11 +894,9 @@ function connectSSHTunnel(tunnelConfig: TunnelConfig, retryAttempt = 0): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
stream.stdout?.on("data", (data: Buffer) => {
|
stream.stdout?.on("data", (data: Buffer) => {
|
||||||
// Ignore stdout data
|
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.on("error", (err: Error) => {
|
stream.on("error", (err: Error) => {
|
||||||
// Ignore stream errors
|
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.stderr.on("data", (data) => {
|
stream.stderr.on("data", (data) => {
|
||||||
@@ -909,10 +910,11 @@ function connectSSHTunnel(tunnelConfig: TunnelConfig, retryAttempt = 0): void {
|
|||||||
host: tunnelConfig.sourceIP,
|
host: tunnelConfig.sourceIP,
|
||||||
port: tunnelConfig.sourceSSHPort,
|
port: tunnelConfig.sourceSSHPort,
|
||||||
username: tunnelConfig.sourceUsername,
|
username: tunnelConfig.sourceUsername,
|
||||||
keepaliveInterval: 5000,
|
keepaliveInterval: 30000,
|
||||||
keepaliveCountMax: 10,
|
keepaliveCountMax: 3,
|
||||||
readyTimeout: 10000,
|
readyTimeout: 10000,
|
||||||
tcpKeepAlive: true,
|
tcpKeepAlive: true,
|
||||||
|
tcpKeepAliveInitialDelay: 30000,
|
||||||
algorithms: {
|
algorithms: {
|
||||||
kex: [
|
kex: [
|
||||||
'diffie-hellman-group14-sha256',
|
'diffie-hellman-group14-sha256',
|
||||||
@@ -1025,10 +1027,11 @@ function killRemoteTunnelByMarker(tunnelConfig: TunnelConfig, tunnelName: string
|
|||||||
host: tunnelConfig.sourceIP,
|
host: tunnelConfig.sourceIP,
|
||||||
port: tunnelConfig.sourceSSHPort,
|
port: tunnelConfig.sourceSSHPort,
|
||||||
username: tunnelConfig.sourceUsername,
|
username: tunnelConfig.sourceUsername,
|
||||||
keepaliveInterval: 5000,
|
keepaliveInterval: 30000,
|
||||||
keepaliveCountMax: 10,
|
keepaliveCountMax: 3,
|
||||||
readyTimeout: 10000,
|
readyTimeout: 10000,
|
||||||
tcpKeepAlive: true,
|
tcpKeepAlive: true,
|
||||||
|
tcpKeepAliveInitialDelay: 30000,
|
||||||
algorithms: {
|
algorithms: {
|
||||||
kex: [
|
kex: [
|
||||||
'diffie-hellman-group14-sha256',
|
'diffie-hellman-group14-sha256',
|
||||||
@@ -1087,8 +1090,10 @@ function killRemoteTunnelByMarker(tunnelConfig: TunnelConfig, tunnelName: string
|
|||||||
conn.end();
|
conn.end();
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
stream.on('data', () => {});
|
stream.on('data', () => {
|
||||||
stream.stderr.on('data', () => {});
|
});
|
||||||
|
stream.stderr.on('data', () => {
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
conn.on('error', (err) => {
|
conn.on('error', (err) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user