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 (?)
|
||||
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);
|
||||
|
|
232
lib/app.js
232
lib/app.js
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue