working cleanup

This commit is contained in:
AJ ONeal 2018-05-31 04:10:47 -06:00
parent e848e1466b
commit acaf22c200
4 changed files with 201 additions and 72 deletions

View File

@ -3,6 +3,7 @@
'use strict'; 'use strict';
var pkg = require('../package.json'); var pkg = require('../package.json');
console.log(pkg.name, pkg.version);
var url = require('url'); var url = require('url');
var remote = require('../remote.js'); var remote = require('../remote.js');
@ -37,7 +38,12 @@ function help() {
process.exit(0); process.exit(0);
} }
if (-1 === confIndex || -1 !== argv.indexOf('-h') || -1 !== argv.indexOf('--help')) { if (-1 === confIndex) {
confpath = require('path').join(require('os').homedir(), '.config/telebit/telebit.yml');
console.info('Using default --config "' + confpath + '"');
}
if (-1 !== argv.indexOf('-h') || -1 !== argv.indexOf('--help')) {
help(); help();
} }
if (!confpath || /^--/.test(confpath)) { if (!confpath || /^--/.test(confpath)) {
@ -74,7 +80,6 @@ require('fs').readFile(confpath, 'utf8', function (err, text) {
}); });
function connectTunnel() { function connectTunnel() {
var services = { https: {}, http: {}, tcp: {} };
state.net = { state.net = {
createConnection: function (info, cb) { createConnection: function (info, cb) {
// data is the hello packet / first chunk // data is the hello packet / first chunk
@ -86,66 +91,17 @@ function connectTunnel() {
} }
}; };
// Note: the remote needs to know:
// what servernames to forward
// what ports to forward
// what udp ports to forward
// redirect http to https automatically
// redirect www to nowww automatically
if (state.config.http) {
Object.keys(state.config.http).forEach(function (hostname) {
if ('*' === hostname) {
state.config.servernames.forEach(function (servername) {
services.https[servername] = state.config.http[hostname];
services.http[servername] = 'redirect-https';
});
return;
}
services.https[hostname] = state.config.http[hostname];
services.http[hostname] = 'redirect-https';
});
}
/*
Object.keys(state.config.localPorts).forEach(function (port) {
var proto = state.config.localPorts[port];
if (!proto) { return; }
if ('http' === proto) {
state.config.servernames.forEach(function (servername) {
services.http[servername] = port;
});
return;
}
if ('https' === proto) {
state.config.servernames.forEach(function (servername) {
services.https[servername] = port;
});
return;
}
if (true === proto) { proto = 'tcp'; }
if ('tcp' !== proto) { throw new Error("unsupported protocol '" + proto + "'"); }
//services[proxy.protocol]['*'] = proxy.port;
//services[proxy.protocol][proxy.hostname] = proxy.port;
services[proto]['*'] = port;
});
*/
state.services = services;
Object.keys(services).forEach(function (protocol) {
var subServices = state.services[protocol];
Object.keys(subServices).forEach(function (hostname) {
console.info('[local proxy]', protocol + '://' + hostname + ' => ' + subServices[hostname]);
});
});
console.info('');
state.greenlock = state.config.greenlock || {}; state.greenlock = state.config.greenlock || {};
if (!state.config.sortingHat) {
state.config.sortingHat = './lib/sorting-hat.js';
}
state.config.sortingHat = require('path').resolve(__dirname, '..', state.config.sortingHat);
// TODO Check undefined vs false for greenlock config // TODO Check undefined vs false for greenlock config
var tun = remote.connect({ var tun = remote.connect({
relay: state.config.relay relay: state.config.relay
, config: state.config , config: state.config
, sortingHat: state.config.sortingHat || './lib/sorting-hat.js' , sortingHat: state.config.sortingHat
, locals: state.config.servernames
, services: state.services
, net: state.net , net: state.net
, insecure: state.config.relay_ignore_invalid_certificates , insecure: state.config.relay_ignore_invalid_certificates
, token: state.token , token: state.token
@ -157,6 +113,7 @@ function connectTunnel() {
, configDir: state.greenlock.configDir || '~/acme/etc/' , configDir: state.greenlock.configDir || '~/acme/etc/'
// TODO, store: require(state.greenlock.store.name || 'le-store-certbot').create(state.greenlock.store.options || {}) // TODO, store: require(state.greenlock.store.name || 'le-store-certbot').create(state.greenlock.store.options || {})
, approveDomains: function (opts, certs, cb) { , approveDomains: function (opts, certs, cb) {
console.log("trying approve domains");
// Certs being renewed are listed in certs.altnames // Certs being renewed are listed in certs.altnames
if (certs) { if (certs) {
opts.domains = certs.altnames; opts.domains = certs.altnames;
@ -164,16 +121,22 @@ function connectTunnel() {
return; return;
} }
if (-1 !== state.config.servernames.indexOf(opts.domains[0])) { // by virtue of the fact that it's being tunneled through a
// trusted source that is already checking, we're good
//if (-1 !== state.config.servernames.indexOf(opts.domains[0])) {
opts.email = state.greenlock.email || state.config.email; opts.email = state.greenlock.email || state.config.email;
opts.agreeTos = state.greenlock.agree || state.agreeTos; opts.agreeTos = state.greenlock.agree || state.agreeTos;
cb(null, { options: opts, certs: certs }); cb(null, { options: opts, certs: certs });
return; return;
} //}
//cb(new Error("servername not found in allowed list"));
} }
} }
}); });
require(state.config.sortingHat).print(state.config);
function sigHandler() { function sigHandler() {
console.log('SIGINT'); console.log('SIGINT');
@ -208,7 +171,7 @@ function rawTunnel() {
if (!state.config.token) { if (!state.config.token) {
var jwt = require('jsonwebtoken'); var jwt = require('jsonwebtoken');
var tokenData = { var tokenData = {
domains: state.config.servernames domains: Object.keys(state.config.servernames).filter(function (name) { return /\./.test(name); })
, aud: aud , aud: aud
, iss: Math.round(Date.now() / 1000) , iss: Math.round(Date.now() / 1000)
}; };

View File

@ -1,16 +1,185 @@
module.exports.print = function (config) {
var services = { https: {}, http: {}, tcp: {} };
// Note: the remote needs to know:
// what servernames to forward
// what ports to forward
// what udp ports to forward
// redirect http to https automatically
// redirect www to nowww automatically
if (config.http) {
Object.keys(config.http).forEach(function (hostname) {
if ('*' === hostname) {
config.servernames.forEach(function (servername) {
services.https[servername] = config.http[hostname];
services.http[servername] = 'redirect-https';
});
return;
}
services.https[hostname] = config.http[hostname];
services.http[hostname] = 'redirect-https';
});
}
/*
Object.keys(config.localPorts).forEach(function (port) {
var proto = config.localPorts[port];
if (!proto) { return; }
if ('http' === proto) {
config.servernames.forEach(function (servername) {
services.http[servername] = port;
});
return;
}
if ('https' === proto) {
config.servernames.forEach(function (servername) {
services.https[servername] = port;
});
return;
}
if (true === proto) { proto = 'tcp'; }
if ('tcp' !== proto) { throw new Error("unsupported protocol '" + proto + "'"); }
//services[proxy.protocol]['*'] = proxy.port;
//services[proxy.protocol][proxy.hostname] = proxy.port;
services[proto]['*'] = port;
});
*/
Object.keys(services).forEach(function (protocol) {
var subServices = services[protocol];
Object.keys(subServices).forEach(function (hostname) {
console.info('[local proxy]', protocol + '://' + hostname + ' => ' + subServices[hostname]);
});
});
console.info('');
};
module.exports.assign = function (state, tun, cb) { module.exports.assign = function (state, tun, cb) {
var net = state.net || require('net'); var net = state.net || require('net');
var service = tun.service.toLowerCase();
var portList = state.services[service];
var port;
if (!tun.name && !tun.serviceport) { if (!tun.name && !tun.serviceport) {
console.log('tun:\n',tun); console.log('tun:\n',tun);
//console.warn(tun.data.toString()); //console.warn(tun.data.toString());
cb(new Error("missing routing information for ':tun_id'")); cb(new Error("No routing information for ':tun_id'. Missing both 'name' and 'serviceport'."));
return; return;
} }
if (!state.config.servernames) {
state.config.servernames = {};
}
var handlers = {};
handlers.http = function (socket) {
if (!state.greenlock) {
state.greenlock = require('greenlock').create(state.greenlockConfig);
}
if (!state.httpRedirectServer) {
state.redirectHttps = require('redirect-https')();
state.httpRedirectServer = require('http').createServer(state.greenlock.middleware(state.redirectHttps));
}
state.httpRedirectServer.emit('connection', socket);
};
handlers.https = function (tlsSocket) {
if (!state.defaultHttpServer) {
state.defaultHttpServer = require('http').createServer(function (req, res) {
console.log('[hit http/s server]');
res.end('Hello, Encrypted Tunnel World!');
});
}
state.defaultHttpServer.emit('connection', tlsSocket);
};
if ('http' === tun.service || 'https' === tun.service) {
if (!tun.name) {
cb(new Error("No routing information for ':tun_id'. Service '" + tun.service + "' is missing 'name'."));
return;
}
}
function redirectHttp(cb) {
var socketPair = require('socket-pair');
conn = socketPair.create(function (err, other) {
if (err) { cb(err); return; }
handlers.http(other);
cb(null, conn);
});
//if (tun.data) { conn.write(tun.data); }
return conn;
}
var handled;
if ('http' === tun.service) {
// TODO match *.example.com
handled = Object.keys(state.config.servernames).some(function (sn) {
if (sn !== tun.name) { return; }
console.log('Found config match for PLAIN', tun.name);
if (!state.config.servernames[sn]) { return; }
if (false === state.config.servernames[sn].terminate) {
cb(new Error("insecure http not supported yet"));
return true;
}
console.log('Redirecting HTPTP for', tun.name);
redirectHttp(cb);
return true;
});
if (!handled) {
redirectHttp(cb);
}
return;
}
function terminateTls(cb) {
var socketPair = require('socket-pair');
conn = socketPair.create(function (err, other) {
if (err) { cb(err); return; }
if (!state.greenlock) {
state.greenlock = require('greenlock').create(state.greenlockConfig);
}
if (!state.terminatorServer) {
state.terminatorServer = require('tls').createServer(state.greenlock.tlsOptions, function (tlsSocket) {
console.log('[hit tls server]');
if (err) { cb(err); return; }
handlers.https(tlsSocket);
});
}
console.log('[emitting tls connection]');
state.terminatorServer.emit('connection', other);
cb(null, conn);
});
//if (tun.data) { conn.write(tun.data); }
return conn;
}
if ('https' === tun.service) {
// TODO match *.example.com
handled = Object.keys(state.config.servernames).some(function (sn) {
if (sn !== tun.name) { return; }
console.log('Found config match for TLS', tun.name);
if (!state.config.servernames[sn]) { return; }
if (false === state.config.servernames[sn].terminate) {
cb(new Error("insecure http not supported yet"));
return true;
}
console.log('Terminating TLS for', tun.name);
terminateTls(cb);
return true;
});
if (!handled) {
terminateTls(cb);
}
return;
}
return;
var portList = state.services[service];
var port;
port = portList[tun.name]; port = portList[tun.name];
if (!port) { if (!port) {
// Check for any wildcard domains, sorted longest to shortest so the one with the // Check for any wildcard domains, sorted longest to shortest so the one with the
@ -48,7 +217,7 @@ module.exports.assign = function (state, tun, cb) {
function handleNow(socket) { function handleNow(socket) {
var httpServer; var httpServer;
var tlsServer; var tlsServer;
if ('https' === service) { if ('https' === tun.service) {
if (!state.greenlock) { if (!state.greenlock) {
state.greenlock = require('greenlock').create(state.greenlockConfig); state.greenlock = require('greenlock').create(state.greenlockConfig);
} }
@ -77,9 +246,7 @@ module.exports.assign = function (state, tun, cb) {
conn = socketPair.create(function (err, other) { conn = socketPair.create(function (err, other) {
if (err) { console.error('[Error] ' + err.message); } if (err) { console.error('[Error] ' + err.message); }
handleNow(other); handleNow(other);
if (createOpts.data) { //if (createOpts.data) { conn.write(createOpts.data); }
conn.write(createOpts.data);
}
}); });
/* /*
var streamPair = require('stream-pair'); var streamPair = require('stream-pair');
@ -96,9 +263,7 @@ module.exports.assign = function (state, tun, cb) {
// this will happen before 'data' or 'readable' is triggered // this will happen before 'data' or 'readable' is triggered
// We use the data from the createOpts object so that the createConnection function has // We use the data from the createOpts object so that the createConnection function has
// the oppurtunity of removing/changing it if it wants/needs to handle it differently. // the oppurtunity of removing/changing it if it wants/needs to handle it differently.
if (createOpts.data) { //if (createOpts.data) { conn.write(createOpts.data); }
conn.write(createOpts.data);
}
}); });
} }
cb(null, conn); cb(null, conn);

View File

@ -52,11 +52,11 @@
"greenlock": "^2.2.19", "greenlock": "^2.2.19",
"js-yaml": "^3.11.0", "js-yaml": "^3.11.0",
"jsonwebtoken": "^7.1.9", "jsonwebtoken": "^7.1.9",
"proxy-packer": "^1.4.3",
"recase": "^1.0.4", "recase": "^1.0.4",
"redirect-https": "^1.1.5", "redirect-https": "^1.1.5",
"sni": "^1.0.0", "sni": "^1.0.0",
"socket-pair": "^1.0.3", "socket-pair": "^1.0.3",
"proxy-packer": "^1.4.3",
"ws": "^2.2.3" "ws": "^2.2.3"
} }
} }

View File

@ -281,6 +281,7 @@ function run(state) {
return; return;
} }
clientHandlers.add(conn, cid, tun); clientHandlers.add(conn, cid, tun);
if (tun.data) { conn.write(tun.data); }
wstunneler.resume(); wstunneler.resume();
}); });
} }