From cd2fda3f2bfa7c862143e66758bb43121a9c8c6d Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 11 Oct 2016 17:20:10 -0600 Subject: [PATCH] partial tunnel integration --- lib/tunnel.js | 114 ++++++++++++++++++++++++++++++++++++++++++++++++-- serve.js | 34 +++++++++++---- 2 files changed, 137 insertions(+), 11 deletions(-) diff --git a/lib/tunnel.js b/lib/tunnel.js index ea09873..dc1010f 100644 --- a/lib/tunnel.js +++ b/lib/tunnel.js @@ -1,20 +1,75 @@ 'use strict'; -module.exports.create = function (opts/*, servers*/) { +module.exports.create = function (opts, servers) { // servers = { plainserver, server } - var tunnel = require('daplie-tunnel'); + 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'); + } + }; - return tunnel.token({ + 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 ] }).then(function (result) { // { jwt, tunnelUrl } - stunnel.connect({ + 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 @@ -25,6 +80,57 @@ module.exports.create = function (opts/*, servers*/) { , 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; + } + } + //*/ }); }); }; diff --git a/serve.js b/serve.js index b4f7fcd..1c6e082 100755 --- a/serve.js +++ b/serve.js @@ -3,6 +3,7 @@ //var PromiseA = global.Promise; var PromiseA = require('bluebird'); +var tls = require('tls'); var https = require('httpolyglot'); var http = require('http'); var fs = require('fs'); @@ -107,6 +108,7 @@ 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 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 @@ -116,10 +118,10 @@ function createServer(port, pubdir, content, opts) { // , challenges: { 'http-01': leChallengeFs - , 'tls-sni-01': leChallengeFs + , 'tls-sni-01': leChallengeFs // leChallengeSni , 'dns-01': leChallengeDns } - , challengeType: 'dns-01' + , challengeType: (opts.tunnel ? 'http-01' : 'dns-01') , store: require('le-store-certbot').create({ webrootPath: webrootPath }) , webrootPath: webrootPath @@ -129,7 +131,20 @@ function createServer(port, pubdir, content, opts) { , approveDomains: approveDomains }); - opts.httpsOptions.SNICallback = lex.httpsOptions.SNICallback; + 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) { @@ -211,7 +226,6 @@ function run() { var pubdir = path.resolve(argv.d || argv._[1] || process.cwd()); var content = argv.c; var letsencryptHost = argv['letsencrypt-certs']; - var tls = require('tls'); if (argv.V || argv.version || argv.v) { if (argv.v) { @@ -239,6 +253,7 @@ function run() { var peerCa; var p; + opts.PromiseA = PromiseA; opts.httpsOptions.SNICallback = function (servername, cb) { if (!secureContext) { secureContext = tls.createSecureContext(opts.httpsOptions); @@ -354,7 +369,7 @@ function run() { }; 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; @@ -424,8 +439,13 @@ function run() { } }); } - else { - require('./lib/tunnel.js').create(opts); + 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); } Object.keys(opts.ifaces).forEach(function (iname) {