webssh2/index.js
billchurch 9ebd56ddb9 fixed error message status overwriting
Fixed issue where error message would be overwritten in status bar as
websocket connection is closed. Added additional logging elements in
some events.
2017-02-10 10:56:11 -05:00

131 lines
5.5 KiB
JavaScript

/*
* WebSSH2 - Web to SSH2 gateway
* Bill Church - https://github.com/billchurch - April 2016
*
*/
var express = require('express');
var app = express();
var cookieParser = require('cookie-parser')
var server = require('http').Server(app);
var io = require('socket.io')(server);
var path = require('path');
var basicAuth = require('basic-auth');
var ssh = require('ssh2');
var readConfig = require('read-config'),
config = readConfig(__dirname + '/config.json');
var myError = " - ";
function logErrors(err, req, res, next) {
console.error(err.stack);
next(err);
}
server.listen({
host: config.listen.ip,
port: config.listen.port
}).on('error', function (err) {
if (err.code === 'EADDRINUSE') {
config.listen.port++;
console.log('Address in use, retrying on port ' + config.listen.port);
setTimeout(function () {
server.listen(config.listen.port);
}, 250);
}
});
app.use(express.static(__dirname + '/public')).use(function(req, res, next) {
var myAuth = basicAuth(req);
if (myAuth === undefined) {
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="WebSSH"');
res.end('Username and password required for web SSH service.');
} else if (myAuth.name == "") {
res.statusCode = 401
res.setHeader('WWW-Authenticate', 'Basic realm="WebSSH"');
res.end('Username and password required for web SSH service.');
} else {
config.user.name = myAuth.name;
config.user.password = myAuth.pass;
next();
}
}).use(cookieParser()).get('/ssh/host/:host?', function(req, res) {
res.sendFile(path.join(__dirname + '/public/client.htm'));
config.ssh.host = req.params.host;
if (typeof req.query.port !== 'undefined' && req.query.port !== null){ config.host.port = req.query.port;}
if (typeof req.query.header !== 'undefined' && req.query.header !== null){ config.header.text = req.query.header;}
if (typeof req.query.headerBackground !== 'undefined' && req.query.headerBackground !== null){ config.header.background = req.query.headerBackground;}
console.log ('webssh2 Login: user=' + config.user.name + ' from=' + req.ip + ' host=' + config.ssh.host + ' port=' + config.ssh.port + ' sessionID=' + req.headers['sessionid'] + ' allowreplay=' + req.headers['allowreplay']);
console.log ('Headers: ' + JSON.stringify(req.headers));
config.options.allowreplay = req.headers['allowreplay'];
}).use('/style',express.static(__dirname + '/public')).use('/src',express.static(__dirname + '/node_modules/xterm/dist')).use('/addons',express.static(__dirname + '/node_modules/xterm/dist/addons'));
io.on('connection', function(socket) {
var conn = new ssh();
conn.on('banner', function(d) {
//need to convert to cr/lf for proper formatting
d = d.replace(/\r?\n/g, "\r\n");
socket.emit('data', d.toString('binary'));
}).on('ready', function() {
socket.emit('title', 'ssh://' + config.ssh.host);
socket.emit('headerBackground', config.header.background);
socket.emit('header', config.header.text);
socket.emit('footer', 'ssh://' + config.user.name + '@' + config.ssh.host + ':' + config.ssh.port);
socket.emit('status', 'SSH CONNECTION ESTABLISHED');
socket.emit('statusBackground', 'green');
socket.emit('allowreplay', config.options.allowreplay)
conn.shell(function(err, stream) {
if (err) {
console.log (err.message);
myError = myError + err.message
return socket.emit('status', 'SSH EXEC ERROR: ' + err.message).emit('statusBackground', 'red');
}
socket.on('data', function(data) {
stream.write(data);
});
socket.on('control', function(controlData) {
switch(controlData) {
case 'replayCredentials':
stream.write(config.user.password + '\n');
default:
console.log ('controlData: '+ controlData);
};
});
stream.on('data', function(d) {
socket.emit('data', d.toString('binary'));
}).on('close', function(code, signal) {
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal);
conn.end();
}).stderr.on('data', function(data) {
console.log('STDERR: ' + data);
});
});
}).on('end', function() {
socket.emit('status', 'SSH CONNECTION CLOSED BY HOST' + myError);
socket.emit('statusBackground', 'red');
}).on('close', function() {
socket.emit('status', 'SSH CONNECTION CLOSE' + myError);
socket.emit('statusBackground', 'red');
}).on('error', function(err) {
myError = myError + err
socket.emit('status', 'SSH CONNECTION ERROR' + myError);
socket.emit('statusBackground', 'red');
console.log('on.error' + myError);
}).on('keyboard-interactive', function(name, instructions, instructionsLang, prompts, finish) {
console.log('Connection :: keyboard-interactive');
finish([config.user.password]);
}).connect({
host: config.ssh.host,
port: config.ssh.port,
username: config.user.name,
password: config.user.password,
tryKeyboard: true,
// some cisco routers need the these cipher strings
algorithms: {
'cipher': ['aes128-cbc', '3des-cbc', 'aes256-cbc'],
'hmac': ['hmac-sha1', 'hmac-sha1-96', 'hmac-md5-96']
}
});
});