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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deps.tunnelServer.isClientDomain(separatePort(headers.host).host)) {
|
if (deps.stunneld.isClientDomain(separatePort(headers.host).host)) {
|
||||||
deps.tunnelServer.handleClientConn(conn);
|
deps.stunneld.handleClientConn(conn);
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
conn.unshift(opts.firstChunk);
|
conn.unshift(opts.firstChunk);
|
||||||
conn.resume();
|
conn.resume();
|
||||||
|
@ -214,8 +214,8 @@ module.exports.create = function (deps, conf, tcpMods) {
|
||||||
return emitConnection(adminServer, conn, opts);
|
return emitConnection(adminServer, conn, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deps.tunnelServer.isAdminDomain(host)) {
|
if (deps.stunneld.isAdminDomain(host)) {
|
||||||
deps.tunnelServer.handleAdminConn(conn);
|
deps.stunneld.handleAdminConn(conn);
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
conn.unshift(opts.firstChunk);
|
conn.unshift(opts.firstChunk);
|
||||||
conn.resume();
|
conn.resume();
|
||||||
|
|
|
@ -229,7 +229,6 @@ module.exports.create = function (deps, config) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
deps.tunnelClients = require('../tunnel-client-manager').create(deps, config);
|
deps.tunnelClients = require('../tunnel-client-manager').create(deps, config);
|
||||||
deps.tunnelServer = require('../tunnel-server-manager').create(deps, config);
|
|
||||||
|
|
||||||
function updateListeners() {
|
function updateListeners() {
|
||||||
var current = listeners.list();
|
var current = listeners.list();
|
||||||
|
|
|
@ -291,8 +291,8 @@ module.exports.create = function (deps, config, tcpMods) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deps.tunnelServer.isClientDomain(opts.servername)) {
|
if (deps.stunneld.isClientDomain(opts.servername)) {
|
||||||
deps.tunnelServer.handleClientConn(socket);
|
deps.stunneld.handleClientConn(socket);
|
||||||
if (!opts.hyperPeek) {
|
if (!opts.hyperPeek) {
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
socket.unshift(opts.firstChunk);
|
socket.unshift(opts.firstChunk);
|
||||||
|
|
|
@ -1,61 +1,131 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports.create = function (deps, config) {
|
function httpsTunnel(servername, conn) {
|
||||||
if (!config.tunnelServer || !Array.isArray(config.tunnelServer.servernames) || !config.tunnelServer.secret) {
|
console.error('tunnel server received encrypted connection to', servername);
|
||||||
return {
|
conn.end();
|
||||||
isAdminDomain: function () { return false; }
|
}
|
||||||
, isClientDomain: function () { return false; }
|
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);
|
var defaultConfig = {
|
||||||
// This function should not be called because connections to the admin domains
|
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
|
// should already be decrypted, and connections to non-client domains should never
|
||||||
// be given to us in the first place.
|
// be given to us in the first place.
|
||||||
tunnelOpts.httpsTunnel = function (servername, conn) {
|
httpsTunnel: httpsTunnel
|
||||||
console.error('tunnel server received encrypted connection to', servername);
|
, httpsInvalid: httpsTunnel
|
||||||
conn.end();
|
// These function should not be called because ACME challenges should be handled
|
||||||
};
|
|
||||||
tunnelOpts.httpsInvalid = tunnelOpts.httpsTunnel;
|
|
||||||
// This function should not be called because ACME challenges should be handled
|
|
||||||
// before admin domain connections are given to us, and the only non-encrypted
|
// before admin domain connections are given to us, and the only non-encrypted
|
||||||
// client connections that should be given to us are ACME challenges.
|
// client connections that should be given to us are ACME challenges.
|
||||||
tunnelOpts.handleHttp = function (servername, conn) {
|
, handleHttp: handleHttp
|
||||||
console.error('tunnel server received un-encrypted connection to', servername);
|
, handleInsecureHttp: handleHttp
|
||||||
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;
|
|
||||||
|
|
||||||
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) {
|
var httpServer, wsServer, stunneld;
|
||||||
// status code 426 = Upgrade Required
|
function start() {
|
||||||
res.statusCode = 426;
|
if (httpServer || wsServer || stunneld) {
|
||||||
res.setHeader('Content-Type', 'application/json');
|
throw new Error('trying to start already started tunnel server');
|
||||||
res.end(JSON.stringify({error: {
|
}
|
||||||
message: 'Only websockets accepted for tunnel server'
|
httpServer = require('http').createServer(rejectNonWebsocket);
|
||||||
}}));
|
enableDestroy(httpServer);
|
||||||
});
|
|
||||||
var wsServer = new (require('ws').Server)({ server: httpServer });
|
wsServer = new (require('ws').Server)({ server: httpServer });
|
||||||
wsServer.on('connection', tunnelServer.ws);
|
|
||||||
|
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 {
|
return {
|
||||||
isAdminDomain: function (domain) {
|
isAdminDomain: function (domain) {
|
||||||
return config.tunnelServer.servernames.indexOf(domain) !== -1;
|
return currentOpts.servernames.indexOf(domain) !== -1;
|
||||||
}
|
}
|
||||||
, handleAdminConn: function (conn) {
|
, 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
|
, isClientDomain: function (domain) {
|
||||||
, handleClientConn: tunnelServer.tcp
|
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)
|
, mdns: require('./mdns').create(deps, conf)
|
||||||
, udp: require('./udp').create(deps, conf)
|
, udp: require('./udp').create(deps, conf)
|
||||||
, tcp: require('./tcp').create(deps, conf)
|
, tcp: require('./tcp').create(deps, conf)
|
||||||
|
, stunneld: require('./tunnel-server-manager').create(deps, config)
|
||||||
};
|
};
|
||||||
Object.assign(deps, modules);
|
Object.assign(deps, modules);
|
||||||
|
|
||||||
|
|
|
@ -2086,7 +2086,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stunnel": {
|
"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": {
|
"requires": {
|
||||||
"bluebird": "3.5.0",
|
"bluebird": "3.5.0",
|
||||||
"commander": "2.9.0",
|
"commander": "2.9.0",
|
||||||
|
@ -2098,7 +2098,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stunneld": {
|
"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": {
|
"requires": {
|
||||||
"bluebird": "3.5.0",
|
"bluebird": "3.5.0",
|
||||||
"cluster-store": "2.0.6",
|
"cluster-store": "2.0.6",
|
||||||
|
@ -2108,8 +2108,15 @@
|
||||||
"localhost.daplie.me-certificates": "1.3.5",
|
"localhost.daplie.me-certificates": "1.3.5",
|
||||||
"redirect-https": "1.1.4",
|
"redirect-https": "1.1.4",
|
||||||
"sni": "1.0.0",
|
"sni": "1.0.0",
|
||||||
"tunnel-packer": "1.3.0",
|
"tunnel-packer": "1.4.0",
|
||||||
"ws": "2.3.1"
|
"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": {
|
"terminal-forms.js": {
|
||||||
|
|
Loading…
Reference in New Issue