forked from coolaj86/goldilocks.js
		
	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
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
* `-d <dirpath>` - i.e. `serve-https -d /tmp/` (defaults to `pwd`)
 | 
			
		||||
* `-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%
 | 
			
		||||
 | 
			
		||||
* `--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:
 | 
			
		||||
 | 
			
		||||
* `--key /path/to/privkey.pem` specifies the server private key
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										88
									
								
								lib/ddns.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								lib/ddns.js
									
									
									
									
									
										Normal file
									
								
							@ -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);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										117
									
								
								lib/match-ips.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								lib/match-ips.js
									
									
									
									
									
										Normal file
									
								
							@ -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;
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										137
									
								
								lib/tunnel.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								lib/tunnel.js
									
									
									
									
									
										Normal file
									
								
							@ -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",
 | 
			
		||||
  "version": "1.6.1",
 | 
			
		||||
  "version": "2.0.2",
 | 
			
		||||
  "description": "Serves HTTPS using TLS (SSL) certs for localhost.daplie.com - great for testing and development.",
 | 
			
		||||
  "main": "serve.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
@ -38,14 +38,24 @@
 | 
			
		||||
  },
 | 
			
		||||
  "homepage": "https://github.com/Daplie/serve-https#readme",
 | 
			
		||||
  "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",
 | 
			
		||||
    "httpolyglot": "^0.1.1",
 | 
			
		||||
    "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",
 | 
			
		||||
    "minimist": "^1.1.1",
 | 
			
		||||
    "oauth3-cli": "git+https://github.com/OAuth3/oauth3-cli.git#master",
 | 
			
		||||
    "redirect-https": "^1.1.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 fs = require('fs');
 | 
			
		||||
var path = require('path');
 | 
			
		||||
var DDNS = require('ddns-cli');
 | 
			
		||||
var httpPort = 80;
 | 
			
		||||
var httpsPort = 443;
 | 
			
		||||
var lrPort = 35729;
 | 
			
		||||
@ -56,9 +57,42 @@ function createInsecureServer(port, pubdir, 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) {
 | 
			
		||||
    var server = https.createServer(opts.httpsOptions);
 | 
			
		||||
    var app = require('./app');
 | 
			
		||||
    var app = require('./lib/app.js');
 | 
			
		||||
 | 
			
		||||
    var directive = { public: pubdir, content: content, livereload: opts.livereload
 | 
			
		||||
      , 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) {
 | 
			
		||||
      if (opts.errorPort || opts.manualPort) {
 | 
			
		||||
        showError(err, port);
 | 
			
		||||
@ -93,7 +169,7 @@ function createServer(port, pubdir, content, opts) {
 | 
			
		||||
        var server2 = livereload.createServer({
 | 
			
		||||
          https: opts.httpsOptions
 | 
			
		||||
        , port: opts.lrPort
 | 
			
		||||
        , exclusions: [ '.hg', '.git', '.svn', 'node_modules' ]
 | 
			
		||||
        , exclusions: [ 'node_modules' ]
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        console.info("[livereload] watching " + pubdir);
 | 
			
		||||
@ -119,7 +195,8 @@ function createServer(port, pubdir, content, opts) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
@ -165,6 +242,7 @@ function run() {
 | 
			
		||||
  var opts = {
 | 
			
		||||
    agreeTos: argv.agreeTos || argv['agree-tos']
 | 
			
		||||
  , debug: argv.debug
 | 
			
		||||
  , device: argv.device
 | 
			
		||||
  , email: argv.email
 | 
			
		||||
  , httpsOptions: {
 | 
			
		||||
      key: httpsOptions.key
 | 
			
		||||
@ -174,7 +252,9 @@ function run() {
 | 
			
		||||
  , argv: argv
 | 
			
		||||
  };
 | 
			
		||||
  var peerCa;
 | 
			
		||||
  var p;
 | 
			
		||||
 | 
			
		||||
  opts.PromiseA = PromiseA;
 | 
			
		||||
  opts.httpsOptions.SNICallback = function (servername, cb) {
 | 
			
		||||
    if (!secureContext) {
 | 
			
		||||
      secureContext = tls.createSecureContext(opts.httpsOptions);
 | 
			
		||||
@ -244,6 +324,9 @@ function run() {
 | 
			
		||||
  if (argv.p || argv.port || argv._[0]) {
 | 
			
		||||
    opts.manualPort = true;
 | 
			
		||||
  }
 | 
			
		||||
  if (argv.t || argv.tunnel) {
 | 
			
		||||
    opts.tunnel = true;
 | 
			
		||||
  }
 | 
			
		||||
  if (argv.i || argv['insecure-port']) {
 | 
			
		||||
    opts.manualInsecurePort = true;
 | 
			
		||||
  }
 | 
			
		||||
@ -257,13 +340,37 @@ function run() {
 | 
			
		||||
    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
 | 
			
		||||
  opts.redirectOptions = {
 | 
			
		||||
    port: opts.port
 | 
			
		||||
  };
 | 
			
		||||
  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 p;
 | 
			
		||||
    var httpsUrl;
 | 
			
		||||
@ -292,12 +399,12 @@ function run() {
 | 
			
		||||
 | 
			
		||||
    if (!(argv.servername && defaultServername !== argv.servername && !(argv.key && argv.cert))) {
 | 
			
		||||
      // ifaces
 | 
			
		||||
      opts.ifaces = require('./local-ip.js').find();
 | 
			
		||||
      opts.ifaces = require('./lib/local-ip.js').find();
 | 
			
		||||
      promise = PromiseA.resolve();
 | 
			
		||||
    } else {
 | 
			
		||||
      console.info("Attempting to resolve external connection for '" + argv.servername + "'");
 | 
			
		||||
      try {
 | 
			
		||||
        promise = require('./match-ips.js').match(argv.servername, opts);
 | 
			
		||||
        promise = require('./lib/match-ips.js').match(argv.servername, opts);
 | 
			
		||||
      } catch(e) {
 | 
			
		||||
        console.warn("Upgrade to version 2.x to use automatic certificate issuance for '" + argv.servername + "'");
 | 
			
		||||
        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) {
 | 
			
		||||
        var iface = opts.ifaces[iname];
 | 
			
		||||
@ -360,6 +478,7 @@ function run() {
 | 
			
		||||
      console.info('');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (require.main === module) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user