webssh2/index.js
2017-05-08 12:07:47 -07:00

151 lines
5.5 KiB
JavaScript

/*
* 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(path.join(__dirname, 'config.json')),
myError = ' - ',
termCols,
termRows
// 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(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
})
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')
/* 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']
}
})
})