diff --git a/README.md b/README.md index 07933cb..005eec7 100644 --- a/README.md +++ b/README.md @@ -224,6 +224,30 @@ autossh({
+#### Setting up a Reverse (Remote) Tunnel + +To set up a reverse tunnel set `reverse` to `true` in the config object. + +```javascript +autossh({ + host: '111.22.333.444', + username: 'root', + localPort: 22, + remotePort: 5432, + reverse: true +}) +.on('error', err => { + console.error('ERROR: ', err); +}) +.on('connect', connection => { + console.log('connection pid: ' + connection.pid); +}); +``` + +When using the reverse tunnel option, the `localPort` value cannot be `'auto'`. + +
+ #### Adjusting/Disabling Max Poll Count When first trying to establish the ssh tunnel, `autoshh` will poll the local port until the connection has been established. The default max poll count is `30`. diff --git a/index.js b/index.js index 6a20de2..7226aba 100644 --- a/index.js +++ b/index.js @@ -36,21 +36,7 @@ var AutoSSH = function (_EventEmitter) { var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(AutoSSH).call(this)); - _this.host = conf.host; - _this.username = conf.username || 'root'; - _this.remotePort = conf.remotePort; - _this.localPort = conf.localPort || 'auto'; - - _this.pollCount = 0; - _this.maxPollCount = conf.maxPollCount || 30; - _this.pollTimeout = 75; - - _this.serverAliveInterval = typeof conf.serverAliveInterval === 'number' ? conf.serverAliveInterval : 120; - - _this.serverAliveCountMax = typeof conf.serverAliveCountMax === 'number' ? conf.serverAliveCountMax : 1; - - _this.sshPort = conf.sshPort || 22; - _this.privateKey = conf.privateKey || null; + _this.configure(conf); setImmediate(function () { var confErrors = _this.getConfErrors(conf); @@ -68,28 +54,55 @@ var AutoSSH = function (_EventEmitter) { return _this; } - /* - */ - - _createClass(AutoSSH, [{ + key: 'configure', + value: function configure(conf) { + this.reverse = conf.reverse === true || false; + + this.host = conf.host; + this.username = conf.username || 'root'; + this.remotePort = conf.remotePort; + + if (this.reverse) this.localPort = parseInt(conf.localPort) || 22;else this.localPort = conf.localPort || 'auto'; + + this.pollCount = 0; + this.maxPollCount = conf.maxPollCount || 30; + this.pollTimeout = 75; + + this.serverAliveInterval = typeof conf.serverAliveInterval === 'number' ? conf.serverAliveInterval : 120; + + this.serverAliveCountMax = typeof conf.serverAliveCountMax === 'number' ? conf.serverAliveCountMax : 1; + + this.sshPort = conf.sshPort || 22; + this.privateKey = conf.privateKey || null; + } + + /* + */ + + }, { key: 'connect', value: function connect(conf) { var _this2 = this; var port = this.localPort === 'auto' ? this.generateRandomPort() : this.localPort; - - _portfinder2.default.getPort({ port: port }, function (portfinderErr, freePort) { - if (_this2.killed) return; - if (portfinderErr) _this2.emit('error', 'Port error: ' + portfinderErr); - if (_this2.localPort !== 'auto' && _this2.localPort !== freePort) _this2.emit('error', 'Port ' + _this2.localPort + ' is not available');else { - _this2.localPort = freePort; - // creates tunnel and then polls port until connection is established - _this2.execTunnel(function () { - _this2.pollConnection(); - }); - } - }); + if (this.reverse) { + this.execTunnel(function () { + _this2.pollConnection(); + }); + } else { + _portfinder2.default.getPort({ port: port }, function (portfinderErr, freePort) { + if (_this2.killed) return; + if (portfinderErr) _this2.emit('error', 'Port error: ' + portfinderErr); + if (_this2.localPort !== 'auto' && _this2.localPort !== freePort) _this2.emit('error', 'Port ' + _this2.localPort + ' is not available');else { + _this2.localPort = freePort; + // creates tunnel and then polls port until connection is established + _this2.execTunnel(function () { + _this2.pollConnection(); + }); + } + }); + } } /* @@ -196,7 +209,8 @@ var AutoSSH = function (_EventEmitter) { key: 'getConfErrors', value: function getConfErrors(conf) { var errors = []; - if (!conf.localPort) errors.push('Missing localPort');else if (isNaN(parseInt(conf.localPort)) && conf.localPort !== 'auto') errors.push('Invalid localPort'); + if (!conf.localPort) errors.push('Missing localPort'); + if (conf.reverse === true && (conf.localPort === 'auto' || isNaN(parseInt(conf.localPort)))) errors.push('Invalid value for localPort');else if (isNaN(parseInt(conf.localPort)) && conf.localPort !== 'auto') errors.push('Invalid value for localPort'); if (!conf.host) errors.push('Missing host');else if (typeof conf.host !== 'string') { errors.push('host must be type "string". was given "' + _typeof(conf.host) + '"'); @@ -259,7 +273,7 @@ var AutoSSH = function (_EventEmitter) { var serverAliveOpts = this.generateServerAliveOptions(); var defaultOpts = this.generateDefaultOptions(); var privateKey = this.privateKey ? '-i ' + this.privateKey : ''; - var sshPort = this.sshPort ? '-p ' + this.sshPort : ''; + var sshPort = this.sshPort === 22 ? '' : '-p ' + this.sshPort; return defaultOpts + ' ' + serverAliveOpts + ' ' + privateKey + ' ' + sshPort; } @@ -270,11 +284,14 @@ var AutoSSH = function (_EventEmitter) { }, { key: 'generateExecString', value: function generateExecString() { - var bindAddress = this.localPort + ':localhost:' + this.remotePort; + var startPort = this.reverse ? this.remotePort : this.localPort; + var endPort = this.reverse ? this.localPort : this.remotePort; + var bindAddress = startPort + ':localhost:' + endPort; var options = this.generateExecOptions(); var userAtHost = this.username + '@' + this.host; + var method = this.reverse ? 'R' : 'L'; - return 'ssh -NL ' + bindAddress + ' ' + options + ' ' + userAtHost; + return 'ssh -N' + method + ' ' + bindAddress + ' ' + options + ' ' + userAtHost; } /* diff --git a/src/index.js b/src/index.js index 7a949f8..b97f6e8 100644 --- a/src/index.js +++ b/src/index.js @@ -10,23 +10,7 @@ class AutoSSH extends EventEmitter { constructor(conf = {}) { super(); - this.host = conf.host; - this.username = conf.username || 'root'; - this.remotePort = conf.remotePort; - this.localPort = conf.localPort || 'auto'; - - this.pollCount = 0; - this.maxPollCount = conf.maxPollCount || 30; - this.pollTimeout = 75; - - this.serverAliveInterval = typeof conf.serverAliveInterval === 'number' ? - conf.serverAliveInterval : 120; - - this.serverAliveCountMax = typeof conf.serverAliveCountMax === 'number' ? - conf.serverAliveCountMax : 1; - - this.sshPort = conf.sshPort || 22; - this.privateKey = conf.privateKey || null; + this.configure(conf); setImmediate(() => { const confErrors = this.getConfErrors(conf); @@ -42,26 +26,58 @@ class AutoSSH extends EventEmitter { }); } + configure(conf) { + this.reverse = conf.reverse === true || false; + + this.host = conf.host; + this.username = conf.username || 'root'; + this.remotePort = conf.remotePort; + + if (this.reverse) + this.localPort = parseInt(conf.localPort) || 22; + else + this.localPort = conf.localPort || 'auto'; + + this.pollCount = 0; + this.maxPollCount = conf.maxPollCount || 30; + this.pollTimeout = 75; + + this.serverAliveInterval = typeof conf.serverAliveInterval === 'number' ? + conf.serverAliveInterval : 120; + + this.serverAliveCountMax = typeof conf.serverAliveCountMax === 'number' ? + conf.serverAliveCountMax : 1; + + this.sshPort = conf.sshPort || 22; + this.privateKey = conf.privateKey || null; + } + /* */ connect(conf) { const port = this.localPort === 'auto' ? this.generateRandomPort() : this.localPort; - - portfinder.getPort({ port }, (portfinderErr, freePort) => { - if (this.killed) - return; - if (portfinderErr) - this.emit('error', 'Port error: ' + portfinderErr); - if (this.localPort !== 'auto' && this.localPort !== freePort) - this.emit('error', `Port ${this.localPort} is not available`); - else { - this.localPort = freePort; - // creates tunnel and then polls port until connection is established - this.execTunnel(() => { - this.pollConnection(); - }); - } - }); + if (this.reverse) { + this.execTunnel(() => { + this.pollConnection(); + }); + } + else { + portfinder.getPort({ port }, (portfinderErr, freePort) => { + if (this.killed) + return; + if (portfinderErr) + this.emit('error', 'Port error: ' + portfinderErr); + if (this.localPort !== 'auto' && this.localPort !== freePort) + this.emit('error', `Port ${this.localPort} is not available`); + else { + this.localPort = freePort; + // creates tunnel and then polls port until connection is established + this.execTunnel(() => { + this.pollConnection(); + }); + } + }); + } } /* @@ -151,8 +167,10 @@ class AutoSSH extends EventEmitter { const errors = []; if (!conf.localPort) errors.push('Missing localPort'); + if (conf.reverse === true && (conf.localPort === 'auto' || isNaN(parseInt(conf.localPort)))) + errors.push('Invalid value for localPort'); else if (isNaN(parseInt(conf.localPort)) && conf.localPort !== 'auto') - errors.push('Invalid localPort'); + errors.push('Invalid value for localPort'); if (!conf.host) errors.push('Missing host'); @@ -217,7 +235,7 @@ class AutoSSH extends EventEmitter { const serverAliveOpts = this.generateServerAliveOptions(); const defaultOpts = this.generateDefaultOptions(); const privateKey = this.privateKey ? `-i ${this.privateKey}` : ''; - const sshPort = this.sshPort ? `-p ${this.sshPort}` : ''; + const sshPort = this.sshPort === 22 ? '' : `-p ${this.sshPort}`; return `${defaultOpts} ${serverAliveOpts} ${privateKey} ${sshPort}`; } @@ -225,11 +243,14 @@ class AutoSSH extends EventEmitter { /* */ generateExecString() { - const bindAddress = `${this.localPort}:localhost:${this.remotePort}`; + const startPort = this.reverse ? this.remotePort : this.localPort; + const endPort = this.reverse ? this.localPort : this.remotePort; + const bindAddress = `${startPort}:localhost:${endPort}`; const options = this.generateExecOptions(); const userAtHost = `${this.username}@${this.host}`; + const method = this.reverse ? 'R' : 'L'; - return `ssh -NL ${bindAddress} ${options} ${userAtHost}`; + return `ssh -N${method} ${bindAddress} ${options} ${userAtHost}`; } /*