refactoring
moved some events into socket/index.js to better organize code created session.ssh property for application session variables moved crypto algorithms to config.json and assigned to ..session.ssh.algorithms variable
This commit is contained in:
parent
938e2fbfa4
commit
2e912dd9cc
6 changed files with 217 additions and 170 deletions
|
@ -5,12 +5,17 @@
|
||||||
- Snyk, Bithound, Travis CI
|
- Snyk, Bithound, Travis CI
|
||||||
- Cross platform improvements (path mappings)
|
- Cross platform improvements (path mappings)
|
||||||
- Session fixup between Express and Socket.io
|
- Session fixup between Express and Socket.io
|
||||||
|
- Session secret settings in config.json
|
||||||
- env variable `DEBUG=ssh2` will put the `ssh2` module into debug mode
|
- env variable `DEBUG=ssh2` will put the `ssh2` module into debug mode
|
||||||
- env variable `debug=WebSSH2` will output additional debug messages for functions
|
- env variable `DEBUG=WebSSH2` will output additional debug messages for functions
|
||||||
and events in the application (not including the ssh2 module debug)
|
and events in the application (not including the ssh2 module debug)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- erorr handling in public/client.js
|
- erorr handling in public/client.js
|
||||||
|
- moved socket.io operations to their own file /socket/index.js, more changes like this to come (./socket/index.js)
|
||||||
|
- all session based variables are now under the req.session.ssh property or socket.request.ssh (./index.js)
|
||||||
|
- moved SSH algorithms to config.json and defined as a session variable (..session.ssh.algorithms)
|
||||||
|
-- prep for future feature to define algorithims in header or some other method to enable seperate ciphers per host
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Multiple errors may ovewrite status bar which would cause confusion as to what originally caused the error. Example, ssh server disconnects which prompts a cascade of events (conn.on('end'), socket.on('disconnect'), conn.on('close')) and the original reason (conn.on('end')) would be lost and the user would erroneously receive a WEBSOCKET error as the last event to fire would be the websocket connection closing from the app.
|
- Multiple errors may ovewrite status bar which would cause confusion as to what originally caused the error. Example, ssh server disconnects which prompts a cascade of events (conn.on('end'), socket.on('disconnect'), conn.on('close')) and the original reason (conn.on('end')) would be lost and the user would erroneously receive a WEBSOCKET error as the last event to fire would be the websocket connection closing from the app.
|
||||||
|
|
19
config.json
19
config.json
|
@ -10,17 +10,32 @@
|
||||||
"ssh": {
|
"ssh": {
|
||||||
"host": null,
|
"host": null,
|
||||||
"port": 22,
|
"port": 22,
|
||||||
"term": "xterm-color",
|
"term": "xterm-color"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"text": "My Header",
|
"text": "My Header",
|
||||||
"background": "green"
|
"background": "green"
|
||||||
},
|
},
|
||||||
"session": {
|
"session": {
|
||||||
"name": "WebSSH2id",
|
"name": "WebSSH2",
|
||||||
"secret": "mysecret"
|
"secret": "mysecret"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"challengeButton": true
|
"challengeButton": true
|
||||||
|
},
|
||||||
|
"algorithms": {
|
||||||
|
"cipher": [
|
||||||
|
"aes128-cbc",
|
||||||
|
"3des-cbc",
|
||||||
|
"aes256-cbc",
|
||||||
|
"aes128-ctr",
|
||||||
|
"aes192-ctr",
|
||||||
|
"aes256-ctr"
|
||||||
|
],
|
||||||
|
"hmac": [
|
||||||
|
"hmac-sha1",
|
||||||
|
"hmac-sha1-96",
|
||||||
|
"hmac-md5-96"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
194
index.js
194
index.js
|
@ -8,40 +8,17 @@ var app = express()
|
||||||
var server = require('http').Server(app)
|
var server = require('http').Server(app)
|
||||||
var io = require('socket.io')(server)
|
var io = require('socket.io')(server)
|
||||||
var path = require('path')
|
var path = require('path')
|
||||||
var SSH = require('ssh2').Client
|
|
||||||
var config = require('read-config')(path.join(__dirname, 'config.json'))
|
var config = require('read-config')(path.join(__dirname, 'config.json'))
|
||||||
var debug = require('debug')
|
// var debug = require('debug')
|
||||||
var debugWebSSH2 = debug('WebSSH2')
|
// var debugWebSSH2 = debug('WebSSH2')
|
||||||
var util = require('./util')
|
var myutil = require('./util')
|
||||||
var SocketUtil = require('./socket')
|
var socket = require('./socket/index.js')
|
||||||
var session = require('express-session')({
|
var session = require('express-session')({
|
||||||
secret: config.session.secret,
|
secret: config.session.secret,
|
||||||
name: config.session.name,
|
name: config.session.name,
|
||||||
resave: true,
|
resave: true,
|
||||||
saveUninitialized: false
|
saveUninitialized: false,
|
||||||
})
|
unset: 'destroy'
|
||||||
var colors = require('colors/safe')
|
|
||||||
var termCols, termRows
|
|
||||||
// var LogPrefix
|
|
||||||
// var dataBuffer = ''
|
|
||||||
|
|
||||||
// server
|
|
||||||
|
|
||||||
server.listen({
|
|
||||||
host: config.listen.ip,
|
|
||||||
port: config.listen.port
|
|
||||||
})
|
|
||||||
|
|
||||||
server.on('error', function (err) {
|
|
||||||
if (err.code === 'EADDRINUSE') {
|
|
||||||
config.listen.port++
|
|
||||||
console.warn('Address in use, retrying on port ' + config.listen.port)
|
|
||||||
setTimeout(function () {
|
|
||||||
server.listen(config.listen.port)
|
|
||||||
}, 250)
|
|
||||||
} else {
|
|
||||||
console.log('server.listen ERROR: ' + err.code)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// express
|
// express
|
||||||
|
@ -58,30 +35,33 @@ var expressOptions = {
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use(session)
|
app.use(session)
|
||||||
app.use(util.basicAuth)
|
app.use(myutil.basicAuth)
|
||||||
|
|
||||||
app.disable('x-powered-by')
|
app.disable('x-powered-by')
|
||||||
|
|
||||||
app.use(express.static(path.join(__dirname, 'public'), expressOptions))
|
|
||||||
|
|
||||||
app.get('/ssh/host/:host?', function (req, res, next) {
|
app.get('/ssh/host/:host?', function (req, res, next) {
|
||||||
res.sendFile(path.join(path.join(__dirname, 'public', 'client.htm')))
|
res.sendFile(path.join(path.join(__dirname, 'public', 'client.htm')))
|
||||||
// capture url variables if defined
|
// capture and assign variables
|
||||||
config.ssh.host = req.params.host || config.ssh.host
|
req.session.ssh = {
|
||||||
config.ssh.port = req.query.port || config.ssh.port
|
host: req.params.host || config.ssh.host,
|
||||||
config.header.text = req.query.header || config.header.text
|
port: req.query.port || config.ssh.port,
|
||||||
config.header.background = req.query.headerBackground || config.header.background
|
header: {
|
||||||
console.log('webssh2 Login: user=' + req.session.username + ' from=' + req.ip + ' host=' + config.ssh.host + ' port=' + config.ssh.port + ' sessionID=' + req.sessionID + ' allowreplay=' + req.headers.allowreplay)
|
name: req.query.header || config.header.text,
|
||||||
debugWebSSH2('Headers: ' + colors.yellow(JSON.stringify(req.headers)))
|
background: req.query.headerBackground || config.header.background
|
||||||
config.options.allowreplay = req.headers.allowreplay
|
},
|
||||||
|
algorithms: config.algorithms,
|
||||||
|
term: config.ssh.term,
|
||||||
|
allowreplay: req.headers.allowreplay || false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// static files
|
||||||
|
app.use(express.static(path.join(__dirname, 'public'), expressOptions))
|
||||||
app.use('/style', express.static(path.join(__dirname, 'public')))
|
app.use('/style', express.static(path.join(__dirname, 'public')))
|
||||||
|
|
||||||
app.use('/src', express.static(path.join(__dirname, 'node_modules', 'xterm', 'dist')))
|
app.use('/src', express.static(path.join(__dirname, 'node_modules', 'xterm', 'dist')))
|
||||||
|
|
||||||
app.use('/addons', express.static(path.join(__dirname, 'node_modules', 'xterm', 'dist', 'addons')))
|
app.use('/addons', express.static(path.join(__dirname, 'node_modules', 'xterm', 'dist', 'addons')))
|
||||||
|
|
||||||
|
// express error handling
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
res.status(404).send("Sorry can't find that!")
|
res.status(404).send("Sorry can't find that!")
|
||||||
})
|
})
|
||||||
|
@ -92,124 +72,28 @@ app.use(function (err, req, res, next) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// socket.io
|
// socket.io
|
||||||
|
// expose express session with socket.request.session
|
||||||
io.use(function (socket, next) {
|
io.use(function (socket, next) {
|
||||||
if (socket.request.res) {
|
(socket.request.res) ? session(socket.request, socket.request.res, next) : next()
|
||||||
session(socket.request, socket.request.res, next)
|
|
||||||
} else {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
io.on('connection', function (socket) {
|
// bring up socket
|
||||||
// if websocket connection arrives without an express session, kill it
|
io.on('connection', socket)
|
||||||
if (!socket.request.session) {
|
|
||||||
socket.disconnect(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var socketutil = new SocketUtil(socket, io)
|
|
||||||
var conn = new SSH()
|
|
||||||
socket.on('geometry', function (cols, rows) {
|
|
||||||
termCols = cols
|
|
||||||
termRows = rows
|
|
||||||
})
|
|
||||||
|
|
||||||
conn.on('banner', function (d) {
|
// server
|
||||||
// need to convert to cr/lf for proper formatting
|
server.listen({
|
||||||
d = d.replace(/\r?\n/g, '\r\n')
|
host: config.listen.ip,
|
||||||
socket.emit('data', d.toString('binary'))
|
port: config.listen.port
|
||||||
})
|
})
|
||||||
|
|
||||||
conn.on('ready', function () {
|
server.on('error', function (err) {
|
||||||
socket.emit('title', 'ssh://' + config.ssh.host)
|
if (err.code === 'EADDRINUSE') {
|
||||||
socket.emit('headerBackground', config.header.background)
|
config.listen.port++
|
||||||
socket.emit('header', config.header.text)
|
console.warn('Address in use, retrying on port ' + config.listen.port)
|
||||||
socket.emit('footer', 'ssh://' + socket.request.session.username + '@' + config.ssh.host + ':' + config.ssh.port)
|
setTimeout(function () {
|
||||||
socket.emit('status', 'SSH CONNECTION ESTABLISHED')
|
server.listen(config.listen.port)
|
||||||
socket.emit('statusBackground', config.header.background)
|
}, 250)
|
||||||
socket.emit('allowreplay', config.options.allowreplay)
|
|
||||||
|
|
||||||
conn.shell({
|
|
||||||
term: config.ssh.term,
|
|
||||||
cols: termCols,
|
|
||||||
rows: termRows
|
|
||||||
}, function (err, stream) {
|
|
||||||
if (err) {
|
|
||||||
socketutil.SSHerror('EXEC ERROR' + err)
|
|
||||||
conn.end()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
socket.on('data', function (data) {
|
|
||||||
stream.write(data)
|
|
||||||
// poc to log commands from client
|
|
||||||
// if (data === '\r') {
|
|
||||||
// console.log(LogPrefix + ': ' + dataBuffer)
|
|
||||||
// dataBuffer = ''
|
|
||||||
// } else {
|
|
||||||
// dataBuffer = dataBuffer + data
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
socket.on('control', function (controlData) {
|
|
||||||
switch (controlData) {
|
|
||||||
case 'replayCredentials':
|
|
||||||
stream.write(socket.request.session.userpassword + '\n')
|
|
||||||
/* falls through */
|
|
||||||
default:
|
|
||||||
console.log('controlData: ' + controlData)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.on('disconnecting', function (reason) { debugWebSSH2('SOCKET DISCONNECTING: ' + reason) })
|
|
||||||
|
|
||||||
socket.on('disconnect', function (reason) {
|
|
||||||
debugWebSSH2('SOCKET DISCONNECT: ' + reason)
|
|
||||||
err = { message: reason }
|
|
||||||
socketutil.SSHerror('CLIENT SOCKET DISCONNECT', err)
|
|
||||||
conn.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.on('error', function (error) { debugWebSSH2('SOCKET ERROR: ' + JSON.stringify(error)) })
|
|
||||||
|
|
||||||
stream.on('data', function (d) { socket.emit('data', d.toString('binary')) })
|
|
||||||
|
|
||||||
stream.on('close', function (code, signal) {
|
|
||||||
err = { message: ((code || signal) ? (((code) ? 'CODE: ' + code : '') + ((code && signal) ? ' ' : '') + ((signal) ? 'SIGNAL: ' + signal : '')) : undefined) }
|
|
||||||
socketutil.SSHerror('STREAM CLOSE', err)
|
|
||||||
conn.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
stream.stderr.on('data', function (data) {
|
|
||||||
console.log('STDERR: ' + data)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
conn.on('end', function (err) { socketutil.SSHerror('CONN END BY HOST', err) })
|
|
||||||
conn.on('close', function (err) { socketutil.SSHerror('CONN CLOSE', err) })
|
|
||||||
conn.on('error', function (err) { socketutil.SSHerror('CONN ERROR', err) })
|
|
||||||
|
|
||||||
conn.on('keyboard-interactive', function (name, instructions, instructionsLang, prompts, finish) {
|
|
||||||
debugWebSSH2('Connection :: keyboard-interactive')
|
|
||||||
finish([socket.request.session.userpassword])
|
|
||||||
})
|
|
||||||
if (socket.request.session.username && socket.request.session.userpassword) {
|
|
||||||
conn.connect({
|
|
||||||
host: config.ssh.host,
|
|
||||||
port: config.ssh.port,
|
|
||||||
username: socket.request.session.username,
|
|
||||||
password: socket.request.session.userpassword,
|
|
||||||
tryKeyboard: true,
|
|
||||||
// some cisco routers need the these cipher strings
|
|
||||||
algorithms: {
|
|
||||||
'cipher': ['aes128-cbc', '3des-cbc', 'aes256-cbc', 'aes128-ctr', 'aes192-ctr', 'aes256-ctr'],
|
|
||||||
'hmac': ['hmac-sha1', 'hmac-sha1-96', 'hmac-md5-96']
|
|
||||||
},
|
|
||||||
debug: debug('ssh2')
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('Attempt to connect without session.username/password defined, potentially previously abandoned client session. disconnecting websocket client.\r\nHandshake information: \r\n ' + JSON.stringify(socket.handshake))
|
console.log('server.listen ERROR: ' + err.code)
|
||||||
socket.emit('statusBackground', 'red')
|
|
||||||
socket.emit('status', 'WEBSOCKET ERROR - Reload and try again')
|
|
||||||
socket.disconnect(true)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -39,10 +39,12 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index",
|
"start": "node index",
|
||||||
"test": "snyk test"
|
"test": "snyk test",
|
||||||
|
"watch": "nodemon index.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bithound": "^1.7.0",
|
"bithound": "^1.7.0",
|
||||||
|
"nodemon": "^1.11.0",
|
||||||
"snyk": "^1.30.1"
|
"snyk": "^1.30.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
147
socket/index.js
147
socket/index.js
|
@ -1,12 +1,143 @@
|
||||||
var debug = require('debug')('WebSSH2')
|
// private
|
||||||
var myError
|
var debug = require('debug')
|
||||||
|
var debugWebSSH2 = require('debug')('WebSSH2')
|
||||||
|
var SSH = require('ssh2').Client
|
||||||
|
var termCols, termRows
|
||||||
|
|
||||||
module.exports = function (socket, io) {
|
// public
|
||||||
this.SSHerror = function (myFunc, err) {
|
module.exports = function (socket) {
|
||||||
myError = (myError) || ((err) ? err.message : undefined)
|
function SSHerror (myFunc, err) {
|
||||||
var thisError = (myError) ? ': ' + myError : ''
|
socket.request.session.error = (socket.request.session.error) || ((err) ? err.message : undefined)
|
||||||
debug('SSH ' + myFunc + thisError)
|
var theError = (socket.request.session.error) ? ': ' + socket.request.session.error : ''
|
||||||
socket.emit('ssherror', 'SSH ' + myFunc + thisError)
|
// log unsuccessful login attempt
|
||||||
|
if (err && (err.level === 'client-authentication')) {
|
||||||
|
console.log('webssh2 ' + 'error: Authentication failure'.red.bold +
|
||||||
|
' user=' + socket.request.session.username.yellow.bold.underline +
|
||||||
|
' from=' + socket.handshake.address.yellow.bold.underline)
|
||||||
|
}
|
||||||
|
switch (myFunc) {
|
||||||
|
case 'STREAM CLOSE':
|
||||||
|
debugWebSSH2('SSH ' + myFunc + theError.red)
|
||||||
|
socket.emit('ssherror', 'SSH ' + myFunc + theError)
|
||||||
|
socket.disconnect(true)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
debugWebSSH2('SSHerror: default'.red)
|
||||||
|
debugWebSSH2('SSH ' + myFunc + theError)
|
||||||
|
socket.emit('ssherror', 'SSH ' + myFunc + theError)
|
||||||
|
socket.disconnect(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
var conn = new SSH()
|
||||||
|
socket.on('geometry', function (cols, rows) {
|
||||||
|
termCols = cols
|
||||||
|
termRows = rows
|
||||||
|
})
|
||||||
|
console.log('webssh2 ' + 'IO ON:'.cyan.bold + ' 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)
|
||||||
|
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'))
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.on('ready', function () {
|
||||||
|
console.log('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 + ' allowreplay=' + socket.request.session.ssh.allowreplay + ' term=' + socket.request.session.ssh.term)
|
||||||
|
socket.emit('title', 'ssh://' + socket.request.session.ssh.host)
|
||||||
|
socket.emit('headerBackground', socket.request.session.ssh.header.background)
|
||||||
|
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', socket.request.session.ssh.header.background)
|
||||||
|
socket.emit('allowreplay', socket.request.session.ssh.allowreplay)
|
||||||
|
|
||||||
|
conn.shell({
|
||||||
|
term: socket.request.session.ssh.term,
|
||||||
|
cols: termCols,
|
||||||
|
rows: termRows
|
||||||
|
}, function (err, stream) {
|
||||||
|
if (err) {
|
||||||
|
SSHerror('EXEC ERROR' + err)
|
||||||
|
conn.end()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// poc to log commands from client
|
||||||
|
// var dataBuffer
|
||||||
|
socket.on('data', function (data) {
|
||||||
|
stream.write(data)
|
||||||
|
// poc to log commands from client
|
||||||
|
// if (data === '\r') {
|
||||||
|
// console.log(socket.request.session.id + '/' + socket.id + ' command: ' + socket.request.session.ssh.host + ': ' + dataBuffer)
|
||||||
|
// dataBuffer = undefined
|
||||||
|
// } else {
|
||||||
|
// dataBuffer = (dataBuffer) ? dataBuffer + data : data
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
socket.on('control', function (controlData) {
|
||||||
|
switch (controlData) {
|
||||||
|
case 'replayCredentials':
|
||||||
|
stream.write(socket.request.session.userpassword + '\n')
|
||||||
|
/* falls through */
|
||||||
|
default:
|
||||||
|
console.log('controlData: ' + controlData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('disconnecting', function (reason) { debugWebSSH2('SOCKET DISCONNECTING: ' + reason) })
|
||||||
|
|
||||||
|
socket.on('disconnect', function (reason) {
|
||||||
|
debugWebSSH2('SOCKET DISCONNECT: ' + reason)
|
||||||
|
err = { message: reason }
|
||||||
|
SSHerror('CLIENT SOCKET DISCONNECT', err)
|
||||||
|
conn.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('error', function (error) { debugWebSSH2('SOCKET ERROR: ' + JSON.stringify(error)) })
|
||||||
|
|
||||||
|
stream.on('data', function (d) { socket.emit('data', d.toString('binary')) })
|
||||||
|
|
||||||
|
stream.on('close', function (code, signal) {
|
||||||
|
err = { message: ((code || signal) ? (((code) ? 'CODE: ' + code : '') + ((code && signal) ? ' ' : '') + ((signal) ? 'SIGNAL: ' + signal : '')) : undefined) }
|
||||||
|
SSHerror('STREAM CLOSE', err)
|
||||||
|
conn.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
stream.stderr.on('data', function (data) {
|
||||||
|
console.log('STDERR: ' + data)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.on('end', function (err) { SSHerror('CONN END BY HOST', err) })
|
||||||
|
conn.on('close', function (err) { SSHerror('CONN CLOSE', err) })
|
||||||
|
conn.on('error', function (err) { SSHerror('CONN ERROR', err) })
|
||||||
|
|
||||||
|
conn.on('keyboard-interactive', function (name, instructions, instructionsLang, prompts, finish) {
|
||||||
|
debugWebSSH2('conn.on(\'keyboard-interactive\')')
|
||||||
|
finish([socket.request.session.userpassword])
|
||||||
|
})
|
||||||
|
if (socket.request.session.username && socket.request.session.userpassword) {
|
||||||
|
conn.connect({
|
||||||
|
host: socket.request.session.ssh.host,
|
||||||
|
port: socket.request.session.ssh.port,
|
||||||
|
username: socket.request.session.username,
|
||||||
|
password: socket.request.session.userpassword,
|
||||||
|
tryKeyboard: true,
|
||||||
|
// some cisco routers need the these cipher strings
|
||||||
|
algorithms: socket.request.session.ssh.algorithms,
|
||||||
|
debug: debug('ssh2')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.warn('Attempt to connect without session.username/password defined, potentially previously abandoned client session. disconnecting websocket client.\r\nHandshake information: \r\n ' + JSON.stringify(socket.handshake))
|
||||||
|
socket.emit('statusBackground', 'red')
|
||||||
|
socket.emit('status', 'WEBSOCKET ERROR - Reload and try again')
|
||||||
socket.disconnect(true)
|
socket.disconnect(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
// private
|
||||||
|
require('colors') // allow for color property extensions in log messages
|
||||||
var debug = require('debug')('WebSSH2')
|
var debug = require('debug')('WebSSH2')
|
||||||
var colors = require('colors')
|
|
||||||
var Auth = require('basic-auth')
|
var Auth = require('basic-auth')
|
||||||
|
var util = require('util')
|
||||||
|
|
||||||
console.warn = makeColorConsole(console.warn, 'yellow')
|
console.warn = makeColorConsole(console.warn, 'yellow')
|
||||||
console.error = makeColorConsole(console.error, 'red')
|
console.error = makeColorConsole(console.error, 'red')
|
||||||
|
|
||||||
|
// public
|
||||||
function makeColorConsole (fct, color) {
|
function makeColorConsole (fct, color) {
|
||||||
return function () {
|
return function () {
|
||||||
for (var i in arguments) {
|
for (var i in arguments) {
|
||||||
|
@ -19,7 +22,7 @@ exports.basicAuth = function (req, res, next) {
|
||||||
if (myAuth) {
|
if (myAuth) {
|
||||||
req.session.username = myAuth.name
|
req.session.username = myAuth.name
|
||||||
req.session.userpassword = myAuth.pass
|
req.session.userpassword = myAuth.pass
|
||||||
debug('myAuth.name: ' + myAuth.name + ' and password ' + ((myAuth.pass) ? 'exists' : 'is blank'.underline.red))
|
debug('myAuth.name: ' + myAuth.name.yellow.bold.underline + ' and password ' + ((myAuth.pass) ? 'exists'.yellow.bold.underline : 'is blank'.underline.red.bold))
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
res.statusCode = 401
|
res.statusCode = 401
|
||||||
|
@ -28,3 +31,10 @@ exports.basicAuth = function (req, res, next) {
|
||||||
res.end('Username and password required for web SSH service.')
|
res.end('Username and password required for web SSH service.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expects headers to be a JSON object, will replace authroization header with 'Sanatized//Exists'
|
||||||
|
// we don't want to log basic auth header since it contains a password...
|
||||||
|
exports.SanatizeHeaders = function (headers) {
|
||||||
|
if (headers.authorization) { headers.authorization = 'Sanitized//Exists' }
|
||||||
|
return (headers)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue