diff --git a/README.md b/README.md
index 378a0d8..fefbe6d 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,18 @@
+
+
+About Daplie: We're taking back the Internet!
+--------------
+
+Down with Google, Apple, and Facebook!
+
+We're re-decentralizing the web and making it read-write again - one home cloud system at a time.
+
+Tired of serving the Empire? Come join the Rebel Alliance:
+
+jobs@daplie.com | [Invest in Daplie on Wefunder](https://daplie.com/invest/) | [Pre-order Cloud](https://daplie.com/preorder/), The World's First Home Server for Everyone
+
+
+
walnut
======
diff --git a/boot/master.js b/boot/master.js
index 7aec33a..5442a39 100644
--- a/boot/master.js
+++ b/boot/master.js
@@ -9,27 +9,58 @@ console.info('arch:', process.arch);
console.info('platform:', process.platform);
console.info('\n\n\n[MASTER] Welcome to WALNUT!');
+var path = require('path');
var cluster = require('cluster');
//var minWorkers = 2;
var numCores = 2; // Math.max(minWorkers, require('os').cpus().length);
var workers = [];
-var config = require('../../config');
-var useCaddy = require('fs').existsSync(config.caddy.bin);
-var conf = {
- localPort: process.argv[2] || (useCaddy ? 4080 : 443) // system / local network
-, insecurePort: process.argv[3] || (useCaddy ? 80 : 80) // meh
-, externalPort: 443 // world accessible
+var config = {
+ externalPort: 443 // world accessible
, externalPortInsecure: 80 // world accessible
// TODO externalInsecurePort?
, locked: false // TODO XXX
, ipcKey: null
-, caddyfilepath: config.caddy.conf
+ // XXX
// TODO needs mappings from db
-, caddy: config.caddy
+ // TODO autoconfig Caddy caddy
+ // XXX
+, caddy: {
+ conf: __dirname + '/Caddyfile'
+ , bin: '/usr/local/bin/caddy'
+ , sitespath: path.join(__dirname, 'sites-enabled')
+ }
+, redirects: [
+ { "ip": false, "id": "*", "value": false } // default no-www
+
+ , { "ip": false, "id": "daplie.domains", "value": null }
+ , { "ip": false, "id": "*.daplie.domains", "value": false }
+ , { "ip": false, "id": "no.daplie.domains", "value": false }
+ , { "ip": false, "id": "*.no.daplie.domains", "value": false }
+ , { "ip": false, "id": "ns2.daplie.domains", "value": false }
+
+ , { "ip": true, "id": "maybe.daplie.domains", "value": null }
+ , { "ip": true, "id": "*.maybe.daplie.domains", "value": null }
+
+ , { "ip": true, "id": "www.daplie.domains", "value": null }
+ , { "ip": true, "id": "yes.daplie.domains", "value": true }
+ , { "ip": true, "id": "*.yes.daplie.domains", "value": true }
+ , { "ip": true, "id": "ns1.daplie.domains", "value": false }
+ ]
+ // TODO use sqlite3 or autogenerate ?
+, privkey: require('fs').readFileSync(__dirname + '/../../' + '/nsx.redirect-www.org.key.pem', 'ascii')
+, pubkey: require('fs').readFileSync(__dirname + '/../../' + '/nsx.redirect-www.org.key.pem.pub', 'ascii')
+// keys
+// letsencrypt
+// com.example.provider
+// com.example.consumer
};
+var useCaddy = require('fs').existsSync(config.caddy.bin);
var state = {};
var caddy;
+config.localPort = process.argv[2] || (useCaddy ? 4080 : 443); // system / local network
+config.insecurePort = process.argv[3] || (useCaddy ? 80 : 80); // meh
+
function fork() {
if (workers.length < numCores) {
workers.push(cluster.fork());
@@ -37,29 +68,26 @@ function fork() {
}
cluster.on('online', function (worker) {
- // TODO XXX Should these be configurable? If so, where?
- var certPaths = config.certPaths;
- var info;
- conf.ddns = config.ddns;
- conf.redirects = config.redirects;
-
console.info('[MASTER] Worker ' + worker.process.pid + ' is online');
fork();
+ // TODO XXX Should these be configurable? If so, where?
+ var certPaths = [
+ path.join(__dirname, 'certs', 'live')
+ , path.join(__dirname, 'letsencrypt', 'live')
+ ];
// TODO communicate config with environment vars?
- info = {
+ var info = {
type: 'walnut.init'
, conf: {
protocol: useCaddy ? 'http' : 'https'
- , externalPort: conf.externalPort
- , localPort: conf.localPort
- , insecurePort: conf.insecurePort
+ , externalPort: config.externalPort
+ , localPort: config.localPort
+ , insecurePort: config.insecurePort
, trustProxy: useCaddy ? true : false
, certPaths: useCaddy ? null : certPaths
, ipcKey: null
// TODO let this load after server is listening
- , redirects: config.redirects
- , ddns: config.ddns
, 'org.oauth3.consumer': config['org.oauth3.consumer']
, 'org.oauth3.provider': config['org.oauth3.provider']
, keys: config.keys
@@ -77,20 +105,14 @@ cluster.on('online', function (worker) {
// calls init if init has not been called
state.caddy = caddy;
state.workers = workers;
- require('../lib/master').touch(conf, state).then(function () {
+ require('../lib/master').touch(config, state).then(function () {
info.type = 'walnut.webserver.onrequest';
- info.conf.ipcKey = conf.ipcKey;
- info.conf.memstoreSock = conf.memstoreSock;
- info.conf.sqlite3Sock = conf.sqlite3Sock;
- // TODO get this from db config instead
- var config = require('../../config');
- info.conf.primaryNameserver = config.ddns.primaryNameserver;
- info.conf.nameservers = config.ddns.nameservers;
+ info.conf.ipcKey = config.ipcKey;
+ info.conf.memstoreSock = config.memstoreSock;
+ info.conf.sqlite3Sock = config.sqlite3Sock;
// TODO get this from db config instead
info.conf.privkey = config.privkey;
info.conf.pubkey = config.pubkey;
- info.conf.redirects = config.redirects;
- info.conf.ddns = config.ddns;
worker.send(info);
});
}
@@ -116,7 +138,7 @@ cluster.on('exit', function (worker, code, signal) {
fork();
if (useCaddy) {
- caddy = require('../lib/spawn-caddy').create(conf);
+ caddy = require('../lib/spawn-caddy').create(config);
// relies on { localPort, locked }
- caddy.spawn(conf);
+ caddy.spawn(config);
}
diff --git a/etc/init/walnut.conf b/etc/init/walnut.conf
new file mode 100644
index 0000000..6b10255
--- /dev/null
+++ b/etc/init/walnut.conf
@@ -0,0 +1,21 @@
+description "WALNUT, by Daplie"
+version "0.7"
+author "Daplie Inc"
+
+# Upstart has nothing in $PATH by default
+env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+
+# Keep the server running on crash or machine reboot
+respawn
+respawn limit 10 120
+start on runlevel [2345]
+
+# Start the server using spark and redirect output to log files
+script
+ DATE=`date '+%F_%H-%M-%S'`
+ cd /srv/walnut
+ mkdir -p logs
+ exec node ./core/bin/walnut \
+ > "./logs/access.${DATE}.log" \
+ 2> "./logs/error.${DATE}.log"
+end script
diff --git a/etc/letsencrypt/live/.gitkeep b/etc/letsencrypt/live/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/etc/letsencrypt/live/www.example.com/fullchain.pem b/etc/letsencrypt/live/www.example.com/fullchain.pem
new file mode 100644
index 0000000..3845f76
--- /dev/null
+++ b/etc/letsencrypt/live/www.example.com/fullchain.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSTCCAjECCQCyFV5BDXN2ADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJV
+UzENMAsGA1UECBMEVXRhaDEOMAwGA1UEBxMFUHJvdm8xIzAhBgNVBAoTGkFDTUUg
+U2lnbmluZyBBdXRob3JpdHkgSW5jMRQwEgYDVQQDEwtleGFtcGxlLmNvbTAeFw0x
+NTAxMjgwMjQyMjNaFw0xODAxMjcwMjQyMjNaMGYxCzAJBgNVBAYTAlVTMQ0wCwYD
+VQQIEwRVdGFoMQ4wDAYDVQQHEwVQcm92bzEVMBMGA1UEChMMQUNNRSBTZXJ2aWNl
+MSEwHwYDVQQDExhsb2NhbC5oZWxsb3dvcmxkMzAwMC5jb20wggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDIRtMZnPXfMT8ejvdndHUx0pBh0ToOyLgtXP6q
+PlziXUN8GJoQ1mxT2QNtkFHDoB5Nl2g3HDtEAGxNXlm8qMFrsbL772qidUN45oz0
+8dqOgOxENAwVqeA70YqqLdDhqW1qe8aRG5QyA43WGTiv18xZUZ6YG4lgHloqwNfK
+7KObACzTA5i8zuhl7z1eHm5va06pkXIdAbxNQwk9YLVM71B2pOF2jqIcsBZ/qrvD
+TdKQsXLIy9NBMHykNuD6uPTNuBnOxzo59RqlQMzaVxqHYzCkWszlXLC05I+Xhcg5
+JKXbnvl1A+aMqEnqjY8H2t4S6bDfeA1v6z1oL77/t6S4u2LhAgMBAAEwDQYJKoZI
+hvcNAQEFBQADggEBAAOlX8d05CbPjVeS91zfQBz8Ev0sXjdIElm6DMEmX9OzCuk+
++j5Ptn3g3bFraFqcDO9U9lgbYooiulCnlrYX46eyuCNfojF0Q1lGu3OGK1TYKmmK
+DUTLcZ/5NL3azhZJ4iOFw8Kv0OvwTvGRBoKM+VsRkX+wtTLyY9b5+C4ON4htu6Qm
+V0Tm25qRZQZx/kVZQolfYiR9mL2RBr+sQfJ4mfZJoFu+m99LPKgAc5qcbS1MmwNq
+l2INMkFtPxPWXzt1INdLtLHfA7LpMZBLsBfj7mPcr1xLBLpiFBT+Pvsgc+Y+LrGz
+QruRjat3F7YKt/M5Y1jSanvvyAKZNhTZoJeVQhI=
+-----END CERTIFICATE-----
diff --git a/etc/letsencrypt/live/www.example.com/privkey.pem b/etc/letsencrypt/live/www.example.com/privkey.pem
new file mode 100644
index 0000000..0d6a52b
--- /dev/null
+++ b/etc/letsencrypt/live/www.example.com/privkey.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAyEbTGZz13zE/Ho73Z3R1MdKQYdE6Dsi4LVz+qj5c4l1DfBia
+ENZsU9kDbZBRw6AeTZdoNxw7RABsTV5ZvKjBa7Gy++9qonVDeOaM9PHajoDsRDQM
+FangO9GKqi3Q4altanvGkRuUMgON1hk4r9fMWVGemBuJYB5aKsDXyuyjmwAs0wOY
+vM7oZe89Xh5ub2tOqZFyHQG8TUMJPWC1TO9QdqThdo6iHLAWf6q7w03SkLFyyMvT
+QTB8pDbg+rj0zbgZzsc6OfUapUDM2lcah2MwpFrM5VywtOSPl4XIOSSl2575dQPm
+jKhJ6o2PB9reEumw33gNb+s9aC++/7ekuLti4QIDAQABAoIBAQCaEvKAN+JnbEl0
+xuPKMSufwq/UsNOLYBtgurQoYIWwl7M9Gugbn/oCn2cluOtOJjZp1tsT2qI1dGXd
+N4Ktwt3abgCWFN/iT3gy4Jr8LbvH+aLJ7YiGegPJwTB+L3UMxf0o+EQQijj+KQHK
+/ehATzIkXMg3mKeczHzteyIIOPi8Wo+2r2cRsHv+7GUV5iMWp0UF1DbuA0yqqLw8
+8tMHcl4+IAamvaNhwWGvCVathu/TIIQCI4ZYT/+KZmzEMZ5Q+uZdtoj6jd8KGi/c
+EBZYi2X+DViej0kGuQncn3evMyfcSsyU70ytkFYgO9X/lk3al7vl6FjLiD8tb8T5
+axSuRdhhAoGBAOUGUCuW5mMkGPDDAkvvRlc5QxJk7Q4y1e7nFdaQ+Dz5a9KhGhav
+7ylC/+wxTqdKS4n+4rIFkPkphFULSmM1SNH/V3efjvJlZ5Xwaf9yNkr4f+A3oj4+
+BGFVt6zXI5pHsonWDf4mhJfNOAax+K8dC9hMCcuODGFbEpjp1fHOmu+NAoGBAN/d
+r0jJiivLMWvCoJpjQFYRiueVQf0cMjqsKzTg7DEB7aw0HAg5VZ7eMGmuBEKSCq4m
+Wt1Q/3MY2mt3Xb03/uMb82B/0Z7fDdFxVakhDElQ450njKZTkOrJy6Xau9z/yakX
+C5dGj+PKW2F29Qi+AWWCn2y/Y4B+knApMkTbWTGlAoGBAM33poP9GscQgKmOB3O5
+BZ6N7ecOAy9gwosvIbGtAML9YV206tx6bvDBw51sHptOsq6xHrkRTnb2Qn+lPsoH
+8qyBIe1//rO6RLVQ2FMWKQO1hnNkNTIa3h+XUi9L8EXgsw1JB56Ij3hh5EAJMGh0
+C+IDSE/bOQEZQZlpurHTp/1FAoGAcIkvYWa3B7bYfDRVqea0489jxXD5wDRZZ7c+
+7REYirZOdR+o69VunjNRoP5yc8iYh90OFm1uTLy0qAtUWxd4EVDuspbumG/GsX/I
+sQHE/GImWc0U3lPp6K1Sq2hewgTvqgnEsOIu2TLTZxZ2rVNevlnArNk1OtXl5W5A
+L+8YUmUCgYAHavun7/NKAoydxI/2/Uok4/eQ6BlwtfDG2Tlz1GZctkD3wju2ongA
+WFLckyZ1Y4eV77NgyxiVRzSDY6rGLtK/INK+GrP6D9AqWMS/JKVhwZYe5rz9TWuO
+ZwkbdVr+uwLwP+3DQltHev0yuVtRa+qLUssQdW6VRJZgQ2T9ue4MzQ==
+-----END RSA PRIVATE KEY-----
diff --git a/holepunch/beacon.js b/holepunch/beacon.js
deleted file mode 100644
index c5124bc..0000000
--- a/holepunch/beacon.js
+++ /dev/null
@@ -1,83 +0,0 @@
-'use strict';
-
-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')) {
- throw new Error('not yet supported \'' + port.protocol + '\'');
- }
-
- if (false === port.testable) {
- return PromiseA.resolve();
- }
-
- return loopbackHttps.create(ip, port.private, port.public).then(function () {
- console.log('success');
- }).catch(function (err) {
- // TODO test err
- return upnpForward(port).catch(function (err) {
- console.error('[ERROR] UPnP Port Forward');
- console.error(err);
- // TODO test err
- return pmpForward(port);
- }).then(function () {
- return loopbackHttps.create(ip, port.private, port.public);
- });
- });
-}
-
-// 1. update dyndns
-// 1.5. check ip every 5 min
-// 2. loopback test on ip for http / https / ssh
-// 3. if needed: discover gateway, map ports
-function beacon(hostnames, ports) {
- // test with
- // dig -p 53 @redirect-www.org pi.nadal.daplie.com A
- return updateIp({
- updater: 'redirect-www.org'
- , port: 65443
- , ddns: hostnames.map(function (hostname) {
- return { "name": hostname /*, "value": ipaddress, "type": "A"*/ };
- })
- }).then(function (data) {
- var promises = [];
-
- console.log("Updated DynDNS");
- console.log(data);
-
- ports.forEach(function (port) {
- promises.push(openPort(JSON.parse(data)[0].answers[0] || hostname, port));
- });
-
- return PromiseA.all(promises);
- }).then(function () {
- console.log('opened ports');
- });
-
-/*
- request.getAsync('http://checkip.hellabit.com').spread(function (resp, data) {
- console.log("External IP is", data);
- }).then(function () {
- return upnpForward().catch(function (err) {
- console.error('ERROR: UPnP failure:');
- console.error(err);
- });
- }).then(function () {
- return pmpForward().catch(function (err) {
- console.error('TODO: Notify user that their router is not compatible');
- });
- });
-
- // TODO test roundtrip
-*/
-}
-
-//setInterval(beacon, 5 * 60 * 1000);
-exports.run = beacon;
diff --git a/holepunch/helpers/pmp-forward.js b/holepunch/helpers/pmp-forward.js
deleted file mode 100644
index c105bf3..0000000
--- a/holepunch/helpers/pmp-forward.js
+++ /dev/null
@@ -1,81 +0,0 @@
-'use strict';
-
-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;
-
- 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);
-
- // create a "client" instance connecting to your local gateway
- var client = natpmp.connect(gw);
-
- function setPortForward() {
- // setup a new port mapping
- client.portMapping({
- private: port.private || port.public
- , public: port.public || port.private
- , ttl: port.ttl || 0 // 600
- }, function (err, info) {
- if (err) {
- reject(err);
- return;
- }
-
- console.log(info);
- // {
- // type: 'tcp',
- // epoch: 8922109,
- // private: 22,
- // public: 2222,
- // ...
- // }
- resolve();
- });
- }
-
- // explicitly ask for the current external IP address
- setTimeout(function () {
- client.externalIp(function (err, info) {
- if (err) throw err;
- console.log('Current external IP address: %s', info.ip.join('.'));
- setPortForward();
- });
- });
- });
- });
-};
-
-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();
-}
diff --git a/holepunch/helpers/upnp-forward.js b/holepunch/helpers/upnp-forward.js
deleted file mode 100644
index f42dfe0..0000000
--- a/holepunch/helpers/upnp-forward.js
+++ /dev/null
@@ -1,81 +0,0 @@
-'use strict';
-
-var PromiseA = require('bluebird').Promise;
-var natUpnp = require('nat-upnp');
-
-exports.upnpForward = function (port) {
- return natUpnp.createClient({ timeout: 1800 }).then(function (client) {
- return client.portMapping({
- public: port.public,
- private: port.private || port.public,
- ttl: port.ttl || 0
- })/*.then(function () {
- var promitter = client.getMappings();
-
- promitter.on('entry', function (entry, i) {
- console.log('entry', i);
- console.log(entry);
- }).then(function (mappings) {
- console.log('mappings');
- console.log(mappings);
- });
-
- return promitter;
- })*/;
- });
-};
-
-/*
-client.portUnmapping({
- public: 80
-});
-
-.findGateway().then(function (stuff) {
- console.log('[a] gateway');
- console.log(stuff.gateway);
- console.log('[a] address');
- console.log(stuff.address);
- }).then(function () {
- return client
-*/
-
-/*
-client.getMappings({ local: true }, function(err, results) {
- console.log('local mappings', results);
-});
-
-client.externalIp(function(err, ip) {
- console.log('ext-ip', ip);
-});
-*/
-
-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');
- console.error(err);
- throw err;
- });
-}
-
-if (require.main === module) {
- run();
- return;
-}
diff --git a/holepunch/loopback-https.js b/holepunch/loopback-https.js
deleted file mode 100644
index b5a098f..0000000
--- a/holepunch/loopback-https.js
+++ /dev/null
@@ -1,124 +0,0 @@
-'use strict';
-
-var https = require('https');
-var path = require('path');
-var fs = require('fs');
-var PromiseA = global.Promise || require('bluebird').Promise;
-
-exports.create = function (ip, localPort, externalPort) {
- return new PromiseA(function (resolve, reject) {
- var token = Math.random().toString(16).split('.')[1];
- var tokenPath = Math.random().toString(16).split('.')[1];
- var options;
- var server;
- var options;
- var certsPath = path.join(__dirname, 'certs', 'server');
- var caCertsPath = path.join(__dirname, 'certs', 'ca');
-
-
- function testConnection() {
- var awesome = false;
- var timetok;
- var webreq;
- var options = {
- // not hostname because we set headers.host on our own
- host: ip
- , headers: {
- // whatever's on the fake cert
- 'Host': 'redirect-www.org'
- }
- , port: externalPort
- , path: '/' + tokenPath
- , ca: fs.readFileSync(path.join(caCertsPath, 'my-root-ca.crt.pem'))
- };
- options.agent = new https.Agent(options);
-
- timetok = setTimeout(function () {
- reject(new Error("timed out while testing NAT loopback for port " + externalPort));
- }, 2000);
-
- function finishHim(err) {
- clearTimeout(timetok);
- server.close(function () {
- if (!err && awesome) {
- resolve();
- }
- });
-
- if (err || !awesome) {
- if (err) {
- reject(err);
- }
- else if (!awesome) {
- reject(new Error("loopback failed. Why? here's my best guess: "
- + "the ssl cert matched, so you've probably got two boxes and this isn't the right one"));
- }
- return;
- }
- }
-
- webreq = https.request(options, function(res) {
- res.on('data', function (resToken) {
- if (resToken.toString() === token) {
- awesome = true;
- return;
- }
- });
- res.on('error', function (err) {
- console.error('[ERROR] https.request.response');
- console.error(err);
- finishHim(new Error("loopback failed. Why? here's my best guess: "
- + "the connection was interrupted"));
- });
- res.on('end', function () {
- finishHim();
- });
- });
-
- webreq.on('error', function (err) {
- console.error('[ERROR] https.request');
- console.error(err);
- if (/ssl|cert|chain/i.test(err.message || err.toString())) {
- finishHim(new Error("loopback failed. Why? here's my best guess: "
- + "the ssl cert validation may have failed (might port-forward to the wrong box)"));
- } else {
- finishHim(new Error("loopback failed. Why? here's my best guess: "
- + "port forwarding isn't configured for " + ip + ":" + externalPort + " to " + localPort));
- }
- });
- webreq.end();
- }
-
- //
- // SSL Certificates
- //
- options = {
- key: fs.readFileSync(path.join(certsPath, 'my-server.key.pem'))
- , ca: [ fs.readFileSync(path.join(caCertsPath, 'my-root-ca.crt.pem')) ]
- , cert: fs.readFileSync(path.join(certsPath, 'my-server.crt.pem'))
- , requestCert: false
- , rejectUnauthorized: false
- };
-
- //
- // Serve an Express App securely with HTTPS
- //
- server = https.createServer(options);
- function listen(app) {
- server.on('request', app);
- server.listen(localPort, function () {
- localPort = server.address().port;
- setTimeout(testConnection, 2000);
- });
- }
-
- listen(function (req, res) {
- if (('/' + tokenPath) === req.url) {
- res.end(token);
- return;
- }
-
- res.end('loopback failure');
- });
- });
-};
diff --git a/install.sh b/install.sh
new file mode 100644
index 0000000..f91b31e
--- /dev/null
+++ b/install.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+sudo mkdir -p /srv/walnut/{certs,core,letsencrypt,lib}
+sudo mkdir -p /srv/walnut/packages/{api,pages,services}
+sudo chown -R $(whoami):$(whoami) /srv/walnut
+
+#git clone git@github.com:Daplie/walnut.git
+git clone https://github.com/Daplie/walnut.git /srv/walnut/core
+
+pushd /srv/walnut/core
+npm install
+sudo rsync -av /srv/walnut/core/etc/init/walnut.conf /etc/init/walnut.conf
+rsync -av /srv/walnut/core/etc/letsencrypt/ /srv/walnut/certs/
+
+popd
+mv /srv/walnut/core/node_modules /srv/walnut
+
+sudo service walnut stop
+sudo service walnut start
diff --git a/lib/ddns-updater.js b/lib/ddns-updater.js
deleted file mode 100644
index fd94146..0000000
--- a/lib/ddns-updater.js
+++ /dev/null
@@ -1,82 +0,0 @@
-'use strict';
-
-var updateIp = require('ddns-cli').update;
-
-/*
- * @param {string[]} hostnames - A list of hostnames
- * @param {Object[]} addresses - A list of { address: , family: <4|6> }
- */
-function update(services, hostnames, addresses, ddnsToken) {
- // TODO use not-yet-built API to get and store tokens
- // TODO use API to add and remove nameservers
- 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 = require('../dyndns-token.js').token;
- , "token": ddnsToken
- };
-
- 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({
- hostname: service.hostname
- , port: service.port
- , pathname: service.pathname
- , cacert: service.cacert
- , token: ddnsToken
- , 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 (services, hostnames, ddnsToken) {
- 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(services, hostnames, result.addresses, ddnsToken);
- });
-};
-
-if (require.main === module) {
- module.exports.update().then(function (results) {
- console.log('results');
- console.log(results);
- });
-}
diff --git a/lib/insecure-server.js b/lib/insecure-server.js
index 359fed9..afb2a28 100644
--- a/lib/insecure-server.js
+++ b/lib/insecure-server.js
@@ -46,6 +46,10 @@ module.exports.create = function (securePort, insecurePort, info, serverCallback
if (/redirect-www.org$/.test(host) && useAppInsecurely(req, res)) {
return true;
}
+ if (/^\/.well-known\/acme-challenge/.test(req.url) && useAppInsecurely(req, res)) {
+ console.log('exception for acme-challenge');
+ return true;
+ }
var escapeHtml = require('escape-html');
var newLocation = 'https://'
diff --git a/lib/ip-checker.js b/lib/ip-checker.js
deleted file mode 100644
index 9a05592..0000000
--- a/lib/ip-checker.js
+++ /dev/null
@@ -1,126 +0,0 @@
-"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: <>
- }, 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: <>
- }, 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;
diff --git a/lib/master.js b/lib/master.js
index 414146c..80ea606 100644
--- a/lib/master.js
+++ b/lib/master.js
@@ -4,42 +4,10 @@ var cluster = require('cluster');
var PromiseA = require('bluebird');
var memstore;
var sqlstore;
-var config;
// TODO
// var rootMasterKey;
-function updateIps() {
- console.log('[UPDATE IP]');
- var allMap = {};
- var hostnames = config.redirects.reduce(function (all, redirect) {
- if (redirect.ip && !allMap[redirect.id]) {
- allMap[redirect.id] = true;
- all.push(redirect.id);
- }
-
- return all;
- }, []);
-
- require('./ddns-updater').update(
- config.ddns.services
- , hostnames
- , config.ddns.token
- ).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);
- });
-}
-
function init(conf/*, state*/) {
- config = conf;
if (!conf.ipcKey) {
conf.ipcKey = require('crypto').randomBytes(16).toString('base64');
}
@@ -101,11 +69,6 @@ function init(conf/*, state*/) {
*/
});
- // TODO check the IP every 5 minutes and update it every hour
- setInterval(updateIps, 60 * 60 * 1000);
- // we don't want this to load right away (extra procesing time)
- setTimeout(updateIps, 1);
-
return promise;
}
diff --git a/lib/package-server.js b/lib/package-server.js
index 7721728..bb5863f 100644
--- a/lib/package-server.js
+++ b/lib/package-server.js
@@ -89,13 +89,19 @@ function loadPages(pkgConf, packagedPage, req, res, next) {
if (!packagedPage._promise_page) {
packagedPage._promise_page = new PromiseA(function (resolve, reject) {
fs.exists(pkgpath, function (exists) {
+ var staticServer;
+
if (!exists) {
reject(new Error("package '" + pkgpath + "' is registered but does not exist"));
return;
}
//console.log('[static mount]', pkgpath);
- resolve(require('serve-static')(pkgpath));
+ // https://github.com/expressjs/serve-static/issues/54
+ // https://github.com/pillarjs/send/issues/91
+ // https://example.com/.well-known/acme-challenge/xxxxxxxxxxxxxxx
+ staticServer = require('serve-static')(pkgpath, { dotfiles: undefined });
+ resolve(staticServer);
});
});
}
@@ -415,10 +421,27 @@ function mapToApp(opts, req, res, next) {
return;
}
+ // TODO .well-known can be an API (webfinger, letsencrypt, oauth3)
+ // or static (...???)
+
if (!router._re_api.test(req.url)) {
//console.log('[static router]');
//console.log(router._re_api, req.url);
- layerItUp(pkgConf, router, req, res, next);
+ layerItUp(pkgConf, router, req, res, function (err) {
+ if (err) {
+ next(err);
+ return;
+ }
+
+ if (/\/\.well-known([\/?]|$)/.test(req.url)) {
+ console.log('[TODO] handle .well-known as API');
+ // rewrite api as /api/org.ietf/.well-known ?
+ // pass through simply as /.well-known ?
+ // runApi(opts, router, req, res, next)
+ }
+
+ next();
+ });
return;
}
diff --git a/lib/worker.js b/lib/worker.js
index e165372..b2f9fc7 100644
--- a/lib/worker.js
+++ b/lib/worker.js
@@ -172,8 +172,6 @@ module.exports.create = function (webserver, info, state) {
, vhostPatterns: null
, server: webserver
, externalPort: info.conf.externalPort
- , primaryNameserver: info.conf.primaryNameserver
- , nameservers: info.conf.nameservers
, privkey: info.conf.privkey
, pubkey: info.conf.pubkey
, redirects: info.conf.redirects
diff --git a/package.json b/package.json
index 4a7abed..64dcf91 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,8 @@
"homepage": "https://github.com/Daplie/walnut",
"dependencies": {
"accepts": "^1.2.5",
- "authcodes": "git://bitbucket.org/daplie/authcodes.git",
+ "app-scoped-ids": "^1.0.1",
+ "authcodes": "git://github.com/Daplie/authcodes.git",
"authenticator": "^1.0.0",
"bluebird": "2.x",
"body-parser": "1.x",
@@ -81,7 +82,7 @@
"json-storage": "2.x",
"jsonwebtoken": "^5.4.0",
"lodash": "2.x",
- "masterquest-sqlite3": "git://github.com/coolaj86/masterquest-sqlite3.git",
+ "masterquest-sqlite3": "git://git.daplie.com/coolaj86/node-masterquest-sqlite3.git",
"media-typer": "^0.3.0",
"methods": "^1.1.1",
"mime": "^1.3.4",