merge master
This commit is contained in:
		
						commit
						e00e0853e5
					
				
							
								
								
									
										15
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								README.md
									
									
									
									
									
								
							@ -1,3 +1,18 @@
 | 
			
		||||
<!-- BANNER_TPL_BEGIN -->
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
<a href="mailto:jobs@daplie.com">jobs@daplie.com</a> | [Invest in Daplie on Wefunder](https://daplie.com/invest/) | [Pre-order Cloud](https://daplie.com/preorder/), The World's First Home Server for Everyone
 | 
			
		||||
 | 
			
		||||
<!-- BANNER_TPL_END -->
 | 
			
		||||
 | 
			
		||||
walnut
 | 
			
		||||
======
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								etc/init/walnut.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								etc/init/walnut.conf
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
							
								
								
									
										0
									
								
								etc/letsencrypt/live/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								etc/letsencrypt/live/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										20
									
								
								etc/letsencrypt/live/www.example.com/fullchain.pem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								etc/letsencrypt/live/www.example.com/fullchain.pem
									
									
									
									
									
										Normal file
									
								
							@ -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-----
 | 
			
		||||
							
								
								
									
										27
									
								
								etc/letsencrypt/live/www.example.com/privkey.pem
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								etc/letsencrypt/live/www.example.com/privkey.pem
									
									
									
									
									
										Normal file
									
								
							@ -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-----
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -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();
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
@ -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');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										19
									
								
								install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								install.sh
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
@ -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: <ip-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);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
@ -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://'
 | 
			
		||||
 | 
			
		||||
@ -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: <<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;
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user