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:
|
You can customize the installation:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export NODEJS_VER=v8.11.1
|
export NODEJS_VER=v8.11.2
|
||||||
export TELEBITD_PATH=/opt/telebitd
|
export TELEBITD_PATH=/opt/telebitd
|
||||||
curl -fsS https://get.telebit.cloud/ | bash
|
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 argv = process.argv.slice(2);
|
||||||
var telebitd = require('../telebitd.js');
|
var telebitd = require('../telebitd.js');
|
||||||
var greenlock = require('greenlock');
|
var Greenlock = require('greenlock');
|
||||||
|
|
||||||
var confIndex = argv.indexOf('--config');
|
var confIndex = argv.indexOf('--config');
|
||||||
var confpath;
|
var confpath;
|
||||||
|
@ -55,6 +55,80 @@ function applyConfig(config) {
|
||||||
console.info("");
|
console.info("");
|
||||||
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('../handlers').create(state); // adds directly to config for now...
|
||||||
|
|
||||||
//require('cluster-store').create().then(function (store) {
|
//require('cluster-store').create().then(function (store) {
|
||||||
|
@ -77,60 +151,6 @@ function applyConfig(config) {
|
||||||
state.tcp[port].on('connection', netConnHandlers.tcp);
|
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) {
|
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 wrapSocket = require('tunnel-packer').wrapSocket;
|
||||||
var redirectHttps = require('redirect-https')();
|
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) {
|
module.exports.create = function (state) {
|
||||||
var tunnelAdminTlsOpts = {};
|
var tunnelAdminTlsOpts = {};
|
||||||
var setupSniCallback;
|
var setupSniCallback;
|
||||||
|
@ -62,6 +70,9 @@ module.exports.create = function (state) {
|
||||||
// things get a little messed up here
|
// things get a little messed up here
|
||||||
state.httpInvalidSniServer.emit('connection', tlsSocket);
|
state.httpInvalidSniServer.emit('connection', tlsSocket);
|
||||||
});
|
});
|
||||||
|
state.tlsInvalidSniServer.on('tlsClientError', function () {
|
||||||
|
console.error('tlsClientError InvalidSniServer');
|
||||||
|
});
|
||||||
state.httpsInvalid = function (servername, socket) {
|
state.httpsInvalid = function (servername, socket) {
|
||||||
// none of these methods work:
|
// none of these methods work:
|
||||||
// httpsServer.emit('connection', socket); // this didn't work
|
// httpsServer.emit('connection', socket); // this didn't work
|
||||||
|
@ -95,6 +106,9 @@ module.exports.create = function (state) {
|
||||||
});
|
});
|
||||||
httpInvalidSniServer.emit('connection', tlsSocket);
|
httpInvalidSniServer.emit('connection', tlsSocket);
|
||||||
});
|
});
|
||||||
|
tlsInvalidSniServer.on('tlsClientError', function () {
|
||||||
|
console.error('tlsClientError InvalidSniServer httpsInvalid');
|
||||||
|
});
|
||||||
tlsInvalidSniServer.emit('connection', wrapSocket(socket));
|
tlsInvalidSniServer.emit('connection', wrapSocket(socket));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,15 +124,24 @@ module.exports.create = function (state) {
|
||||||
Object.keys(state.tlsOptions).forEach(function (key) {
|
Object.keys(state.tlsOptions).forEach(function (key) {
|
||||||
tunnelAdminTlsOpts[key] = state.tlsOptions[key];
|
tunnelAdminTlsOpts[key] = state.tlsOptions[key];
|
||||||
});
|
});
|
||||||
tunnelAdminTlsOpts.SNICallback = (state.greenlock && state.greenlock.tlsOptions && function (servername, cb) {
|
if (state.greenlock && state.greenlock.tlsOptions) {
|
||||||
console.log("time to handle '" + servername + "'");
|
console.log('greenlock tlsOptions for SNICallback');
|
||||||
state.greenlock.tlsOptions.SNICallback(servername, cb);
|
tunnelAdminTlsOpts.SNICallback = function (servername, cb) {
|
||||||
}) || tunnelAdminTlsOpts.SNICallback;
|
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) {
|
state.tlsTunnelServer = tls.createServer(tunnelAdminTlsOpts, function (tlsSocket) {
|
||||||
console.log('tls connection');
|
console.log('(Admin) tls connection');
|
||||||
// things get a little messed up here
|
// things get a little messed up here
|
||||||
(state.httpTunnelServer || state.httpServer).emit('connection', tlsSocket);
|
(state.httpTunnelServer || state.httpServer).emit('connection', tlsSocket);
|
||||||
});
|
});
|
||||||
|
state.tlsTunnelServer.on('tlsClientError', function () {
|
||||||
|
console.error('tlsClientError TunnelServer client error');
|
||||||
|
});
|
||||||
state.httpsTunnel = function (servername, socket) {
|
state.httpsTunnel = function (servername, socket) {
|
||||||
// none of these methods work:
|
// none of these methods work:
|
||||||
// httpsServer.emit('connection', socket); // this didn't 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
|
// things get a little messed up here
|
||||||
state.httpSetupServer.emit('connection', tlsSocket);
|
state.httpSetupServer.emit('connection', tlsSocket);
|
||||||
});
|
});
|
||||||
|
state.tlsSetupServer.on('tlsClientError', function () {
|
||||||
|
console.error('tlsClientError SetupServer');
|
||||||
|
});
|
||||||
state.httpsSetupServer = function (servername, socket) {
|
state.httpsSetupServer = function (servername, socket) {
|
||||||
console.log('httpsTunnel (Admin) servername', servername);
|
console.log('httpsTunnel (Setup) servername', servername);
|
||||||
state._servernames = [servername];
|
state._servernames = [servername];
|
||||||
state.config.agreeTos = true; // TODO: BUG XXX BAD, make user accept
|
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));
|
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;
|
var m;
|
||||||
|
|
||||||
function tryTls() {
|
function tryTls() {
|
||||||
|
var vhost;
|
||||||
|
|
||||||
|
console.log("");
|
||||||
|
|
||||||
if (!copts.servernames.length) {
|
if (!copts.servernames.length) {
|
||||||
console.log("https => admin => setup => (needs bogus tls certs to start?)");
|
console.log("https => admin => setup => (needs bogus tls certs to start?)");
|
||||||
copts.httpsSetupServer(servername, conn);
|
copts.httpsSetupServer(servername, conn);
|
||||||
|
@ -92,21 +96,44 @@ module.exports.createTcpConnectionHandler = function (copts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!servername) {
|
if (copts.config.nowww && /^www\./i.test(servername)) {
|
||||||
console.log("No SNI was given, so there's nothing we can do here");
|
console.log("TODO: use www bare redirect");
|
||||||
copts.httpsInvalid(servername, conn);
|
}
|
||||||
|
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextDevice = Devices.next(copts.deviceLists, servername);
|
run();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155
|
// https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155
|
||||||
|
|
Loading…
Reference in New Issue