Merge branch 'master' of bitbucket.org:coolaj86/walnut
This commit is contained in:
commit
c1b550f219
|
@ -14,6 +14,7 @@ cli.parse({
|
|||
, insecure: [ false, '(deprecated) allow insecure non-https connections', 'boolean' ]
|
||||
, cacert: [ false, '(not implemented) specify a CA for "self-signed" https certificates', 'string' ]
|
||||
, answer: [ 'a', 'The answer', 'string' ]
|
||||
, token: [ false, 'Token (TODO or filepath to token)', 'string' ]
|
||||
});
|
||||
|
||||
cli.main(function (args, options) {
|
||||
|
@ -22,8 +23,9 @@ cli.main(function (args, options) {
|
|||
options.answer = options.answer || args[1]
|
||||
|
||||
if (options.insecure) {
|
||||
console.error('--insecure is not supported. You must use secure connections.');
|
||||
return;
|
||||
//console.error('--insecure is not supported. You must use secure connections.');
|
||||
//return;
|
||||
options.cacert = false;
|
||||
}
|
||||
|
||||
if (!options.hostname) {
|
||||
|
@ -41,20 +43,26 @@ cli.main(function (args, options) {
|
|||
updater: options.service
|
||||
, port: options.port
|
||||
, cacert: options.cacert
|
||||
, token: options.token
|
||||
, ddns: [
|
||||
{ "name": options.hostname
|
||||
, "value": options.answer
|
||||
, "type": options.type
|
||||
, "priority": options.priority
|
||||
, "token": options.token
|
||||
}
|
||||
]
|
||||
}).then(function (data) {
|
||||
if ('string') {
|
||||
data = JSON.parse(data);
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
console.error(data);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(data, null, ' '));
|
||||
console.log('Test with');
|
||||
console.log('dig ' + options.hostname + ' ' + options.type);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,38 +1,52 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
// dig -p 53 @redirect-www.org pi.nadal.daplie.com A
|
||||
var updateIp = require('./holepunch/helpers/update-ip.js').update;
|
||||
// TODO have a quick timeout
|
||||
require('ipify')(function (err, ip) {
|
||||
console.log('ip', ip);
|
||||
|
||||
var redirects = require('./redirects');
|
||||
var ddns = [];
|
||||
var ddnsMap = {};
|
||||
var path = require('path');
|
||||
// dig -p 53 @redirect-www.org pi.nadal.daplie.com A
|
||||
var updateIp = require('./holepunch/helpers/update-ip.js').update;
|
||||
|
||||
function add(hostname) {
|
||||
ddns.push({
|
||||
"name": hostname
|
||||
var redirects = require('./redirects');
|
||||
var ddns = [];
|
||||
var ddnsMap = {};
|
||||
|
||||
function add(hostname) {
|
||||
ddns.push({
|
||||
"name": hostname
|
||||
, "answer": ip
|
||||
});
|
||||
}
|
||||
|
||||
redirects.forEach(function (r) {
|
||||
if (!ddnsMap[r.from.hostname.toLowerCase()]) {
|
||||
add(r.from.hostname);
|
||||
}
|
||||
if (!ddnsMap[r.to.hostname.toLowerCase()]) {
|
||||
add(r.to.hostname);
|
||||
}
|
||||
});
|
||||
}
|
||||
redirects.forEach(function (r) {
|
||||
if (!ddnsMap[r.from.hostname.toLowerCase()]) {
|
||||
add(r.from.hostname);
|
||||
}
|
||||
if (!ddnsMap[r.to.hostname.toLowerCase()]) {
|
||||
add(r.to.hostname);
|
||||
}
|
||||
});
|
||||
|
||||
return updateIp({
|
||||
updater: 'redirect-www.org'
|
||||
, port: 65443
|
||||
, cacert: null
|
||||
, ddns: ddns
|
||||
}).then(function (data) {
|
||||
if ('string') {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
return updateIp({
|
||||
updater: 'ns1.redirect-www.org'
|
||||
, port: 65443
|
||||
, cacert: path.join(__dirname, 'certs/ca/ns1-test.root.crt.pem')
|
||||
, ddns: ddns
|
||||
, token: require('./dyndns-token').token
|
||||
}).then(function (data) {
|
||||
if ('string' === typeof data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
console.error('[ERROR] bad json response');
|
||||
console.error(data);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(data, null, ' '));
|
||||
console.log('Test with');
|
||||
console.log('dig <<hostname>> A');
|
||||
console.log(JSON.stringify(data, null, ' '));
|
||||
console.log('Test with');
|
||||
console.log('dig <<hostname>> A');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
module.exports = { token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" };
|
|
@ -1,14 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
var PromiseA = require('bluebird').Promise
|
||||
, updateIp = require('./helpers/update-ip.js').update
|
||||
, request = PromiseA.promisifyAll(require('request'))
|
||||
, requestAsync = PromiseA.promisify(require('request'))
|
||||
, upnpForward = require('./helpers/upnp-forward').upnpForward
|
||||
, pmpForward = require('./helpers/pmp-forward').pmpForward
|
||||
, loopbackHttps = require('./loopback-https')
|
||||
//, checkip = require('check-ip-address')
|
||||
;
|
||||
var PromiseA = require('bluebird').Promise;
|
||||
var updateIp = require('./helpers/update-ip.js').update;
|
||||
var request = PromiseA.promisifyAll(require('request'));
|
||||
var requestAsync = PromiseA.promisify(require('request'));
|
||||
var upnpForward = require('./helpers/upnp-forward').upnpForward;
|
||||
var pmpForward = require('./helpers/pmp-forward').pmpForward;
|
||||
var loopbackHttps = require('./loopback-https');
|
||||
//var checkip = require('check-ip-address');
|
||||
|
||||
function openPort(ip, port) {
|
||||
if (!/tcp|https|http/.test(port.protocol || 'tcp')) {
|
||||
|
@ -52,7 +51,7 @@ function beacon(hostnames, ports) {
|
|||
|
||||
console.log("Updated DynDNS");
|
||||
console.log(data);
|
||||
|
||||
|
||||
ports.forEach(function (port) {
|
||||
promises.push(openPort(JSON.parse(data)[0].answers[0] || hostname, port));
|
||||
});
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
var PromiseA = require('bluebird').Promise
|
||||
, natpmp = require('nat-pmp')
|
||||
, exec = require('child_process').exec
|
||||
;
|
||||
var PromiseA = require('bluebird').Promise;
|
||||
var natpmp = require('nat-pmp');
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
exports.pmpForward = function (port) {
|
||||
return new PromiseA(function (resolve, reject) {
|
||||
exec('ip route show default', function (err, stdout, stderr) {
|
||||
var gw
|
||||
;
|
||||
var gw;
|
||||
|
||||
if (err || stderr) { reject(err || stderr); return; }
|
||||
|
||||
|
||||
// default via 192.168.1.1 dev eth0
|
||||
gw = stdout.replace(/^default via (\d+\.\d+\.\d+\.\d+) dev[\s\S]+/m, '$1');
|
||||
console.log('Possible PMP gateway is', gw);
|
||||
|
@ -55,3 +53,29 @@ exports.pmpForward = function (port) {
|
|||
});
|
||||
});
|
||||
};
|
||||
|
||||
function usage() {
|
||||
console.warn("");
|
||||
console.warn("node helpers/pmp-forward [public port] [private port] [ttl]");
|
||||
console.warn("");
|
||||
}
|
||||
|
||||
function run() {
|
||||
var pubPort = parseInt(process.argv[2], 10) || 0;
|
||||
var privPort = parseInt(process.argv[3], 10) || pubPort;
|
||||
var ttl = parseInt(process.argv[4], 10) || 0;
|
||||
var options = { public: pubPort, private: privPort, ttl: ttl };
|
||||
|
||||
if (!pubPort) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
exports.pmpForward(options).then(function () {
|
||||
console.log('done');
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
run();
|
||||
}
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
var PromiseA = require('bluebird').Promise
|
||||
, https = require('https')
|
||||
, fs = require('fs')
|
||||
, path = require('path')
|
||||
;
|
||||
var PromiseA = require('bluebird').Promise;
|
||||
var https = require('https');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
module.exports.update = function (opts) {
|
||||
return new PromiseA(function (resolve, reject) {
|
||||
var options
|
||||
, hostname = opts.updater || 'redirect-www.org'
|
||||
, port = opts.port || 65443
|
||||
;
|
||||
var options;
|
||||
var hostname = opts.updater || 'redirect-www.org';
|
||||
var port = opts.port || 65443;
|
||||
var req;
|
||||
|
||||
options = {
|
||||
host: hostname
|
||||
|
@ -22,12 +21,36 @@ module.exports.update = function (opts) {
|
|||
'Content-Type': 'application/json'
|
||||
}
|
||||
, path: '/api/ddns'
|
||||
, auth: opts.auth || 'admin:secret'
|
||||
, ca: [ fs.readFileSync(path.join(__dirname, '..', 'certs', 'ca', 'my-root-ca.crt.pem')) ]
|
||||
//, auth: opts.auth || 'admin:secret'
|
||||
};
|
||||
|
||||
if (opts.cacert) {
|
||||
if (!Array.isArray(opts.cacert)) {
|
||||
opts.cacert = [opts.cacert];
|
||||
}
|
||||
options.ca = opts.cacert;
|
||||
} else {
|
||||
options.ca = [path.join(__dirname, '..', 'certs', 'ca', 'my-root-ca.crt.pem')]
|
||||
}
|
||||
|
||||
options.ca = options.ca.map(function (str) {
|
||||
if ('string' === typeof str && str.length < 1000) {
|
||||
str = fs.readFileSync(str);
|
||||
}
|
||||
return str;
|
||||
});
|
||||
|
||||
if (opts.token || opts.jwt) {
|
||||
options.headers['Authorization'] = 'Bearer ' + (opts.token || opts.jwt);
|
||||
}
|
||||
|
||||
if (false === opts.cacert) {
|
||||
options.rejectUnauthorized = false;
|
||||
}
|
||||
|
||||
options.agent = new https.Agent(options);
|
||||
|
||||
https.request(options, function(res) {
|
||||
req = https.request(options, function(res) {
|
||||
var textData = '';
|
||||
|
||||
res.on('error', function (err) {
|
||||
|
@ -38,8 +61,22 @@ module.exports.update = function (opts) {
|
|||
// console.log(chunk.toString());
|
||||
});
|
||||
res.on('end', function () {
|
||||
resolve(textData);
|
||||
var err;
|
||||
try {
|
||||
resolve(JSON.parse(textData));
|
||||
} catch(e) {
|
||||
err = new Error("Unparsable Server Response");
|
||||
err.code = 'E_INVALID_SERVER_RESPONSE';
|
||||
err.data = textData;
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}).end(JSON.stringify(opts.ddns, null, ' '));
|
||||
});
|
||||
|
||||
req.on('error', function () {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
req.end(JSON.stringify(opts.ddns, null, ' '));
|
||||
});
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ exports.upnpForward = function (port) {
|
|||
console.log('mappings');
|
||||
console.log(mappings);
|
||||
});
|
||||
|
||||
|
||||
return promitter;
|
||||
})*/;
|
||||
});
|
||||
|
@ -49,8 +49,24 @@ client.externalIp(function(err, ip) {
|
|||
});
|
||||
*/
|
||||
|
||||
if (require.main === module) {
|
||||
exports.upnpForward({ public: 65080, private: 65080, ttl: 0 }).then(function () {
|
||||
function usage() {
|
||||
console.warn("");
|
||||
console.warn("node helpers/upnp-forward [public port] [private port] [ttl]");
|
||||
console.warn("");
|
||||
}
|
||||
|
||||
function run() {
|
||||
var pubPort = parseInt(process.argv[2], 10) || 0;
|
||||
var privPort = parseInt(process.argv[3], 10) || pubPort;
|
||||
var ttl = parseInt(process.argv[4], 10) || 0;
|
||||
var options = { public: pubPort, private: privPort, ttl: ttl };
|
||||
|
||||
if (!pubPort) {
|
||||
usage();
|
||||
return;
|
||||
}
|
||||
|
||||
exports.upnpForward(options).then(function () {
|
||||
console.log('done');
|
||||
}).catch(function (err) {
|
||||
console.error('ERROR');
|
||||
|
@ -58,3 +74,8 @@ if (require.main === module) {
|
|||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
run();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
'use strict';
|
||||
|
||||
var updateIp = require('../holepunch/helpers/update-ip.js').update;
|
||||
// TODO XXX use API + storage
|
||||
var token = require('../dyndns-token.js').token;
|
||||
|
||||
/*
|
||||
* @param {string[]} hostnames - A list of hostnames
|
||||
* @param {Object[]} addresses - A list of { address: <ip-address>, family: <4|6> }
|
||||
*/
|
||||
function update(hostnames, addresses) {
|
||||
// TODO use not-yet-built API to get and store tokens
|
||||
// TODO use API to add and remove nameservers
|
||||
var services = [
|
||||
// TODO XXX don't disable cacert checking
|
||||
{ hostname: 'ns1.redirect-www.org', port: 65443, cacert: false }
|
||||
, { hostname: 'ns2.redirect-www.org', port: 65443, cacert: false }
|
||||
];
|
||||
var answers = [];
|
||||
var promises;
|
||||
var results = [];
|
||||
var PromiseA;
|
||||
|
||||
hostnames.forEach(function (hostname) {
|
||||
addresses.forEach(function (address) {
|
||||
var answer = {
|
||||
"name": hostname
|
||||
, "value": address.address
|
||||
, "type": null
|
||||
, "priority": null
|
||||
, "token": token
|
||||
};
|
||||
|
||||
if (4 === address.family) {
|
||||
answer.type = 'A';
|
||||
}
|
||||
else if (6 === address.family) {
|
||||
answer.type = 'AAAA';
|
||||
}
|
||||
else {
|
||||
console.error('[ERROR] unspported address:');
|
||||
console.error(address);
|
||||
return;
|
||||
}
|
||||
|
||||
answers.push(answer);
|
||||
});
|
||||
});
|
||||
|
||||
promises = services.map(function (service, i) {
|
||||
return updateIp({
|
||||
updater: service.hostname
|
||||
, port: service.port
|
||||
, cacert: service.cacert
|
||||
, token: token
|
||||
, ddns: answers
|
||||
}).then(function (data) {
|
||||
results[i] = { service: service, data: data };
|
||||
return data;
|
||||
}).error(function (err) {
|
||||
results[i] = { service: service, error: err };
|
||||
});
|
||||
});
|
||||
|
||||
PromiseA = require('bluebird').Promise;
|
||||
return PromiseA.all(promises).then(function () {
|
||||
return results;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.update = function () {
|
||||
var allMap = {};
|
||||
var hostnames = require('../redirects.json').reduce(function (all, redirect) {
|
||||
if (!allMap[redirect.from.hostname]) {
|
||||
allMap[redirect.from.hostname] = true;
|
||||
all.push(redirect.from.hostname);
|
||||
}
|
||||
if (!all[redirect.to.hostname]) {
|
||||
allMap[redirect.to.hostname] = true;
|
||||
all.push(redirect.to.hostname);
|
||||
}
|
||||
|
||||
return all;
|
||||
}, []);
|
||||
|
||||
return require('./ip-checker').getExternalAddresses().then(function (result) {
|
||||
//console.log(Object.keys(allMap), result);
|
||||
//console.log(hostnames)
|
||||
//console.log(result.addresses);
|
||||
console.log('[IP CHECKER] hostnames.length', hostnames.length);
|
||||
console.log('[IP CHECKER] result.addresses.length', result.addresses.length);
|
||||
return update(hostnames, result.addresses);
|
||||
});
|
||||
};
|
||||
|
||||
if (require.main === module) {
|
||||
module.exports.update().then(function (results) {
|
||||
console.log('results');
|
||||
console.log(results);
|
||||
});
|
||||
}
|
|
@ -6,6 +6,8 @@ module.exports.create = function (securePort, insecurePort, redirects) {
|
|||
var escapeRe;
|
||||
|
||||
function redirectHttps(req, res) {
|
||||
res.setHeader('Strict-Transport-Security', 'max-age=10886400; includeSubDomains; preload');
|
||||
|
||||
var insecureRedirects;
|
||||
var host = req.headers.host || '';
|
||||
var url = req.url;
|
||||
|
@ -40,19 +42,21 @@ module.exports.create = function (securePort, insecurePort, redirects) {
|
|||
);
|
||||
});
|
||||
|
||||
var escapeHtml = require('escape-html');
|
||||
var newLocation = 'https://'
|
||||
+ host.replace(/:\d+/, ':' + securePort) + url
|
||||
;
|
||||
var safeLocation = escapeHtml(newLocation);
|
||||
|
||||
var metaRedirect = ''
|
||||
+ '<html>\n'
|
||||
+ '<head>\n'
|
||||
+ ' <style>* { background-color: white; color: white; text-decoration: none; }</style>\n'
|
||||
+ ' <META http-equiv="refresh" content="0;URL=' + newLocation + '">\n'
|
||||
+ ' <META http-equiv="refresh" content="0;URL=' + safeLocation + '">\n'
|
||||
+ '</head>\n'
|
||||
+ '<body style="display: none;">\n'
|
||||
+ ' <p>You requested an insecure resource. Please use this instead: \n'
|
||||
+ ' <a href="' + newLocation + '">' + newLocation + '</a></p>\n'
|
||||
+ ' <a href="' + safeLocation + '">' + safeLocation + '</a></p>\n'
|
||||
+ '</body>\n'
|
||||
+ '</html>\n'
|
||||
;
|
||||
|
@ -72,7 +76,7 @@ module.exports.create = function (securePort, insecurePort, redirects) {
|
|||
// To minimize this, we give browser users a mostly optimal experience,
|
||||
// but people experimenting with the API get a message letting them know
|
||||
// that they're doing it wrong and thus forces them to ensure they encrypt.
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
res.end(metaRedirect);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
"use strict";
|
||||
|
||||
var PromiseA = require('bluebird').Promise;
|
||||
var ifaces = require('os').networkInterfaces();
|
||||
var dns = PromiseA.promisifyAll(require('dns'));
|
||||
var https = require('https');
|
||||
|
||||
function getExternalAddresses() {
|
||||
var iftypes = {};
|
||||
|
||||
Object.keys(ifaces).forEach(function (ifname) {
|
||||
ifaces[ifname].forEach(function (iface) {
|
||||
if (iface.internal) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
if (/^(::|f[cde])/.test(iface.address)) {
|
||||
console.log('non-public ipv6');
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
iftypes[iface.family] = true;
|
||||
});
|
||||
});
|
||||
|
||||
var now = Date.now();
|
||||
|
||||
return PromiseA.all([
|
||||
dns.lookupAsync('api.ipify.org', { family: 4/*, all: true*/ }).then(function (ans) {
|
||||
iftypes.IPv4 = { address: ans[0], family: ans[1], time: Date.now() - now };
|
||||
}).error(function () {
|
||||
//console.log('no ipv4', Date.now() - now);
|
||||
iftypes.IPv4 = false;
|
||||
})
|
||||
, dns.lookupAsync('api.ipify.org', { family: 6/*, all: true*/ }).then(function (ans) {
|
||||
iftypes.IPv6 = { address: ans[0], family: ans[1], time: Date.now() - now };
|
||||
}).error(function () {
|
||||
//console.log('no ipv6', Date.now() - now);
|
||||
iftypes.IPv6 = false;
|
||||
})
|
||||
]).then(function () {
|
||||
var requests = [];
|
||||
|
||||
if (iftypes.IPv4) {
|
||||
requests.push(new PromiseA(function (resolve) {
|
||||
var req = https.request({
|
||||
method: 'GET'
|
||||
, hostname: iftypes.IPv4.address
|
||||
, port: 443
|
||||
, headers: {
|
||||
Host: 'api.ipify.org'
|
||||
}
|
||||
, path: '/'
|
||||
//, family: 4
|
||||
// TODO , localAddress: <<external_ipv4>>
|
||||
}, function (res) {
|
||||
var result = '';
|
||||
|
||||
res.on('error', function (/*err*/) {
|
||||
resolve(null);
|
||||
});
|
||||
|
||||
res.on('data', function (chunk) {
|
||||
result += chunk.toString('utf8');
|
||||
});
|
||||
res.on('end', function () {
|
||||
resolve({ address: result, family: 4/*, wan: result === iftypes.IPv4.localAddress*/, time: iftypes.IPv4.time });
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function () {
|
||||
resolve(null);
|
||||
});
|
||||
req.end();
|
||||
}));
|
||||
}
|
||||
|
||||
if (iftypes.IPv6) {
|
||||
requests.push(new PromiseA(function (resolve) {
|
||||
var req = https.request({
|
||||
method: 'GET'
|
||||
, hostname: iftypes.IPv6.address
|
||||
, port: 443
|
||||
, headers: {
|
||||
Host: 'api.ipify.org'
|
||||
}
|
||||
, path: '/'
|
||||
//, family: 6
|
||||
// TODO , localAddress: <<external_ipv6>>
|
||||
}, function (res) {
|
||||
var result = '';
|
||||
|
||||
res.on('error', function (/*err*/) {
|
||||
resolve(null);
|
||||
});
|
||||
|
||||
res.on('data', function (chunk) {
|
||||
result += chunk.toString('utf8');
|
||||
});
|
||||
res.on('end', function () {
|
||||
resolve({ address: result, family: 6/*, wan: result === iftypes.IPv6.localAaddress*/, time: iftypes.IPv4.time });
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function () {
|
||||
resolve(null);
|
||||
});
|
||||
req.end();
|
||||
}));
|
||||
}
|
||||
|
||||
return PromiseA.all(requests).then(function (ips) {
|
||||
ips = ips.filter(function (ip) {
|
||||
return ip;
|
||||
});
|
||||
|
||||
return {
|
||||
addresses: ips
|
||||
, time: Date.now() - now
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
exports.getExternalAddresses = getExternalAddresses;
|
|
@ -8,7 +8,9 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
var path = require('path');
|
||||
var dummyCerts;
|
||||
var serveFavicon;
|
||||
var secureContexts = {};
|
||||
var secureContexts = {};
|
||||
var loopbackApp;
|
||||
var loopbackToken = require('crypto').randomBytes(32).toString('hex');
|
||||
|
||||
function loadDummyCerts() {
|
||||
if (dummyCerts) {
|
||||
|
@ -132,6 +134,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
|
||||
console.log('[log] [once] Preparing mount for', domaininfo.hostname + '/' + domaininfo.dirpathname);
|
||||
domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname] = function (req, res, next) {
|
||||
res.setHeader('Strict-Transport-Security', 'max-age=10886400; includeSubDomains; preload');
|
||||
function loadThatApp() {
|
||||
var time = Date.now();
|
||||
|
||||
|
@ -206,6 +209,13 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
});
|
||||
}
|
||||
|
||||
function suckItDubDubDub(req, res) {
|
||||
var newLoc = 'https://' + (req.headers.host||'').replace(/^www\./) + req.url;
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', newLoc);
|
||||
res.end("<html><head><title></title></head><body><!-- redirecting nowww --></body><html>");
|
||||
}
|
||||
|
||||
function nextify() {
|
||||
if (!appContext) {
|
||||
appContext = loadThatApp();
|
||||
|
@ -230,17 +240,27 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
connectContext.static = serveStatic(path.join(vhostsdir, domaininfo.dirname, 'public'));
|
||||
}
|
||||
|
||||
if (/^\/api\//.test(req.url)) {
|
||||
nextify();
|
||||
if (/^www\./.test(req.headers.host)) {
|
||||
if (/\.appcache\b/.test(req.url)) {
|
||||
res.setHeader('Content-Type', 'text/cache-manifest');
|
||||
res.end('CACHE MANIFEST\n\n# v0__DELETE__CACHE__MANIFEST__\n\nNETWORK:\n*');
|
||||
return;
|
||||
}
|
||||
suckItDubDubDub(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
connectContext.static(req, res, nextify);
|
||||
if (/^\/api\//.test(req.url)) {
|
||||
nextify();
|
||||
return;
|
||||
}
|
||||
|
||||
connectContext.static(req, res, nextify);
|
||||
};
|
||||
domainMergeMap[domaininfo.hostname].apps.use(
|
||||
'/' + domaininfo.pathname
|
||||
, domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname]
|
||||
);
|
||||
'/' + domaininfo.pathname
|
||||
, domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname]
|
||||
);
|
||||
|
||||
return PromiseA.resolve();
|
||||
}
|
||||
|
@ -276,9 +296,20 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
};
|
||||
}
|
||||
|
||||
function getLoopbackApp() {
|
||||
return function (req, res) {
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||
res.end(JSON.stringify({ "success": true, "token": loopbackToken }));
|
||||
};
|
||||
}
|
||||
|
||||
function getAppContext(domaininfo) {
|
||||
var localApp;
|
||||
|
||||
if ('loopback.daplie.invalid' === domaininfo.dirname) {
|
||||
return getLoopbackApp();
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO live reload required modules
|
||||
localApp = require(path.join(vhostsdir, domaininfo.dirname, 'app.js'));
|
||||
|
@ -287,9 +318,9 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
// TODO pass in websocket
|
||||
localApp = localApp.create(secureServer, {
|
||||
dummyCerts: dummyCerts
|
||||
, hostname: domaininfo.hostname
|
||||
, port: securePort
|
||||
, url: domaininfo.pathname
|
||||
, hostname: domaininfo.hostname
|
||||
, port: securePort
|
||||
, url: domaininfo.pathname
|
||||
});
|
||||
|
||||
if (!localApp) {
|
||||
|
@ -324,7 +355,38 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
console.log('[log] [once] Loading all mounts for ' + domainApp.hostname);
|
||||
domainApp._loaded = true;
|
||||
app.use(vhost(domainApp.hostname, domainApp.apps));
|
||||
app.use(vhost('www.' + domainApp.hostname, domainApp.apps));
|
||||
app.use(vhost('www.' + domainApp.hostname, function (req, res, next) {
|
||||
if (/\.appcache\b/.test(req.url)) {
|
||||
res.setHeader('Content-Type', 'text/cache-manifest');
|
||||
res.end('CACHE MANIFEST\n\n# v0__DELETE__CACHE__MANIFEST__\n\nNETWORK:\n*');
|
||||
//domainApp.apps(req, res, next);
|
||||
return;
|
||||
}
|
||||
// TODO XXX this is in the api section, so it should hard break
|
||||
//res.statusCode = 301;
|
||||
//res.setHeader('Location', newLoc);
|
||||
|
||||
// TODO port number for non-443
|
||||
var escapeHtml = require('escape-html');
|
||||
var newLocation = 'https://' + domainApp.hostname + req.url;
|
||||
var safeLocation = escapeHtml(newLocation);
|
||||
|
||||
var metaRedirect = ''
|
||||
+ '<html>\n'
|
||||
+ '<head>\n'
|
||||
+ ' <style>* { background-color: white; color: white; text-decoration: none; }</style>\n'
|
||||
+ ' <META http-equiv="refresh" content="0;URL=' + safeLocation + '">\n'
|
||||
+ '</head>\n'
|
||||
+ '<body style="display: none;">\n'
|
||||
+ ' <p>You requested an old resource. Please use this instead: \n'
|
||||
+ ' <a href="' + safeLocation + '">' + safeLocation + '</a></p>\n'
|
||||
+ '</body>\n'
|
||||
+ '</html>\n'
|
||||
;
|
||||
|
||||
// 301 redirects will not work for appcache
|
||||
res.end(metaRedirect);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -400,14 +462,28 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
var secOpts;
|
||||
|
||||
try {
|
||||
var nodes = fs.readdirSync(path.join(certsPath, 'server'));
|
||||
var keyNode = nodes.filter(function (node) { return /\.key\.pem$/.test(node); })[0];
|
||||
var crtNode = nodes.filter(function (node) { return /\.crt\.pem$/.test(node); })[0];
|
||||
var nodes = fs.readdirSync(certsPath);
|
||||
var keyNode = nodes.filter(function (node) { return 'privkey.pem' === node; })[0];
|
||||
var crtNode = nodes.filter(function (node) { return 'fullchain.pem' === node; })[0];
|
||||
|
||||
if (keyNode && crtNode) {
|
||||
keyNode = path.join(certsPath, keyNode);
|
||||
crtNode = path.join(certsPath, crtNode);
|
||||
} else {
|
||||
nodes = fs.readdirSync(path.join(certsPath, 'server'));
|
||||
keyNode = nodes.filter(function (node) { return /^privkey(\.key)?\.pem$/.test(node) || /\.key\.pem$/.test(node); })[0];
|
||||
crtNode = nodes.filter(function (node) { return /^fullchain(\.crt)?\.pem$/.test(node) || /\.crt\.pem$/.test(node); })[0];
|
||||
keyNode = path.join(certsPath, 'server', keyNode);
|
||||
crtNode = path.join(certsPath, 'server', crtNode);
|
||||
}
|
||||
|
||||
secOpts = {
|
||||
key: fs.readFileSync(path.join(certsPath, 'server', keyNode))
|
||||
, cert: fs.readFileSync(path.join(certsPath, 'server', crtNode))
|
||||
key: fs.readFileSync(keyNode)
|
||||
, cert: fs.readFileSync(crtNode)
|
||||
};
|
||||
|
||||
// I misunderstood what the ca option was for
|
||||
/*
|
||||
if (fs.existsSync(path.join(certsPath, 'ca'))) {
|
||||
secOpts.ca = fs.readdirSync(path.join(certsPath, 'ca')).filter(function (node) {
|
||||
console.log('[log ca]', node);
|
||||
|
@ -416,6 +492,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
return fs.readFileSync(path.join(certsPath, 'ca', node));
|
||||
});
|
||||
}
|
||||
*/
|
||||
} catch(err) {
|
||||
// TODO Let's Encrypt / ACME HTTPS
|
||||
console.error("[ERROR] Couldn't READ HTTPS certs from '" + certsPath + "':");
|
||||
|
@ -446,14 +523,18 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
// fallback / default dummy certs
|
||||
key: localDummyCerts.key
|
||||
, cert: localDummyCerts.cert
|
||||
, ca: localDummyCerts.ca
|
||||
// changes from default: disallow RC4
|
||||
, ciphers: "ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:!RC4:HIGH:!MD5:!aNULL"
|
||||
//, ca: localDummyCerts.ca
|
||||
// io.js defaults have disallowed insecure algorithms as of 2015-06-29
|
||||
// https://iojs.org/api/tls.html
|
||||
// previous version could use something like this
|
||||
//, ciphers: "ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:!RC4:HIGH:!MD5:!aNULL"
|
||||
};
|
||||
|
||||
function addSniWorkaroundCallback() {
|
||||
//SNICallback is passed the domain name, see NodeJS docs on TLS
|
||||
secureOpts.SNICallback = function (domainname, cb) {
|
||||
domainname = domainname.replace(/^www\./, '')
|
||||
|
||||
if (/(^|\.)proxyable\./.test(domainname)) {
|
||||
// device-id-12345678.proxyable.myapp.mydomain.com => myapp.mydomain.com
|
||||
// proxyable.myapp.mydomain.com => myapp.mydomain.com
|
||||
|
@ -521,5 +602,23 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
|||
});
|
||||
}
|
||||
|
||||
function updateIps() {
|
||||
console.log('[UPDATE IP]');
|
||||
require('./ddns-updater').update().then(function (results) {
|
||||
results.forEach(function (result) {
|
||||
if (result.error) {
|
||||
console.error(result);
|
||||
} else {
|
||||
console.log('[SUCCESS]', result.service.hostname);
|
||||
}
|
||||
});
|
||||
}).error(function (err) {
|
||||
console.error('[UPDATE IP] ERROR');
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
// TODO check the IP every 5 minutes and update it every hour
|
||||
setInterval(updateIps, 60 * 60 * 1000);
|
||||
updateIps();
|
||||
return runServer();
|
||||
};
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
"ee-first": "^1.1.0",
|
||||
"errorhandler": "1.x",
|
||||
"es6-promise": "2.x",
|
||||
"escape-html": "^1.0.1",
|
||||
"escape-html": "^1.0.2",
|
||||
"escape-string-regexp": "1.x",
|
||||
"etag": "^1.5.1",
|
||||
"express": "4.x",
|
||||
|
@ -74,6 +74,7 @@
|
|||
"fresh": "^0.2.4",
|
||||
"human-readable-ids": "1.x",
|
||||
"inherits": "^2.0.1",
|
||||
"ipify": "^1.0.5",
|
||||
"jarson": "1.x",
|
||||
"json-storage": "2.x",
|
||||
"knex": "^0.6.23",
|
||||
|
@ -87,7 +88,7 @@
|
|||
"negotiator": "^0.5.1",
|
||||
"node-pre-gyp": "^0.6.4",
|
||||
"node-uuid": "1.x",
|
||||
"nodemailer": "1.x",
|
||||
"nodemailer": "^1.4.0",
|
||||
"nodemailer-mailgun-transport": "1.x",
|
||||
"oauth": "0.9.x",
|
||||
"oauth2orize": "git://github.com/coolaj86/oauth2orize.git#v1.0.1+scope.1",
|
||||
|
|
Loading…
Reference in New Issue