cp dines
This commit is contained in:
parent
b332d35bb1
commit
5d62a0bc1d
11 changed files with 319 additions and 597 deletions
|
@ -10,21 +10,9 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="box">
|
||||
<div id="header"></div>
|
||||
<div id="terminal-container" class="terminal"></div>
|
||||
<div id="bottomdiv">
|
||||
<div class="dropup" id="menu">
|
||||
<i class="fas fa-bars fa-fw"></i> Menu
|
||||
<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="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>
|
||||
</div>
|
||||
<div id="footer"></div>
|
||||
<div id="status"></div>
|
||||
<div id="countdown"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/ssh/webssh2.bundle.js" defer></script>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -10,21 +10,9 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="box">
|
||||
<div id="header"></div>
|
||||
<div id="terminal-container" class="terminal"></div>
|
||||
<div id="bottomdiv">
|
||||
<div class="dropup" id="menu">
|
||||
<i class="fas fa-bars fa-fw"></i> Menu
|
||||
<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="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>
|
||||
</div>
|
||||
<div id="footer"></div>
|
||||
<div id="status"></div>
|
||||
<div id="countdown"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/ssh/webssh2.bundle.js" defer></script>
|
||||
|
|
|
@ -15,8 +15,6 @@ require('../css/style.css');
|
|||
/* global Blob, logBtn, credentialsBtn, reauthBtn, downloadLogBtn */ // eslint-disable-line
|
||||
let sessionLogEnable = false;
|
||||
let loggedData = false;
|
||||
let allowreplay = false;
|
||||
let allowreauth = false;
|
||||
let sessionLog: string;
|
||||
let sessionFooter: any;
|
||||
let logDate: {
|
||||
|
@ -33,13 +31,8 @@ let errorExists: boolean;
|
|||
const term = new Terminal();
|
||||
// DOM properties
|
||||
const logBtn = document.getElementById('logBtn');
|
||||
const credentialsBtn = document.getElementById('credentialsBtn');
|
||||
const reauthBtn = document.getElementById('reauthBtn');
|
||||
const downloadLogBtn = document.getElementById('downloadLogBtn');
|
||||
const status = document.getElementById('status');
|
||||
const header = document.getElementById('header');
|
||||
const footer = document.getElementById('footer');
|
||||
const countdown = document.getElementById('countdown');
|
||||
const fitAddon = new FitAddon();
|
||||
const terminalContainer = document.getElementById('terminal-container');
|
||||
term.loadAddon(fitAddon);
|
||||
|
@ -49,10 +42,12 @@ fitAddon.fit();
|
|||
|
||||
const socket = io({
|
||||
path: '/ssh/socket.io',
|
||||
transports: ['websocket'],
|
||||
});
|
||||
|
||||
// reauthenticate
|
||||
function reauthSession () { // eslint-disable-line
|
||||
function reauthSession() {
|
||||
// eslint-disable-line
|
||||
debug('re-authenticating');
|
||||
socket.emit('control', 'reauth');
|
||||
window.location.href = '/ssh/reauth';
|
||||
|
@ -61,7 +56,8 @@ function reauthSession () { // eslint-disable-line
|
|||
|
||||
// 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
|
||||
|
@ -72,13 +68,13 @@ function downloadLog () { // eslint-disable-line
|
|||
sessionLog.replace(
|
||||
// eslint-disable-next-line no-control-regex
|
||||
/[\u001b\u009b][[\]()#;?]*(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-ORZcf-nqry=><;]/g,
|
||||
''
|
||||
'',
|
||||
),
|
||||
],
|
||||
{
|
||||
// eslint-disable-line no-control-regex
|
||||
type: 'text/plain',
|
||||
}
|
||||
},
|
||||
);
|
||||
const elem = window.document.createElement('a');
|
||||
elem.href = window.URL.createObjectURL(blob);
|
||||
|
@ -91,7 +87,8 @@ 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;
|
||||
|
@ -119,31 +116,14 @@ function toggleLog () { // eslint-disable-line
|
|||
}
|
||||
|
||||
// replay password to server, requires
|
||||
function replayCredentials () { // eslint-disable-line
|
||||
function replayCredentials() {
|
||||
// eslint-disable-line
|
||||
socket.emit('control', 'replayCredentials');
|
||||
debug(`control: replayCredentials`);
|
||||
term.focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// draw/re-draw menu and reattach listeners
|
||||
// when dom is changed, listeners are abandonded
|
||||
function drawMenu() {
|
||||
logBtn.addEventListener('click', toggleLog);
|
||||
if (allowreauth) {
|
||||
reauthBtn.addEventListener('click', reauthSession);
|
||||
reauthBtn.style.display = 'block';
|
||||
}
|
||||
if (allowreplay) {
|
||||
credentialsBtn.addEventListener('click', replayCredentials);
|
||||
credentialsBtn.style.display = 'block';
|
||||
}
|
||||
if (loggedData) {
|
||||
downloadLogBtn.addEventListener('click', downloadLog);
|
||||
downloadLogBtn.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
function resizeScreen() {
|
||||
fitAddon.fit();
|
||||
socket.emit('resize', { cols: term.cols, rows: term.rows });
|
||||
|
@ -181,71 +161,59 @@ socket.on(
|
|||
lineHeight: number;
|
||||
}) => {
|
||||
term.options = data;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
socket.on('title', (data: string) => {
|
||||
document.title = data;
|
||||
});
|
||||
// socket.on('ssherror', (data: string) => {
|
||||
// status.innerHTML = data;
|
||||
// status.style.backgroundColor = 'red';
|
||||
// errorExists = true;
|
||||
// });
|
||||
|
||||
socket.on('menu', () => {
|
||||
drawMenu();
|
||||
});
|
||||
// socket.on('headerBackground', (data: string) => {
|
||||
// header.style.backgroundColor = data;
|
||||
// });
|
||||
|
||||
socket.on('status', (data: string) => {
|
||||
status.innerHTML = data;
|
||||
});
|
||||
// socket.on('header', (data: string) => {
|
||||
// if (data) {
|
||||
// header.innerHTML = data;
|
||||
// header.style.display = 'block';
|
||||
// // header is 19px and footer is 19px, recaculate new terminal-container and resize
|
||||
// terminalContainer.style.height = 'calc(100% - 38px)';
|
||||
// resizeScreen();
|
||||
// }
|
||||
// });
|
||||
|
||||
socket.on('ssherror', (data: string) => {
|
||||
status.innerHTML = data;
|
||||
status.style.backgroundColor = 'red';
|
||||
errorExists = true;
|
||||
});
|
||||
// socket.on('footer', (data: string) => {
|
||||
// sessionFooter = data;
|
||||
// footer.innerHTML = data;
|
||||
// });
|
||||
|
||||
socket.on('headerBackground', (data: string) => {
|
||||
header.style.backgroundColor = data;
|
||||
});
|
||||
// socket.on('statusBackground', (data: string) => {
|
||||
// status.style.backgroundColor = data;
|
||||
// });
|
||||
|
||||
socket.on('header', (data: string) => {
|
||||
if (data) {
|
||||
header.innerHTML = data;
|
||||
header.style.display = 'block';
|
||||
// header is 19px and footer is 19px, recaculate new terminal-container and resize
|
||||
terminalContainer.style.height = 'calc(100% - 38px)';
|
||||
resizeScreen();
|
||||
}
|
||||
});
|
||||
// socket.on('allowreplay', (data: boolean) => {
|
||||
// if (data === true) {
|
||||
// debug(`allowreplay: ${data}`);
|
||||
// allowreplay = true;
|
||||
// drawMenu();
|
||||
// } else {
|
||||
// allowreplay = false;
|
||||
// debug(`allowreplay: ${data}`);
|
||||
// }
|
||||
// });
|
||||
|
||||
socket.on('footer', (data: string) => {
|
||||
sessionFooter = data;
|
||||
footer.innerHTML = data;
|
||||
});
|
||||
|
||||
socket.on('statusBackground', (data: string) => {
|
||||
status.style.backgroundColor = data;
|
||||
});
|
||||
|
||||
socket.on('allowreplay', (data: boolean) => {
|
||||
if (data === true) {
|
||||
debug(`allowreplay: ${data}`);
|
||||
allowreplay = true;
|
||||
drawMenu();
|
||||
} else {
|
||||
allowreplay = false;
|
||||
debug(`allowreplay: ${data}`);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('allowreauth', (data: boolean) => {
|
||||
if (data === true) {
|
||||
debug(`allowreauth: ${data}`);
|
||||
allowreauth = true;
|
||||
drawMenu();
|
||||
} else {
|
||||
allowreauth = false;
|
||||
debug(`allowreauth: ${data}`);
|
||||
}
|
||||
});
|
||||
// socket.on('allowreauth', (data: boolean) => {
|
||||
// if (data === true) {
|
||||
// debug(`allowreauth: ${data}`);
|
||||
// allowreauth = true;
|
||||
// drawMenu();
|
||||
// } else {
|
||||
// allowreauth = false;
|
||||
// debug(`allowreauth: ${data}`);
|
||||
// }
|
||||
// });
|
||||
|
||||
socket.on('disconnect', (err: any) => {
|
||||
if (!errorExists) {
|
||||
|
@ -253,33 +221,31 @@ socket.on('disconnect', (err: any) => {
|
|||
status.innerHTML = `WEBSOCKET SERVER DISCONNECTED: ${err}`;
|
||||
}
|
||||
socket.io.reconnection(false);
|
||||
countdown.classList.remove('active');
|
||||
});
|
||||
|
||||
socket.on('error', (err: any) => {
|
||||
if (!errorExists) {
|
||||
status.style.backgroundColor = 'red';
|
||||
status.innerHTML = `ERROR: ${err}`;
|
||||
}
|
||||
});
|
||||
// socket.on('error', (err: any) => {
|
||||
// if (!errorExists) {
|
||||
// status.style.backgroundColor = 'red';
|
||||
// status.innerHTML = `ERROR: ${err}`;
|
||||
// }
|
||||
// });
|
||||
|
||||
socket.on('reauth', () => {
|
||||
if (allowreauth) {
|
||||
reauthSession();
|
||||
}
|
||||
});
|
||||
// socket.on('reauth', () => {
|
||||
// if (allowreauth) {
|
||||
// reauthSession();
|
||||
// }
|
||||
// });
|
||||
|
||||
// safe shutdown
|
||||
let hasCountdownStarted = false;
|
||||
|
||||
socket.on('shutdownCountdownUpdate', (remainingSeconds: any) => {
|
||||
if (!hasCountdownStarted) {
|
||||
countdown.classList.add('active');
|
||||
hasCountdownStarted = true;
|
||||
}
|
||||
countdown.innerText = `Shutting down in ${remainingSeconds}s`;
|
||||
});
|
||||
// socket.on('shutdownCountdownUpdate', (remainingSeconds: any) => {
|
||||
// if (!hasCountdownStarted) {
|
||||
// countdown.classList.add('active');
|
||||
// hasCountdownStarted = true;
|
||||
// }
|
||||
// countdown.innerText = `Shutting down in ${remainingSeconds}s`;
|
||||
// });
|
||||
|
||||
term.onTitleChange((title) => {
|
||||
document.title = title;
|
||||
});
|
||||
// term.onTitleChange((title) => {
|
||||
// document.title = title;
|
||||
// });
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"socketio": {
|
||||
"serveClient": false,
|
||||
"path": "/ssh/socket.io",
|
||||
"origins": ["localhost:2222"]
|
||||
"origins": ["localhost:2224"]
|
||||
},
|
||||
"user": {
|
||||
"name": null,
|
||||
|
@ -62,16 +62,8 @@
|
|||
"aes256-gcm@openssh.com",
|
||||
"aes256-cbc"
|
||||
],
|
||||
"hmac": [
|
||||
"hmac-sha2-256",
|
||||
"hmac-sha2-512",
|
||||
"hmac-sha1"
|
||||
],
|
||||
"compress": [
|
||||
"none",
|
||||
"zlib@openssh.com",
|
||||
"zlib"
|
||||
]
|
||||
"hmac": ["hmac-sha2-256", "hmac-sha2-512", "hmac-sha1"],
|
||||
"compress": ["none", "zlib@openssh.com", "zlib"]
|
||||
},
|
||||
"serverlog": {
|
||||
"client": false,
|
||||
|
|
|
@ -1,36 +1,15 @@
|
|||
{
|
||||
"name": "webssh2",
|
||||
"name": "myssh",
|
||||
"version": "0.6.0-pre-1",
|
||||
"ignore": [
|
||||
".gitignore"
|
||||
],
|
||||
"bin": "./index.js",
|
||||
"description": "A Websocket to SSH2 gateway using term.js, socket.io, ssh2, and express",
|
||||
"homepage": "https://github.com/billchurch/WebSSH2",
|
||||
"keywords": [
|
||||
"ssh",
|
||||
"webssh",
|
||||
"terminal",
|
||||
"webterminal"
|
||||
],
|
||||
"license": "SEE LICENSE IN FILE - LICENSE",
|
||||
"private": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/billchurch/WebSSH2.git"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Bill Church",
|
||||
"email": "wmchurch@gmail.com"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/billchurch/WebSSH2/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@runloop/api-client": "^0.1.0-alpha.23",
|
||||
"basic-auth": "~2.0.1",
|
||||
|
@ -42,7 +21,7 @@
|
|||
"node-forge": "^1.3.1",
|
||||
"read-config-ng": "^3.0.7",
|
||||
"serve-favicon": "^2.5.0",
|
||||
"socket.io": "^4.7.5",
|
||||
"socket.io": "4.7.5",
|
||||
"ssh2": "^1.15.0",
|
||||
"validator": "^13.11.0",
|
||||
"winston": "^3.13.0"
|
||||
|
|
|
@ -11,22 +11,41 @@ const nodeRoot = path.dirname(require.main.filename);
|
|||
const publicPath = path.join(nodeRoot, 'client', 'public');
|
||||
const express = require('express');
|
||||
const logger = require('morgan');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const expressConfig = {
|
||||
secret: crypto.randomBytes(20).toString('hex'),
|
||||
name: 'WebSSH2',
|
||||
resave: true,
|
||||
saveUninitialized: false,
|
||||
unset: 'destroy',
|
||||
ssh: {
|
||||
dotfiles: 'ignore',
|
||||
etag: false,
|
||||
extensions: ['htm', 'html'],
|
||||
index: false,
|
||||
maxAge: '1s',
|
||||
redirect: false,
|
||||
setHeaders(res) {
|
||||
res.set('x-timestamp', Date.now());
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const app = express();
|
||||
const server = require('http').createServer(app);
|
||||
const favicon = require('serve-favicon');
|
||||
const io = require('socket.io')(server, config.socketio);
|
||||
const session = require('express-session')(config.express);
|
||||
const io = require('socket.io')(server, { transports: ['websocket'], ...config.socketio });
|
||||
const session = require('express-session')(expressConfig);
|
||||
|
||||
const appSocket = require('./socket');
|
||||
const { setDefaultCredentials, basicAuth } = require('./util');
|
||||
// const { setDefaultCredentials } = require('./util');
|
||||
const { webssh2debug } = require('./logging');
|
||||
const { reauth, connect, notfound, handleErrors } = require('./routes');
|
||||
const { connect } = require('./routes');
|
||||
|
||||
setDefaultCredentials(config.user);
|
||||
// setDefaultCredentials(config.user);
|
||||
|
||||
// safe shutdown
|
||||
let remainingSeconds = config.safeShutdownDuration;
|
||||
|
||||
let shutdownMode = false;
|
||||
let shutdownInterval;
|
||||
let connectionCount = 0;
|
||||
|
@ -37,19 +56,18 @@ function safeShutdownGuard(req, res, next) {
|
|||
}
|
||||
// express
|
||||
app.use(safeShutdownGuard);
|
||||
app.use(session);
|
||||
// app.use(session);
|
||||
if (config.accesslog) app.use(logger('common'));
|
||||
app.disable('x-powered-by');
|
||||
app.use(favicon(path.join(publicPath, 'favicon.ico')));
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.post('/ssh/host/:host?', connect);
|
||||
app.post('/ssh', express.static(publicPath, config.express.ssh));
|
||||
app.use('/ssh', express.static(publicPath, config.express.ssh));
|
||||
app.use(basicAuth);
|
||||
app.get('/ssh/reauth', reauth);
|
||||
app.post('/ssh', express.static(publicPath, expressConfig.ssh));
|
||||
app.use('/ssh', express.static(publicPath, expressConfig.ssh));
|
||||
//app.use(basicAuth);
|
||||
//app.get('/ssh/reauth', reauth);
|
||||
app.get('/ssh/host/:host?', connect);
|
||||
app.use(notfound);
|
||||
app.use(handleErrors);
|
||||
// app.use(notfound);
|
||||
// app.use(handleErrors);
|
||||
|
||||
// clean stop
|
||||
function stopApp(reason) {
|
||||
|
@ -63,36 +81,10 @@ function stopApp(reason) {
|
|||
// bring up socket
|
||||
io.on('connection', appSocket);
|
||||
|
||||
// socket.io
|
||||
// expose express session with socket.request.session
|
||||
io.use((socket, next) => {
|
||||
socket.request.res ? session(socket.request, socket.request.res, next) : next(next); // eslint disable-line
|
||||
});
|
||||
|
||||
function countdownTimer() {
|
||||
if (!shutdownMode) clearInterval(shutdownInterval);
|
||||
remainingSeconds -= 1;
|
||||
if (remainingSeconds <= 0) {
|
||||
stopApp('Countdown is over');
|
||||
} else io.emit('shutdownCountdownUpdate', remainingSeconds);
|
||||
}
|
||||
|
||||
const signals = ['SIGTERM', 'SIGINT'];
|
||||
signals.forEach((signal) =>
|
||||
process.on(signal, () => {
|
||||
if (shutdownMode) stopApp('Safe shutdown aborted, force quitting');
|
||||
if (!connectionCount > 0) stopApp('All connections ended');
|
||||
shutdownMode = true;
|
||||
console.error(
|
||||
`\r\n${connectionCount} client(s) are still connected.\r\nStarting a ${remainingSeconds} seconds countdown.\r\nPress Ctrl+C again to force quit`
|
||||
);
|
||||
if (!shutdownInterval) shutdownInterval = setInterval(countdownTimer, 1000);
|
||||
})
|
||||
);
|
||||
|
||||
module.exports = { server, config };
|
||||
|
||||
const onConnection = (socket) => {
|
||||
console.log('connected');
|
||||
connectionCount += 1;
|
||||
socket.on('disconnect', () => {
|
||||
connectionCount -= 1;
|
||||
|
@ -102,9 +94,9 @@ const onConnection = (socket) => {
|
|||
});
|
||||
socket.on('geometry', (cols, rows) => {
|
||||
// TODO need to rework how we pass settings to ssh2, this is less than ideal
|
||||
socket.request.session.ssh.cols = cols;
|
||||
socket.request.session.ssh.rows = rows;
|
||||
webssh2debug(socket, `SOCKET GEOMETRY: termCols = ${cols}, termRows = ${rows}`);
|
||||
//socket.request.session.ssh.cols = cols; //TODO make this part of the terminal config on connect
|
||||
//socket.request.session.ssh.rows = rows;
|
||||
//webssh2debug(socket, `SOCKET GEOMETRY: termCols = ${cols}, termRows = ${rows}`);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ const configDefault = {
|
|||
socketio: {
|
||||
serveClient: false,
|
||||
path: '/ssh/socket.io',
|
||||
origins: ['localhost:2222'],
|
||||
origins: ['localhost:*'],
|
||||
},
|
||||
express: {
|
||||
secret: crypto.randomBytes(20).toString('hex'),
|
||||
|
@ -103,7 +103,7 @@ const configDefault = {
|
|||
try {
|
||||
if (!fs.existsSync(configPath)) {
|
||||
console.error(
|
||||
`\n\nERROR: Missing config.json for WebSSH2. Current config: ${util.inspect(myConfig)}`
|
||||
`\n\nERROR: Missing config.json for WebSSH2. Current config: ${util.inspect(myConfig)}`,
|
||||
);
|
||||
console.error('\n See config.json.sample for details\n\n');
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ try {
|
|||
} catch (err) {
|
||||
myConfig = configDefault;
|
||||
console.error(
|
||||
`\n\nERROR: Missing config.json for WebSSH2. Current config: ${util.inspect(myConfig)}`
|
||||
`\n\nERROR: Missing config.json for WebSSH2. Current config: ${util.inspect(myConfig)}`,
|
||||
);
|
||||
console.error('\n See config.json.sample for details\n\n');
|
||||
console.error(`ERROR:\n\n ${err}`);
|
||||
|
|
|
@ -9,195 +9,110 @@ const publicPath = path.join(nodeRoot, 'client', 'public');
|
|||
const { parseBool } = require('./util');
|
||||
const config = require('./config');
|
||||
|
||||
exports.reauth = function reauth(req, res) {
|
||||
let { referer } = req.headers;
|
||||
if (!validator.isURL(referer, { host_whitelist: ['localhost'] })) {
|
||||
console.error(
|
||||
`WebSSH2 (${req.sessionID}) ERROR: Referrer '${referer}' for '/reauth' invalid. Setting to '/' which will probably fail.`
|
||||
);
|
||||
referer = '/';
|
||||
}
|
||||
res
|
||||
.status(401)
|
||||
.send(
|
||||
`<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${referer}"></head><body bgcolor="#000"></body></html>`
|
||||
);
|
||||
};
|
||||
// exports.reauth = function reauth(req, res) {
|
||||
// let { referer } = req.headers;
|
||||
// if (!validator.isURL(referer, { host_whitelist: ['localhost'] })) {
|
||||
// console.error(
|
||||
// `WebSSH2 (${req.sessionID}) ERROR: Referrer '${referer}' for '/reauth' invalid. Setting to '/' which will probably fail.`,
|
||||
// );
|
||||
// referer = '/';
|
||||
// }
|
||||
// res
|
||||
// .status(401)
|
||||
// .send(
|
||||
// `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${referer}"></head><body bgcolor="#000"></body></html>`,
|
||||
// );
|
||||
// };
|
||||
|
||||
exports.connect = function connect(req, res) {
|
||||
res.sendFile(path.join(path.join(publicPath, 'client.htm')));
|
||||
|
||||
let { host, port } = config.ssh;
|
||||
let { text: header, background: headerBackground } = config.header;
|
||||
let { term: sshterm, readyTimeout } = config.ssh;
|
||||
let {
|
||||
cursorBlink,
|
||||
scrollback,
|
||||
tabStopWidth,
|
||||
bellStyle,
|
||||
fontSize,
|
||||
fontFamily,
|
||||
letterSpacing,
|
||||
lineHeight,
|
||||
} = config.terminal;
|
||||
// let { host, port } = config.ssh;
|
||||
// let { text: header, background: headerBackground } = config.header;
|
||||
// let { term: sshterm, readyTimeout } = config.ssh;
|
||||
// let {
|
||||
// cursorBlink,
|
||||
// scrollback,
|
||||
// tabStopWidth,
|
||||
// bellStyle,
|
||||
// fontSize,
|
||||
// fontFamily,
|
||||
// letterSpacing,
|
||||
// lineHeight,
|
||||
// } = config.terminal;
|
||||
|
||||
// capture, assign, and validate variables
|
||||
|
||||
if (req.params?.host) {
|
||||
if (
|
||||
validator.isIP(`${req.params.host}`) ||
|
||||
validator.isFQDN(req.params.host) ||
|
||||
/^(([a-z]|[A-Z]|\d|[!^(){}\-_~])+)?\w$/.test(req.params.host)
|
||||
) {
|
||||
host = req.params.host;
|
||||
}
|
||||
}
|
||||
// if (req.params?.host) {
|
||||
// if (
|
||||
// validator.isIP(`${req.params.host}`) ||
|
||||
// validator.isFQDN(req.params.host) ||
|
||||
// /^(([a-z]|[A-Z]|\d|[!^(){}\-_~])+)?\w$/.test(req.params.host)
|
||||
// ) {
|
||||
// host = req.params.host;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (req.method === 'POST' && req.body.username && req.body.userpassword) {
|
||||
req.session.username = req.body.username;
|
||||
req.session.userpassword = req.body.userpassword;
|
||||
// //// ADding exta
|
||||
// if (req.params?.devboxID) {
|
||||
// devboxID = req.params.devboxID;
|
||||
// }
|
||||
// if (req.params?.supabaseAuth) {
|
||||
// supaBaseAuth = req.params.supaBaseAuth;
|
||||
// }
|
||||
|
||||
if (req.body.port && validator.isInt(`${req.body.port}`, { min: 1, max: 65535 }))
|
||||
port = req.body.port;
|
||||
// req.session.ssh = {
|
||||
// host,
|
||||
// port,
|
||||
// localAddress: config.ssh.localAddress,
|
||||
// localPort: config.ssh.localPort,
|
||||
// header: {
|
||||
// name: header,
|
||||
// background: headerBackground,
|
||||
// },
|
||||
// algorithms: config.algorithms,
|
||||
// keepaliveInterval: config.ssh.keepaliveInterval,
|
||||
// keepaliveCountMax: config.ssh.keepaliveCountMax,
|
||||
// allowedSubnets: config.ssh.allowedSubnets,
|
||||
// term: sshterm,
|
||||
// terminal: {
|
||||
// cursorBlink,
|
||||
// scrollback,
|
||||
// tabStopWidth,
|
||||
// bellStyle,
|
||||
// fontSize,
|
||||
// fontFamily,
|
||||
// letterSpacing,
|
||||
// lineHeight,
|
||||
// },
|
||||
// cols: null,
|
||||
// rows: null,
|
||||
// allowreplay:
|
||||
// config.options.challengeButton ||
|
||||
// (validator.isBoolean(`${req.headers.allowreplay}`)
|
||||
// ? parseBool(req.headers.allowreplay)
|
||||
// : false),
|
||||
// allowreauth: config.options.allowreauth || false,
|
||||
// mrhsession:
|
||||
// validator.isAlphanumeric(`${req.headers.mrhsession}`) && req.headers.mrhsession
|
||||
// ? req.headers.mrhsession
|
||||
// : 'none',
|
||||
// serverlog: {
|
||||
// client: config.serverlog.client || false,
|
||||
// server: config.serverlog.server || false,
|
||||
// },
|
||||
// readyTimeout,
|
||||
// };
|
||||
// if (req.session.ssh.header.name) validator.escape(req.session.ssh.header.name);
|
||||
// if (req.session.ssh.header.background) validator.escape(req.session.ssh.header.background);
|
||||
// };
|
||||
|
||||
if (req.body.header) header = req.body.header;
|
||||
// exports.notfound = function notfound(_req, res) {
|
||||
// res.status(404).send("Sorry, can't find that!");
|
||||
// };
|
||||
|
||||
if (req.body.headerBackground) {
|
||||
headerBackground = req.body.headerBackground;
|
||||
console.log(`background: ${req.body.headerBackground}`);
|
||||
}
|
||||
|
||||
if (req.body.sshterm && /^(([a-z]|[A-Z]|\d|[!^(){}\-_~])+)?\w$/.test(req.body.sshterm))
|
||||
sshterm = req.body.sshterm;
|
||||
|
||||
if (req.body.cursorBlink && validator.isBoolean(`${req.body.cursorBlink}`))
|
||||
cursorBlink = parseBool(req.body.cursorBlink);
|
||||
|
||||
if (req.body.scrollback && validator.isInt(`${req.body.scrollback}`, { min: 1, max: 200000 }))
|
||||
scrollback = req.body.scrollback;
|
||||
|
||||
if (req.body.tabStopWidth && validator.isInt(`${req.body.tabStopWidth}`, { min: 1, max: 100 }))
|
||||
tabStopWidth = req.body.tabStopWidth;
|
||||
|
||||
if (req.body.bellStyle && ['sound', 'none'].indexOf(req.body.bellStyle) > -1)
|
||||
bellStyle = req.body.bellStyle;
|
||||
|
||||
if (
|
||||
req.body.readyTimeout &&
|
||||
validator.isInt(`${req.body.readyTimeout}`, { min: 1, max: 300000 })
|
||||
)
|
||||
readyTimeout = req.body.readyTimeout;
|
||||
|
||||
if (req.body.fontSize && validator.isNumeric(`${req.body.fontSize}`))
|
||||
fontSize = req.body.fontSize;
|
||||
|
||||
if (req.body.fontFamily) fontFamily = req.body.fontFamily;
|
||||
|
||||
if (req.body.letterSpacing && validator.isNumeric(`${req.body.letterSpacing}`))
|
||||
letterSpacing = req.body.letterSpacing;
|
||||
|
||||
if (req.body.lineHeight && validator.isNumeric(`${req.body.lineHeight}`))
|
||||
lineHeight = req.body.lineHeight;
|
||||
}
|
||||
|
||||
if (req.method === 'GET') {
|
||||
if (req.query?.port && validator.isInt(`${req.query.port}`, { min: 1, max: 65535 }))
|
||||
port = req.query.port;
|
||||
|
||||
if (req.query?.header) header = req.query.header;
|
||||
|
||||
if (req.query?.headerBackground) headerBackground = req.query.headerBackground;
|
||||
|
||||
if (req.query?.sshterm && /^(([a-z]|[A-Z]|\d|[!^(){}\-_~])+)?\w$/.test(req.query.sshterm))
|
||||
sshterm = req.query.sshterm;
|
||||
|
||||
if (req.query?.cursorBlink && validator.isBoolean(`${req.query.cursorBlink}`))
|
||||
cursorBlink = parseBool(req.query.cursorBlink);
|
||||
|
||||
if (
|
||||
req.query?.scrollback &&
|
||||
validator.isInt(`${req.query.scrollback}`, { min: 1, max: 200000 })
|
||||
)
|
||||
scrollback = req.query.scrollback;
|
||||
|
||||
if (
|
||||
req.query?.tabStopWidth &&
|
||||
validator.isInt(`${req.query.tabStopWidth}`, { min: 1, max: 100 })
|
||||
)
|
||||
tabStopWidth = req.query.tabStopWidth;
|
||||
|
||||
if (req.query?.bellStyle && ['sound', 'none'].indexOf(req.query.bellStyle) > -1)
|
||||
bellStyle = req.query.bellStyle;
|
||||
|
||||
if (
|
||||
req.query?.readyTimeout &&
|
||||
validator.isInt(`${req.query.readyTimeout}`, { min: 1, max: 300000 })
|
||||
)
|
||||
readyTimeout = req.query.readyTimeout;
|
||||
|
||||
if (req.query?.fontSize && validator.isNumeric(`${req.query.fontSize}`))
|
||||
fontSize = req.query.fontSize;
|
||||
|
||||
if (req.query?.fontFamily) fontFamily = req.query.fontFamily;
|
||||
|
||||
if (req.query?.lineHeight && validator.isNumeric(`${req.query.lineHeight}`))
|
||||
lineHeight = req.query.lineHeight;
|
||||
|
||||
if (req.query?.letterSpacing && validator.isNumeric(`${req.query.letterSpacing}`))
|
||||
letterSpacing = req.query.letterSpacing;
|
||||
}
|
||||
|
||||
req.session.ssh = {
|
||||
host,
|
||||
port,
|
||||
localAddress: config.ssh.localAddress,
|
||||
localPort: config.ssh.localPort,
|
||||
header: {
|
||||
name: header,
|
||||
background: headerBackground,
|
||||
},
|
||||
algorithms: config.algorithms,
|
||||
keepaliveInterval: config.ssh.keepaliveInterval,
|
||||
keepaliveCountMax: config.ssh.keepaliveCountMax,
|
||||
allowedSubnets: config.ssh.allowedSubnets,
|
||||
term: sshterm,
|
||||
terminal: {
|
||||
cursorBlink,
|
||||
scrollback,
|
||||
tabStopWidth,
|
||||
bellStyle,
|
||||
fontSize,
|
||||
fontFamily,
|
||||
letterSpacing,
|
||||
lineHeight,
|
||||
},
|
||||
cols: null,
|
||||
rows: null,
|
||||
allowreplay:
|
||||
config.options.challengeButton ||
|
||||
(validator.isBoolean(`${req.headers.allowreplay}`)
|
||||
? parseBool(req.headers.allowreplay)
|
||||
: false),
|
||||
allowreauth: config.options.allowreauth || false,
|
||||
mrhsession:
|
||||
validator.isAlphanumeric(`${req.headers.mrhsession}`) && req.headers.mrhsession
|
||||
? req.headers.mrhsession
|
||||
: 'none',
|
||||
serverlog: {
|
||||
client: config.serverlog.client || false,
|
||||
server: config.serverlog.server || false,
|
||||
},
|
||||
readyTimeout,
|
||||
};
|
||||
if (req.session.ssh.header.name) validator.escape(req.session.ssh.header.name);
|
||||
if (req.session.ssh.header.background) validator.escape(req.session.ssh.header.background);
|
||||
};
|
||||
|
||||
exports.notfound = function notfound(_req, res) {
|
||||
res.status(404).send("Sorry, can't find that!");
|
||||
};
|
||||
|
||||
exports.handleErrors = function handleErrors(err, _req, res) {
|
||||
console.error(err.stack);
|
||||
res.status(500).send('Something broke!');
|
||||
// exports.handleErrors = function handleErrors(err, _req, res) {
|
||||
// console.error(err.stack);
|
||||
// res.status(500).send('Something broke!');
|
||||
// };
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ const SSH = require('ssh2').Client;
|
|||
const CIDRMatcher = require('cidr-matcher');
|
||||
const validator = require('validator');
|
||||
const dnsPromises = require('dns').promises;
|
||||
const debug = require('debug');
|
||||
const { Client } = require('ssh2');
|
||||
const tls = require('tls');
|
||||
const forge = require('node-forge');
|
||||
|
@ -14,7 +15,6 @@ function convertPKCS8toPKCS1(pkcs8Key) {
|
|||
|
||||
// Convert the private key to PKCS#1 format
|
||||
const pkcs1Pem = forge.pki.privateKeyToPem(privateKeyInfo);
|
||||
|
||||
return pkcs1Pem;
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,7 @@ const proxyConnect = (hostname, callback) => {
|
|||
host: 'ssh.runloop.pro', // Proxy server address
|
||||
port: 443, // Proxy port (HTTPS over TLS)
|
||||
servername: hostname, // Target hostname, acts like -servername in openssl
|
||||
checkServerIdentity: () => {
|
||||
return undefined;
|
||||
}, // Disable hostname validation
|
||||
checkServerIdentity: () => undefined, // Disable hostname validation
|
||||
},
|
||||
() => {
|
||||
console.log('TLS connection established');
|
||||
|
@ -44,18 +42,16 @@ const proxyConnect = (hostname, callback) => {
|
|||
};
|
||||
|
||||
// Main function to establish the SSH connection over the TLS proxy
|
||||
async function establishConnection() {
|
||||
async function establishConnection(targetDevbox, bearerToken) {
|
||||
const runloop = new Runloop({
|
||||
baseURL: 'https://api.runloop.pro',
|
||||
// This is gotten by just inspecting the browser cookies on platform.runloop.pro
|
||||
bearerToken:
|
||||
'ss_eyJhbGciOiJIUzI1NiIsImtpZCI6IkEyZExNNUlheFE4L29acW4iLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2t5aGpvaG1xbXFrdmZxc2t4dnNkLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiJmZjRjOWRjOS1kNzQ1LTQ2MmItYTFiNS1lZmIxMDgwMjU0ZTkiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzI1NDc5ODU4LCJpYXQiOjE3MjU0NzYyNTgsImVtYWlsIjoiZXZhbkBydW5sb29wLmFpIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCIsImdpdGh1YiJdfSwidXNlcl9tZXRhZGF0YSI6eyJhdmF0YXJfdXJsIjoiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzE1NTQ3NTU1Nz92PTQiLCJlbWFpbCI6ImV2YW5AcnVubG9vcC5haSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpc3MiOiJodHRwczovL2FwaS5naXRodWIuY29tIiwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJldmFuLXJ1bmxvb3BhaSIsInByb3ZpZGVyX2lkIjoiMTU1NDc1NTU3Iiwic3ViIjoiMTU1NDc1NTU3IiwidXNlcl9uYW1lIjoiZXZhbi1ydW5sb29wYWkifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJvYXV0aCIsInRpbWVzdGFtcCI6MTcyNDQ1MjY4MX1dLCJzZXNzaW9uX2lkIjoiNzc4ODUzMjQtNmYwYy00ZGRhLWFjMDMtZWJiMDAxZWFkYTc4IiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.ghcTeoYcppsNj6xIg1AhG-lL5RyNWKMSWdH--iZZ2co',
|
||||
bearerToken,
|
||||
});
|
||||
const targetDevbox = 'dbx_2xb2Swl1rMFqrvGVX0Q4N';
|
||||
|
||||
const sshKeyCreateResp = await runloop.devboxes.createSSHKey(targetDevbox);
|
||||
const hostname = sshKeyCreateResp.url;
|
||||
console.log("EVAN SSH KEY RESP", sshKeyCreateResp)
|
||||
console.log('EVAN SSH KEY RESP', sshKeyCreateResp);
|
||||
|
||||
// SS KEY
|
||||
// Environment
|
||||
|
@ -87,8 +83,8 @@ async function establishConnection() {
|
|||
// });
|
||||
// });
|
||||
})
|
||||
.on('error', (err) => {
|
||||
console.error('SSH Connection error:', err);
|
||||
.on('error', (error) => {
|
||||
console.error('SSH Connection error:', error);
|
||||
})
|
||||
.connect({
|
||||
sock: tlsSocket, // Pass the TLS socket as the connection
|
||||
|
@ -96,100 +92,29 @@ async function establishConnection() {
|
|||
privateKey: convertPKCS8toPKCS1(sshKeyCreateResp.ssh_private_key), // Replace with the path to your private key
|
||||
hostHash: 'md5', // Optional: Match host keys by hash
|
||||
strictHostKeyChecking: false, // Disable strict host key checking
|
||||
|
||||
// algorithms: socket.request.session.ssh.algorithms,
|
||||
readyTimeout: 10000,
|
||||
keepaliveInterval: 120000,
|
||||
keepaliveCountMax: 10,
|
||||
debug: debug('ssh2'),
|
||||
});
|
||||
});
|
||||
}
|
||||
// var fs = require('fs')
|
||||
// var hostkeys = JSON.parse(fs.readFileSync('./hostkeyhashes.json', 'utf8'))
|
||||
let termCols;
|
||||
let termRows;
|
||||
|
||||
// public
|
||||
module.exports = function appSocket(socket) {
|
||||
async function setupConnection() {
|
||||
// TODO AUTH?
|
||||
// if websocket connection arrives without an express session, kill it
|
||||
if (!socket.request.session) {
|
||||
socket.emit('401 UNAUTHORIZED');
|
||||
debugWebSSH2('SOCKET: No Express Session / REJECTED');
|
||||
socket.disconnect(true);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handling for various events. Outputs error to client, logs to
|
||||
* server, destroys session and disconnects socket.
|
||||
* @param {string} myFunc Function calling this function
|
||||
* @param {object} err error object or error message
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
function SSHerror(myFunc, err) {
|
||||
let theError;
|
||||
if (socket.request.session) {
|
||||
// we just want the first error of the session to pass to the client
|
||||
const firstError = socket.request.session.error || (err ? err.message : undefined);
|
||||
theError = firstError ? `: ${firstError}` : '';
|
||||
// log unsuccessful login attempt
|
||||
if (err && err.level === 'client-authentication') {
|
||||
console.error(
|
||||
`WebSSH2 ${'error: Authentication failure'.red.bold} user=${socket.request.session.username.yellow.bold.underline} from=${socket.handshake.address.yellow.bold.underline}`,
|
||||
);
|
||||
socket.emit('allowreauth', socket.request.session.ssh.allowreauth);
|
||||
socket.emit('reauth');
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`WebSSH2 Logout: user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host} port=${socket.request.session.ssh.port} sessionID=${socket.request.sessionID}/${socket.id} allowreplay=${socket.request.session.ssh.allowreplay} term=${socket.request.session.ssh.term}`,
|
||||
);
|
||||
if (err) {
|
||||
theError = err ? `: ${err.message}` : '';
|
||||
console.error(`WebSSH2 error${theError}`);
|
||||
}
|
||||
}
|
||||
socket.emit('ssherror', `SSH ${myFunc}${theError}`);
|
||||
socket.request.session.destroy();
|
||||
socket.disconnect(true);
|
||||
} else {
|
||||
theError = err ? `: ${err.message}` : '';
|
||||
socket.disconnect(true);
|
||||
}
|
||||
debugWebSSH2(`SSHerror ${myFunc}${theError}`);
|
||||
}
|
||||
// If configured, check that requsted host is in a permitted subnet
|
||||
if (
|
||||
(((socket.request.session || {}).ssh || {}).allowedSubnets || {}).length &&
|
||||
socket.request.session.ssh.allowedSubnets.length > 0
|
||||
) {
|
||||
let ipaddress = socket.request.session.ssh.host;
|
||||
if (!validator.isIP(`${ipaddress}`)) {
|
||||
try {
|
||||
const result = await dnsPromises.lookup(socket.request.session.ssh.host);
|
||||
ipaddress = result.address;
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`WebSSH2 ${`error: ${err.code} ${err.hostname}`.red.bold} user=${
|
||||
socket.request.session.username.yellow.bold.underline
|
||||
} from=${socket.handshake.address.yellow.bold.underline}`,
|
||||
);
|
||||
socket.emit('ssherror', '404 HOST IP NOT FOUND');
|
||||
socket.disconnect(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const matcher = new CIDRMatcher(socket.request.session.ssh.allowedSubnets);
|
||||
if (!matcher.contains(ipaddress)) {
|
||||
console.error(
|
||||
`WebSSH2 ${
|
||||
`error: Requested host ${ipaddress} outside configured subnets / REJECTED`.red.bold
|
||||
} user=${socket.request.session.username.yellow.bold.underline} from=${
|
||||
socket.handshake.address.yellow.bold.underline
|
||||
}`,
|
||||
);
|
||||
socket.emit('ssherror', '401 UNAUTHORIZED');
|
||||
socket.disconnect(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if (!socket.request.session) {
|
||||
// socket.emit('401 UNAUTHORIZED');
|
||||
// debugWebSSH2('SOCKET: No Express Session / REJECTED');
|
||||
// socket.disconnect(true);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const conn = new SSH();
|
||||
socket.on('geometry', (cols, rows) => {
|
||||
|
@ -202,33 +127,24 @@ module.exports = function appSocket(socket) {
|
|||
});
|
||||
|
||||
conn.on('ready', () => {
|
||||
debugWebSSH2(
|
||||
`WebSSH2 Login: user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host} port=${socket.request.session.ssh.port} sessionID=${socket.request.sessionID}/${socket.id} mrhsession=${socket.request.session.ssh.mrhsession} allowreplay=${socket.request.session.ssh.allowreplay} term=${socket.request.session.ssh.term}`,
|
||||
);
|
||||
socket.emit('menu');
|
||||
socket.emit('allowreauth', socket.request.session.ssh.allowreauth);
|
||||
socket.emit('setTerminalOpts', socket.request.session.ssh.terminal);
|
||||
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.emit('status', 'SSH CONNECTION ESTABLISHED');
|
||||
socket.emit('statusBackground', 'green');
|
||||
socket.emit('allowreplay', socket.request.session.ssh.allowreplay);
|
||||
// debugWebSSH2(
|
||||
// `WebSSH2 Login: user=${socket.request.session.username} from=${socket.handshake.address} host=${socket.request.session.ssh.host} port=${socket.request.session.ssh.port} sessionID=${socket.request.sessionID}/${socket.id} mrhsession=${socket.request.session.ssh.mrhsession} allowreplay=${socket.request.session.ssh.allowreplay} term=${socket.request.session.ssh.term}`,
|
||||
// );
|
||||
socket.emit('setTerminalOpts', {
|
||||
cursorBlink: true,
|
||||
scrollback: 10000,
|
||||
tabStopWidth: 8,
|
||||
bellStyle: 'sound',
|
||||
});
|
||||
conn.shell(
|
||||
{
|
||||
term: socket.request.session.ssh.term,
|
||||
term: 'xterm-color',
|
||||
cols: termCols,
|
||||
rows: termRows,
|
||||
},
|
||||
(err, stream) => {
|
||||
if (err) {
|
||||
SSHerror(`EXEC ERROR${err}`);
|
||||
// SSHerror(`EXEC ERROR${err}`);
|
||||
conn.end();
|
||||
return;
|
||||
}
|
||||
|
@ -236,11 +152,12 @@ module.exports = function appSocket(socket) {
|
|||
stream.write(data);
|
||||
});
|
||||
socket.on('control', (controlData) => {
|
||||
// Todo probably remove
|
||||
switch (controlData) {
|
||||
case 'replayCredentials':
|
||||
if (socket.request.session.ssh.allowreplay) {
|
||||
stream.write(`${socket.request.session.userpassword}\n`);
|
||||
}
|
||||
// case 'replayCredentials':
|
||||
// if (socket.request.session.ssh.allowreplay) {
|
||||
// stream.write(`${socket.request.session.userpassword}\n`);
|
||||
// }
|
||||
/* falls through */
|
||||
default:
|
||||
debugWebSSH2(`controlData: ${controlData}`);
|
||||
|
@ -254,13 +171,13 @@ module.exports = function appSocket(socket) {
|
|||
});
|
||||
socket.on('disconnect', (reason) => {
|
||||
debugWebSSH2(`SOCKET DISCONNECT: ${reason}`);
|
||||
const errMsg = { message: reason };
|
||||
SSHerror('CLIENT SOCKET DISCONNECT', errMsg);
|
||||
//const errMsg = { message: reason };
|
||||
// SSHerror('CLIENT SOCKET DISCONNECT', errMsg);
|
||||
conn.end();
|
||||
// socket.request.session.destroy()
|
||||
});
|
||||
socket.on('error', (errMsg) => {
|
||||
SSHerror('SOCKET ERROR', errMsg);
|
||||
// SSHerror('SOCKET ERROR', errMsg);
|
||||
conn.end();
|
||||
});
|
||||
|
||||
|
@ -276,7 +193,7 @@ module.exports = function appSocket(socket) {
|
|||
(signal ? `SIGNAL: ${signal}` : '')
|
||||
: undefined,
|
||||
};
|
||||
SSHerror('STREAM CLOSE', errMsg);
|
||||
//SSHerror('STREAM CLOSE', errMsg);
|
||||
conn.end();
|
||||
});
|
||||
stream.stderr.on('data', (data) => {
|
||||
|
@ -287,23 +204,18 @@ module.exports = function appSocket(socket) {
|
|||
});
|
||||
|
||||
conn.on('end', (err) => {
|
||||
SSHerror('CONN END BY HOST', err);
|
||||
//SSHerror('CONN END BY HOST', err);
|
||||
});
|
||||
conn.on('close', (err) => {
|
||||
SSHerror('CONN CLOSE', err);
|
||||
//SSHerror('CONN CLOSE', err);
|
||||
});
|
||||
conn.on('error', (err) => {
|
||||
SSHerror('CONN ERROR', err);
|
||||
//SSHerror('CONN ERROR', err);
|
||||
});
|
||||
conn.on('keyboard-interactive', (name, instructions, instructionsLang, prompts, finish) => {
|
||||
debugWebSSH2("conn.on('keyboard-interactive')");
|
||||
finish([socket.request.session.userpassword]);
|
||||
});
|
||||
if (
|
||||
socket.request.session.username &&
|
||||
(socket.request.session.userpassword || socket.request.session.privatekey) &&
|
||||
socket.request.session.ssh
|
||||
) {
|
||||
// conn.on('keyboard-interactive', (name, instructions, instructionsLang, prompts, finish) => {
|
||||
// debugWebSSH2("conn.on('keyboard-interactive')");
|
||||
// finish([socket.request.session.userpassword]);
|
||||
// });
|
||||
// console.log('hostkeys: ' + hostkeys[0].[0])
|
||||
// conn.connect({
|
||||
// host: socket.request.session.ssh.host,
|
||||
|
@ -320,19 +232,10 @@ module.exports = function appSocket(socket) {
|
|||
// keepaliveCountMax: socket.request.session.ssh.keepaliveCountMax,
|
||||
// debug: debug('ssh2'),
|
||||
// });
|
||||
console.log('EVAN HERE');
|
||||
await establishConnection();
|
||||
} else {
|
||||
debugWebSSH2(
|
||||
`Attempt to connect without session.username/password or session varialbles defined, potentially previously abandoned client session. disconnecting websocket client.\r\nHandshake information: \r\n ${JSON.stringify(
|
||||
socket.handshake,
|
||||
)}`,
|
||||
await establishConnection(
|
||||
'dbx_2xb6oS1G1e6TAihVMtjn6',
|
||||
'ss_eyJhbGciOiJIUzI1NiIsImtpZCI6IkEyZExNNUlheFE4L29acW4iLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2t5aGpvaG1xbXFrdmZxc2t4dnNkLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5NmRlMTVjZC1lZWJmLTRjNzctODQwNy1jZTkwNzNlZTZkMjIiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzI1NDg1NTQwLCJpYXQiOjE3MjU0ODE5NDAsImVtYWlsIjoiYWxleEBydW5sb29wLmFpIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCIsImdpdGh1YiJdfSwidXNlcl9tZXRhZGF0YSI6eyJhdmF0YXJfdXJsIjoiaHR0cHM6Ly9hdmF0YXJzLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzE2MDA3NzkyND92PTQiLCJlbWFpbCI6ImFsZXhAcnVubG9vcC5haSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmdWxsX25hbWUiOiJBbGV4YW5kZXIgRGluZXMiLCJpc3MiOiJodHRwczovL2FwaS5naXRodWIuY29tIiwibmFtZSI6IkFsZXhhbmRlciBEaW5lcyIsInBob25lX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGluZXMtcmwiLCJwcm92aWRlcl9pZCI6IjE2MDA3NzkyNCIsInN1YiI6IjE2MDA3NzkyNCIsInVzZXJfbmFtZSI6ImRpbmVzLXJsIn0sInJvbGUiOiJhdXRoZW50aWNhdGVkIiwiYWFsIjoiYWFsMSIsImFtciI6W3sibWV0aG9kIjoib2F1dGgiLCJ0aW1lc3RhbXAiOjE3MjM1OTAxODB9XSwic2Vzc2lvbl9pZCI6IjU5MjhkZTIxLTI3M2ItNDkzNC1iMGFmLThlYWE3MDUxOGE3MiIsImlzX2Fub255bW91cyI6ZmFsc2V9.Qby46q2eDZUWjpPPSvmyzQ5bGKGEkpg2r9zBAUTpc3Q',
|
||||
);
|
||||
socket.emit('ssherror', 'WEBSOCKET ERROR - Refresh the browser and try again');
|
||||
socket.request.session.destroy();
|
||||
socket.disconnect(true);
|
||||
}
|
||||
}
|
||||
setupConnection();
|
||||
// establishConnection();
|
||||
};
|
||||
|
|
|
@ -2,44 +2,43 @@
|
|||
// util.js
|
||||
|
||||
// private
|
||||
const debug = require('debug')('WebSSH2');
|
||||
const Auth = require('basic-auth');
|
||||
// const debug = require('debug')('WebSSH2');
|
||||
// const Auth = require('basic-auth');
|
||||
|
||||
let defaultCredentials = { username: null, password: null, privatekey: null };
|
||||
|
||||
exports.setDefaultCredentials = function setDefaultCredentials({
|
||||
name: username,
|
||||
password,
|
||||
privatekey,
|
||||
overridebasic,
|
||||
}) {
|
||||
defaultCredentials = { username, password, privatekey, overridebasic };
|
||||
};
|
||||
// exports.setDefaultCredentials = function setDefaultCredentials({
|
||||
// name: username,
|
||||
// password,
|
||||
// privatekey,
|
||||
// overridebasic,
|
||||
// }) {
|
||||
// defaultCredentials = { username, password, privatekey, overridebasic };
|
||||
// };
|
||||
|
||||
exports.basicAuth = function basicAuth(req, res, next) {
|
||||
const myAuth = Auth(req);
|
||||
// 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;
|
||||
if (myAuth && myAuth.pass !== '' && !overridebasic) {
|
||||
req.session.username = myAuth.name;
|
||||
req.session.userpassword = myAuth.pass;
|
||||
debug(`myAuth.name: ${myAuth.name} and password ${myAuth.pass ? 'exists' : 'is blank'}`);
|
||||
} else {
|
||||
req.session.username = username;
|
||||
req.session.userpassword = password;
|
||||
req.session.privatekey = privatekey;
|
||||
}
|
||||
if (!req.session.userpassword && !req.session.privatekey) {
|
||||
res.statusCode = 401;
|
||||
debug('basicAuth credential request (401)');
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="WebSSH"');
|
||||
res.end('Username and password required for web SSH service.');
|
||||
return;
|
||||
}
|
||||
next();
|
||||
};
|
||||
// exports.basicAuth = function basicAuth(req, res, next) {
|
||||
// const myAuth = Auth(req);
|
||||
// // 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;
|
||||
// if (myAuth && myAuth.pass !== '' && !overridebasic) {
|
||||
// req.session.username = myAuth.name;
|
||||
// req.session.userpassword = myAuth.pass;
|
||||
// debug(`myAuth.name: ${myAuth.name} and password ${myAuth.pass ? 'exists' : 'is blank'}`);
|
||||
// } else {
|
||||
// req.session.username = username;
|
||||
// req.session.userpassword = password;
|
||||
// req.session.privatekey = privatekey;
|
||||
// }
|
||||
// if (!req.session.userpassword && !req.session.privatekey) {
|
||||
// res.statusCode = 401;
|
||||
// debug('basicAuth credential request (401)');
|
||||
// res.setHeader('WWW-Authenticate', 'Basic realm="WebSSH"');
|
||||
// res.end('Username and password required for web SSH service.');
|
||||
// return;
|
||||
// }
|
||||
// next();
|
||||
// };
|
||||
|
||||
// takes a string, makes it boolean (true if the string is true, false otherwise)
|
||||
exports.parseBool = function parseBool(str) {
|
||||
|
|
Loading…
Reference in a new issue