updates
This commit is contained in:
parent
3a1efa7cdf
commit
36b15be276
2
app.js
2
app.js
|
@ -21,7 +21,7 @@ module.exports = function (opts) {
|
||||||
if (opts.livereload) {
|
if (opts.livereload) {
|
||||||
livereload = '<script src="//'
|
livereload = '<script src="//'
|
||||||
+ (res.getHeader('Host') || opts.servername).split(':')[0]
|
+ (res.getHeader('Host') || opts.servername).split(':')[0]
|
||||||
+ ':35729/livereload.js?snipver=1"></script>';
|
+ ':' + opts.lrPort + '/livereload.js?snipver=1"></script>';
|
||||||
addLen = livereload.length;
|
addLen = livereload.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var PromiseA = require('bluebird');
|
||||||
|
|
||||||
|
module.exports.match = function (servername, opts) {
|
||||||
|
return PromiseA.promisify(require('ipify'))().then(function (ip) {
|
||||||
|
var dns = PromiseA.promisifyAll(require('dns'));
|
||||||
|
|
||||||
|
opts.externalIps = [ { address: ip, family: 'IPv4' } ];
|
||||||
|
opts.ifaces = require('./local-ip.js').find({ externals: opts.externalIps });
|
||||||
|
opts.externalIfaces = Object.keys(opts.ifaces).reduce(function (all, iname) {
|
||||||
|
var iface = opts.ifaces[iname];
|
||||||
|
|
||||||
|
iface.ipv4.forEach(function (addr) {
|
||||||
|
if (addr.external) {
|
||||||
|
addr.iface = iname;
|
||||||
|
all.push(addr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
iface.ipv6.forEach(function (addr) {
|
||||||
|
if (addr.external) {
|
||||||
|
addr.iface = iname;
|
||||||
|
all.push(addr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return all;
|
||||||
|
}, []).filter(Boolean);
|
||||||
|
|
||||||
|
function resolveIps(hostname) {
|
||||||
|
var allIps = [];
|
||||||
|
|
||||||
|
return PromiseA.all([
|
||||||
|
dns.resolve4Async(hostname).then(function (records) {
|
||||||
|
records.forEach(function (ip) {
|
||||||
|
allIps.push({
|
||||||
|
address: ip
|
||||||
|
, family: 'IPv4'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, function () {})
|
||||||
|
, dns.resolve6Async(hostname).then(function (records) {
|
||||||
|
records.forEach(function (ip) {
|
||||||
|
allIps.push({
|
||||||
|
address: ip
|
||||||
|
, family: 'IPv6'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, function () {})
|
||||||
|
]).then(function () {
|
||||||
|
return allIps;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveIpsAndCnames(hostname) {
|
||||||
|
return PromiseA.all([
|
||||||
|
resolveIps(hostname)
|
||||||
|
, dns.resolveCnameAsync(hostname).then(function (records) {
|
||||||
|
return PromiseA.all(records.map(function (hostname) {
|
||||||
|
return resolveIps(hostname);
|
||||||
|
})).then(function (allIps) {
|
||||||
|
return allIps.reduce(function (all, ips) {
|
||||||
|
return all.concat(ips);
|
||||||
|
}, []);
|
||||||
|
});
|
||||||
|
}, function () {
|
||||||
|
return [];
|
||||||
|
})
|
||||||
|
]).then(function (ips) {
|
||||||
|
return ips.reduce(function (all, set) {
|
||||||
|
return all.concat(set);
|
||||||
|
}, []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveIpsAndCnames(servername).then(function (allIps) {
|
||||||
|
var matchingIps = [];
|
||||||
|
|
||||||
|
if (!allIps.length) {
|
||||||
|
console.warn("Could not resolve '" + servername + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// { address, family }
|
||||||
|
allIps.some(function (ip) {
|
||||||
|
function match(addr) {
|
||||||
|
if (ip.address === addr.address) {
|
||||||
|
matchingIps.push(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.externalIps.forEach(match);
|
||||||
|
// opts.externalIfaces.forEach(match);
|
||||||
|
|
||||||
|
Object.keys(opts.ifaces).forEach(function (iname) {
|
||||||
|
var iface = opts.ifaces[iname];
|
||||||
|
|
||||||
|
iface.ipv4.forEach(match);
|
||||||
|
iface.ipv6.forEach(match);
|
||||||
|
});
|
||||||
|
|
||||||
|
return matchingIps.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
return matchingIps;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "serve-https",
|
"name": "serve-https",
|
||||||
"version": "1.5.6",
|
"version": "2.0.0",
|
||||||
"description": "Serves HTTPS using TLS (SSL) certs for localhost.daplie.com - great for testing and development.",
|
"description": "Serves HTTPS using TLS (SSL) certs for localhost.daplie.com - great for testing and development.",
|
||||||
"main": "serve.js",
|
"main": "serve.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -38,8 +38,12 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/Daplie/serve-https#readme",
|
"homepage": "https://github.com/Daplie/serve-https#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bluebird": "^3.4.6",
|
||||||
"finalhandler": "^0.4.0",
|
"finalhandler": "^0.4.0",
|
||||||
"ipify": "^1.1.0",
|
"ipify": "^1.1.0",
|
||||||
|
"le-challenge-dns": "^2.0.1",
|
||||||
|
"le-challenge-fs": "^2.0.5",
|
||||||
|
"letsencrypt-express": "^2.0.2",
|
||||||
"livereload": "^0.4.0",
|
"livereload": "^0.4.0",
|
||||||
"localhost.daplie.com-certificates": "^1.2.0",
|
"localhost.daplie.com-certificates": "^1.2.0",
|
||||||
"minimist": "^1.1.1",
|
"minimist": "^1.1.1",
|
||||||
|
|
136
serve.js
136
serve.js
|
@ -6,6 +6,7 @@ var https = require('https');
|
||||||
var http = require('http');
|
var http = require('http');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
var DDNS = require('ddns-cli');
|
||||||
var portFallback = 8443;
|
var portFallback = 8443;
|
||||||
var insecurePortFallback = 4080;
|
var insecurePortFallback = 4080;
|
||||||
|
|
||||||
|
@ -49,13 +50,74 @@ function createInsecureServer(port, pubdir, opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createServer(port, pubdir, content, opts) {
|
function createServer(port, pubdir, content, opts) {
|
||||||
|
function approveDomains(params, certs, cb) {
|
||||||
|
// This is where you check your database and associated
|
||||||
|
// email addresses with domains and agreements and such
|
||||||
|
var domains = params.domains;
|
||||||
|
//var p;
|
||||||
|
console.log('approveDomains');
|
||||||
|
console.log(domains);
|
||||||
|
|
||||||
|
|
||||||
|
// The domains being approved for the first time are listed in opts.domains
|
||||||
|
// Certs being renewed are listed in certs.altnames
|
||||||
|
if (certs) {
|
||||||
|
params.domains = certs.altnames;
|
||||||
|
//p = PromiseA.resolve();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//params.email = opts.email;
|
||||||
|
if (!opts.agreeTos) {
|
||||||
|
console.error("You have not previously registered '" + domains + "' so you must specify --agree-tos to agree to both the Let's Encrypt and Daplie DNS terms of service.");
|
||||||
|
process.exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params.agreeTos = opts.agreeTos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ddns.token(params.email, domains[0])
|
||||||
|
params.email = opts.email;
|
||||||
|
params.refreshToken = opts.refreshToken;
|
||||||
|
params.challengeType = 'dns-01';
|
||||||
|
params.cli = opts.argv;
|
||||||
|
|
||||||
|
cb(null, { options: params, certs: certs });
|
||||||
|
}
|
||||||
|
|
||||||
return new PromiseA(function (resolve) {
|
return new PromiseA(function (resolve) {
|
||||||
var server = https.createServer(opts);
|
|
||||||
var app = require('./app');
|
var app = require('./app');
|
||||||
|
|
||||||
var directive = { public: pubdir, content: content, livereload: opts.livereload
|
var directive = { public: pubdir, content: content, livereload: opts.livereload
|
||||||
, servername: opts.servername, expressApp: opts.expressApp };
|
, servername: opts.servername, expressApp: opts.expressApp };
|
||||||
|
|
||||||
|
// returns an instance of node-letsencrypt with additional helper methods
|
||||||
|
var webrootPath = require('os').tmpdir();
|
||||||
|
var leChallengeFs = require('le-challenge-fs').create({ webrootPath: webrootPath });
|
||||||
|
var leChallengeDns = require('le-challenge-dns').create({ ttl: 1 });
|
||||||
|
var lex = require('letsencrypt-express').create({
|
||||||
|
// set to https://acme-v01.api.letsencrypt.org/directory in production
|
||||||
|
server: opts.debug ? 'staging' : 'https://acme-v01.api.letsencrypt.org/directory'
|
||||||
|
|
||||||
|
// If you wish to replace the default plugins, you may do so here
|
||||||
|
//
|
||||||
|
, challenges: {
|
||||||
|
'http-01': leChallengeFs
|
||||||
|
, 'tls-sni-01': leChallengeFs
|
||||||
|
, 'dns-01': leChallengeDns
|
||||||
|
}
|
||||||
|
, challengeType: 'dns-01'
|
||||||
|
, store: require('le-store-certbot').create({ webrootPath: webrootPath })
|
||||||
|
, webrootPath: webrootPath
|
||||||
|
|
||||||
|
// You probably wouldn't need to replace the default sni handler
|
||||||
|
// See https://github.com/Daplie/le-sni-auto if you think you do
|
||||||
|
//, sni: require('le-sni-auto').create({})
|
||||||
|
|
||||||
|
, approveDomains: approveDomains
|
||||||
|
});
|
||||||
|
opts.httpsOptions.SNICallback = lex.httpsOptions.SNICallback;
|
||||||
|
var server = https.createServer(opts.httpsOptions);
|
||||||
|
|
||||||
server.on('error', function (err) {
|
server.on('error', function (err) {
|
||||||
if (opts.errorPort || opts.manualPort) {
|
if (opts.errorPort || opts.manualPort) {
|
||||||
showError(err, port);
|
showError(err, port);
|
||||||
|
@ -71,8 +133,9 @@ function createServer(port, pubdir, content, opts) {
|
||||||
server.listen(port, function () {
|
server.listen(port, function () {
|
||||||
opts.port = port;
|
opts.port = port;
|
||||||
|
|
||||||
|
opts.lrPort = 35729;
|
||||||
var livereload = require('livereload');
|
var livereload = require('livereload');
|
||||||
var server2 = livereload.createServer({ https: opts });
|
var server2 = livereload.createServer({ https: opts.httpsOptions, port: opts.lrPort });
|
||||||
|
|
||||||
server2.watch(pubdir);
|
server2.watch(pubdir);
|
||||||
|
|
||||||
|
@ -90,6 +153,8 @@ function createServer(port, pubdir, content, opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
server.on('request', function (req, res) {
|
server.on('request', function (req, res) {
|
||||||
|
console.log('[' + req.method + '] ' + req.url);
|
||||||
|
|
||||||
if ('function' === typeof app) {
|
if ('function' === typeof app) {
|
||||||
app(req, res);
|
app(req, res);
|
||||||
return;
|
return;
|
||||||
|
@ -118,22 +183,23 @@ function run() {
|
||||||
var tls = require('tls');
|
var tls = require('tls');
|
||||||
|
|
||||||
// letsencrypt
|
// letsencrypt
|
||||||
var email = argv.email;
|
|
||||||
var agreeTos = argv.agreeTos || argv['agree-tos'];
|
|
||||||
|
|
||||||
var cert = require('localhost.daplie.com-certificates');
|
var cert = require('localhost.daplie.com-certificates');
|
||||||
var opts = {
|
var opts = {
|
||||||
key: cert.key
|
agreeTos: argv.agreeTos || argv['agree-tos']
|
||||||
, cert: cert.cert
|
, debug: argv.debug
|
||||||
//, ca: cert.ca
|
, email: argv.email
|
||||||
|
, httpsOptions: {
|
||||||
, email: email
|
key: cert.key
|
||||||
, agreeTos: agreeTos
|
, cert: cert.cert
|
||||||
|
//, ca: cert.ca
|
||||||
|
}
|
||||||
|
, argv: argv
|
||||||
};
|
};
|
||||||
var peerCa;
|
var peerCa;
|
||||||
|
var p;
|
||||||
|
|
||||||
opts.SNICallback = function (servername, cb) {
|
opts.httpsOptions.SNICallback = function (servername, cb) {
|
||||||
cb(null, tls.createSecureContext(opts));
|
cb(null, tls.createSecureContext(opts.httpsOptions));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -161,8 +227,8 @@ function run() {
|
||||||
argv.root = [argv.root];
|
argv.root = [argv.root];
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.key = fs.readFileSync(argv.key);
|
opts.httpsOptions.key = fs.readFileSync(argv.key);
|
||||||
opts.cert = fs.readFileSync(argv.cert);
|
opts.httpsOptions.cert = fs.readFileSync(argv.cert);
|
||||||
|
|
||||||
// turn multiple-cert pemfile into array of cert strings
|
// turn multiple-cert pemfile into array of cert strings
|
||||||
peerCa = argv.root.reduce(function (roots, fullpath) {
|
peerCa = argv.root.reduce(function (roots, fullpath) {
|
||||||
|
@ -181,9 +247,9 @@ function run() {
|
||||||
|
|
||||||
// TODO * `--verify /path/to/root.pem` require peers to present certificates from said authority
|
// TODO * `--verify /path/to/root.pem` require peers to present certificates from said authority
|
||||||
if (argv.verify) {
|
if (argv.verify) {
|
||||||
opts.ca = peerCa;
|
opts.httpsOptions.ca = peerCa;
|
||||||
opts.requestCert = true;
|
opts.httpsOptions.requestCert = true;
|
||||||
opts.rejectUnauthorized = true;
|
opts.httpsOptions.rejectUnauthorized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argv['serve-root']) {
|
if (argv['serve-root']) {
|
||||||
|
@ -208,6 +274,29 @@ function run() {
|
||||||
opts.expressApp = require(path.resolve(process.cwd(), argv['express-app']));
|
opts.expressApp = require(path.resolve(process.cwd(), argv['express-app']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts.email || opts.servername) {
|
||||||
|
if (!opts.agreeTos) {
|
||||||
|
console.warn("You may need to specify --agree-tos to agree to both the Let's Encrypt and Daplie DNS terms of service.");
|
||||||
|
}
|
||||||
|
if (!opts.email) {
|
||||||
|
// TODO store email in .ddnsrc.json
|
||||||
|
console.warn("You may need to specify --email to register with both the Let's Encrypt and Daplie DNS.");
|
||||||
|
}
|
||||||
|
p = DDNS.refreshToken({
|
||||||
|
email: opts.email
|
||||||
|
, silent: true
|
||||||
|
}, {
|
||||||
|
debug: false
|
||||||
|
, email: opts.argv.email
|
||||||
|
}).then(function (refreshToken) {
|
||||||
|
opts.refreshToken = refreshToken;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p = PromiseA.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.then(function () {
|
||||||
return createServer(port, pubdir, content, opts).then(function () {
|
return createServer(port, pubdir, content, opts).then(function () {
|
||||||
var msg;
|
var msg;
|
||||||
var p;
|
var p;
|
||||||
|
@ -252,7 +341,7 @@ function run() {
|
||||||
return promise.then(function (matchingIps) {
|
return promise.then(function (matchingIps) {
|
||||||
if (matchingIps) {
|
if (matchingIps) {
|
||||||
if (!matchingIps.length) {
|
if (!matchingIps.length) {
|
||||||
console.log("Neither the attached nor external interfaces match '" + argv.servername + "'");
|
console.info("Neither the attached nor external interfaces match '" + argv.servername + "'");
|
||||||
}
|
}
|
||||||
opts.matchingIps = matchingIps || [];
|
opts.matchingIps = matchingIps || [];
|
||||||
}
|
}
|
||||||
|
@ -292,11 +381,11 @@ function run() {
|
||||||
}
|
}
|
||||||
console.info('\t' + httpsUrl);
|
console.info('\t' + httpsUrl);
|
||||||
|
|
||||||
httpsUrl = 'https://[' + iface.ipv6[0].address + ']';
|
|
||||||
if (443 !== opts.port) {
|
|
||||||
httpsUrl += ':' + opts.port;
|
|
||||||
}
|
|
||||||
if (iface.ipv6.length) {
|
if (iface.ipv6.length) {
|
||||||
|
httpsUrl = 'https://[' + iface.ipv6[0].address + ']';
|
||||||
|
if (443 !== opts.port) {
|
||||||
|
httpsUrl += ':' + opts.port;
|
||||||
|
}
|
||||||
console.info('\t' + httpsUrl);
|
console.info('\t' + httpsUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -305,6 +394,7 @@ function run() {
|
||||||
console.info('');
|
console.info('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
|
|
Loading…
Reference in New Issue