one step closer to the edge, but without the break
This commit is contained in:
parent
d3022c246e
commit
7db2f0f703
|
@ -36,7 +36,7 @@ for convenience.
|
|||
You can customize the installation:
|
||||
|
||||
```bash
|
||||
export NODEJS_VER=v8.11.1
|
||||
export NODEJS_VER=v8.11.2
|
||||
export TELEBITD_PATH=/opt/telebitd
|
||||
curl -fsS https://get.telebit.cloud/ | bash
|
||||
```
|
||||
|
|
130
bin/telebitd.js
130
bin/telebitd.js
|
@ -6,7 +6,7 @@ var pkg = require('../package.json');
|
|||
|
||||
var argv = process.argv.slice(2);
|
||||
var telebitd = require('../telebitd.js');
|
||||
var greenlock = require('greenlock');
|
||||
var Greenlock = require('greenlock');
|
||||
|
||||
var confIndex = argv.indexOf('--config');
|
||||
var confpath;
|
||||
|
@ -55,6 +55,80 @@ function applyConfig(config) {
|
|||
console.info("");
|
||||
console.info("");
|
||||
}
|
||||
|
||||
function approveDomains(opts, certs, cb) {
|
||||
console.log('[debug] approveDomains', opts.domains);
|
||||
// This is where you check your database and associated
|
||||
// email addresses with domains and agreements and such
|
||||
|
||||
// The domains being approved for the first time are listed in opts.domains
|
||||
// Certs being renewed are listed in certs.altnames
|
||||
if (certs) {
|
||||
opts.domains = certs.altnames;
|
||||
cb(null, { options: opts, certs: certs });
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.config.vhost) {
|
||||
console.log('[sni] vhost checking is turned on');
|
||||
var vhost = state.config.vhost.replace(/:hostname/, opts.domains[0]);
|
||||
require('fs').readdir(vhost, function (err, nodes) {
|
||||
console.log('[sni] checking fs vhost');
|
||||
if (err) { check(); return; }
|
||||
if (nodes) { approve(); }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
function approve() {
|
||||
opts.email = state.config.email;
|
||||
opts.agreeTos = state.config.agreeTos;
|
||||
opts.challenges = {
|
||||
// TODO dns-01
|
||||
'http-01': require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' })
|
||||
};
|
||||
opts.communityMember = state.config.communityMember;
|
||||
cb(null, { options: opts, certs: certs });
|
||||
}
|
||||
|
||||
function check() {
|
||||
console.log('[sni] checking servername');
|
||||
if (-1 !== state.servernames.indexOf(opts.domain) || -1 !== (state._servernames||[]).indexOf(opts.domain)) {
|
||||
approve();
|
||||
} else {
|
||||
cb(new Error("failed the approval chain '" + opts.domains[0] + "'"));
|
||||
}
|
||||
console.log('Approve Domains cb');
|
||||
}
|
||||
check();
|
||||
}
|
||||
|
||||
/*
|
||||
if (!config.email || !config.agreeTos) {
|
||||
console.error("You didn't specify --email <EMAIL> and --agree-tos");
|
||||
console.error("(required for ACME / Let's Encrypt / Greenlock TLS/SSL certs)");
|
||||
console.error("");
|
||||
process.exit(1);
|
||||
}
|
||||
*/
|
||||
|
||||
state.greenlock = Greenlock.create({
|
||||
|
||||
version: 'draft-11'
|
||||
, server: 'https://acme-v02.api.letsencrypt.org/directory'
|
||||
//, server: 'https://acme-staging-v02.api.letsencrypt.org/directory'
|
||||
|
||||
, store: require('le-store-certbot').create({ debug: true, webrootPath: '/tmp/acme-challenges' })
|
||||
|
||||
, approveDomains: approveDomains
|
||||
|
||||
, configDir: '/root/acme'
|
||||
, debug: true
|
||||
|
||||
//, approvedDomains: program.servernames
|
||||
|
||||
});
|
||||
|
||||
require('../handlers').create(state); // adds directly to config for now...
|
||||
|
||||
//require('cluster-store').create().then(function (store) {
|
||||
|
@ -77,60 +151,6 @@ function applyConfig(config) {
|
|||
state.tcp[port].on('connection', netConnHandlers.tcp);
|
||||
});
|
||||
//});
|
||||
|
||||
function approveDomains(opts, certs, cb) {
|
||||
console.log('Approve Domains', opts.domains);
|
||||
// This is where you check your database and associated
|
||||
// email addresses with domains and agreements and such
|
||||
|
||||
// The domains being approved for the first time are listed in opts.domains
|
||||
// Certs being renewed are listed in certs.altnames
|
||||
if (certs) {
|
||||
opts.domains = certs.altnames;
|
||||
} else {
|
||||
if (-1 !== state.servernames.indexOf(opts.domain) || -1 !== (state._servernames||[]).indexOf(opts.domain)) {
|
||||
opts.email = state.config.email;
|
||||
opts.agreeTos = state.config.agreeTos;
|
||||
opts.challenges = {
|
||||
// TODO dns-01
|
||||
'http-01': require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' })
|
||||
};
|
||||
opts.communityMember = state.config.communityMember;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: you can also change other options such as `challengeType` and `challenge`
|
||||
// opts.challengeType = 'http-01';
|
||||
// opts.challenge = require('le-challenge-fs').create({});
|
||||
|
||||
console.log('Approve Domains cb');
|
||||
cb(null, { options: opts, certs: certs });
|
||||
}
|
||||
|
||||
/*
|
||||
if (!config.email || !config.agreeTos) {
|
||||
console.error("You didn't specify --email <EMAIL> and --agree-tos");
|
||||
console.error("(required for ACME / Let's Encrypt / Greenlock TLS/SSL certs)");
|
||||
console.error("");
|
||||
process.exit(1);
|
||||
}
|
||||
*/
|
||||
|
||||
state.greenlock = greenlock.create({
|
||||
|
||||
version: 'draft-11'
|
||||
, server: 'https://acme-staging-v02.api.letsencrypt.org/directory'
|
||||
|
||||
, store: require('le-store-certbot').create({ debug: true, webrootPath: '/tmp/acme-challenges' })
|
||||
|
||||
, approveDomains: approveDomains
|
||||
|
||||
, configDir: '/root/acme'
|
||||
, debug: true
|
||||
|
||||
//, approvedDomains: program.servernames
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
require('fs').readFile(confpath, 'utf8', function (err, text) {
|
||||
|
|
80
handlers.js
80
handlers.js
|
@ -5,6 +5,14 @@ var tls = require('tls');
|
|||
var wrapSocket = require('tunnel-packer').wrapSocket;
|
||||
var redirectHttps = require('redirect-https')();
|
||||
|
||||
function noSniCallback(tag) {
|
||||
return function _noSniCallback(servername, cb) {
|
||||
var err = new Error("[noSniCallback] no handler set for '" + tag + "':'" + servername + "'");
|
||||
console.error(err.message);
|
||||
cb(new Error(err));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.create = function (state) {
|
||||
var tunnelAdminTlsOpts = {};
|
||||
var setupSniCallback;
|
||||
|
@ -62,6 +70,9 @@ module.exports.create = function (state) {
|
|||
// things get a little messed up here
|
||||
state.httpInvalidSniServer.emit('connection', tlsSocket);
|
||||
});
|
||||
state.tlsInvalidSniServer.on('tlsClientError', function () {
|
||||
console.error('tlsClientError InvalidSniServer');
|
||||
});
|
||||
state.httpsInvalid = function (servername, socket) {
|
||||
// none of these methods work:
|
||||
// httpsServer.emit('connection', socket); // this didn't work
|
||||
|
@ -95,6 +106,9 @@ module.exports.create = function (state) {
|
|||
});
|
||||
httpInvalidSniServer.emit('connection', tlsSocket);
|
||||
});
|
||||
tlsInvalidSniServer.on('tlsClientError', function () {
|
||||
console.error('tlsClientError InvalidSniServer httpsInvalid');
|
||||
});
|
||||
tlsInvalidSniServer.emit('connection', wrapSocket(socket));
|
||||
};
|
||||
|
||||
|
@ -110,15 +124,24 @@ module.exports.create = function (state) {
|
|||
Object.keys(state.tlsOptions).forEach(function (key) {
|
||||
tunnelAdminTlsOpts[key] = state.tlsOptions[key];
|
||||
});
|
||||
tunnelAdminTlsOpts.SNICallback = (state.greenlock && state.greenlock.tlsOptions && function (servername, cb) {
|
||||
console.log("time to handle '" + servername + "'");
|
||||
state.greenlock.tlsOptions.SNICallback(servername, cb);
|
||||
}) || tunnelAdminTlsOpts.SNICallback;
|
||||
if (state.greenlock && state.greenlock.tlsOptions) {
|
||||
console.log('greenlock tlsOptions for SNICallback');
|
||||
tunnelAdminTlsOpts.SNICallback = function (servername, cb) {
|
||||
console.log("time to handle '" + servername + "'");
|
||||
state.greenlock.tlsOptions.SNICallback(servername, cb);
|
||||
};
|
||||
} else {
|
||||
console.log('custom or null tlsOptions for SNICallback');
|
||||
tunnelAdminTlsOpts.SNICallback = tunnelAdminTlsOpts.SNICallback || noSniCallback('admin');
|
||||
}
|
||||
state.tlsTunnelServer = tls.createServer(tunnelAdminTlsOpts, function (tlsSocket) {
|
||||
console.log('tls connection');
|
||||
console.log('(Admin) tls connection');
|
||||
// things get a little messed up here
|
||||
(state.httpTunnelServer || state.httpServer).emit('connection', tlsSocket);
|
||||
});
|
||||
state.tlsTunnelServer.on('tlsClientError', function () {
|
||||
console.error('tlsClientError TunnelServer client error');
|
||||
});
|
||||
state.httpsTunnel = function (servername, socket) {
|
||||
// none of these methods work:
|
||||
// httpsServer.emit('connection', socket); // this didn't work
|
||||
|
@ -152,11 +175,54 @@ module.exports.create = function (state) {
|
|||
// things get a little messed up here
|
||||
state.httpSetupServer.emit('connection', tlsSocket);
|
||||
});
|
||||
state.tlsSetupServer.on('tlsClientError', function () {
|
||||
console.error('tlsClientError SetupServer');
|
||||
});
|
||||
state.httpsSetupServer = function (servername, socket) {
|
||||
console.log('httpsTunnel (Admin) servername', servername);
|
||||
console.log('httpsTunnel (Setup) servername', servername);
|
||||
state._servernames = [servername];
|
||||
state.config.agreeTos = true; // TODO: BUG XXX BAD, make user accept
|
||||
setupSniCallback = state.greenlock.tlsOptions.SNICallback;
|
||||
setupSniCallback = state.greenlock.tlsOptions.SNICallback || noSniCallback('setup');
|
||||
state.tlsSetupServer.emit('connection', wrapSocket(socket));
|
||||
};
|
||||
|
||||
//
|
||||
// vhost
|
||||
//
|
||||
state.httpVhost = http.createServer(function (req, res) {
|
||||
console.log('httpVhost (local)');
|
||||
console.log('req.socket.encrypted', req.socket.encrypted);
|
||||
|
||||
var finalhandler = require('finalhandler');
|
||||
// TODO compare SNI to hostname?
|
||||
var host = (req.headers.host||'').toLowerCase().trim();
|
||||
var serveSetup = require('serve-static')(state.config.vhost.replace(/:hostname/g, host), { redirect: true });
|
||||
|
||||
if (req.socket.encrypted) { serveSetup(req, res, finalhandler(req, res)); return; }
|
||||
|
||||
console.log('try greenlock middleware for vhost');
|
||||
(state.greenlock && state.greenlock.middleware(redirectHttpsAndClose)
|
||||
|| redirectHttpsAndClose)(req, res, function () {
|
||||
console.log('fallthrough to vhost serving???');
|
||||
serveSetup(req, res, finalhandler(req, res));
|
||||
});
|
||||
});
|
||||
state.tlsVhost = tls.createServer(
|
||||
{ SNICallback: function (servername, cb) {
|
||||
console.log('tlsVhost debug SNICallback', servername);
|
||||
tunnelAdminTlsOpts.SNICallback(servername, cb);
|
||||
}
|
||||
}
|
||||
, function (tlsSocket) {
|
||||
console.log('tlsVhost (local)');
|
||||
state.httpVhost.emit('connection', tlsSocket);
|
||||
}
|
||||
);
|
||||
state.tlsVhost.on('tlsClientError', function () {
|
||||
console.error('tlsClientError Vhost');
|
||||
});
|
||||
state.httpsVhost = function (servername, socket) {
|
||||
console.log('httpsVhost (local)', servername);
|
||||
state.tlsVhost.emit('connection', wrapSocket(socket));
|
||||
};
|
||||
};
|
||||
|
|
|
@ -80,6 +80,10 @@ module.exports.createTcpConnectionHandler = function (copts) {
|
|||
var m;
|
||||
|
||||
function tryTls() {
|
||||
var vhost;
|
||||
|
||||
console.log("");
|
||||
|
||||
if (!copts.servernames.length) {
|
||||
console.log("https => admin => setup => (needs bogus tls certs to start?)");
|
||||
copts.httpsSetupServer(servername, conn);
|
||||
|
@ -92,21 +96,44 @@ module.exports.createTcpConnectionHandler = function (copts) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!servername) {
|
||||
console.log("No SNI was given, so there's nothing we can do here");
|
||||
copts.httpsInvalid(servername, conn);
|
||||
if (copts.config.nowww && /^www\./i.test(servername)) {
|
||||
console.log("TODO: use www bare redirect");
|
||||
}
|
||||
|
||||
function run() {
|
||||
if (!servername) {
|
||||
console.log("No SNI was given, so there's nothing we can do here");
|
||||
copts.httpsInvalid(servername, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
var nextDevice = Devices.next(copts.deviceLists, servername);
|
||||
if (!nextDevice) {
|
||||
console.log("No devices match the given servername");
|
||||
copts.httpsInvalid(servername, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("pipeWs(servername, service, socket, deviceLists['" + servername + "'])");
|
||||
pipeWs(servername, service, conn, nextDevice);
|
||||
}
|
||||
|
||||
if (copts.config.vhost) {
|
||||
console.log("VHOST path", copts.config.vhost);
|
||||
vhost = copts.config.vhost.replace(/:hostname/, (servername||''));
|
||||
console.log("VHOST name", vhost);
|
||||
conn.pause();
|
||||
//copts.httpsVhost(servername, conn);
|
||||
//return;
|
||||
require('fs').readdir(vhost, function (err, nodes) {
|
||||
console.log("VHOST error?", err);
|
||||
if (err) { run(); return; }
|
||||
if (nodes) { copts.httpsVhost(servername, conn); }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var nextDevice = Devices.next(copts.deviceLists, servername);
|
||||
if (!nextDevice) {
|
||||
console.log("No devices match the given servername");
|
||||
copts.httpsInvalid(servername, conn);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("pipeWs(servername, service, socket, deviceLists['" + servername + "'])");
|
||||
pipeWs(servername, service, conn, nextDevice);
|
||||
run();
|
||||
}
|
||||
|
||||
// https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155
|
||||
|
|
Loading…
Reference in New Issue