diff --git a/index.js b/index.js
index 9264974..1628572 100644
--- a/index.js
+++ b/index.js
@@ -1,151 +1,151 @@
/*
* WebSSH2 - Web to SSH2 gateway
* Bill Church - https://github.com/billchurch - April 2016
- *
+ *
*/
var express = require('express'),
- app = express(),
- cookieParser = require('cookie-parser'),
- server = require('http').Server(app),
- io = require('socket.io')(server),
- path = require('path'),
- basicAuth = require('basic-auth'),
- ssh = require('ssh2').Client,
- readConfig = require('read-config'),
- config = readConfig(__dirname + '/config.json'),
- myError = " - ",
- termCols,
- termRows;
+ app = express(),
+ cookieParser = require('cookie-parser'),
+ server = require('http').Server(app),
+ io = require('socket.io')(server),
+ path = require('path'),
+ basicAuth = require('basic-auth'),
+ ssh = require('ssh2').Client,
+ readConfig = require('read-config'),
+ config = readConfig(path.join(__dirname, 'config.json')),
+ myError = ' - ',
+ termCols,
+ termRows
-function logErrors(err, req, res, next) {
- console.error(err.stack);
- next(err);
-}
+// 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);
- }
-});
+ 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.ssh.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'));
+app.use(express.static(path.join(__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(path.join(__dirname, 'public', 'client.htm')))
+ config.ssh.host = req.params.host
+ if (typeof req.query.port !== 'undefined' && req.query.port !== null) {
+ config.ssh.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(path.join(__dirname, 'public'))).use('/src', express.static(path.join(__dirname, 'node_modules', 'xterm', 'dist'))).use('/addons', express.static(path.join(__dirname, 'node_modules', 'xterm', 'dist', 'addons')))
-io.on('connection', function(socket) {
- var conn = new ssh();
- socket.on('geometry', function(cols, rows) {
- termCols = cols;
- termRows = rows;
- });
+io.on('connection', function (socket) {
+ var conn = new ssh()
+ socket.on('geometry', function (cols, rows) {
+ termCols = cols
+ termRows = rows
+ })
- 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.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({
- term: config.ssh.term,
- cols: termCols,
- rows: termRows
- }, 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');
+ conn.shell({
+ term: config.ssh.term,
+ cols: termCols,
+ rows: termRows
+ }, 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')
/* falls through */
- 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();
- socket.disconnect();
- }).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');
- socket.disconnect();
- }).on('close', function() {
- socket.emit('status', 'SSH CONNECTION CLOSE' + myError);
- socket.emit('statusBackground', 'red');
- socket.disconnect();
- }).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']
+ default:
+ console.log('controlData: ' + controlData)
}
- });
-});
\ No newline at end of file
+ })
+
+ 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()
+ socket.disconnect()
+ }).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')
+ socket.disconnect()
+ }).on('close', function () {
+ socket.emit('status', 'SSH CONNECTION CLOSE' + myError)
+ socket.emit('statusBackground', 'red')
+ socket.disconnect()
+ }).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']
+ }
+ })
+})
diff --git a/public/client.js b/public/client.js
index beb0661..8f24d3f 100644
--- a/public/client.js
+++ b/public/client.js
@@ -1,117 +1,117 @@
var sessionLog,
- sessionLogEnable = false,
- sessionFooter,
- logDate;
+ sessionLogEnable = false,
+ sessionFooter,
+ logDate
-// replay password to server, requires
-function replayCredentials() {
- socket.emit('control', 'replayCredentials');
- console.log("replaying credentials");
- return false;
+// replay password to server, requires
+function replayCredentials () {
+ socket.emit('control', 'replayCredentials')
+ console.log('replaying credentials')
+ return false
}
-// Set variable to toggle log data from client/server to a varialble
+// Set variable to toggle log data from client/server to a varialble
// for later download
-function toggleLog() {
- if (sessionLogEnable == true) {
- sessionLogEnable = false;
- document.getElementById('toggleLog').innerHTML = 'Start Log';
- console.log("stopping log, " + sessionLogEnable);
- 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";
- logDate = currentDate;
- return false;
- } else {
- sessionLogEnable = true;
- document.getElementById('toggleLog').innerHTML = 'Logging - STOP LOG';
- document.getElementById('downloadLog').style.display = 'inline';
- console.log("starting log, " + sessionLogEnable);
- 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";
- logDate = currentDate;
- return false;
- }
+function toggleLog () {
+ if (sessionLogEnable === true) {
+ sessionLogEnable = false
+ document.getElementById('toggleLog').innerHTML = 'Start Log'
+ console.log('stopping log, ' + sessionLogEnable)
+ 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'
+ logDate = currentDate
+ return false
+ } else {
+ sessionLogEnable = true
+ document.getElementById('toggleLog').innerHTML = 'Logging - STOP LOG'
+ document.getElementById('downloadLog').style.display = 'inline'
+ console.log('starting log, ' + sessionLogEnable)
+ 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'
+ logDate = currentDate
+ return false
+ }
}
// cross browser method to "download" an element to the local system
// used for our client-side logging feature
-function downloadLog() {
- myFile = "WebSSH2-" + logDate.getFullYear() + (logDate.getMonth() + 1) + logDate.getDate() + "_" + logDate.getHours() + logDate.getMinutes() + logDate.getSeconds() + ".log";
+function downloadLog () {
+ myFile = 'WebSSH2-' + logDate.getFullYear() + (logDate.getMonth() + 1) + logDate.getDate() + '_' + logDate.getHours() + logDate.getMinutes() + logDate.getSeconds() + '.log'
// regex should eliminate escape sequences from being logged.
- var blob = new Blob([sessionLog.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '')], {
- type: 'text/plain'
- });
- if (window.navigator.msSaveOrOpenBlob) {
- window.navigator.msSaveBlob(blob, myFile);
- } else {
- var elem = window.document.createElement('a');
- elem.href = window.URL.createObjectURL(blob);
- elem.download = myFile;
- document.body.appendChild(elem);
- elem.click();
- document.body.removeChild(elem);
- }
+ var blob = new Blob([sessionLog.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '')], {
+ type: 'text/plain'
+ })
+ if (window.navigator.msSaveOrOpenBlob) {
+ window.navigator.msSaveBlob(blob, myFile)
+ } else {
+ var elem = window.document.createElement('a')
+ elem.href = window.URL.createObjectURL(blob)
+ elem.download = myFile
+ document.body.appendChild(elem)
+ elem.click()
+ document.body.removeChild(elem)
+ }
}
-document.getElementById('downloadLog').style.display = 'none';
-document.getElementById('credentials').style.display = 'none';
+document.getElementById('downloadLog').style.display = 'none'
+document.getElementById('credentials').style.display = 'none'
var terminalContainer = document.getElementById('terminal-container'),
- term = new Terminal({
- cursorBlink: true
- }),
- socket,
- termid;
-term.open(terminalContainer);
-term.fit();
+ term = new Terminal({
+ cursorBlink: true
+ }),
+ socket,
+ termid
+term.open(terminalContainer)
+term.fit()
if (document.location.pathname) {
- var parts = document.location.pathname.split('/'),
- base = parts.slice(0, parts.length - 1).join('/') + '/',
- resource = base.substring(1) + 'socket.io';
- socket = io.connect(null, {
- resource: resource
- });
+ var parts = document.location.pathname.split('/'),
+ base = parts.slice(0, parts.length - 1).join('/') + '/',
+ resource = base.substring(1) + 'socket.io'
+ socket = io.connect(null, {
+ resource: resource
+ })
} else {
- socket = io.connect();
+ socket = io.connect()
}
-socket.on('connect', function() {
- socket.emit('geometry', term.cols, term.rows);
- term.on('data', function(data) {
- socket.emit('data', data);
- });
- socket.on('title', function(data) {
- document.title = data;
- }).on('status', function(data) {
- document.getElementById('status').innerHTML = data;
- }).on('headerBackground', function(data) {
- document.getElementById('header').style.backgroundColor = data;
- }).on('header', function(data) {
- document.getElementById('header').innerHTML = data;
- }).on('footer', function(data) {
- sessionFooter = data;
- document.getElementById('footer').innerHTML = data;
- }).on('statusBackground', function(data) {
- document.getElementById('status').style.backgroundColor = data;
- }).on('allowreplay', function(data) {
- if (data == 'true') {
- console.log('allowreplay: ' + data);
- document.getElementById('credentials').style.display = 'inline';
- } else {
- document.getElementById('credentials').style.display = 'none';
- }
- }).on('data', function(data) {
- term.write(data);
- if (sessionLogEnable) {
- sessionLog = sessionLog + data
- }
- }).on('disconnect', function(err) {
- document.getElementById('status').style.backgroundColor = 'red';
- document.getElementById('status').innerHTML = 'WEBSOCKET SERVER DISCONNECTED' + err;
- socket.io.reconnection(false);
- }).on('error', function(err) {
- document.getElementById('status').style.backgroundColor = 'red';
- document.getElementById('status').innerHTML = 'ERROR ' + err;
- });
-});
+socket.on('connect', function () {
+ socket.emit('geometry', term.cols, term.rows)
+ term.on('data', function (data) {
+ socket.emit('data', data)
+ })
+ socket.on('title', function (data) {
+ document.title = data
+ }).on('status', function (data) {
+ document.getElementById('status').innerHTML = data
+ }).on('headerBackground', function (data) {
+ document.getElementById('header').style.backgroundColor = data
+ }).on('header', function (data) {
+ document.getElementById('header').innerHTML = data
+ }).on('footer', function (data) {
+ sessionFooter = data
+ document.getElementById('footer').innerHTML = data
+ }).on('statusBackground', function (data) {
+ document.getElementById('status').style.backgroundColor = data
+ }).on('allowreplay', function (data) {
+ if (data === 'true') {
+ console.log('allowreplay: ' + data)
+ document.getElementById('credentials').style.display = 'inline'
+ } else {
+ document.getElementById('credentials').style.display = 'none'
+ }
+ }).on('data', function (data) {
+ term.write(data)
+ if (sessionLogEnable) {
+ sessionLog = sessionLog + data
+ }
+ }).on('disconnect', function (err) {
+ document.getElementById('status').style.backgroundColor = 'red'
+ document.getElementById('status').innerHTML = 'WEBSOCKET SERVER DISCONNECTED' + err
+ socket.io.reconnection(false)
+ }).on('error', function (err) {
+ document.getElementById('status').style.backgroundColor = 'red'
+ document.getElementById('status').innerHTML = 'ERROR ' + err
+ })
+})