made workaround for the TLS issue that I should have ignored...

This commit is contained in:
AJ ONeal 2017-04-28 13:07:05 -06:00
parent f2b05ee7af
commit eacf2e0dbf
4 changed files with 200 additions and 140 deletions

View File

@ -114,7 +114,7 @@ function readConfigAndRun(args) {
// TODO maybe move to config.state.addresses (?)
config.addresses = addresses;
config.device = { hostname: 'TODO: fetch hostname from device and from ip and try to make a match' };
config.device = { hostname: 'daplien-pod' };
if (config.tcp.ports) {
run(config);

View File

@ -1,6 +1,6 @@
'use strict';
module.exports = function (deps, conf, overrideHttp) {
module.exports = function (myDeps, conf, overrideHttp) {
var express = require('express');
//var finalhandler = require('finalhandler');
var serveStatic = require('serve-static');
@ -100,118 +100,132 @@ module.exports = function (deps, conf, overrideHttp) {
}
};
return require('../packages/apis/com.daplie.goldilocks').create({
PromiseA: PromiseA
, OAUTH3: OAUTH3
, storage: {
owners: owners
myDeps.PromiseA = PromiseA;
myDeps.OAUTH3 = OAUTH3;
myDeps.storage = { owners: owners };
myDeps.recase = require('recase').create({});
myDeps.request = request;
myDeps.api = {
// TODO move loopback to oauth3.api('tunnel:loopback')
loopback: function (deps, session, opts2) {
var crypto = require('crypto');
var token = crypto.randomBytes(16).toString('hex');
var keyAuthorization = crypto.randomBytes(16).toString('hex');
var nonce = crypto.randomBytes(16).toString('hex');
// TODO set token and keyAuthorization to /.well-known/cloud-challenge/:token
return request({
method: 'POST'
, url: 'https://oauth3.org/api/org.oauth3.tunnel/loopback'
, json: {
address: opts2.address
, port: opts2.port
, token: token
, keyAuthorization: keyAuthorization
, servername: opts2.servername
, nonce: nonce
, scheme: 'https'
, iat: Date.now()
}
}).then(function (result) {
// TODO this will always fail at the moment
console.log('loopback result:');
return result;
});
}
, recase: require('recase').create({})
, request: request
, options: conf
, api: {
// TODO move loopback to oauth3.api('tunnel:loopback')
loopback: function (deps, session, opts2) {
var crypto = require('crypto');
var token = crypto.randomBytes(16).toString('hex');
var keyAuthorization = crypto.randomBytes(16).toString('hex');
var nonce = crypto.randomBytes(16).toString('hex');
// TODO set token and keyAuthorization to /.well-known/cloud-challenge/:token
return request({
method: 'POST'
, url: 'https://oauth3.org/api/org.oauth3.tunnel/loopback'
, json: {
address: opts2.address
, port: opts2.port
, token: token
, keyAuthorization: keyAuthorization
, servername: opts2.servername
, nonce: nonce
, scheme: 'https'
, iat: Date.now()
}
}).then(function (result) {
// TODO this will always fail at the moment
console.log('loopback result:');
return result;
});
}
, tunnel: function (deps, session) {
// TODO save session to config and turn tunnel on
var OAUTH3 = deps.OAUTH3;
var url = require('url');
var providerUri = session.token.aud;
var urlObj = url.parse(OAUTH3.url.normalize(session.token.azp));
var oauth3 = OAUTH3.create(urlObj, {
providerUri: providerUri
, session: session
});
//var crypto = require('crypto');
//var id = crypto.createHash('sha256').update(session.token.sub).digest('hex');
return oauth3.setProvider(providerUri).then(function () {
/*
return oauth3.api('domains.list').then(function (domains) {
var domainsMap = {};
domains.forEach(function (d) {
if (!d.device) {
return;
}
if (d.device !== deps.options.device.hostname) {
return;
}
domainsMap[d.name] = true;
});
*/
//console.log('domains matching hostname', Object.keys(domainsMap));
//console.log('device', deps.options.device);
return oauth3.api('tunnel.token', {
data: {
// filter to all domains that are on this device
//domains: Object.keys(domainsMap)
device: {
hostname: deps.options.device.hostname
, id: deps.options.device.uid || deps.options.device.id
}
}
}).then(function (result) {
console.log('got a token from the tunnel server?');
console.log(result);
if (!result.tunnelUrl) {
result.tunnelUrl = ('wss://' + (new Buffer(result.jwt.split('.')[1], 'base64').toString('ascii')).aud + '/');
}
var opts3 = {
token: result.jwt
, stunneld: result.tunnelUrl
// we'll provide faux networking and pipe as we please
, services: { https: { '*': 443 }, http: { '*': 80 }, smtp: { '*': 25}, smtps: { '*': 587 /*also 465/starttls*/ } /*, ssh: { '*': 22 }*/ }
, net: deps.tunnel.net || deps.net
};
if (tun) {
if (tun.append) {
tun.append(result.jwt);
}
else if (tun.end) {
tun.end();
tun = null;
}
}
if (!tun) {
tun = stunnel.connect(opts3);
conf.tun = true;
}
});
/*
, tunnel: function (deps, session) {
// TODO save session to config and turn tunnel on
var OAUTH3 = deps.OAUTH3;
var url = require('url');
var providerUri = session.token.aud;
var urlObj = url.parse(OAUTH3.url.normalize(session.token.azp));
var oauth3 = OAUTH3.create(urlObj, {
providerUri: providerUri
, session: session
});
//var crypto = require('crypto');
//var id = crypto.createHash('sha256').update(session.token.sub).digest('hex');
return oauth3.setProvider(providerUri).then(function () {
/*
return oauth3.api('domains.list').then(function (domains) {
var domainsMap = {};
domains.forEach(function (d) {
if (!d.device) {
return;
}
if (d.device !== conf.device.hostname) {
return;
}
domainsMap[d.name] = true;
});
*/
*/
//console.log('domains matching hostname', Object.keys(domainsMap));
//console.log('device', conf.device);
return oauth3.api('tunnel.token', {
data: {
// filter to all domains that are on this device
//domains: Object.keys(domainsMap)
device: {
hostname: conf.device.hostname
, id: conf.device.uid || conf.device.id
}
}
}).then(function (result) {
console.log('got a token from the tunnel server?');
console.log(result);
if (!result.tunnelUrl) {
result.tunnelUrl = ('wss://' + (new Buffer(result.jwt.split('.')[1], 'base64').toString('ascii')).aud + '/');
}
var services = { https: { '*': 443 }, http: { '*': 80 }, smtp: { '*': 25}, smtps: { '*': 587 /*also 465/starttls*/ } /*, ssh: { '*': 22 }*/ };
/*
console.log('blah');
console.log(result.jwt);
console.log(result.tunnelUrl);
console.log(services);
console.log('deps.tunnel');
console.log(deps.tunnel);
console.log('deps.tunnel.net');
console.log(deps.tunnel.net.toString());
console.log('deps.net');
console.log(deps.net);
*/
var opts3 = {
token: result.jwt
, stunneld: result.tunnelUrl
// we'll provide faux networking and pipe as we please
, services: services
, net: myDeps.tunnel.net
};
console.log('blah 2');
if (tun) {
console.log('balh 3');
if (tun.append) {
tun.append(result.jwt);
}
else if (tun.end) {
tun.end();
tun = null;
}
}
console.log('might have tunnel?');
if (!tun) {
console.log('connecting to the tunnel');
tun = stunnel.connect(opts3);
conf.tun = true;
}
});
/*
});
//, { token: token, refresh: refresh });
}
*/
});
//, { token: token, refresh: refresh });
}
}, conf);
};
return require('../packages/apis/com.daplie.goldilocks').create(myDeps, conf);
}
app = express();

View File

@ -11,8 +11,10 @@ module.exports.create = function (deps, config) {
var modules = { };
var program = {
tlsOptions: require('localhost.daplie.me-certificates').merge({})
, acmeDirectoryUrl: 'https://acme-v01.api.letsencrypt.org/directory'
, challengeType: 'tls-sni-01'
// , acmeDirectoryUrl: 'https://acme-v01.api.letsencrypt.org/directory'
, acmeDirectoryUrl: 'https://acme-staging.api.letsencrypt.org/directory'
// , challengeType: 'tls-sni-01' // won't work with a tunnel
, challengeType: 'http-01'
};
var secureContexts = {};
var tunnelAdminTlsOpts = {};
@ -191,8 +193,8 @@ module.exports.create = function (deps, config) {
function peek(conn, firstChunk, opts) {
// TODO port/service-based routing can do here
// TLS
if (22 === firstChunk[0]) {
// TLS byte 1 is handshake and byte 6 is client hello
if (0x16 === firstChunk[0]/* && 0x01 === firstChunk[5]*/) {
console.log('tryTls');
opts.servername = (parseSni(firstChunk)||'').toLowerCase() || 'localhost.invalid';
tlsRouter.get(opts.localAddress || conn.localAddress, conn.localPort)(conn, firstChunk, opts);
@ -205,6 +207,7 @@ module.exports.create = function (deps, config) {
// even though we've already peeked, this logic is just as well to let be
// since it works properly either way, unlike the tls socket
conn.once('data', function (chunk) {
console.log('hyperPeek re-peek data', chunk.toString('utf8'));
tcpRouter.get(opts.localAddress || conn.localAddress, conn.localPort)(conn, chunk, opts);
});
return;
@ -275,7 +278,7 @@ module.exports.create = function (deps, config) {
modules.http = require('./modules/http.js').create(deps, config);
}
modules.http.checkServername(opts.domain).then(function (stuff) {
if (!stuff.domains) {
if (!stuff || !stuff.domains) {
// TODO once precheck is implemented we can just let it pass if it passes, yknow?
cb(new Error('domain is not allowed'));
return;
@ -284,9 +287,10 @@ module.exports.create = function (deps, config) {
complete(null, {
domain: stuff.domain || stuff.domains[0]
, domains: stuff.domains
, email: program.email
, server: program.acmeDirectoryUrl
, challengeType: program.challengeType
, email: stuff.email || program.email
, server: stuff.acmeDirectoryUrl || program.acmeDirectoryUrl
, challengeType: stuff.challengeType || program.challengeType
, challenge: stuff.challenge
});
return;
}, cb);
@ -321,35 +325,76 @@ module.exports.create = function (deps, config) {
deps.tunnel = deps.tunnel || {};
deps.tunnel.net = {
createConnection: function (opts, cb) {
var Duplex = require('stream').Duplex;
var myDuplex = new Duplex();
console.log('[gl.tunnel] creating connection');
// here "reader" means the socket that looks like the connection being accepted
// here "writer" means the remote-looking part of the socket that driving the connection
var writer;
var wrapOpts = {};
var rawTls = opts.tls || (0x16 === opts.data[0]) && (0x01 === opts.data[5]);
// this has the normal net/tcp stuff plus our custom stuff
// opts = { address, port,
// hostname, servername, encrypted, data, localAddress, localPort, remoteAddress, remotePort, remoteFamily }
Object.keys(opts).forEach(function (key) {
wrapOpts[key] = opts[key];
myDuplex[key] = opts[key];
});
function usePair(err, reader) {
if (err) {
process.nextTick(function () {
writer.emit('error', err);
});
return;
}
// this has the normal net/tcp stuff plus our custom stuff
// opts = { address, port,
// hostname, servername, tls, encrypted, data, localAddress, localPort, remoteAddress, remotePort, remoteFamily }
Object.keys(opts).forEach(function (key) {
wrapOpts[key] = opts[key];
try {
reader[key] = opts[key];
} catch(e) {
// can't set real socket getters, like remoteAddr
}
});
// A few more extra specialty options
wrapOpts.localAddress = wrapOpts.localAddress || '127.0.0.2'; // TODO use the tunnel's external address
wrapOpts.localPort = wrapOpts.localPort || 'tunnel-0';
try {
reader._remoteAddress = wrapOpts.remoteAddress;
reader._remotePort = wrapOpts.remotePort;
reader._localAddress = wrapOpts.localAddress;
reader._localPort = wrapOpts.localPort;
} catch(e) {
}
netHandler(reader, wrapOpts);
process.nextTick(function () {
//opts.data = wrapOpts.data;
// this cb will cause the stream to emit its (actually) first data event
// (even though it already gave a peek into that first data chunk)
console.log('[tunnel] callback, data should begin to flow');
cb();
});
}
// A few more extra specialty options
wrapOpts.firstChunk = opts.data;
wrapOpts.hyperPeek = !!opts.data;
wrapOpts.localAddress = wrapOpts.localAddress || '127.0.0.2'; // TODO use the tunnel's external address
wrapOpts.localPort = wrapOpts.localPort || 'tunnel-0';
// encrypted meaning is *terminated* TLS
// tls meaning is *raw* TLS
if (rawTls) {
// TLS sockets must actually use a socket with a file descriptor
// https://nodejs.org/api/net.html#net_class_net_socket
netHandler(myDuplex, wrapOpts);
writer = require('socket-pair').create(function (err, other) {
usePair(err, other);
});
}
else {
// stream-pair can only be used by TCP sockets, not tls
writer = require('stream-pair').create();
usePair(null, writer.other);
}
process.nextTick(function () {
//opts.data = wrapOpts.data;
// this cb will cause the stream to emit its (actually) first data event
// (even though it already gave a peek into that first data chunk)
cb();
});
return myDuplex;
return writer;
}
};

View File

@ -64,6 +64,7 @@
"serve-index": "^1.7.0",
"serve-static": "^1.10.0",
"server-destroy": "^1.0.1",
"stream-pair": "^1.0.3",
"stunnel": "git+https://git.daplie.com/Daplie/node-tunnel-client.git#v1"
}
}