From 176e1c06a3890f31eb18d9761d54a24bd5161be0 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 4 Apr 2017 22:31:58 -0400 Subject: [PATCH 1/4] WIP refactor for greenlock --- bin/stunneld.js | 86 +++++++++++++++++++-- handlers.js | 74 ++++++++++++++++++ package.json | 1 + wstunneld.js | 197 ++++++++++++++++++++++++++---------------------- 4 files changed, 264 insertions(+), 94 deletions(-) create mode 100644 handlers.js diff --git a/bin/stunneld.js b/bin/stunneld.js index 6c055a8..22354fd 100755 --- a/bin/stunneld.js +++ b/bin/stunneld.js @@ -7,6 +7,15 @@ var pkg = require('../package.json'); var program = require('commander'); var url = require('url'); var stunneld = require('../wstunneld.js'); +var greenlock = require('greenlock'); + +function collectServernames(val, memo) { + val.split(/,/).forEach(function (servername) { + memo.push(servername.toLowerCase()); + }); + + return memo; +} function collectProxies(val, memo) { var vals = val.split(/,/g); @@ -68,8 +77,11 @@ program .action(function (url) { program.url = url; }) + .option('--agree-tos', "Accept the Daplie and Let's Encrypt Terms of Service") + .option('--email ', "Email to use for Daplie and Let's Encrypt accounts") .option('--serve ', 'comma separated list of :: to which matching incoming http and https should forward (reverse proxy). Ex: https://john.example.com,tls:*:1337', collectProxies, [ ]) .option('--ports ', 'comma separated list of ports on which to listen. Ex: 80,443,1337', collectPorts, [ ]) + .option('--servernames ', 'comma separated list of servernames to use for the admin interface. Ex: tunnel.example.com,tunnel.example.net', collectServernames, [ ]) .option('--secret ', 'the same secret used by stunneld (used for JWT authentication)') .parse(process.argv) ; @@ -115,18 +127,82 @@ if (!program.serve.length) { throw new Error("must specify at least on server"); } -// TODO letsencrypt -program.tlsOptions = require('localhost.daplie.com-certificates').merge({}); if (!program.secret) { // TODO randomly generate and store in file? - console.warn("[SECURITY] using default --secret 'shhhhh'"); - program.secret = 'shhhhh'; + console.warn("[SECURITY] you must provide --secret '" + require('crypto').randomBytes(16).toString('hex') + "'"); + process.exit(1); + return; } +// TODO letsencrypt +program.tlsOptions = require('localhost.daplie.com-certificates').merge({}); +/* +program.tlsOptions.SNICallback = program.greenlock.SNICallback; +program.middleware = program.greenlock.middleware(function (req, res) { + res.end('Hello, World!'); +}); +*/ +/* +function approveDomains(opts, certs, cb) { + // 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 { + opts.email = program.email; + opts.agreeTos = program.agreeTos; + } + + // NOTE: you can also change other options such as `challengeType` and `challenge` + // opts.challengeType = 'http-01'; + // opts.challenge = require('le-challenge-fs').create({}); + + cb(null, { options: opts, certs: certs }); +} + +program.greenlock = greenlock.create({ + + server: 'staging' + // server: 'https://acme-v01.api.letsencrypt.org/directory' + +, challenges: { + // TODO dns-01 + 'http-01': require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' }) + } + +, store: require('le-store-certbot').create({ webrootPath: '/tmp/acme-challenges' }) + +, email: program.email + +, agreeTos: program.agreeTos + +, approveDomains: program.servernames // approveDomains + +}); +*/ + +require('../handlers').create(program); // adds directly to program for now... + //require('cluster-store').create().then(function (store) { //program.store = store; - stunneld.create(program); + var net = require('net'); + var netConnHandlers = stunneld.create(program); // { tcp, ws } + var WebSocketServer = require('ws').Server; + var wss = new WebSocketServer({ server: (program.httpTunnelServer || program.httpServer) }); + wss.on('connection', netConnHandlers.ws); + program.ports.forEach(function (port) { + var tcp3000 = net.createServer(); + tcp3000.listen(port, function () { + console.log('listening on ' + port); + }); + tcp3000.on('connection', netConnHandlers.tcp); + }); //}); }()); diff --git a/handlers.js b/handlers.js new file mode 100644 index 0000000..f6915d9 --- /dev/null +++ b/handlers.js @@ -0,0 +1,74 @@ +'use strict'; + +var http = require('http'); +var tls = require('tls'); +var packerStream = require('tunnel-packer').Stream; + +module.exports.create = function (program) { + program.httpServer = http.createServer(function (req, res) { + console.log('req.socket.encrypted', req.socket.encrypted); + res.end("Look! I can do a thing!"); + }); + program.httpTunnelServer = http.createServer(function (req, res) { + console.log('req.socket.encrypted', req.socket.encrypted); + res.end('Hello, World!'); + }); + program.httpInvalidSniServer = http.createServer(function (req, res) { + res.end("You're doing strange things that make me feel uncomfortable. Please don't touch me there any more."); + }); + program.tlsTunnelServer = tls.createServer(program.tlsOptions, function (tlsSocket) { + console.log('tls connection'); + // things get a little messed up here + (program.httpTunnelServer || program.httpServer).emit('connection', tlsSocket); + }); + program.tlsInvalidSniServer = tls.createServer(program.tlsOptions, function (tlsSocket) { + console.log('tls connection'); + // things get a little messed up here + program.httpInvalidSniServer.emit('connection', tlsSocket); + }); + program.handleInsecureHttp = function (servername, socket) { + console.log("handleInsecureHttp('" + servername + "', socket)"); + socket.__my_servername = servername; + program.httpInsecureServer.emit('connection', socket); + }; + program.httpsInvalid = function (servername, socket) { + // none of these methods work: + // httpsServer.emit('connection', socket); // this didn't work + // tlsServer.emit('connection', socket); // this didn't work either + //console.log('chunkLen', firstChunk.byteLength); + + var myDuplex = packerStream.create(socket); + + console.log('httpsInvalid servername', servername); + program.tlsInvalidSniServer.emit('connection', myDuplex); + + socket.on('data', function (chunk) { + console.log('[' + Date.now() + '] socket data', chunk.byteLength); + myDuplex.push(chunk); + }); + socket.on('error', function (err) { + console.error('[error] httpsInvalid TODO close'); + console.error(err); + }); + }; + program.httpsTunnel = function (servername, socket) { + // none of these methods work: + // httpsServer.emit('connection', socket); // this didn't work + // tlsServer.emit('connection', socket); // this didn't work either + //console.log('chunkLen', firstChunk.byteLength); + + var myDuplex = packerStream.create(socket); + + console.log('httpsTunnel servername', servername); + program.tlsTunnelServer.emit('connection', myDuplex); + + socket.on('data', function (chunk) { + console.log('[' + Date.now() + '] socket data', chunk.byteLength); + myDuplex.push(chunk); + }); + socket.on('error', function (err) { + console.error('[error] httpsTunnel TODO close'); + console.error(err); + }); + }; +}; diff --git a/package.json b/package.json index a775cb0..e8121ff 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "dependencies": { "cluster-store": "^2.0.4", "commander": "^2.9.0", + "greenlock": "^2.1.12", "jsonwebtoken": "^7.1.9", "localhost.daplie.com-certificates": "^1.2.3", "redirect-https": "^1.1.0", diff --git a/wstunneld.js b/wstunneld.js index 74fd489..52012f6 100644 --- a/wstunneld.js +++ b/wstunneld.js @@ -1,23 +1,84 @@ 'use strict'; -var net = require('net'); -var tls = require('tls'); -var http = require('http'); var sni = require('sni'); var url = require('url'); var jwt = require('jsonwebtoken'); var packer = require('tunnel-packer'); -var WebSocketServer = require('ws').Server; +var Devices = {}; +Devices.replace = function (store, servername, newDevice) { + var devices = Devices.list(store, servername); + var oldDevice; + if (!devices.some(function (device, i) { + if ((device.deviceId && device.deviceId === newDevice.deviceId) + || (device.servername && device.servername === newDevice.servername)) { + oldDevice = devices[i]; + devices[i] = newDevice; + return true; + } + })) { + devices.push(newDevice); + store[servername] = devices; + } + return oldDevice; +}; +Devices.remove = function (store, servername, newDevice) { + var devices = Devices.list(store, servername); + var oldDevice; + devices.some(function (device, i) { + if ((device.deviceId && device.deviceId === newDevice.deviceId) + || (device.servername && device.servername === newDevice.servername)) { + oldDevice = devices.splice(i, 1); + return true; + } + }); + return oldDevice; +}; +Devices.list = function (store, servername) { + return store[servername] || []; +}; +Devices.exist = function (store, servername) { + return (store[servername] || []).length; +}; +Devices.next = function (store, servername) { + var devices = Devices.list(store, servername); + + if (devices._index >= devices.length) { + devices._index = 0; + } + devices._index = (devices._index || 0) + 1; + + return devices[devices._index]; +}; + +module.exports.store = { Devices: Devices }; module.exports.create = function (copts) { + var deviceLists = {}; function onWsConnection(ws) { var location = url.parse(ws.upgradeReq.url, true); - //var token = jwt.decode(location.query.access_token); + var authn = (ws.upgradeReq.headers.authorization||'').split(/\s+/); + var jwtoken; var token; try { - token = jwt.verify(location.query.access_token, secret); + if (authn[0]) { + if ('basic' === authn[0].toLowerCase()) { + authn = new Buffer(authn[1], 'base64').toString('ascii').split(':'); + } + /* + if (-1 !== [ 'bearer', 'jwk' ].indexOf(authn[0].toLowerCase())) { + jwtoken = authn[1]; + } + */ + } + jwtoken = authn[1] || location.query.access_token; + } catch(e) { + jwtoken = null; + } + + try { + token = jwt.verify(jwtoken, copts.secret); } catch(e) { token = null; } @@ -36,6 +97,8 @@ module.exports.create = function (copts) { return; } + //console.log('[wstunneld.js] DEBUG', token); + if (!Array.isArray(token.domains)) { if ('string' === typeof token.name) { token.domains = [ token.name ]; @@ -50,13 +113,13 @@ module.exports.create = function (copts) { var remote; token.domains.some(function (domainname) { - remote = remotes[domainname]; + remote = Devices.next(deviceLists, domainname); return remote; }); remote = remote || {}; token.domains.forEach(function (domainname) { console.log('domainname', domainname); - remotes[domainname] = remote; + Devices.replace(deviceLists, domainname, remote); }); var handlers = { onmessage: function (opts) { @@ -100,7 +163,8 @@ module.exports.create = function (copts) { }; // TODO allow more than one remote per servername remote.ws = ws; - remote.servername = token.domains.join(','); + remote.servername = (token.device && token.device.hostname) || token.domains.join(','); + remote.deviceId = (token.device && token.device.id) || null; remote.id = packer.socketToId(ws.upgradeReq.socket); console.log("remote.id", remote.id); // TODO allow tls to be decrypted by server if client is actually a browser @@ -110,12 +174,14 @@ module.exports.create = function (copts) { remote.clients = {}; remote.handle = { address: null, handle: null }; remote.unpacker = packer.create(handlers); - ws.on('message', function (chunk) { + remote.domains = token.domains; + + function forwardMessage(chunk) { console.log('message from home cloud to tunneler to browser', chunk.byteLength); //console.log(chunk.toString()); remote.unpacker.fns.addChunk(chunk); - }); - ws.on('close', function () { + } + function hangup() { // the remote will handle closing its local connections Object.keys(remote.clients).forEach(function (cid) { try { @@ -124,46 +190,23 @@ module.exports.create = function (copts) { // ignore } }); - }); - ws.on('error', function () { - // ignore - // the remote will retry if it wants to - }); + token.domains.forEach(function (domainname) { + Devices.remove(deviceLists, domainname, remote); + }); + } + function die() { + hangup(); + } - //store.set(token.name, remote.handle); + ws.on('message', forwardMessage); + ws.on('close', hangup); + ws.on('error', die); } - function connectHttp(servername, socket) { - console.log("connectHttp('" + servername + "', socket)"); - socket.__my_servername = servername; - redirectServer.emit('connection', socket); - } - - function connectHttps(servername, socket) { - // none of these methods work: - // httpsServer.emit('connection', socket); // this didn't work - // tlsServer.emit('connection', socket); // this didn't work either - //console.log('chunkLen', firstChunk.byteLength); - - var myDuplex = packer.Stream.create(socket); - - console.log('connectHttps servername', servername); - tls3000.emit('connection', myDuplex); - - socket.on('data', function (chunk) { - console.log('[' + Date.now() + '] socket data', chunk.byteLength); - myDuplex.push(chunk); - }); - socket.on('error', function (err) { - console.error('[error] connectHttps TODO close'); - console.error(err); - }); - } - function pipeWs(servername, service, browser, remote) { console.log('pipeWs'); - //var remote = remotes[servername]; + //var remote = deviceLists[servername]; var ws = remote.ws; //var address = packer.socketToAddr(ws.upgradeReq.socket); var baddress = packer.socketToAddr(browser); @@ -259,14 +302,22 @@ module.exports.create = function (copts) { var m; function tryTls() { - if (!servername || (-1 !== selfnames.indexOf(servername)) || !remotes[servername]) { - console.log('this is a server or an unknown'); - connectHttps(servername, browser); + var nextDevice; + + if (-1 !== copts.servernames.indexOf(servername)) { + copts.httpsTunnel(servername, browser); return; } - console.log("pipeWs(servername, service, socket, remotes['" + servername + "'])"); - pipeWs(servername, service, browser, remotes[servername]); + nextDevice = Devices.next(deviceLists, servername); + if (!servername || !nextDevice) { + console.log('this is a server or an unknown'); + copts.httpsInvalid(servername, browser); + return; + } + + console.log("pipeWs(servername, service, socket, deviceLists['" + servername + "'])"); + pipeWs(servername, service, browser, nextDevice); } // https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155 @@ -286,17 +337,17 @@ module.exports.create = function (copts) { console.log('servername', servername); if (/HTTP\//i.test(str)) { service = 'http'; - if (/^\/\.well-known\/acme-challenge\//.test(str)) { + if (/well-known/.test(str)) { // HTTP - if (remotes[servername]) { - pipeWs(servername, service, browser, remotes[servername]); + if (Devices.exist(deviceLists, servername)) { + pipeWs(servername, service, browser, Devices.next(deviceLists, servername)); return; } - connectHttp(servername, browser); + copts.handleInsecureHttp(servername, browser); } else { // redirect to https - connectHttp(servername, browser); + copts.handleInsecureHttp(servername, browser); } return; } @@ -316,37 +367,5 @@ module.exports.create = function (copts) { } - var tlsOpts = copts.tlsOptions; - //var store = copts.store; - - var remotes = {}; - var selfnames = copts.servernames; - var secret = copts.secret; - var redirectHttps = require('redirect-https')(); - - var redirectServer = http.createServer(function (req, res) { - res.setHeader('Connection', 'close'); - redirectHttps(req, res); - }); - var httpServer = http.createServer(function (req, res) { - console.log('req.socket.encrypted', req.socket.encrypted); - res.end('Hello, World!'); - }); - var tls3000 = tls.createServer(tlsOpts, function (tlsSocket) { - console.log('tls connection'); - // things get a little messed up here - httpServer.emit('connection', tlsSocket); - }); - var wss = new WebSocketServer({ server: httpServer }); - - wss.on('connection', onWsConnection); - - copts.ports.forEach(function (port) { - var tcp3000 = net.createServer(); - tcp3000.listen(port, function () { - console.log('listening on ' + port); - }); - tcp3000.on('connection', onTcpConnection); - }); - + return { tcp: onTcpConnection, ws: onWsConnection }; }; From 50a4d9360abd9c3722d95b2b7813830152faa9ac Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 5 Apr 2017 03:01:43 -0400 Subject: [PATCH 2/4] refactor: separate handlers, allow multiple devices to respond --- handlers.js | 6 ++++++ wstunneld.js | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/handlers.js b/handlers.js index f6915d9..6f6e19c 100644 --- a/handlers.js +++ b/handlers.js @@ -3,12 +3,18 @@ var http = require('http'); var tls = require('tls'); var packerStream = require('tunnel-packer').Stream; +var redirectHttps = require('redirect-https')(); module.exports.create = function (program) { program.httpServer = http.createServer(function (req, res) { console.log('req.socket.encrypted', req.socket.encrypted); res.end("Look! I can do a thing!"); }); + + program.httpInsecureServer = http.createServer(function (req, res) { + res.setHeader('Connection', 'close'); + redirectHttps(req, res); + }); program.httpTunnelServer = http.createServer(function (req, res) { console.log('req.socket.encrypted', req.socket.encrypted); res.end('Hello, World!'); diff --git a/wstunneld.js b/wstunneld.js index 52012f6..4c490c7 100644 --- a/wstunneld.js +++ b/wstunneld.js @@ -42,13 +42,15 @@ Devices.exist = function (store, servername) { }; Devices.next = function (store, servername) { var devices = Devices.list(store, servername); + var device; if (devices._index >= devices.length) { devices._index = 0; } + device = devices[devices._index || 0]; devices._index = (devices._index || 0) + 1; - return devices[devices._index]; + return device; }; module.exports.store = { Devices: Devices }; @@ -337,6 +339,8 @@ module.exports.create = function (copts) { console.log('servername', servername); if (/HTTP\//i.test(str)) { service = 'http'; + // TODO disallow http entirely + // /^\/\.well-known\/acme-challenge\//.test(str) if (/well-known/.test(str)) { // HTTP if (Devices.exist(deviceLists, servername)) { From 81dce2f0a1c20448a053e057ab0261235b5cc5b4 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 5 Apr 2017 04:13:03 -0400 Subject: [PATCH 3/4] tested certs issued via greenlock --- bin/stunneld.js | 34 +++++++++++++++--------- handlers.js | 70 +++++++++++++++++++++++++++++++++---------------- wstunneld.js | 13 ++++++--- 3 files changed, 79 insertions(+), 38 deletions(-) diff --git a/bin/stunneld.js b/bin/stunneld.js index 22354fd..230ad2d 100755 --- a/bin/stunneld.js +++ b/bin/stunneld.js @@ -136,26 +136,21 @@ if (!program.secret) { // TODO letsencrypt program.tlsOptions = require('localhost.daplie.com-certificates').merge({}); -/* -program.tlsOptions.SNICallback = program.greenlock.SNICallback; -program.middleware = program.greenlock.middleware(function (req, res) { - res.end('Hello, World!'); -}); -*/ -/* + function approveDomains(opts, certs, cb) { // 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 { - opts.email = program.email; - opts.agreeTos = program.agreeTos; + if (-1 !== program.servernames.indexOf(opts.domain)) { + opts.email = program.email; + opts.agreeTos = program.agreeTos; + } } // NOTE: you can also change other options such as `challengeType` and `challenge` @@ -165,10 +160,16 @@ function approveDomains(opts, certs, cb) { cb(null, { options: opts, certs: certs }); } +if (!program.email || !program.agreeTos) { + console.error("You didn't specify --email and --agree-tos"); + console.error("(required for ACME / Let's Encrypt / Greenlock TLS/SSL certs)"); + console.error(""); + process.exit(1); +} program.greenlock = greenlock.create({ - server: 'staging' - // server: 'https://acme-v01.api.letsencrypt.org/directory' + //server: 'staging' + server: 'https://acme-v01.api.letsencrypt.org/directory' , challenges: { // TODO dns-01 @@ -181,8 +182,15 @@ program.greenlock = greenlock.create({ , agreeTos: program.agreeTos -, approveDomains: program.servernames // approveDomains +, approveDomains: approveDomains +//, approvedDomains: program.servernames + +}); +//program.tlsOptions.SNICallback = program.greenlock.SNICallback; +/* +program.middleware = program.greenlock.middleware(function (req, res) { + res.end('Hello, World!'); }); */ diff --git a/handlers.js b/handlers.js index 6f6e19c..cfc4676 100644 --- a/handlers.js +++ b/handlers.js @@ -6,37 +6,43 @@ var packerStream = require('tunnel-packer').Stream; var redirectHttps = require('redirect-https')(); module.exports.create = function (program) { - program.httpServer = http.createServer(function (req, res) { - console.log('req.socket.encrypted', req.socket.encrypted); - res.end("Look! I can do a thing!"); - }); + var tunnelAdminTlsOpts = {}; - program.httpInsecureServer = http.createServer(function (req, res) { + // Probably a reverse proxy on an internal network + program.httpServer = http.createServer(program.greenlock.middleware(function (req, res) { + console.log('req.socket.encrypted', req.socket.encrypted); + res.statusCode = 404; + res.end("File not found.\n"); + })); + program.handleHttp = function (servername, socket) { + console.log("handleHttp('" + servername + "', socket)"); + socket.__my_servername = servername; + program.httpServer.emit('connection', socket); + }; + + // Probably something that needs to be redirected to https + program.httpInsecureServer = http.createServer(program.greenlock.middleware(function (req, res) { res.setHeader('Connection', 'close'); redirectHttps(req, res); - }); - program.httpTunnelServer = http.createServer(function (req, res) { - console.log('req.socket.encrypted', req.socket.encrypted); - res.end('Hello, World!'); - }); + })); + program.handleInsecureHttp = function (servername, socket) { + console.log("handleInsecureHttp('" + servername + "', socket)"); + socket.__my_servername = servername; + program.httpInsecureServer.emit('connection', socket); + }; + + + // + // SNI is not recogonized / cannot be handled + // program.httpInvalidSniServer = http.createServer(function (req, res) { res.end("You're doing strange things that make me feel uncomfortable. Please don't touch me there any more."); }); - program.tlsTunnelServer = tls.createServer(program.tlsOptions, function (tlsSocket) { - console.log('tls connection'); - // things get a little messed up here - (program.httpTunnelServer || program.httpServer).emit('connection', tlsSocket); - }); program.tlsInvalidSniServer = tls.createServer(program.tlsOptions, function (tlsSocket) { console.log('tls connection'); // things get a little messed up here program.httpInvalidSniServer.emit('connection', tlsSocket); }); - program.handleInsecureHttp = function (servername, socket) { - console.log("handleInsecureHttp('" + servername + "', socket)"); - socket.__my_servername = servername; - program.httpInsecureServer.emit('connection', socket); - }; program.httpsInvalid = function (servername, socket) { // none of these methods work: // httpsServer.emit('connection', socket); // this didn't work @@ -57,6 +63,26 @@ module.exports.create = function (program) { console.error(err); }); }; + + // + // To ADMIN / CONTROL PANEL of the Tunnel Server Itself + // + program.httpTunnelServer = http.createServer(function (req, res) { + console.log('req.socket.encrypted', req.socket.encrypted); + res.end('Hello, World!'); + }); + Object.keys(program.tlsOptions).forEach(function (key) { + tunnelAdminTlsOpts[key] = program.tlsOptions[key]; + }); + tunnelAdminTlsOpts.SNICallback = (program.greenlock && program.greenlock.httpsOptions && function (servername, cb) { + console.log("time to handle '" + servername + "'"); + program.greenlock.httpsOptions.SNICallback(servername, cb); + }) || tunnelAdminTlsOpts.SNICallback; + program.tlsTunnelServer = tls.createServer(tunnelAdminTlsOpts, function (tlsSocket) { + console.log('tls connection'); + // things get a little messed up here + (program.httpTunnelServer || program.httpServer).emit('connection', tlsSocket); + }); program.httpsTunnel = function (servername, socket) { // none of these methods work: // httpsServer.emit('connection', socket); // this didn't work @@ -65,7 +91,7 @@ module.exports.create = function (program) { var myDuplex = packerStream.create(socket); - console.log('httpsTunnel servername', servername); + console.log('httpsTunnel (Admin) servername', servername); program.tlsTunnelServer.emit('connection', myDuplex); socket.on('data', function (chunk) { @@ -73,7 +99,7 @@ module.exports.create = function (program) { myDuplex.push(chunk); }); socket.on('error', function (err) { - console.error('[error] httpsTunnel TODO close'); + console.error('[error] httpsTunnel (Admin) TODO close'); console.error(err); }); }; diff --git a/wstunneld.js b/wstunneld.js index 4c490c7..93472eb 100644 --- a/wstunneld.js +++ b/wstunneld.js @@ -307,13 +307,20 @@ module.exports.create = function (copts) { var nextDevice; if (-1 !== copts.servernames.indexOf(servername)) { + console.log("Lock and load, admin interface time!"); copts.httpsTunnel(servername, browser); return; } + if (!servername) { + console.log("No SNI was given, so there's nothing we can do here"); + copts.httpsInvalid(servername, browser); + return; + } + nextDevice = Devices.next(deviceLists, servername); - if (!servername || !nextDevice) { - console.log('this is a server or an unknown'); + if (!nextDevice) { + console.log("No devices match the given servername"); copts.httpsInvalid(servername, browser); return; } @@ -347,7 +354,7 @@ module.exports.create = function (copts) { pipeWs(servername, service, browser, Devices.next(deviceLists, servername)); return; } - copts.handleInsecureHttp(servername, browser); + copts.handleHttp(servername, browser); } else { // redirect to https From 714377bbf90244924554cd87922bd4d209ca00cc Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 5 Apr 2017 04:18:35 -0400 Subject: [PATCH 4/4] make ACME / greenlock optional --- bin/stunneld.js | 31 ++++++++++++++++--------------- handlers.js | 18 +++++++++++++----- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/bin/stunneld.js b/bin/stunneld.js index 230ad2d..65f60da 100755 --- a/bin/stunneld.js +++ b/bin/stunneld.js @@ -164,30 +164,31 @@ if (!program.email || !program.agreeTos) { console.error("You didn't specify --email and --agree-tos"); console.error("(required for ACME / Let's Encrypt / Greenlock TLS/SSL certs)"); console.error(""); - process.exit(1); } -program.greenlock = greenlock.create({ +else { + program.greenlock = greenlock.create({ - //server: 'staging' - server: 'https://acme-v01.api.letsencrypt.org/directory' + //server: 'staging' + server: 'https://acme-v01.api.letsencrypt.org/directory' -, challenges: { - // TODO dns-01 - 'http-01': require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' }) - } + , challenges: { + // TODO dns-01 + 'http-01': require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' }) + } -, store: require('le-store-certbot').create({ webrootPath: '/tmp/acme-challenges' }) + , store: require('le-store-certbot').create({ webrootPath: '/tmp/acme-challenges' }) -, email: program.email + , email: program.email -, agreeTos: program.agreeTos + , agreeTos: program.agreeTos -, approveDomains: approveDomains + , approveDomains: approveDomains -//, approvedDomains: program.servernames + //, approvedDomains: program.servernames -}); -//program.tlsOptions.SNICallback = program.greenlock.SNICallback; + }); +} +//program.tlsOptions.SNICallback = program.greenlock.httpsOptions.SNICallback; /* program.middleware = program.greenlock.middleware(function (req, res) { res.end('Hello, World!'); diff --git a/handlers.js b/handlers.js index cfc4676..efd044b 100644 --- a/handlers.js +++ b/handlers.js @@ -8,12 +8,16 @@ var redirectHttps = require('redirect-https')(); module.exports.create = function (program) { var tunnelAdminTlsOpts = {}; - // Probably a reverse proxy on an internal network - program.httpServer = http.createServer(program.greenlock.middleware(function (req, res) { + // Probably a reverse proxy on an internal network (or ACME challenge) + function notFound(req, res) { console.log('req.socket.encrypted', req.socket.encrypted); res.statusCode = 404; res.end("File not found.\n"); - })); + } + program.httpServer = http.createServer( + program.greenlock && program.greenlock.middleware(notFound) + || notFound + ); program.handleHttp = function (servername, socket) { console.log("handleHttp('" + servername + "', socket)"); socket.__my_servername = servername; @@ -21,10 +25,14 @@ module.exports.create = function (program) { }; // Probably something that needs to be redirected to https - program.httpInsecureServer = http.createServer(program.greenlock.middleware(function (req, res) { + function redirectHttpsAndClose(req, res) { res.setHeader('Connection', 'close'); redirectHttps(req, res); - })); + } + program.httpInsecureServer = http.createServer( + program.greenlock && program.greenlock.middleware(redirectHttpsAndClose) + || redirectHttpsAndClose + ); program.handleInsecureHttp = function (servername, socket) { console.log("handleInsecureHttp('" + servername + "', socket)"); socket.__my_servername = servername;