error handling fixups
added some additional error handing functions and debugging points - `DEBUG=ssh` will put the ssh2 module into debug mode - `debug=WebSSH2` will output additional debug messages for functions and events in the application (not including the ssh2 module debug) - created socket/index.js to start the process of separating out app functions, just holds error logging function at this point - corrected some events on public/client.js so the primary error cause is not overwritten - ensure that ssh connection is terminated when websocked is disconnected by the client
This commit is contained in:
parent
e3b8ff8189
commit
2c1c3ac911
3 changed files with 85 additions and 59 deletions
107
index.js
107
index.js
|
@ -11,7 +11,9 @@ var path = require('path')
|
||||||
var SSH = require('ssh2').Client
|
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 util = require('./util')
|
var util = require('./util')
|
||||||
|
var SocketUtil = require('./socket')
|
||||||
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,
|
||||||
|
@ -22,17 +24,7 @@ var termCols, termRows, myError
|
||||||
// var LogPrefix
|
// var LogPrefix
|
||||||
// var dataBuffer = ''
|
// var dataBuffer = ''
|
||||||
|
|
||||||
var expressOptions = {
|
// server
|
||||||
dotfiles: 'ignore',
|
|
||||||
etag: false,
|
|
||||||
extensions: ['htm', 'html'],
|
|
||||||
index: false,
|
|
||||||
maxAge: '1s',
|
|
||||||
redirect: false,
|
|
||||||
setHeaders: function (res, path, stat) {
|
|
||||||
res.set('x-timestamp', Date.now())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server.listen({
|
server.listen({
|
||||||
host: config.listen.ip,
|
host: config.listen.ip,
|
||||||
|
@ -51,17 +43,22 @@ server.on('error', function (err) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// express
|
||||||
|
var expressOptions = {
|
||||||
|
dotfiles: 'ignore',
|
||||||
|
etag: false,
|
||||||
|
extensions: ['htm', 'html'],
|
||||||
|
index: false,
|
||||||
|
maxAge: '1s',
|
||||||
|
redirect: false,
|
||||||
|
setHeaders: function (res, path, stat) {
|
||||||
|
res.set('x-timestamp', Date.now())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
app.use(session)
|
app.use(session)
|
||||||
app.use(util.basicAuth)
|
app.use(util.basicAuth)
|
||||||
|
|
||||||
io.use(function (socket, next) {
|
|
||||||
if (socket.request.res) {
|
|
||||||
session(socket.request, socket.request.res, next)
|
|
||||||
} else {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.disable('x-powered-by')
|
app.disable('x-powered-by')
|
||||||
|
|
||||||
app.use(express.static(path.join(__dirname, 'public'), expressOptions))
|
app.use(express.static(path.join(__dirname, 'public'), expressOptions))
|
||||||
|
@ -85,13 +82,32 @@ app.use('/src', express.static(path.join(__dirname, 'node_modules', 'xterm', 'di
|
||||||
|
|
||||||
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')))
|
||||||
|
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
res.status(404).send("Sorry can't find that!")
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(function (err, req, res, next) {
|
||||||
|
console.error(err.stack)
|
||||||
|
res.status(500).send('Something broke!')
|
||||||
|
})
|
||||||
|
|
||||||
|
// socket.io
|
||||||
|
|
||||||
|
io.use(function (socket, next) {
|
||||||
|
if (socket.request.res) {
|
||||||
|
session(socket.request, socket.request.res, next)
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
io.on('connection', function (socket) {
|
io.on('connection', function (socket) {
|
||||||
// if websocket connection arrives without an express session, kill it
|
// if websocket connection arrives without an express session, kill it
|
||||||
if (!socket.request.session) {
|
if (!socket.request.session) {
|
||||||
socket.disconnect(true)
|
socket.disconnect(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var socketutil = new SocketUtil(socket, io)
|
||||||
var conn = new SSH()
|
var conn = new SSH()
|
||||||
socket.on('geometry', function (cols, rows) {
|
socket.on('geometry', function (cols, rows) {
|
||||||
termCols = cols
|
termCols = cols
|
||||||
|
@ -119,12 +135,9 @@ io.on('connection', function (socket) {
|
||||||
rows: termRows
|
rows: termRows
|
||||||
}, function (err, stream) {
|
}, function (err, stream) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err.message)
|
socketutil.SSHerror('EXEC ERROR' + err)
|
||||||
myError = err.message
|
conn.end()
|
||||||
socket.emit('status', 'SSH EXEC ERROR: ' + err.message)
|
return
|
||||||
socket.emit('statusBackground', 'red')
|
|
||||||
console.log('conn.shell err: ' + err.message)
|
|
||||||
return socket.close(true)
|
|
||||||
}
|
}
|
||||||
socket.on('data', function (data) {
|
socket.on('data', function (data) {
|
||||||
stream.write(data)
|
stream.write(data)
|
||||||
|
@ -145,15 +158,24 @@ io.on('connection', function (socket) {
|
||||||
console.log('controlData: ' + controlData)
|
console.log('controlData: ' + controlData)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
socket.on('disconnecting', function (reason) { debugWebSSH2('SOCKET DISCONNECTING: ' + reason) })
|
||||||
|
|
||||||
stream.on('data', function (d) {
|
socket.on('disconnect', function (reason) {
|
||||||
socket.emit('data', d.toString('binary'))
|
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) {
|
stream.on('close', function (code, signal) {
|
||||||
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal)
|
err = { message: ((code||signal) ? (((code)? 'CODE: ' + code: '') + ((code&&signal)? ' ':'') + ((signal)? 'SIGNAL: ' + signal : '')) : undefined) }
|
||||||
|
socketutil.SSHerror('STREAM CLOSE', err)
|
||||||
conn.end()
|
conn.end()
|
||||||
socket.disconnect()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
stream.stderr.on('data', function (data) {
|
stream.stderr.on('data', function (data) {
|
||||||
|
@ -162,27 +184,12 @@ io.on('connection', function (socket) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
conn.on('end', function () {
|
conn.on('end', function (err) { socketutil.SSHerror('CONN END BY HOST', err) })
|
||||||
socket.emit('status', 'SSH CONNECTION CLOSED BY HOST ' + myError)
|
conn.on('close', function (err) { socketutil.SSHerror('CONN CLOSE', err) })
|
||||||
socket.emit('statusBackground', 'red')
|
conn.on('error', function (err) { socketutil.SSHerror('CONN ERROR', err ) })
|
||||||
socket.disconnect()
|
|
||||||
})
|
|
||||||
|
|
||||||
conn.on('close', function () {
|
|
||||||
socket.emit('status', 'SSH CONNECTION CLOSE ' + myError)
|
|
||||||
socket.emit('statusBackground', 'red')
|
|
||||||
socket.disconnect()
|
|
||||||
})
|
|
||||||
|
|
||||||
conn.on('error', function (err) {
|
|
||||||
myError = err
|
|
||||||
socket.emit('status', 'SSH CONNECTION ERROR ' + myError)
|
|
||||||
socket.emit('statusBackground', 'red')
|
|
||||||
console.error('conn.on(\'error\'): ' + myError)
|
|
||||||
})
|
|
||||||
|
|
||||||
conn.on('keyboard-interactive', function (name, instructions, instructionsLang, prompts, finish) {
|
conn.on('keyboard-interactive', function (name, instructions, instructionsLang, prompts, finish) {
|
||||||
console.log('Connection :: keyboard-interactive')
|
debugWebSSH2('Connection :: keyboard-interactive')
|
||||||
finish([socket.request.session.userpassword])
|
finish([socket.request.session.userpassword])
|
||||||
})
|
})
|
||||||
if (socket.request.session.username && socket.request.session.userpassword) {
|
if (socket.request.session.username && socket.request.session.userpassword) {
|
||||||
|
@ -197,7 +204,7 @@ io.on('connection', function (socket) {
|
||||||
'cipher': ['aes128-cbc', '3des-cbc', 'aes256-cbc', 'aes128-ctr', 'aes192-ctr', 'aes256-ctr'],
|
'cipher': ['aes128-cbc', '3des-cbc', 'aes256-cbc', 'aes128-ctr', 'aes192-ctr', 'aes256-ctr'],
|
||||||
'hmac': ['hmac-sha1', 'hmac-sha1-96', 'hmac-md5-96']
|
'hmac': ['hmac-sha1', 'hmac-sha1-96', 'hmac-md5-96']
|
||||||
},
|
},
|
||||||
debug: debug('WebSSH2:debug')
|
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.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))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
var sessionLogEnable = false
|
var sessionLogEnable = false
|
||||||
var sessionLog, sessionFooter, logDate, currentDate, myFile
|
var sessionLog, sessionFooter, logDate, currentDate, myFile, errorExists
|
||||||
|
|
||||||
// replay password to server, requires
|
// replay password to server, requires
|
||||||
function replayCredentials () {
|
function replayCredentials () {
|
||||||
|
@ -84,6 +84,10 @@ socket.on('connect', function () {
|
||||||
document.title = data
|
document.title = data
|
||||||
}).on('status', function (data) {
|
}).on('status', function (data) {
|
||||||
document.getElementById('status').innerHTML = data
|
document.getElementById('status').innerHTML = data
|
||||||
|
}).on('ssherror', function (data) {
|
||||||
|
document.getElementById('status').innerHTML = data
|
||||||
|
document.getElementById('status').style.backgroundColor = 'red'
|
||||||
|
errorExists = true
|
||||||
}).on('headerBackground', function (data) {
|
}).on('headerBackground', function (data) {
|
||||||
document.getElementById('header').style.backgroundColor = data
|
document.getElementById('header').style.backgroundColor = data
|
||||||
}).on('header', function (data) {
|
}).on('header', function (data) {
|
||||||
|
@ -105,12 +109,16 @@ socket.on('connect', function () {
|
||||||
if (sessionLogEnable) {
|
if (sessionLogEnable) {
|
||||||
sessionLog = sessionLog + data
|
sessionLog = sessionLog + data
|
||||||
}
|
}
|
||||||
})// .on('disconnect', function (err) {
|
}).on('disconnect', function (err) {
|
||||||
// document.getElementById('status').style.backgroundColor = 'red'
|
if (!errorExists) {
|
||||||
// document.getElementById('status').innerHTML = 'WEBSOCKET SERVER DISCONNECTED' + err
|
document.getElementById('status').style.backgroundColor = 'red'
|
||||||
// socket.io.reconnection(false)
|
document.getElementById('status').innerHTML = 'WEBSOCKET SERVER DISCONNECTED: ' + err
|
||||||
// })//.on('error', function (err) {
|
}
|
||||||
// document.getElementById('status').style.backgroundColor = 'red'
|
socket.io.reconnection(false)
|
||||||
// document.getElementById('status').innerHTML = 'ERROR ' + err
|
}).on('error', function (err) {
|
||||||
// })
|
if (!errorExists) {
|
||||||
|
document.getElementById('status').style.backgroundColor = 'red'
|
||||||
|
document.getElementById('status').innerHTML = 'ERROR: ' + err
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
11
socket/index.js
Normal file
11
socket/index.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
var myError = myError
|
||||||
|
|
||||||
|
module.exports = function (socket, io) {
|
||||||
|
this.SSHerror = function (myFunc, err) {
|
||||||
|
myError = (myError) ? myError : ((err) ? err.message:undefined)
|
||||||
|
thisError = (myError) ? ': ' + myError : ''
|
||||||
|
console.error('SSH ' + myFunc + thisError)
|
||||||
|
socket.emit('ssherror', 'SSH ' + myFunc + thisError)
|
||||||
|
socket.disconnect(true)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue