Add configuration option to restrict connections to specified subnets

Signed-off-by: Matt Oswalt <matt@keepingitclassless.net>
This commit is contained in:
Matt Oswalt 2019-11-22 23:02:04 -08:00
parent 9a96637cb4
commit ccbe327cd6
No known key found for this signature in database
GPG key ID: 90EBA1B26A6D1061
5 changed files with 30 additions and 3 deletions

View file

@ -111,6 +111,8 @@ docker run --name webssh2 -d -p 2222:2222 -v `pwd`/app/config.json:/usr/src/conf
* **ssh.keepaliveCountMax** - _integer_ - How many consecutive, unanswered SSH-level keepalive packets that can be sent to the server before disconnection (similar to OpenSSH's ServerAliveCountMax config option). **Default:** 10. * **ssh.keepaliveCountMax** - _integer_ - How many consecutive, unanswered SSH-level keepalive packets that can be sent to the server before disconnection (similar to OpenSSH's ServerAliveCountMax config option). **Default:** 10.
* **allowedSubnets** - _array_ - A list of subnets that the server is allowed to connect to via SSH. An empty array means all subnets are permitted; no restriction. **Default:** empty array.
* **terminal.cursorBlink** - _boolean_ - Cursor blinks (true), does not (false) **Default:** true. * **terminal.cursorBlink** - _boolean_ - Cursor blinks (true), does not (false) **Default:** true.
* **terminal.scrollback** - _integer_ - Lines in the scrollback buffer. **Default:** 10000. * **terminal.scrollback** - _integer_ - Lines in the scrollback buffer. **Default:** 10000.

View file

@ -16,7 +16,8 @@
"term": "xterm-color", "term": "xterm-color",
"readyTimeout": 20000, "readyTimeout": 20000,
"keepaliveInterval": 120000, "keepaliveInterval": 120000,
"keepaliveCountMax": 10 "keepaliveCountMax": 10,
"allowedSubnets": []
}, },
"terminal": { "terminal": {
"cursorBlink": true, "cursorBlink": true,

View file

@ -41,7 +41,8 @@
"validator": "~12.0.0", "validator": "~12.0.0",
"xterm-addon-fit": "^0.3.0", "xterm-addon-fit": "^0.3.0",
"xterm-addon-search": "^0.3.0", "xterm-addon-search": "^0.3.0",
"xterm-addon-web-links": "^0.2.1" "xterm-addon-web-links": "^0.2.1",
"netmask": "1.0.6"
}, },
"scripts": { "scripts": {
"start": "node index.js", "start": "node index.js",

View file

@ -28,7 +28,8 @@ let config = {
term: 'xterm-color', term: 'xterm-color',
readyTimeout: 20000, readyTimeout: 20000,
keepaliveInterval: 120000, keepaliveInterval: 120000,
keepaliveCountMax: 10 keepaliveCountMax: 10,
allowedSubnets: []
}, },
terminal: { terminal: {
cursorBlink: true, cursorBlink: true,
@ -153,6 +154,7 @@ app.get('/ssh/host/:host?', function (req, res, next) {
algorithms: config.algorithms, algorithms: config.algorithms,
keepaliveInterval: config.ssh.keepaliveInterval, keepaliveInterval: config.ssh.keepaliveInterval,
keepaliveCountMax: config.ssh.keepaliveCountMax, keepaliveCountMax: config.ssh.keepaliveCountMax,
allowedSubnets: config.ssh.allowedSubnets,
term: (/^(([a-z]|[A-Z]|[0-9]|[!^(){}\-_~])+)?\w$/.test(req.query.sshterm) && term: (/^(([a-z]|[A-Z]|[0-9]|[!^(){}\-_~])+)?\w$/.test(req.query.sshterm) &&
req.query.sshterm) || config.ssh.term, req.query.sshterm) || config.ssh.term,
terminal: { terminal: {

View file

@ -6,6 +6,7 @@
var debug = require('debug') var debug = require('debug')
var debugWebSSH2 = require('debug')('WebSSH2') var debugWebSSH2 = require('debug')('WebSSH2')
var SSH = require('ssh2').Client var SSH = require('ssh2').Client
var Netmask = require('netmask').Netmask
// var fs = require('fs') // var fs = require('fs')
// var hostkeys = JSON.parse(fs.readFileSync('./hostkeyhashes.json', 'utf8')) // var hostkeys = JSON.parse(fs.readFileSync('./hostkeyhashes.json', 'utf8'))
var termCols, termRows var termCols, termRows
@ -21,6 +22,26 @@ module.exports = function socket (socket) {
socket.disconnect(true) socket.disconnect(true)
return return
} }
// If configured, check that requsted host is in a permitted subnet
if (socket.request.session.ssh.allowedSubnets.length > 0) {
var permitted = false;
for (const subnet of socket.request.session.ssh.allowedSubnets) {
var subnetBlock = new Netmask(subnet);
if (subnetBlock.contains(socket.request.session.ssh.host)) {
permitted = true;
break;
}
}
if (!permitted) {
socket.emit('401 UNAUTHORIZED')
socket.emit('status', 'SSH CONNECTION ESTABLISHED')
debugWebSSH2('SOCKET: Requested host outside configured subnets / REJECTED')
socket.disconnect(true)
return
}
}
var conn = new SSH() var conn = new SSH()
socket.on('geometry', function socketOnGeometry (cols, rows) { socket.on('geometry', function socketOnGeometry (cols, rows) {
termCols = cols termCols = cols