diff --git a/app/client/src/js/index.js b/app/client/src/js/index.js index 7c2936c..a680775 100644 --- a/app/client/src/js/index.js +++ b/app/client/src/js/index.js @@ -40,20 +40,9 @@ function resizeScreen () { socket.emit('resize', { cols: term.cols, rows: term.rows }) } -// if (document.location.pathname) { -// var parts = document.location.pathname.split('/') -// var base = parts.slice(0, parts.length - 1).join('/') + '/' -// var resource = base.substring(1) + 'socket.io' -// console.log('document.location.pathname resource: ' + resource) - -// socket = io.connect(null, { -// resource: resource, -// path: "/ssh/socket.io" -// }) -// } else { - socket = io.connect({ - path: "/ssh/socket.io" - }) +socket = io.connect({ + path: "/ssh/socket.io" +}) term.onData(function (data) { socket.emit('data', data) diff --git a/app/package-lock.json b/app/package-lock.json index ba12cec..8345951 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "webssh2", - "version": "0.2.10-0", + "version": "0.2.10-1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -586,6 +586,11 @@ } } }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -1080,6 +1085,14 @@ "tslib": "^1.9.0" } }, + "cidr-matcher": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cidr-matcher/-/cidr-matcher-2.1.1.tgz", + "integrity": "sha512-QPJRz4HDQxpB8AZWEqd6ejVp+siArXh3u1MYaUFV85cd293StGSMb87jVe0z9gS92KsFwxCxjb3utO3e5HKHTw==", + "requires": { + "ip6addr": "^0.2.2" + } + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -3136,6 +3149,11 @@ } } }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, "fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", @@ -5039,6 +5057,11 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -5067,6 +5090,17 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, "jsx-ast-utils": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz", diff --git a/app/package.json b/app/package.json index 1fa7ba3..98d36bd 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "webssh2", - "version": "0.2.10-0", + "version": "0.2.10-1", "ignore": [ ".gitignore" ], @@ -28,6 +28,7 @@ }, "dependencies": { "basic-auth": "~2.0.1", + "cidr-matcher": "2.1.1", "colors": "~1.4.0", "compression": "~1.7.4", "debug": "^4.1.1", @@ -38,7 +39,7 @@ "socket.io": "2.2.0", "ssh2": "~0.8.6", "terser-webpack-plugin": "^2.2.1", - "validator": "~12.0.0", + "validator": "^12.1.0", "xterm-addon-fit": "^0.3.0", "xterm-addon-search": "^0.3.0", "xterm-addon-web-links": "^0.2.1" diff --git a/app/server/app.js b/app/server/app.js index ac5652a..bd80bd7 100644 --- a/app/server/app.js +++ b/app/server/app.js @@ -28,7 +28,8 @@ let config = { term: 'xterm-color', readyTimeout: 20000, keepaliveInterval: 120000, - keepaliveCountMax: 10 + keepaliveCountMax: 10, + allowedSubnets: [] }, terminal: { cursorBlink: true, @@ -153,6 +154,7 @@ app.get('/ssh/host/:host?', function (req, res, next) { algorithms: config.algorithms, keepaliveInterval: config.ssh.keepaliveInterval, keepaliveCountMax: config.ssh.keepaliveCountMax, + allowedSubnets: config.ssh.allowedSubnets, term: (/^(([a-z]|[A-Z]|[0-9]|[!^(){}\-_~])+)?\w$/.test(req.query.sshterm) && req.query.sshterm) || config.ssh.term, terminal: { diff --git a/app/server/socket.js b/app/server/socket.js index 156f9e2..da880f8 100644 --- a/app/server/socket.js +++ b/app/server/socket.js @@ -1,3 +1,4 @@ +/* eslint-disable complexity */ 'use strict' /* jshint esversion: 6, asi: true, node: true */ // socket.js @@ -6,6 +7,7 @@ var debug = require('debug') var debugWebSSH2 = require('debug')('WebSSH2') var SSH = require('ssh2').Client +var CIDRMatcher = require('cidr-matcher'); // var fs = require('fs') // var hostkeys = JSON.parse(fs.readFileSync('./hostkeyhashes.json', 'utf8')) var termCols, termRows @@ -21,6 +23,20 @@ module.exports = function socket (socket) { socket.disconnect(true) return } + + // If configured, check that requsted host is in a permitted subnet + if ( (((socket.request.session || {}).ssh || {}).allowedSubnets || {}).length && ( socket.request.session.ssh.allowedSubnets.length > 0 ) ) { + var matcher = new CIDRMatcher(socket.request.session.ssh.allowedSubnets); + if (!matcher.contains(socket.request.session.ssh.host)) { + console.log('WebSSH2 ' + 'error: Requested host outside configured subnets / REJECTED'.red.bold + + ' user=' + socket.request.session.username.yellow.bold.underline + + ' from=' + socket.handshake.address.yellow.bold.underline) + socket.emit('ssherror', '401 UNAUTHORIZED') + socket.disconnect(true) + return + } + } + var conn = new SSH() socket.on('geometry', function socketOnGeometry (cols, rows) { termCols = cols