Merge branch '2.x'
This commit is contained in:
commit
411d7adc20
15
README.md
15
README.md
|
@ -23,12 +23,25 @@ Serving /Users/foo/ at https://localhost.daplie.com:8443
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```
|
||||||
|
node serve.js --servername jane.daplie.me --agree-tos --email jane@example.com --tunnel
|
||||||
|
```
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
* `-p <port>` - i.e. `sudo serve-https -p 443` (defaults to 80+443 or 8443)
|
* `-p <port>` - i.e. `sudo serve-https -p 443` (defaults to 80+443 or 8443)
|
||||||
* `-d <dirpath>` - i.e. `serve-https -d /tmp/` (defaults to `pwd`)
|
* `-d <dirpath>` - i.e. `serve-https -d /tmp/` (defaults to `pwd`)
|
||||||
* `-c <content>` - i.e. `server-https -c 'Hello, World! '` (defaults to directory index)
|
* `-c <content>` - i.e. `server-https -c 'Hello, World! '` (defaults to directory index)
|
||||||
* `--express-app` - path to a file the exports an express-style app (`function (req, res, next) { ... }`)
|
* `--express-app <path>` - path to a file the exports an express-style app (`function (req, res, next) { ... }`)
|
||||||
* `--livereload` - inject livereload into all html pages (see also: [fswatch](http://stackoverflow.com/a/13807906/151312)), but be careful if `<dirpath>` has thousands of files it will spike your CPU usage to 100%
|
* `--livereload` - inject livereload into all html pages (see also: [fswatch](http://stackoverflow.com/a/13807906/151312)), but be careful if `<dirpath>` has thousands of files it will spike your CPU usage to 100%
|
||||||
|
|
||||||
|
* `--email <email>` - email to use for Let's Encrypt, Daplie DNS, Daplie Tunnel
|
||||||
|
* `--agree-tos` - agree to terms for Let's Encrypt, Daplie DNS
|
||||||
|
* `--servername <servername>` - use `<servername>` instead of `localhost.daplie.com`
|
||||||
|
* `--tunnel` - make world-visible (must use `--servername`)
|
||||||
|
|
||||||
Specifying a custom HTTPS certificate:
|
Specifying a custom HTTPS certificate:
|
||||||
|
|
||||||
* `--key /path/to/privkey.pem` specifies the server private key
|
* `--key /path/to/privkey.pem` specifies the server private key
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports.create = function (opts/*, servers*/) {
|
||||||
|
var PromiseA = opts.PromiseA;
|
||||||
|
var dns = PromiseA.promisifyAll(require('dns'));
|
||||||
|
|
||||||
|
return PromiseA.all([
|
||||||
|
dns.resolve4Async(opts.servername).then(function (results) {
|
||||||
|
return results;
|
||||||
|
}, function () {})
|
||||||
|
, dns.resolve6Async(opts.servername).then(function (results) {
|
||||||
|
return results;
|
||||||
|
}, function () {})
|
||||||
|
]).then(function (results) {
|
||||||
|
var ipv4 = results[0] || [];
|
||||||
|
var ipv6 = results[1] || [];
|
||||||
|
var record;
|
||||||
|
|
||||||
|
opts.dnsRecords = {
|
||||||
|
A: ipv4
|
||||||
|
, AAAA: ipv6
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(opts.ifaces).some(function (ifacename) {
|
||||||
|
var iface = opts.ifaces[ifacename];
|
||||||
|
|
||||||
|
return iface.ipv4.some(function (localIp) {
|
||||||
|
return ipv4.some(function (remoteIp) {
|
||||||
|
if (localIp.address === remoteIp) {
|
||||||
|
record = localIp;
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}) || iface.ipv6.some(function (localIp) {
|
||||||
|
return ipv6.forEach(function (remoteIp) {
|
||||||
|
if (localIp.address === remoteIp) {
|
||||||
|
record = localIp;
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!record) {
|
||||||
|
console.info("DNS Record '" + ipv4.concat(ipv6).join(',') + "' does not match any local IP address.");
|
||||||
|
console.info("Use --ddns to allow the people of the Internet to access your server.");
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.externalIps.ipv4.some(function (localIp) {
|
||||||
|
return ipv4.some(function (remoteIp) {
|
||||||
|
if (localIp.address === remoteIp) {
|
||||||
|
record = localIp;
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
opts.externalIps.ipv6.some(function (localIp) {
|
||||||
|
return ipv6.some(function (remoteIp) {
|
||||||
|
if (localIp.address === remoteIp) {
|
||||||
|
record = localIp;
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!record) {
|
||||||
|
console.info("DNS Record '" + ipv4.concat(ipv6).join(',') + "' does not match any local IP address.");
|
||||||
|
console.info("Use --ddns to allow the people of the Internet to access your server.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
var opts = {
|
||||||
|
servername: 'aj.daplie.me'
|
||||||
|
, PromiseA: require('bluebird')
|
||||||
|
};
|
||||||
|
// ifaces
|
||||||
|
opts.ifaces = require('./local-ip.js').find();
|
||||||
|
console.log('opts.ifaces');
|
||||||
|
console.log(opts.ifaces);
|
||||||
|
require('./match-ips.js').match(opts.servername, opts).then(function (ips) {
|
||||||
|
opts.matchingIps = ips.matchingIps || [];
|
||||||
|
opts.externalIps = ips.externalIps;
|
||||||
|
module.exports.create(opts);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var PromiseA = require('bluebird');
|
||||||
|
|
||||||
|
module.exports.match = function (servername, opts) {
|
||||||
|
return PromiseA.promisify(require('ipify'))().then(function (externalIp) {
|
||||||
|
var dns = PromiseA.promisifyAll(require('dns'));
|
||||||
|
|
||||||
|
opts.externalIps = [ { address: externalIp, 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;
|
||||||
|
});
|
||||||
|
|
||||||
|
matchingIps.externalIps = {
|
||||||
|
ipv4: [
|
||||||
|
{ address: externalIp
|
||||||
|
, family: 'IPv4'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, ipv6: [
|
||||||
|
]
|
||||||
|
};
|
||||||
|
matchingIps.matchingIps = matchingIps;
|
||||||
|
return matchingIps;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,137 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports.create = function (opts, servers) {
|
||||||
|
// servers = { plainserver, server }
|
||||||
|
var Oauth3 = require('oauth3-cli');
|
||||||
|
var Tunnel = require('daplie-tunnel').create({
|
||||||
|
Oauth3: Oauth3
|
||||||
|
, PromiseA: opts.PromiseA
|
||||||
|
, CLI: {
|
||||||
|
init: function (/*rs, ws, state, options*/) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).Tunnel;
|
||||||
|
var stunnel = require('stunnel');
|
||||||
|
var killcount = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
var Dup = {
|
||||||
|
write: function (chunk, encoding, cb) {
|
||||||
|
this.__my_socket.push(chunk, encoding);
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
, read: function (size) {
|
||||||
|
var x = this.__my_socket.read(size);
|
||||||
|
if (x) { this.push(x); }
|
||||||
|
}
|
||||||
|
, setTimeout: function () {
|
||||||
|
console.log('TODO implement setTimeout on Duplex');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var httpServer = require('http').createServer(function (req, res) {
|
||||||
|
console.log('req.socket.encrypted', req.socket.encrypted);
|
||||||
|
res.end('Hello, tunneled World!');
|
||||||
|
});
|
||||||
|
|
||||||
|
var tlsServer = require('tls').createServer(opts.httpsOptions, function (tlsSocket) {
|
||||||
|
console.log('tls connection');
|
||||||
|
// things get a little messed up here
|
||||||
|
httpServer.emit('connection', tlsSocket);
|
||||||
|
|
||||||
|
// try again
|
||||||
|
//servers.server.emit('connection', tlsSocket);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
process.on('SIGINT', function () {
|
||||||
|
killcount += 1;
|
||||||
|
console.log('[quit] closing http and https servers');
|
||||||
|
if (killcount >= 3) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (servers.server) {
|
||||||
|
servers.server.close();
|
||||||
|
}
|
||||||
|
if (servers.insecureServer) {
|
||||||
|
servers.insecureServer.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Tunnel.token({
|
||||||
|
refreshToken: opts.refreshToken
|
||||||
|
, email: opts.email
|
||||||
|
, domains: [ opts.servername ]
|
||||||
|
, device: { hostname: opts.devicename || opts.device }
|
||||||
|
}).then(function (result) {
|
||||||
|
// { jwt, tunnelUrl }
|
||||||
|
return stunnel.connect({
|
||||||
|
token: result.jwt
|
||||||
|
, stunneld: result.tunnelUrl
|
||||||
|
// XXX TODO BUG // this is just for testing
|
||||||
|
, insecure: /*opts.insecure*/ true
|
||||||
|
, locals: [
|
||||||
|
{ protocol: 'https'
|
||||||
|
, hostname: opts.servername
|
||||||
|
, port: opts.port
|
||||||
|
}
|
||||||
|
, { protocol: 'http'
|
||||||
|
, hostname: opts.servername
|
||||||
|
, port: opts.insecurePort || opts.port
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// a simple passthru is proving to not be so simple
|
||||||
|
, net: require('net') /*
|
||||||
|
{
|
||||||
|
createConnection: function (info, cb) {
|
||||||
|
// data is the hello packet / first chunk
|
||||||
|
// info = { data, servername, port, host, remoteAddress: { family, address, port } }
|
||||||
|
|
||||||
|
var myDuplex = new (require('stream').Duplex)();
|
||||||
|
var myDuplex2 = new (require('stream').Duplex)();
|
||||||
|
// duplex = { write, push, end, events: [ 'readable', 'data', 'error', 'end' ] };
|
||||||
|
|
||||||
|
myDuplex2.__my_socket = myDuplex;
|
||||||
|
myDuplex.__my_socket = myDuplex2;
|
||||||
|
|
||||||
|
myDuplex2._write = Dup.write;
|
||||||
|
myDuplex2._read = Dup.read;
|
||||||
|
|
||||||
|
myDuplex._write = Dup.write;
|
||||||
|
myDuplex._read = Dup.read;
|
||||||
|
|
||||||
|
myDuplex.remoteFamily = info.remoteFamily;
|
||||||
|
myDuplex.remoteAddress = info.remoteAddress;
|
||||||
|
myDuplex.remotePort = info.remotePort;
|
||||||
|
|
||||||
|
// socket.local{Family,Address,Port}
|
||||||
|
myDuplex.localFamily = 'IPv4';
|
||||||
|
myDuplex.localAddress = '127.0.01';
|
||||||
|
myDuplex.localPort = info.port;
|
||||||
|
|
||||||
|
myDuplex.setTimeout = Dup.setTimeout;
|
||||||
|
|
||||||
|
// this doesn't seem to work so well
|
||||||
|
//servers.server.emit('connection', myDuplex);
|
||||||
|
|
||||||
|
// try a little more manual wrapping / unwrapping
|
||||||
|
var firstByte = info.data[0];
|
||||||
|
if (firstByte < 32 || firstByte >= 127) {
|
||||||
|
tlsServer.emit('connection', myDuplex);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
httpServer.emit('connection', myDuplex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
process.nextTick(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
return myDuplex2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//*/
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
16
package.json
16
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "serve-https",
|
"name": "serve-https",
|
||||||
"version": "1.6.1",
|
"version": "2.0.2",
|
||||||
"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,14 +38,24 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/Daplie/serve-https#readme",
|
"homepage": "https://github.com/Daplie/serve-https#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bluebird": "^3.4.6",
|
||||||
|
"daplie-tunnel": "git+https://github.com/Daplie/daplie-cli-tunnel.git#master",
|
||||||
|
"ddns-cli": "git+https://github.com/Daplie/node-ddns-client.git#master",
|
||||||
"finalhandler": "^0.4.0",
|
"finalhandler": "^0.4.0",
|
||||||
"httpolyglot": "^0.1.1",
|
"httpolyglot": "^0.1.1",
|
||||||
"ipify": "^1.1.0",
|
"ipify": "^1.1.0",
|
||||||
"livereload": "^0.5.0",
|
"le-challenge-ddns": "git+https://github.com/Daplie/le-challenge-ddns.git#master",
|
||||||
|
"le-challenge-fs": "git+https://github.com/Daplie/le-challenge-fs.git#master",
|
||||||
|
"le-challenge-sni": "^2.0.1",
|
||||||
|
"letsencrypt-express": "git+https://github.com/Daplie/letsencrypt-express.git#master",
|
||||||
|
"letsencrypt": "git+https://github.com/Daplie/node-letsencrypt.git#master",
|
||||||
|
"livereload": "^0.6.0",
|
||||||
"localhost.daplie.com-certificates": "^1.2.0",
|
"localhost.daplie.com-certificates": "^1.2.0",
|
||||||
"minimist": "^1.1.1",
|
"minimist": "^1.1.1",
|
||||||
|
"oauth3-cli": "git+https://github.com/OAuth3/oauth3-cli.git#master",
|
||||||
"redirect-https": "^1.1.0",
|
"redirect-https": "^1.1.0",
|
||||||
"serve-index": "^1.7.0",
|
"serve-index": "^1.7.0",
|
||||||
"serve-static": "^1.10.0"
|
"serve-static": "^1.10.0",
|
||||||
|
"stunnel": "git+https://github.com/Daplie/node-tunnel-client.git#master"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
133
serve.js
133
serve.js
|
@ -8,6 +8,7 @@ var https = require('httpolyglot');
|
||||||
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 httpPort = 80;
|
var httpPort = 80;
|
||||||
var httpsPort = 443;
|
var httpsPort = 443;
|
||||||
var lrPort = 35729;
|
var lrPort = 35729;
|
||||||
|
@ -56,9 +57,42 @@ 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 (realResolve) {
|
return new PromiseA(function (realResolve) {
|
||||||
var server = https.createServer(opts.httpsOptions);
|
var app = require('./lib/app.js');
|
||||||
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 };
|
||||||
|
@ -71,6 +105,48 @@ function createServer(port, pubdir, content, opts) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 leChallengeSni = require('le-challenge-sni').create({ webrootPath: webrootPath });
|
||||||
|
var leChallengeDdns = require('le-challenge-ddns').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 // leChallengeSni
|
||||||
|
, 'dns-01': leChallengeDdns
|
||||||
|
}
|
||||||
|
, challengeType: (opts.tunnel ? 'http-01' : '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
|
||||||
|
});
|
||||||
|
var secureContext;
|
||||||
|
opts.httpsOptions.SNICallback = function (servername, cb ) {
|
||||||
|
console.log('[https] servername', servername);
|
||||||
|
|
||||||
|
if ('localhost.daplie.com' === servername) {
|
||||||
|
if (!secureContext) {
|
||||||
|
secureContext = tls.createSecureContext(opts.httpsOptions);
|
||||||
|
}
|
||||||
|
cb(null, secureContext);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lex.httpsOptions.SNICallback(servername, cb);
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
@ -93,7 +169,7 @@ function createServer(port, pubdir, content, opts) {
|
||||||
var server2 = livereload.createServer({
|
var server2 = livereload.createServer({
|
||||||
https: opts.httpsOptions
|
https: opts.httpsOptions
|
||||||
, port: opts.lrPort
|
, port: opts.lrPort
|
||||||
, exclusions: [ '.hg', '.git', '.svn', 'node_modules' ]
|
, exclusions: [ 'node_modules' ]
|
||||||
});
|
});
|
||||||
|
|
||||||
console.info("[livereload] watching " + pubdir);
|
console.info("[livereload] watching " + pubdir);
|
||||||
|
@ -119,7 +195,8 @@ function createServer(port, pubdir, content, opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
server.on('request', function (req, res) {
|
server.on('request', function (req, res) {
|
||||||
if (!req.socket.encrypted) {
|
console.log('[' + req.method + '] ' + req.url);
|
||||||
|
if (!req.socket.encrypted && !/\/\.well-known\/acme-challenge\//.test(req.url)) {
|
||||||
opts.redirectApp(req, res);
|
opts.redirectApp(req, res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -165,6 +242,7 @@ function run() {
|
||||||
var opts = {
|
var opts = {
|
||||||
agreeTos: argv.agreeTos || argv['agree-tos']
|
agreeTos: argv.agreeTos || argv['agree-tos']
|
||||||
, debug: argv.debug
|
, debug: argv.debug
|
||||||
|
, device: argv.device
|
||||||
, email: argv.email
|
, email: argv.email
|
||||||
, httpsOptions: {
|
, httpsOptions: {
|
||||||
key: httpsOptions.key
|
key: httpsOptions.key
|
||||||
|
@ -174,7 +252,9 @@ function run() {
|
||||||
, argv: argv
|
, argv: argv
|
||||||
};
|
};
|
||||||
var peerCa;
|
var peerCa;
|
||||||
|
var p;
|
||||||
|
|
||||||
|
opts.PromiseA = PromiseA;
|
||||||
opts.httpsOptions.SNICallback = function (servername, cb) {
|
opts.httpsOptions.SNICallback = function (servername, cb) {
|
||||||
if (!secureContext) {
|
if (!secureContext) {
|
||||||
secureContext = tls.createSecureContext(opts.httpsOptions);
|
secureContext = tls.createSecureContext(opts.httpsOptions);
|
||||||
|
@ -244,6 +324,9 @@ function run() {
|
||||||
if (argv.p || argv.port || argv._[0]) {
|
if (argv.p || argv.port || argv._[0]) {
|
||||||
opts.manualPort = true;
|
opts.manualPort = true;
|
||||||
}
|
}
|
||||||
|
if (argv.t || argv.tunnel) {
|
||||||
|
opts.tunnel = true;
|
||||||
|
}
|
||||||
if (argv.i || argv['insecure-port']) {
|
if (argv.i || argv['insecure-port']) {
|
||||||
opts.manualInsecurePort = true;
|
opts.manualInsecurePort = true;
|
||||||
}
|
}
|
||||||
|
@ -257,13 +340,37 @@ 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 () {
|
||||||
|
|
||||||
// can be changed to tunnel external port
|
// can be changed to tunnel external port
|
||||||
opts.redirectOptions = {
|
opts.redirectOptions = {
|
||||||
port: opts.port
|
port: opts.port
|
||||||
};
|
};
|
||||||
opts.redirectApp = require('redirect-https')(opts.redirectOptions);
|
opts.redirectApp = require('redirect-https')(opts.redirectOptions);
|
||||||
|
|
||||||
return createServer(port, pubdir, content, opts).then(function () {
|
return createServer(port, pubdir, content, opts).then(function (servers) {
|
||||||
var msg;
|
var msg;
|
||||||
var p;
|
var p;
|
||||||
var httpsUrl;
|
var httpsUrl;
|
||||||
|
@ -292,12 +399,12 @@ function run() {
|
||||||
|
|
||||||
if (!(argv.servername && defaultServername !== argv.servername && !(argv.key && argv.cert))) {
|
if (!(argv.servername && defaultServername !== argv.servername && !(argv.key && argv.cert))) {
|
||||||
// ifaces
|
// ifaces
|
||||||
opts.ifaces = require('./local-ip.js').find();
|
opts.ifaces = require('./lib/local-ip.js').find();
|
||||||
promise = PromiseA.resolve();
|
promise = PromiseA.resolve();
|
||||||
} else {
|
} else {
|
||||||
console.info("Attempting to resolve external connection for '" + argv.servername + "'");
|
console.info("Attempting to resolve external connection for '" + argv.servername + "'");
|
||||||
try {
|
try {
|
||||||
promise = require('./match-ips.js').match(argv.servername, opts);
|
promise = require('./lib/match-ips.js').match(argv.servername, opts);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.warn("Upgrade to version 2.x to use automatic certificate issuance for '" + argv.servername + "'");
|
console.warn("Upgrade to version 2.x to use automatic certificate issuance for '" + argv.servername + "'");
|
||||||
promise = PromiseA.resolve();
|
promise = PromiseA.resolve();
|
||||||
|
@ -333,6 +440,17 @@ function run() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (!opts.tunnel) {
|
||||||
|
console.info("External IP address does not match local IP address.");
|
||||||
|
console.info("Use --tunnel to allow the people of the Internet to access your server.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.tunnel) {
|
||||||
|
require('./lib/tunnel.js').create(opts, servers);
|
||||||
|
}
|
||||||
|
else if (opts.ddns) {
|
||||||
|
require('./lib/ddns.js').create(opts, servers);
|
||||||
|
}
|
||||||
|
|
||||||
Object.keys(opts.ifaces).forEach(function (iname) {
|
Object.keys(opts.ifaces).forEach(function (iname) {
|
||||||
var iface = opts.ifaces[iname];
|
var iface = opts.ifaces[iname];
|
||||||
|
@ -360,6 +478,7 @@ function run() {
|
||||||
console.info('');
|
console.info('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
|
|
Loading…
Reference in New Issue