Resume session upon disconnect
This commit is contained in:
parent
9c0ba04b31
commit
89d9429648
12 changed files with 252 additions and 182 deletions
BIN
app/bun.lockb
Executable file → Normal file
BIN
app/bun.lockb
Executable file → Normal file
Binary file not shown.
|
@ -18,6 +18,7 @@
|
|||
<div id="dropupContent" class="dropup-content">
|
||||
<a id="logBtn"><i class="fas fa-clipboard fa-fw"></i> Start Log</a>
|
||||
<a id="downloadLogBtn"><i class="fas fa-download fa-fw"></i> Download Log</a>
|
||||
<a id="restartBtn"><i class="fas fa-rotate-right fa-fw"></i> Restart Session</a>
|
||||
<a id="reauthBtn" style="display: none;"><i class="fas fa-key fa-fw"></i> Switch User</a>
|
||||
<a id="credentialsBtn" style="display: none;"><i class="fas fa-key fa-fw"></i> Credentials</a>
|
||||
</div>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -341,7 +341,7 @@ body, html {
|
|||
position: absolute;
|
||||
background-color: #f1f1f1;
|
||||
font-size: 16px;
|
||||
min-width: 160px;
|
||||
min-width: 180px;
|
||||
bottom: 18px;
|
||||
z-index: 101;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<div id="dropupContent" class="dropup-content">
|
||||
<a id="logBtn"><i class="fas fa-clipboard fa-fw"></i> Start Log</a>
|
||||
<a id="downloadLogBtn"><i class="fas fa-download fa-fw"></i> Download Log</a>
|
||||
<a id="restartBtn"><i class="fas fa-rotate-right fa-fw"></i> Restart Session</a>
|
||||
<a id="reauthBtn" style="display: none;"><i class="fas fa-key fa-fw"></i> Switch User</a>
|
||||
<a id="credentialsBtn" style="display: none;"><i class="fas fa-key fa-fw"></i> Credentials</a>
|
||||
</div>
|
||||
|
|
|
@ -122,7 +122,7 @@ body, html {
|
|||
position: absolute;
|
||||
background-color: #f1f1f1;
|
||||
font-size: 16px;
|
||||
min-width: 160px;
|
||||
min-width: 180px;
|
||||
bottom: 18px;
|
||||
z-index: 101;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@ import { io } from 'socket.io-client';
|
|||
import { Terminal } from '@xterm/xterm';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import { library, dom } from '@fortawesome/fontawesome-svg-core';
|
||||
import { faBars, faClipboard, faDownload, faKey, faCog } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faBars, faClipboard, faDownload, faRotateRight, faKey, faCog } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
library.add(faBars, faClipboard, faDownload, faKey, faCog);
|
||||
library.add(faBars, faClipboard, faDownload, faRotateRight, faKey, faCog);
|
||||
dom.watch();
|
||||
|
||||
const debug = require('debug')('WebSSH2');
|
||||
|
@ -35,6 +35,7 @@ const term = new Terminal();
|
|||
const logBtn = document.getElementById('logBtn');
|
||||
const credentialsBtn = document.getElementById('credentialsBtn');
|
||||
const reauthBtn = document.getElementById('reauthBtn');
|
||||
const restartBtn = document.getElementById('restartBtn');
|
||||
const downloadLogBtn = document.getElementById('downloadLogBtn');
|
||||
const status = document.getElementById('status');
|
||||
const header = document.getElementById('header');
|
||||
|
@ -52,20 +53,25 @@ const socket = io({
|
|||
});
|
||||
|
||||
// reauthenticate
|
||||
function reauthSession () { // eslint-disable-line
|
||||
function reauthSession() { // eslint-disable-line
|
||||
debug('re-authenticating');
|
||||
socket.emit('control', 'reauth');
|
||||
window.location.href = '/ssh/reauth';
|
||||
return false;
|
||||
}
|
||||
|
||||
function restartSession() { // eslint-disable-line
|
||||
debug('restarting');
|
||||
socket.emit('control', 'reauth');
|
||||
return false;
|
||||
}
|
||||
|
||||
// cross browser method to "download" an element to the local system
|
||||
// used for our client-side logging feature
|
||||
function downloadLog () { // eslint-disable-line
|
||||
function downloadLog() { // eslint-disable-line
|
||||
if (loggedData === true) {
|
||||
myFile = `WebSSH2-${logDate.getFullYear()}${
|
||||
logDate.getMonth() + 1
|
||||
}${logDate.getDate()}_${logDate.getHours()}${logDate.getMinutes()}${logDate.getSeconds()}.log`;
|
||||
myFile = `WebSSH2-${logDate.getFullYear()}${logDate.getMonth() + 1
|
||||
}${logDate.getDate()}_${logDate.getHours()}${logDate.getMinutes()}${logDate.getSeconds()}.log`;
|
||||
// regex should eliminate escape sequences from being logged.
|
||||
const blob = new Blob(
|
||||
[
|
||||
|
@ -91,15 +97,14 @@ function downloadLog () { // eslint-disable-line
|
|||
}
|
||||
// Set variable to toggle log data from client/server to a varialble
|
||||
// for later download
|
||||
function toggleLog () { // eslint-disable-line
|
||||
function toggleLog() { // eslint-disable-line
|
||||
if (sessionLogEnable === true) {
|
||||
sessionLogEnable = false;
|
||||
loggedData = true;
|
||||
logBtn.innerHTML = '<i class="fas fa-clipboard fa-fw"></i> Start Log';
|
||||
currentDate = new Date();
|
||||
sessionLog = `${sessionLog}\r\n\r\nLog End for ${sessionFooter}: ${currentDate.getFullYear()}/${
|
||||
currentDate.getMonth() + 1
|
||||
}/${currentDate.getDate()} @ ${currentDate.getHours()}:${currentDate.getMinutes()}:${currentDate.getSeconds()}\r\n`;
|
||||
sessionLog = `${sessionLog}\r\n\r\nLog End for ${sessionFooter}: ${currentDate.getFullYear()}/${currentDate.getMonth() + 1
|
||||
}/${currentDate.getDate()} @ ${currentDate.getHours()}:${currentDate.getMinutes()}:${currentDate.getSeconds()}\r\n`;
|
||||
logDate = currentDate;
|
||||
term.focus();
|
||||
return false;
|
||||
|
@ -110,16 +115,15 @@ function toggleLog () { // eslint-disable-line
|
|||
downloadLogBtn.style.color = '#000';
|
||||
downloadLogBtn.addEventListener('click', downloadLog);
|
||||
currentDate = new Date();
|
||||
sessionLog = `Log Start for ${sessionFooter}: ${currentDate.getFullYear()}/${
|
||||
currentDate.getMonth() + 1
|
||||
}/${currentDate.getDate()} @ ${currentDate.getHours()}:${currentDate.getMinutes()}:${currentDate.getSeconds()}\r\n\r\n`;
|
||||
sessionLog = `Log Start for ${sessionFooter}: ${currentDate.getFullYear()}/${currentDate.getMonth() + 1
|
||||
}/${currentDate.getDate()} @ ${currentDate.getHours()}:${currentDate.getMinutes()}:${currentDate.getSeconds()}\r\n\r\n`;
|
||||
logDate = currentDate;
|
||||
term.focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// replay password to server, requires
|
||||
function replayCredentials () { // eslint-disable-line
|
||||
function replayCredentials() { // eslint-disable-line
|
||||
socket.emit('control', 'replayCredentials');
|
||||
debug(`control: replayCredentials`);
|
||||
term.focus();
|
||||
|
@ -130,6 +134,7 @@ function replayCredentials () { // eslint-disable-line
|
|||
// when dom is changed, listeners are abandonded
|
||||
function drawMenu() {
|
||||
logBtn.addEventListener('click', toggleLog);
|
||||
restartBtn.addEventListener('click', restartSession)
|
||||
if (allowreauth) {
|
||||
reauthBtn.addEventListener('click', reauthSession);
|
||||
reauthBtn.style.display = 'block';
|
||||
|
@ -144,19 +149,27 @@ function drawMenu() {
|
|||
}
|
||||
}
|
||||
|
||||
function resizeScreen() {
|
||||
function resizeScreen(warmup = false) {
|
||||
fitAddon.fit();
|
||||
if (warmup) {
|
||||
socket.emit('resize', { cols: term.cols - 1, rows: term.rows });
|
||||
}
|
||||
socket.emit('resize', { cols: term.cols, rows: term.rows });
|
||||
debug(`resize: ${JSON.stringify({ cols: term.cols, rows: term.rows })}`);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', resizeScreen, false);
|
||||
window.addEventListener('resize', () => resizeScreen(), false);
|
||||
|
||||
var hasResize = false;
|
||||
term.onData((data) => {
|
||||
socket.emit('data', data);
|
||||
});
|
||||
|
||||
socket.on('data', (data: string | Uint8Array) => {
|
||||
if (!hasResize) {
|
||||
hasResize = true;
|
||||
setTimeout(() => resizeScreen(true), 1000);
|
||||
}
|
||||
term.write(data);
|
||||
if (sessionLogEnable) {
|
||||
sessionLog += data;
|
||||
|
@ -252,7 +265,13 @@ socket.on('disconnect', (err: any) => {
|
|||
status.style.backgroundColor = 'red';
|
||||
status.innerHTML = `WEBSOCKET SERVER DISCONNECTED: ${err}`;
|
||||
}
|
||||
socket.io.reconnection(false);
|
||||
var i = setInterval(() => {
|
||||
if (!socket.connected) {
|
||||
socket.connect()
|
||||
} else {
|
||||
clearInterval(i);
|
||||
}
|
||||
}, 3000);
|
||||
countdown.classList.remove('active');
|
||||
});
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"user": {
|
||||
"name": null,
|
||||
"password": null,
|
||||
"privatekey": null,
|
||||
"privateKey": null,
|
||||
"overridebasic": false
|
||||
},
|
||||
"ssh": {
|
||||
|
|
|
@ -43,7 +43,7 @@ const configDefault = {
|
|||
user: {
|
||||
name: null,
|
||||
password: null,
|
||||
privatekey: null,
|
||||
privateKey: null,
|
||||
overridebasic: false,
|
||||
},
|
||||
ssh: {
|
||||
|
|
|
@ -69,6 +69,161 @@ async function checkSubnet(socket) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Map<string, {
|
||||
conn: SSH;
|
||||
isLogin: () => boolean;
|
||||
changeSocket: (newSocket: any) => void;
|
||||
}>}
|
||||
*/
|
||||
const sshMap = new Map();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import ("socket.io-client").Socket} socket
|
||||
* @returns
|
||||
*/
|
||||
function setupNewConnection(socket) {
|
||||
const conn = new SSH();
|
||||
let login = false;
|
||||
let stream;
|
||||
|
||||
conn.on('banner', (data) => {
|
||||
// need to convert to cr/lf for proper formatting
|
||||
socket.emit('data', data.replace(/\r?\n/g, '\r\n').toString('utf-8'));
|
||||
});
|
||||
|
||||
function handshake() {
|
||||
socket.emit('setTerminalOpts', socket.request.session.ssh.terminal);
|
||||
socket.emit('menu');
|
||||
socket.emit('allowreauth', socket.request.session.ssh.allowreauth);
|
||||
socket.emit('title', `ssh://${socket.request.session.ssh.host}`);
|
||||
if (socket.request.session.ssh.header.background)
|
||||
socket.emit('headerBackground', socket.request.session.ssh.header.background);
|
||||
if (socket.request.session.ssh.header.name)
|
||||
socket.emit('header', socket.request.session.ssh.header.name);
|
||||
socket.emit(
|
||||
'footer',
|
||||
`ssh://${socket.request.session.username}@${socket.request.session.ssh.host}:${socket.request.session.ssh.port}`
|
||||
);
|
||||
|
||||
socket.on('control', (controlData) => {
|
||||
if (!stream) return;
|
||||
if (controlData === 'replayCredentials' && socket.request.session.ssh.allowreplay) {
|
||||
stream.write(`${socket.request.session.userpassword}\n`);
|
||||
}
|
||||
if (controlData === 'reauth' && socket.request.session.username && login === true) {
|
||||
auditLog(
|
||||
socket,
|
||||
`LOGOUT user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host}:${socket.request.session.ssh.port}`
|
||||
);
|
||||
login = false;
|
||||
socket.disconnect(true);
|
||||
conn.end()
|
||||
}
|
||||
webssh2debug(socket, `SOCKET CONTROL: ${controlData}`);
|
||||
});
|
||||
socket.on('resize', (data) => {
|
||||
if (!stream) return;
|
||||
stream.setWindow(data.rows, data.cols);
|
||||
webssh2debug(socket, `SOCKET RESIZE: ${JSON.stringify([data.rows, data.cols])}`);
|
||||
});
|
||||
socket.on('data', (data) => {
|
||||
if (!stream) return;
|
||||
stream.write(data);
|
||||
});
|
||||
}
|
||||
conn.on('handshake', handshake);
|
||||
|
||||
conn.on('ready', () => {
|
||||
webssh2debug(
|
||||
socket,
|
||||
`CONN READY: LOGIN: user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host} port=${socket.request.session.ssh.port} allowreplay=${socket.request.session.ssh.allowreplay} term=${socket.request.session.ssh.term}`
|
||||
);
|
||||
auditLog(
|
||||
socket,
|
||||
`LOGIN user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host}:${socket.request.session.ssh.port}`
|
||||
);
|
||||
login = true;
|
||||
socket.emit('status', 'SSH CONNECTION ESTABLISHED');
|
||||
socket.emit('statusBackground', 'green');
|
||||
socket.emit('allowreplay', socket.request.session.ssh.allowreplay);
|
||||
const { term, cols, rows } = socket.request.session.ssh;
|
||||
conn.shell({ term, cols, rows }, (err, s) => {
|
||||
if (err) {
|
||||
logError(socket, `EXEC ERROR`, err);
|
||||
conn.end();
|
||||
socket.disconnect(true);
|
||||
return;
|
||||
}
|
||||
socket.once('disconnect', (reason) => {
|
||||
webssh2debug(socket, `CLIENT SOCKET DISCONNECT: ${util.inspect(reason)}`);
|
||||
});
|
||||
socket.on('error', (errMsg) => {
|
||||
if (!socket) return;
|
||||
webssh2debug(socket, `SOCKET ERROR: ${errMsg}`);
|
||||
logError(socket, 'SOCKET ERROR', errMsg);
|
||||
conn.end();
|
||||
login = false;
|
||||
socket.disconnect(true);
|
||||
});
|
||||
stream = s;
|
||||
stream.on('data', (data) => {
|
||||
if (!socket) return;
|
||||
socket.emit('data', data.toString('utf-8'));
|
||||
});
|
||||
stream.on('close', (code, signal) => {
|
||||
if (!socket) return;
|
||||
webssh2debug(socket, `STREAM CLOSE: ${util.inspect([code, signal])}`);
|
||||
if (socket.request.session?.username) {
|
||||
auditLog(
|
||||
socket,
|
||||
`LOGOUT user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host}:${socket.request.session.ssh.port}`
|
||||
);
|
||||
}
|
||||
if (code !== 0 && typeof code !== 'undefined')
|
||||
logError(socket, 'STREAM CLOSE', util.inspect({ message: [code, signal] }));
|
||||
socket.disconnect(true);
|
||||
});
|
||||
stream.stderr.on('data', (data) => {
|
||||
console.error(`STDERR: ${data}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
conn.on('end', (err) => {
|
||||
if (err) logError(socket, 'CONN END BY HOST', err);
|
||||
webssh2debug(socket, 'CONN END BY HOST');
|
||||
socket.disconnect(true);
|
||||
});
|
||||
conn.on('close', (err) => {
|
||||
if (err) logError(socket, 'CONN CLOSE', err);
|
||||
webssh2debug(socket, 'CONN CLOSE');
|
||||
socket.disconnect(true);
|
||||
});
|
||||
conn.on('error', (err) => connError(socket, err));
|
||||
|
||||
conn.on('keyboard-interactive', (_name, _instructions, _instructionsLang, _prompts, finish) => {
|
||||
webssh2debug(socket, 'CONN keyboard-interactive');
|
||||
finish([socket.request.session.userpassword]);
|
||||
});
|
||||
|
||||
return {
|
||||
conn, isLogin: () => login, changeSocket: (newSocket) => {
|
||||
if (!socket.disconnected) {
|
||||
socket.disconnect();
|
||||
}
|
||||
socket = newSocket;
|
||||
|
||||
// to display after resuming connection
|
||||
if (login && stream) {
|
||||
handshake();
|
||||
stream.write("\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// public
|
||||
module.exports = function appSocket(socket) {
|
||||
let login = false;
|
||||
|
@ -84,158 +239,52 @@ module.exports = function appSocket(socket) {
|
|||
}
|
||||
});
|
||||
|
||||
async function setupConnection() {
|
||||
// if websocket connection arrives without an express session, kill it
|
||||
if (!socket.request.session) {
|
||||
socket.emit('401 UNAUTHORIZED');
|
||||
webssh2debug(socket, 'SOCKET: No Express Session / REJECTED');
|
||||
socket.disconnect(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// If configured, check that requsted host is in a permitted subnet
|
||||
if (socket.request.session?.ssh?.allowedSubnets?.length > 0) {
|
||||
checkSubnet(socket);
|
||||
}
|
||||
|
||||
const conn = new SSH();
|
||||
|
||||
conn.on('banner', (data) => {
|
||||
// need to convert to cr/lf for proper formatting
|
||||
socket.emit('data', data.replace(/\r?\n/g, '\r\n').toString('utf-8'));
|
||||
});
|
||||
|
||||
conn.on('handshake', () => {
|
||||
socket.emit('setTerminalOpts', socket.request.session.ssh.terminal);
|
||||
socket.emit('menu');
|
||||
socket.emit('allowreauth', socket.request.session.ssh.allowreauth);
|
||||
socket.emit('title', `ssh://${socket.request.session.ssh.host}`);
|
||||
if (socket.request.session.ssh.header.background)
|
||||
socket.emit('headerBackground', socket.request.session.ssh.header.background);
|
||||
if (socket.request.session.ssh.header.name)
|
||||
socket.emit('header', socket.request.session.ssh.header.name);
|
||||
socket.emit(
|
||||
'footer',
|
||||
`ssh://${socket.request.session.username}@${socket.request.session.ssh.host}:${socket.request.session.ssh.port}`
|
||||
);
|
||||
});
|
||||
|
||||
conn.on('ready', () => {
|
||||
webssh2debug(
|
||||
socket,
|
||||
`CONN READY: LOGIN: user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host} port=${socket.request.session.ssh.port} allowreplay=${socket.request.session.ssh.allowreplay} term=${socket.request.session.ssh.term}`
|
||||
);
|
||||
auditLog(
|
||||
socket,
|
||||
`LOGIN user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host}:${socket.request.session.ssh.port}`
|
||||
);
|
||||
login = true;
|
||||
socket.emit('status', 'SSH CONNECTION ESTABLISHED');
|
||||
socket.emit('statusBackground', 'green');
|
||||
socket.emit('allowreplay', socket.request.session.ssh.allowreplay);
|
||||
const { term, cols, rows } = socket.request.session.ssh;
|
||||
conn.shell({ term, cols, rows }, (err, stream) => {
|
||||
if (err) {
|
||||
logError(socket, `EXEC ERROR`, err);
|
||||
conn.end();
|
||||
socket.disconnect(true);
|
||||
return;
|
||||
}
|
||||
socket.once('disconnect', (reason) => {
|
||||
webssh2debug(socket, `CLIENT SOCKET DISCONNECT: ${util.inspect(reason)}`);
|
||||
conn.end();
|
||||
socket.request.session.destroy();
|
||||
});
|
||||
socket.on('error', (errMsg) => {
|
||||
webssh2debug(socket, `SOCKET ERROR: ${errMsg}`);
|
||||
logError(socket, 'SOCKET ERROR', errMsg);
|
||||
conn.end();
|
||||
socket.disconnect(true);
|
||||
});
|
||||
socket.on('control', (controlData) => {
|
||||
if (controlData === 'replayCredentials' && socket.request.session.ssh.allowreplay) {
|
||||
stream.write(`${socket.request.session.userpassword}\n`);
|
||||
}
|
||||
if (controlData === 'reauth' && socket.request.session.username && login === true) {
|
||||
auditLog(
|
||||
socket,
|
||||
`LOGOUT user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host}:${socket.request.session.ssh.port}`
|
||||
);
|
||||
login = false;
|
||||
conn.end();
|
||||
socket.disconnect(true);
|
||||
}
|
||||
webssh2debug(socket, `SOCKET CONTROL: ${controlData}`);
|
||||
});
|
||||
socket.on('resize', (data) => {
|
||||
stream.setWindow(data.rows, data.cols);
|
||||
webssh2debug(socket, `SOCKET RESIZE: ${JSON.stringify([data.rows, data.cols])}`);
|
||||
});
|
||||
socket.on('data', (data) => {
|
||||
stream.write(data);
|
||||
});
|
||||
stream.on('data', (data) => {
|
||||
socket.emit('data', data.toString('utf-8'));
|
||||
});
|
||||
stream.on('close', (code, signal) => {
|
||||
webssh2debug(socket, `STREAM CLOSE: ${util.inspect([code, signal])}`);
|
||||
if (socket.request.session?.username && login === true) {
|
||||
auditLog(
|
||||
socket,
|
||||
`LOGOUT user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host}:${socket.request.session.ssh.port}`
|
||||
);
|
||||
login = false;
|
||||
}
|
||||
if (code !== 0 && typeof code !== 'undefined')
|
||||
logError(socket, 'STREAM CLOSE', util.inspect({ message: [code, signal] }));
|
||||
socket.disconnect(true);
|
||||
conn.end();
|
||||
});
|
||||
stream.stderr.on('data', (data) => {
|
||||
console.error(`STDERR: ${data}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
conn.on('end', (err) => {
|
||||
if (err) logError(socket, 'CONN END BY HOST', err);
|
||||
webssh2debug(socket, 'CONN END BY HOST');
|
||||
socket.disconnect(true);
|
||||
});
|
||||
conn.on('close', (err) => {
|
||||
if (err) logError(socket, 'CONN CLOSE', err);
|
||||
webssh2debug(socket, 'CONN CLOSE');
|
||||
socket.disconnect(true);
|
||||
});
|
||||
conn.on('error', (err) => connError(socket, err));
|
||||
|
||||
conn.on('keyboard-interactive', (_name, _instructions, _instructionsLang, _prompts, finish) => {
|
||||
webssh2debug(socket, 'CONN keyboard-interactive');
|
||||
finish([socket.request.session.userpassword]);
|
||||
});
|
||||
if (
|
||||
socket.request.session.username &&
|
||||
(socket.request.session.userpassword || socket.request.session.privatekey) &&
|
||||
socket.request.session.ssh
|
||||
) {
|
||||
// console.log('hostkeys: ' + hostkeys[0].[0])
|
||||
const { ssh } = socket.request.session;
|
||||
ssh.username = socket.request.session.username;
|
||||
ssh.password = socket.request.session.userpassword;
|
||||
ssh.tryKeyboard = true;
|
||||
ssh.debug = debug('ssh2');
|
||||
conn.connect(ssh);
|
||||
} else {
|
||||
webssh2debug(
|
||||
socket,
|
||||
`CONN CONNECT: Attempt to connect without session.username/password or session varialbles defined, potentially previously abandoned client session. disconnecting websocket client.\r\nHandshake information: \r\n ${util.inspect(
|
||||
socket.handshake
|
||||
)}`
|
||||
);
|
||||
socket.emit('ssherror', 'WEBSOCKET ERROR - Refresh the browser and try again');
|
||||
socket.request.session.destroy();
|
||||
socket.disconnect(true);
|
||||
}
|
||||
// if websocket connection arrives without an express session, kill it
|
||||
if (!socket.request.session) {
|
||||
socket.emit('401 UNAUTHORIZED');
|
||||
webssh2debug(socket, 'SOCKET: No Express Session / REJECTED');
|
||||
socket.disconnect(true);
|
||||
return;
|
||||
}
|
||||
setupConnection();
|
||||
|
||||
if (
|
||||
!socket.request.session.username ||
|
||||
!(socket.request.session.userpassword || socket.request.session.privateKey) ||
|
||||
!socket.request.session.ssh
|
||||
) {
|
||||
webssh2debug(
|
||||
socket,
|
||||
`CONN CONNECT: Attempt to connect without session.username/password or session varialbles defined, potentially previously abandoned client session. disconnecting websocket client.\r\nHandshake information: \r\n ${util.inspect(
|
||||
socket.handshake
|
||||
)}`
|
||||
);
|
||||
socket.emit('ssherror', 'WEBSOCKET ERROR - Refresh the browser and try again');
|
||||
socket.request.session.destroy();
|
||||
socket.disconnect(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// If configured, check that requsted host is in a permitted subnet
|
||||
if (socket.request.session?.ssh?.allowedSubnets?.length > 0) {
|
||||
checkSubnet(socket);
|
||||
}
|
||||
|
||||
var connMap = sshMap.get(socket.request.session.username);
|
||||
if (!connMap) {
|
||||
connMap = setupNewConnection(socket);
|
||||
sshMap.set(socket.request.session.username, connMap);
|
||||
} else {
|
||||
connMap.changeSocket(socket);
|
||||
}
|
||||
const { conn, isLogin } = connMap;
|
||||
|
||||
|
||||
const { ssh } = socket.request.session;
|
||||
ssh.username = socket.request.session.username;
|
||||
ssh.password = socket.request.session.userpassword;
|
||||
ssh.tryKeyboard = true;
|
||||
ssh.debug = debug('ssh2');
|
||||
if (!isLogin())
|
||||
conn.connect(ssh);
|
||||
|
||||
};
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
const debug = require('debug')('WebSSH2');
|
||||
const Auth = require('basic-auth');
|
||||
|
||||
let defaultCredentials = { username: null, password: null, privatekey: null };
|
||||
let defaultCredentials = { username: null, password: null, privateKey: null };
|
||||
|
||||
exports.setDefaultCredentials = function setDefaultCredentials({
|
||||
name: username,
|
||||
password,
|
||||
privatekey,
|
||||
privateKey,
|
||||
overridebasic,
|
||||
}) {
|
||||
defaultCredentials = { username, password, privatekey, overridebasic };
|
||||
defaultCredentials = { username, password, privateKey, overridebasic };
|
||||
};
|
||||
|
||||
exports.basicAuth = function basicAuth(req, res, next) {
|
||||
|
@ -21,7 +21,7 @@ exports.basicAuth = function basicAuth(req, res, next) {
|
|||
// If Authorize: Basic header exists and the password isn't blank
|
||||
// AND config.user.overridebasic is false, extract basic credentials
|
||||
// from client]
|
||||
const { username, password, privatekey, overridebasic } = defaultCredentials;
|
||||
const { username, password, privateKey, overridebasic } = defaultCredentials;
|
||||
if (myAuth && myAuth.pass !== '' && !overridebasic) {
|
||||
req.session.username = myAuth.name;
|
||||
req.session.userpassword = myAuth.pass;
|
||||
|
@ -29,9 +29,9 @@ exports.basicAuth = function basicAuth(req, res, next) {
|
|||
} else {
|
||||
req.session.username = username;
|
||||
req.session.userpassword = password;
|
||||
req.session.privatekey = privatekey;
|
||||
req.session.privateKey = privateKey;
|
||||
}
|
||||
if (!req.session.userpassword && !req.session.privatekey) {
|
||||
if (!req.session.userpassword && !req.session.privateKey) {
|
||||
res.statusCode = 401;
|
||||
debug('basicAuth credential request (401)');
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="WebSSH"');
|
||||
|
|
0
bun.lockb
Executable file → Normal file
0
bun.lockb
Executable file → Normal file
Loading…
Reference in a new issue