forked from coolaj86/goldilocks.js
		
	made tunnel server respond to config changes
This commit is contained in:
		
							parent
							
								
									7423d6065f
								
							
						
					
					
						commit
						a27252eb77
					
				@ -162,8 +162,8 @@ module.exports.create = function (deps, conf, tcpMods) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (deps.tunnelServer.isClientDomain(separatePort(headers.host).host)) {
 | 
			
		||||
      deps.tunnelServer.handleClientConn(conn);
 | 
			
		||||
    if (deps.stunneld.isClientDomain(separatePort(headers.host).host)) {
 | 
			
		||||
      deps.stunneld.handleClientConn(conn);
 | 
			
		||||
      process.nextTick(function () {
 | 
			
		||||
        conn.unshift(opts.firstChunk);
 | 
			
		||||
        conn.resume();
 | 
			
		||||
@ -214,8 +214,8 @@ module.exports.create = function (deps, conf, tcpMods) {
 | 
			
		||||
      return emitConnection(adminServer, conn, opts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (deps.tunnelServer.isAdminDomain(host)) {
 | 
			
		||||
      deps.tunnelServer.handleAdminConn(conn);
 | 
			
		||||
    if (deps.stunneld.isAdminDomain(host)) {
 | 
			
		||||
      deps.stunneld.handleAdminConn(conn);
 | 
			
		||||
      process.nextTick(function () {
 | 
			
		||||
        conn.unshift(opts.firstChunk);
 | 
			
		||||
        conn.resume();
 | 
			
		||||
 | 
			
		||||
@ -229,7 +229,6 @@ module.exports.create = function (deps, config) {
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  deps.tunnelClients = require('../tunnel-client-manager').create(deps, config);
 | 
			
		||||
  deps.tunnelServer = require('../tunnel-server-manager').create(deps, config);
 | 
			
		||||
 | 
			
		||||
  function updateListeners() {
 | 
			
		||||
    var current = listeners.list();
 | 
			
		||||
 | 
			
		||||
@ -291,8 +291,8 @@ module.exports.create = function (deps, config, tcpMods) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (deps.tunnelServer.isClientDomain(opts.servername)) {
 | 
			
		||||
      deps.tunnelServer.handleClientConn(socket);
 | 
			
		||||
    if (deps.stunneld.isClientDomain(opts.servername)) {
 | 
			
		||||
      deps.stunneld.handleClientConn(socket);
 | 
			
		||||
      if (!opts.hyperPeek) {
 | 
			
		||||
        process.nextTick(function () {
 | 
			
		||||
          socket.unshift(opts.firstChunk);
 | 
			
		||||
 | 
			
		||||
@ -1,61 +1,131 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports.create = function (deps, config) {
 | 
			
		||||
  if (!config.tunnelServer || !Array.isArray(config.tunnelServer.servernames) || !config.tunnelServer.secret) {
 | 
			
		||||
    return {
 | 
			
		||||
      isAdminDomain:  function () { return false; }
 | 
			
		||||
    , isClientDomain: function () { return false; }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
function httpsTunnel(servername, conn) {
 | 
			
		||||
  console.error('tunnel server received encrypted connection to', servername);
 | 
			
		||||
  conn.end();
 | 
			
		||||
}
 | 
			
		||||
function handleHttp(servername, conn) {
 | 
			
		||||
  console.error('tunnel server received un-encrypted connection to', servername);
 | 
			
		||||
  conn.end([
 | 
			
		||||
    'HTTP/1.1 404 Not Found'
 | 
			
		||||
  , 'Date: ' + (new Date()).toUTCString()
 | 
			
		||||
  , 'Connection: close'
 | 
			
		||||
  , 'Content-Type: text/html'
 | 
			
		||||
  , 'Content-Length: 9'
 | 
			
		||||
  , ''
 | 
			
		||||
  , 'Not Found'
 | 
			
		||||
  ].join('\r\n'));
 | 
			
		||||
}
 | 
			
		||||
function rejectNonWebsocket(req, res) {
 | 
			
		||||
  // status code 426 = Upgrade Required
 | 
			
		||||
  res.statusCode = 426;
 | 
			
		||||
  res.setHeader('Content-Type', 'application/json');
 | 
			
		||||
  res.send({error: { message: 'Only websockets accepted for tunnel server' }});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  var tunnelOpts = Object.assign({}, config.tunnelServer);
 | 
			
		||||
  // This function should not be called because connections to the admin domains
 | 
			
		||||
var defaultConfig = {
 | 
			
		||||
  servernames: []
 | 
			
		||||
, secret: null
 | 
			
		||||
};
 | 
			
		||||
var tunnelFuncs = {
 | 
			
		||||
  // These functions should not be called because connections to the admin domains
 | 
			
		||||
  // should already be decrypted, and connections to non-client domains should never
 | 
			
		||||
  // be given to us in the first place.
 | 
			
		||||
  tunnelOpts.httpsTunnel = function (servername, conn) {
 | 
			
		||||
    console.error('tunnel server received encrypted connection to', servername);
 | 
			
		||||
    conn.end();
 | 
			
		||||
  };
 | 
			
		||||
  tunnelOpts.httpsInvalid = tunnelOpts.httpsTunnel;
 | 
			
		||||
  // This function should not be called because ACME challenges should be handled
 | 
			
		||||
  httpsTunnel:  httpsTunnel
 | 
			
		||||
, httpsInvalid: httpsTunnel
 | 
			
		||||
  // These function should not be called because ACME challenges should be handled
 | 
			
		||||
  // before admin domain connections are given to us, and the only non-encrypted
 | 
			
		||||
  // client connections that should be given to us are ACME challenges.
 | 
			
		||||
  tunnelOpts.handleHttp = function (servername, conn) {
 | 
			
		||||
    console.error('tunnel server received un-encrypted connection to', servername);
 | 
			
		||||
    conn.end([
 | 
			
		||||
      'HTTP/1.1 404 Not Found'
 | 
			
		||||
    , 'Date: ' + (new Date()).toUTCString()
 | 
			
		||||
    , 'Connection: close'
 | 
			
		||||
    , 'Content-Type: text/html'
 | 
			
		||||
    , 'Content-Length: 9'
 | 
			
		||||
    , ''
 | 
			
		||||
    , 'Not Found'
 | 
			
		||||
    ].join('\r\n'));
 | 
			
		||||
  };
 | 
			
		||||
  tunnelOpts.handleInsecureHttp = tunnelOpts.handleHttp;
 | 
			
		||||
, handleHttp:         handleHttp
 | 
			
		||||
, handleInsecureHttp: handleHttp
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
  var tunnelServer = require('stunneld').create(tunnelOpts);
 | 
			
		||||
module.exports.create = function (deps, config) {
 | 
			
		||||
  var equal = require('deep-equal');
 | 
			
		||||
  var enableDestroy = require('server-destroy');
 | 
			
		||||
  var currentOpts = Object.assign({}, defaultConfig);
 | 
			
		||||
 | 
			
		||||
  var httpServer = require('http').createServer(function (req, res) {
 | 
			
		||||
    // status code 426 = Upgrade Required
 | 
			
		||||
    res.statusCode = 426;
 | 
			
		||||
    res.setHeader('Content-Type', 'application/json');
 | 
			
		||||
    res.end(JSON.stringify({error: {
 | 
			
		||||
      message: 'Only websockets accepted for tunnel server'
 | 
			
		||||
    }}));
 | 
			
		||||
  });
 | 
			
		||||
  var wsServer = new (require('ws').Server)({ server: httpServer });
 | 
			
		||||
  wsServer.on('connection', tunnelServer.ws);
 | 
			
		||||
  var httpServer, wsServer, stunneld;
 | 
			
		||||
  function start() {
 | 
			
		||||
    if (httpServer || wsServer || stunneld) {
 | 
			
		||||
      throw new Error('trying to start already started tunnel server');
 | 
			
		||||
    }
 | 
			
		||||
    httpServer = require('http').createServer(rejectNonWebsocket);
 | 
			
		||||
    enableDestroy(httpServer);
 | 
			
		||||
 | 
			
		||||
    wsServer = new (require('ws').Server)({ server: httpServer });
 | 
			
		||||
 | 
			
		||||
    var tunnelOpts = Object.assign({}, tunnelFuncs, currentOpts);
 | 
			
		||||
    stunneld = require('stunneld').create(tunnelOpts);
 | 
			
		||||
    wsServer.on('connection', stunneld.ws);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function stop() {
 | 
			
		||||
    if (!httpServer || !wsServer || !stunneld) {
 | 
			
		||||
      throw new Error('trying to stop unstarted tunnel server (or it got into semi-initialized state');
 | 
			
		||||
    }
 | 
			
		||||
    wsServer.close();
 | 
			
		||||
    wsServer = null;
 | 
			
		||||
    httpServer.destroy();
 | 
			
		||||
    httpServer = null;
 | 
			
		||||
    // Nothing to close here, just need to set it to null to allow it to be garbage-collected.
 | 
			
		||||
    stunneld = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function updateConf() {
 | 
			
		||||
    var newOpts = Object.assign({}, defaultConfig, config.tunnelServer);
 | 
			
		||||
    if (!Array.isArray(newOpts.servernames)) {
 | 
			
		||||
      newOpts.servernames = [];
 | 
			
		||||
    }
 | 
			
		||||
    var trimmedOpts = {
 | 
			
		||||
      servernames: newOpts.servernames.slice().sort()
 | 
			
		||||
    , secret:      newOpts.secret
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (equal(trimmedOpts, currentOpts)) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    currentOpts = trimmedOpts;
 | 
			
		||||
 | 
			
		||||
    // Stop what's currently running, then if we are still supposed to be running then we
 | 
			
		||||
    // can start it again with the updated options. It might be possible to make use of
 | 
			
		||||
    // the existing http and ws servers when the config changes, but I'm not sure what
 | 
			
		||||
    // state the actions needed to close all existing connections would put them in.
 | 
			
		||||
    if (httpServer || wsServer || stunneld) {
 | 
			
		||||
      stop();
 | 
			
		||||
    }
 | 
			
		||||
    if (currentOpts.servernames.length && currentOpts.secret) {
 | 
			
		||||
      start();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  process.nextTick(updateConf);
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    isAdminDomain: function (domain) {
 | 
			
		||||
      return config.tunnelServer.servernames.indexOf(domain) !== -1;
 | 
			
		||||
      return currentOpts.servernames.indexOf(domain) !== -1;
 | 
			
		||||
    }
 | 
			
		||||
  , handleAdminConn: function (conn) {
 | 
			
		||||
      httpServer.emit('connection', conn);
 | 
			
		||||
      if (!httpServer) {
 | 
			
		||||
        console.error(new Error('handleAdminConn called with no active tunnel server'));
 | 
			
		||||
        conn.end();
 | 
			
		||||
      } else {
 | 
			
		||||
        return httpServer.emit('connection', conn);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  , isClientDomain: tunnelServer.isClientDomain
 | 
			
		||||
  , handleClientConn: tunnelServer.tcp
 | 
			
		||||
  , isClientDomain: function (domain) {
 | 
			
		||||
      if (!stunneld) { return false; }
 | 
			
		||||
      return stunneld.isClientDomain(domain);
 | 
			
		||||
    }
 | 
			
		||||
  , handleClientConn: function (conn) {
 | 
			
		||||
      if (!stunneld) {
 | 
			
		||||
        console.error(new Error('handleClientConn called with no active tunnel server'));
 | 
			
		||||
        conn.end();
 | 
			
		||||
      } else {
 | 
			
		||||
        return stunneld.tcp(conn);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  , updateConf
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -53,6 +53,7 @@ function create(conf) {
 | 
			
		||||
  , mdns:     require('./mdns').create(deps, conf)
 | 
			
		||||
  , udp:      require('./udp').create(deps, conf)
 | 
			
		||||
  , tcp:      require('./tcp').create(deps, conf)
 | 
			
		||||
  , stunneld: require('./tunnel-server-manager').create(deps, config)
 | 
			
		||||
  };
 | 
			
		||||
  Object.assign(deps, modules);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -2086,7 +2086,7 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "stunnel": {
 | 
			
		||||
      "version": "git+https://git.daplie.com/Daplie/node-tunnel-client.git#cad0e561fbea5c5dbbf5fc10ed95833dd3573ebc",
 | 
			
		||||
      "version": "git+https://git.daplie.com/Daplie/node-tunnel-client.git#114847e31abe9a0c5f0598b892dd98b37fe9622e",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "bluebird": "3.5.0",
 | 
			
		||||
        "commander": "2.9.0",
 | 
			
		||||
@ -2098,7 +2098,7 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "stunneld": {
 | 
			
		||||
      "version": "git+https://git.daplie.com/Daplie/node-tunnel-server.git#54ca2782dde84b3d2c61a3257f7d859b7012ea59",
 | 
			
		||||
      "version": "git+https://git.daplie.com/Daplie/node-tunnel-server.git#ae91fd5049251ed1f9fcd6806d7b9872454c67db",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "bluebird": "3.5.0",
 | 
			
		||||
        "cluster-store": "2.0.6",
 | 
			
		||||
@ -2108,8 +2108,15 @@
 | 
			
		||||
        "localhost.daplie.me-certificates": "1.3.5",
 | 
			
		||||
        "redirect-https": "1.1.4",
 | 
			
		||||
        "sni": "1.0.0",
 | 
			
		||||
        "tunnel-packer": "1.3.0",
 | 
			
		||||
        "tunnel-packer": "1.4.0",
 | 
			
		||||
        "ws": "2.3.1"
 | 
			
		||||
      },
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "tunnel-packer": {
 | 
			
		||||
          "version": "1.4.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/tunnel-packer/-/tunnel-packer-1.4.0.tgz",
 | 
			
		||||
          "integrity": "sha512-99GYAtKnbMVd87hQMxiR/Pq62jOWzOH/K6EOs87nU6U4p5uso+fZyYuO+upb+hhonXuNI/sZR/ByVxPFrnzMog=="
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "terminal-forms.js": {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user