made workaround for the TLS issue that I should have ignored...
This commit is contained in:
parent
f2b05ee7af
commit
eacf2e0dbf
|
@ -114,7 +114,7 @@ function readConfigAndRun(args) {
|
||||||
|
|
||||||
// TODO maybe move to config.state.addresses (?)
|
// TODO maybe move to config.state.addresses (?)
|
||||||
config.addresses = 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) {
|
if (config.tcp.ports) {
|
||||||
run(config);
|
run(config);
|
||||||
|
|
232
lib/app.js
232
lib/app.js
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = function (deps, conf, overrideHttp) {
|
module.exports = function (myDeps, conf, overrideHttp) {
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
//var finalhandler = require('finalhandler');
|
//var finalhandler = require('finalhandler');
|
||||||
var serveStatic = require('serve-static');
|
var serveStatic = require('serve-static');
|
||||||
|
@ -100,118 +100,132 @@ module.exports = function (deps, conf, overrideHttp) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return require('../packages/apis/com.daplie.goldilocks').create({
|
myDeps.PromiseA = PromiseA;
|
||||||
PromiseA: PromiseA
|
myDeps.OAUTH3 = OAUTH3;
|
||||||
, OAUTH3: OAUTH3
|
myDeps.storage = { owners: owners };
|
||||||
, storage: {
|
myDeps.recase = require('recase').create({});
|
||||||
owners: owners
|
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({})
|
, tunnel: function (deps, session) {
|
||||||
, request: request
|
// TODO save session to config and turn tunnel on
|
||||||
, options: conf
|
var OAUTH3 = deps.OAUTH3;
|
||||||
, api: {
|
var url = require('url');
|
||||||
// TODO move loopback to oauth3.api('tunnel:loopback')
|
var providerUri = session.token.aud;
|
||||||
loopback: function (deps, session, opts2) {
|
var urlObj = url.parse(OAUTH3.url.normalize(session.token.azp));
|
||||||
var crypto = require('crypto');
|
var oauth3 = OAUTH3.create(urlObj, {
|
||||||
var token = crypto.randomBytes(16).toString('hex');
|
providerUri: providerUri
|
||||||
var keyAuthorization = crypto.randomBytes(16).toString('hex');
|
, session: session
|
||||||
var nonce = crypto.randomBytes(16).toString('hex');
|
});
|
||||||
|
//var crypto = require('crypto');
|
||||||
// TODO set token and keyAuthorization to /.well-known/cloud-challenge/:token
|
//var id = crypto.createHash('sha256').update(session.token.sub).digest('hex');
|
||||||
return request({
|
return oauth3.setProvider(providerUri).then(function () {
|
||||||
method: 'POST'
|
/*
|
||||||
, url: 'https://oauth3.org/api/org.oauth3.tunnel/loopback'
|
return oauth3.api('domains.list').then(function (domains) {
|
||||||
, json: {
|
var domainsMap = {};
|
||||||
address: opts2.address
|
domains.forEach(function (d) {
|
||||||
, port: opts2.port
|
if (!d.device) {
|
||||||
, token: token
|
return;
|
||||||
, keyAuthorization: keyAuthorization
|
}
|
||||||
, servername: opts2.servername
|
if (d.device !== conf.device.hostname) {
|
||||||
, nonce: nonce
|
return;
|
||||||
, scheme: 'https'
|
}
|
||||||
, iat: Date.now()
|
domainsMap[d.name] = true;
|
||||||
}
|
|
||||||
}).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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//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();
|
app = express();
|
||||||
|
|
|
@ -11,8 +11,10 @@ module.exports.create = function (deps, config) {
|
||||||
var modules = { };
|
var modules = { };
|
||||||
var program = {
|
var program = {
|
||||||
tlsOptions: require('localhost.daplie.me-certificates').merge({})
|
tlsOptions: require('localhost.daplie.me-certificates').merge({})
|
||||||
, acmeDirectoryUrl: 'https://acme-v01.api.letsencrypt.org/directory'
|
// , acmeDirectoryUrl: 'https://acme-v01.api.letsencrypt.org/directory'
|
||||||
, challengeType: 'tls-sni-01'
|
, acmeDirectoryUrl: 'https://acme-staging.api.letsencrypt.org/directory'
|
||||||
|
// , challengeType: 'tls-sni-01' // won't work with a tunnel
|
||||||
|
, challengeType: 'http-01'
|
||||||
};
|
};
|
||||||
var secureContexts = {};
|
var secureContexts = {};
|
||||||
var tunnelAdminTlsOpts = {};
|
var tunnelAdminTlsOpts = {};
|
||||||
|
@ -191,8 +193,8 @@ module.exports.create = function (deps, config) {
|
||||||
function peek(conn, firstChunk, opts) {
|
function peek(conn, firstChunk, opts) {
|
||||||
// TODO port/service-based routing can do here
|
// TODO port/service-based routing can do here
|
||||||
|
|
||||||
// TLS
|
// TLS byte 1 is handshake and byte 6 is client hello
|
||||||
if (22 === firstChunk[0]) {
|
if (0x16 === firstChunk[0]/* && 0x01 === firstChunk[5]*/) {
|
||||||
console.log('tryTls');
|
console.log('tryTls');
|
||||||
opts.servername = (parseSni(firstChunk)||'').toLowerCase() || 'localhost.invalid';
|
opts.servername = (parseSni(firstChunk)||'').toLowerCase() || 'localhost.invalid';
|
||||||
tlsRouter.get(opts.localAddress || conn.localAddress, conn.localPort)(conn, firstChunk, opts);
|
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
|
// 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
|
// since it works properly either way, unlike the tls socket
|
||||||
conn.once('data', function (chunk) {
|
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);
|
tcpRouter.get(opts.localAddress || conn.localAddress, conn.localPort)(conn, chunk, opts);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -275,7 +278,7 @@ module.exports.create = function (deps, config) {
|
||||||
modules.http = require('./modules/http.js').create(deps, config);
|
modules.http = require('./modules/http.js').create(deps, config);
|
||||||
}
|
}
|
||||||
modules.http.checkServername(opts.domain).then(function (stuff) {
|
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?
|
// TODO once precheck is implemented we can just let it pass if it passes, yknow?
|
||||||
cb(new Error('domain is not allowed'));
|
cb(new Error('domain is not allowed'));
|
||||||
return;
|
return;
|
||||||
|
@ -284,9 +287,10 @@ module.exports.create = function (deps, config) {
|
||||||
complete(null, {
|
complete(null, {
|
||||||
domain: stuff.domain || stuff.domains[0]
|
domain: stuff.domain || stuff.domains[0]
|
||||||
, domains: stuff.domains
|
, domains: stuff.domains
|
||||||
, email: program.email
|
, email: stuff.email || program.email
|
||||||
, server: program.acmeDirectoryUrl
|
, server: stuff.acmeDirectoryUrl || program.acmeDirectoryUrl
|
||||||
, challengeType: program.challengeType
|
, challengeType: stuff.challengeType || program.challengeType
|
||||||
|
, challenge: stuff.challenge
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}, cb);
|
}, cb);
|
||||||
|
@ -321,35 +325,76 @@ module.exports.create = function (deps, config) {
|
||||||
deps.tunnel = deps.tunnel || {};
|
deps.tunnel = deps.tunnel || {};
|
||||||
deps.tunnel.net = {
|
deps.tunnel.net = {
|
||||||
createConnection: function (opts, cb) {
|
createConnection: function (opts, cb) {
|
||||||
var Duplex = require('stream').Duplex;
|
console.log('[gl.tunnel] creating connection');
|
||||||
var myDuplex = new Duplex();
|
|
||||||
|
// 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 wrapOpts = {};
|
||||||
|
var rawTls = opts.tls || (0x16 === opts.data[0]) && (0x01 === opts.data[5]);
|
||||||
|
|
||||||
// this has the normal net/tcp stuff plus our custom stuff
|
function usePair(err, reader) {
|
||||||
// opts = { address, port,
|
if (err) {
|
||||||
// hostname, servername, encrypted, data, localAddress, localPort, remoteAddress, remotePort, remoteFamily }
|
process.nextTick(function () {
|
||||||
Object.keys(opts).forEach(function (key) {
|
writer.emit('error', err);
|
||||||
wrapOpts[key] = opts[key];
|
});
|
||||||
myDuplex[key] = opts[key];
|
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.firstChunk = opts.data;
|
||||||
wrapOpts.hyperPeek = !!opts.data;
|
wrapOpts.hyperPeek = !!opts.data;
|
||||||
wrapOpts.localAddress = wrapOpts.localAddress || '127.0.0.2'; // TODO use the tunnel's external address
|
// encrypted meaning is *terminated* TLS
|
||||||
wrapOpts.localPort = wrapOpts.localPort || 'tunnel-0';
|
// 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 () {
|
return writer;
|
||||||
//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;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
"serve-index": "^1.7.0",
|
"serve-index": "^1.7.0",
|
||||||
"serve-static": "^1.10.0",
|
"serve-static": "^1.10.0",
|
||||||
"server-destroy": "^1.0.1",
|
"server-destroy": "^1.0.1",
|
||||||
|
"stream-pair": "^1.0.3",
|
||||||
"stunnel": "git+https://git.daplie.com/Daplie/node-tunnel-client.git#v1"
|
"stunnel": "git+https://git.daplie.com/Daplie/node-tunnel-client.git#v1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue