From 90f5eb11d5ab31f405519cc4066983cf27cbffbe Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 15 Mar 2017 03:40:35 -0600 Subject: [PATCH] show many more things --- admin/public/index.html | 164 ++++++++++++++++----- admin/public/js/app.js | 159 +++++++++++++++----- bin/goldilocks.js | 287 +++++++++++++++++++++---------------- package.json | 1 + packages/assets/org.oauth3 | 2 +- 5 files changed, 417 insertions(+), 196 deletions(-) diff --git a/admin/public/index.html b/admin/public/index.html index 1c004f7..408143b 100644 --- a/admin/public/index.html +++ b/admin/public/index.html @@ -18,24 +18,18 @@

Initializing... {{vm.hello}}

+
+ Configure Remote Server + + + + + + +
+ +
- - -
+ +
+ + +
+ todo: allow per-device authorization +
-
+
+

Server Device Name:

+ +

Server Working Directory:

+

Addresses:

+ + + + + + + + + + + + + +
InterfaceAddressFamilyScope
+ +

Managed Domains:

+ +
+ You don't have any domains with this account. +
+ Try a different account? +
+ + +
+ +
+

Enable Tunnel

+ + + + + + + + + + + + + + +
DomainSubDevices
+ +
+ +
+
+
+
+

Global Settings:


-
Pathname: - -
Modulename: {{module.$id}} -
{{key}}: - +
+
+

+
+

{{module.$id}}

+
+ :
+
+
+
+

Per-Domain Settings:

-
- -
-
Pathname: - -
Modulename: {{module.$id}} -
{{key}}: - +
+

+
+

+
+

{{module.$id}}

+
+ :
+
+
+
-
+

Fallback Settings:


-
Pathname: - -
Modulename: {{module.$id}} -
{{key}}: - +
+

+
+

{{module.$id}}

+
+ :
+
+
+
@@ -104,6 +193,7 @@ + diff --git a/admin/public/js/app.js b/admin/public/js/app.js index 5ea17b6..c11d14f 100644 --- a/admin/public/js/app.js +++ b/admin/public/js/app.js @@ -38,6 +38,91 @@ angular.module('com.daplie.cloud', [ 'org.oauth3' ]) }, 250); }; + vm.sortDnsRecords = function (a, b) { + if (a.sld !== b.sld) { + return a.sld > b.sld ? 1 : -1; + } + if (a.tld !== b.tld) { + return a.tld > b.tld ? 1 : -1; + } + // TODO normalize + a.sub = a.sub || ''; + b.sub = b.sub || ''; + if (a.sub !== b.sub) { + if (!a.sub) { + return -1; + } + if (!b.sub) { + return 1; + } + return a.sub > b.sub ? 1 : -1; + } + if (a.domain !== b.domain) { + if (!a.domain) { + return 1; + } + if (!b.domain) { + return -1; + } + } + }; + + vm.viewDomains = function (config, domains, dns) { + vm.dns = dns.slice(0); + vm.domains = domains.slice(0); + vm.dns = vm.dns.filter(function (record) { + if (-1 === [ 'A', 'AAAA', 'ANAME' ].indexOf(record.type)) { + return false; + } + if (record.device !== config.device.hostname) { + return false; + } + return true; + }); + vm.dns.forEach(function (r) { + vm.domains.forEach(function (d) { + if (r.zone === d.domain) { + r.sub = r.name.substr(0, r.name.length - (d.domain.length + 1)); + r.tld = d.tld; + r.sld = d.sld; + } + }); + }); + vm.dns = vm.dns.concat(vm.domains); + vm.dns.sort(vm.sortDnsRecords); + vm.dns.forEach(function (r) { + if (r.domain) { + return; + } + r.devices = r.devices || [ r.device ]; + + dns.forEach(function (r2) { + if (r.name !== r2.name) { + return; + } + if (-1 !== r.devices.indexOf(r2.device)) { + return; + } + r.devices.push(r2.device); + }); + }); + console.log('vm.dns'); + console.log(vm.dns); + /* + vm.domains.forEach(function (d) { + d.devices = []; + dns.forEach(function (r) { + // 0 === r.name.split('').reverse().join('').indexOf(d.domain.split('').reverse().join('')) + if (r.zone === d.domain) { + d.devices.push({ + name: r.device + }); + } + }); + }); + */ + }; + vm.authenticate = function () { // TODO authorization redirect /api/org.oauth3.consumer/authorization_redirect/:provider_uri @@ -46,44 +131,50 @@ angular.module('com.daplie.cloud', [ 'org.oauth3' ]) return oauth3.api('domains.list').then(function (domains) { console.info("domains owned", domains); - vm.domains = domains; - return OAUTH3.request({ - method: 'POST' - , url: 'https://' + vm.clientUri + '/api/com.daplie.caddy/init' - , session: session - , data: { - access_token: session.access_token - , refresh_token: session.refresh_token - , expires_in: session.expires_in - , scope: session.scope - , provider_uri: OAUTH3.uri.normalize(session.provider_uri) - , client_uri: vm.clientUri - , domains: domains.map(function (d) { - return { - id: d.id - , sub: d.sub - , sld: d.sld - , tld: d.tld - }; - }) - , jwk: null // TODO publish public key - } - }).then(function (resp) { - // TODO resp should contain a token - console.info('Initialized Goldilocks', resp); + return oauth3.api('dns.list').then(function (dns) { + console.info("dns records", dns); + return OAUTH3.request({ - method: 'GET' - , url: 'https://' + vm.clientUri + '/api/com.daplie.caddy/config' + method: 'POST' + , url: 'https://' + vm.clientUri + '/api/com.daplie.caddy/init' , session: session - }).then(function (configResp) { - console.log('config', configResp.data); - vm.config = configResp.data; - return resp; + , data: { + access_token: session.access_token + , refresh_token: session.refresh_token + , expires_in: session.expires_in + , scope: session.scope + , provider_uri: OAUTH3.uri.normalize(session.provider_uri) + , client_uri: vm.clientUri + , domains: domains.map(function (d) { + return { + id: d.id + , sub: d.sub + , sld: d.sld + , tld: d.tld + }; + }) + , jwk: null // TODO publish public key + } + }).then(function (resp) { + // TODO resp should contain a token + console.info('Initialized Goldilocks', resp); + return OAUTH3.request({ + method: 'GET' + , url: 'https://' + vm.clientUri + '/api/com.daplie.caddy/config' + , session: session + }).then(function (configResp) { + console.log('config', configResp.data); + vm.config = configResp.data; + //vm.config.ifaces + //vm.config.addresses = []; + vm.viewDomains(vm.config, domains, dns); + return resp; + }); + }, function (err) { + console.error(err); + window.alert("Initialization failed:" + err.message); }); - }, function (err) { - console.error(err); - window.alert("Initialization failed:" + err.message); }); }); }, function (err) { diff --git a/bin/goldilocks.js b/bin/goldilocks.js index 9fd2119..b926c3d 100755 --- a/bin/goldilocks.js +++ b/bin/goldilocks.js @@ -92,152 +92,191 @@ function createServer(port, _delete_me_, content, opts) { return new PromiseA(function (realResolve) { var app = require('../lib/app.js'); + var ipaddr = require('ipaddr.js'); + var addresses = []; - var directive = { - global: opts.global - , sites: opts.sites - , defaults: opts.defaults - , cwd: process.cwd() - }; - var server; - var insecureServer; - - function resolve() { - realResolve({ - plainServer: insecureServer - , server: server + Object.keys(opts.ifaces).forEach(function (ifacename) { + var iface = opts.ifaces[ifacename]; + iface.ipv4.forEach(function (ip) { + addresses.push(ip); + }); + iface.ipv6.forEach(function (ip) { + addresses.push(ip); }); - } - - // returns an instance of node-letsencrypt with additional helper methods - var webrootPath = require('os').tmpdir(); - var leChallengeFs = require('le-challenge-fs').create({ webrootPath: webrootPath }); - //var leChallengeSni = require('le-challenge-sni').create({ webrootPath: webrootPath }); - var leChallengeDdns = require('le-challenge-ddns').create({ ttl: 1 }); - var lex = require('greenlock-express').create({ - // set to https://acme-v01.api.letsencrypt.org/directory in production - server: opts.debug ? 'staging' : 'https://acme-v01.api.letsencrypt.org/directory' - - // If you wish to replace the default plugins, you may do so here - // - , challenges: { - 'http-01': leChallengeFs - , 'tls-sni-01': leChallengeFs // leChallengeSni - , 'dns-01': leChallengeDdns - } - , challengeType: (opts.tunnel ? 'http-01' : 'dns-01') - , store: require('le-store-certbot').create({ - webrootPath: webrootPath - , configDir: path.join((opts.homedir || '~'), 'letsencrypt', 'etc') - , homedir: opts.homedir - }) - , webrootPath: webrootPath - - // You probably wouldn't need to replace the default sni handler - // See https://git.daplie.com/Daplie/le-sni-auto if you think you do - //, sni: require('le-sni-auto').create({}) - - , approveDomains: approveDomains }); - var secureContexts = { - 'localhost.daplie.me': null - }; - opts.httpsOptions.SNICallback = function (sni, cb ) { - var tlsOptions; - console.log('[https] sni', sni); - - // Static Certs - if (/.*localhost.*\.daplie\.me/.test(sni.toLowerCase())) { - // TODO implement - if (!secureContexts[sni]) { - tlsOptions = require('localhost.daplie.me-certificates').mergeTlsOptions(sni, {}); - } - if (tlsOptions) { - secureContexts[sni] = tls.createSecureContext(tlsOptions); - } - cb(null, secureContexts[sni]); - return; + addresses.sort(function (a, b) { + if (a.family !== b.family) { + return 'IPv4' === a.family ? 1 : -1; } - // Dynamic Certs - lex.httpsOptions.SNICallback(sni, cb); - }; - server = https.createServer(opts.httpsOptions); - - server.on('error', function (err) { - if (opts.errorPort || opts.manualPort) { - showError(err, port); - process.exit(1); - return; - } - - opts.errorPort = err.toString(); - - return createServer(portFallback, null, content, opts).then(resolve); + return a.address > b.address ? 1 : -1; }); - server.listen(port, function () { - opts.port = port; - opts.redirectOptions.port = port; + addresses.forEach(function (addr) { + addr.range = ipaddr.parse(addr.address).range(); + }); - if (opts.livereload) { - opts.lrPort = opts.lrPort || lrPort; - var livereload = require('livereload'); - var server2 = livereload.createServer({ - https: opts.httpsOptions - , port: opts.lrPort - , exclusions: [ 'node_modules' ] + var Oauth3 = require('oauth3-cli'); + var oauth3 = Oauth3.create({ device: { hostname: opts.device } }); + return Oauth3.Devices.one(oauth3).then(function (device) { + return Oauth3.Devices.all(oauth3).then(function (devices) { + return { devices: devices, device: device.device || device }; + }); + }).then(function (devices) { + devices.device.secret = undefined; + console.log('devices'); + console.log(devices); + var directive = { + global: opts.global + , sites: opts.sites + , defaults: opts.defaults + , cwd: process.cwd() + , ifaces: opts.ifaces + , addresses: addresses + , devices: devices.devices + , device: devices.device + }; + var server; + var insecureServer; + + function resolve() { + realResolve({ + plainServer: insecureServer + , server: server }); - - console.info("[livereload] watching " + opts.pubdir); - console.warn("WARNING: If CPU usage spikes to 100% it's because too many files are being watched"); - // TODO create map of directories to watch from opts.sites and iterate over it - server2.watch(opts.pubdir); } - // if we haven't disabled insecure port - if ('false' !== opts.insecurePort) { - // and both ports are the default - if ((httpsPort === opts.port && httpPort === opts.insecurePort) - // or other case - || (httpPort !== opts.insecurePort && opts.port !== opts.insecurePort) - ) { - return createInsecureServer(opts.insecurePort, null, opts).then(function (_server) { - insecureServer = _server; - resolve(); - }); + // returns an instance of node-letsencrypt with additional helper methods + var webrootPath = require('os').tmpdir(); + var leChallengeFs = require('le-challenge-fs').create({ webrootPath: webrootPath }); + //var leChallengeSni = require('le-challenge-sni').create({ webrootPath: webrootPath }); + var leChallengeDdns = require('le-challenge-ddns').create({ ttl: 1 }); + var lex = require('greenlock-express').create({ + // set to https://acme-v01.api.letsencrypt.org/directory in production + server: opts.debug ? 'staging' : 'https://acme-v01.api.letsencrypt.org/directory' + + // If you wish to replace the default plugins, you may do so here + // + , challenges: { + 'http-01': leChallengeFs + , 'tls-sni-01': leChallengeFs // leChallengeSni + , 'dns-01': leChallengeDdns } - } + , challengeType: (opts.tunnel ? 'http-01' : 'dns-01') + , store: require('le-store-certbot').create({ + webrootPath: webrootPath + , configDir: path.join((opts.homedir || '~'), 'letsencrypt', 'etc') + , homedir: opts.homedir + }) + , webrootPath: webrootPath - opts.insecurePort = opts.port; - resolve(); - return; - }); + // You probably wouldn't need to replace the default sni handler + // See https://git.daplie.com/Daplie/le-sni-auto if you think you do + //, sni: require('le-sni-auto').create({}) - if ('function' === typeof app) { - app = app(directive); - } else if ('function' === typeof app.create) { - app = app.create(directive); - } + , approveDomains: approveDomains + }); - server.on('request', function (req, res) { - console.log('[' + req.method + '] ' + req.url); - if (!req.socket.encrypted && !/\/\.well-known\/acme-challenge\//.test(req.url)) { - opts.redirectApp(req, res); + var secureContexts = { + 'localhost.daplie.me': null + }; + opts.httpsOptions.SNICallback = function (sni, cb ) { + var tlsOptions; + console.log('[https] sni', sni); + + // Static Certs + if (/.*localhost.*\.daplie\.me/.test(sni.toLowerCase())) { + // TODO implement + if (!secureContexts[sni]) { + tlsOptions = require('localhost.daplie.me-certificates').mergeTlsOptions(sni, {}); + } + if (tlsOptions) { + secureContexts[sni] = tls.createSecureContext(tlsOptions); + } + cb(null, secureContexts[sni]); + return; + } + + // Dynamic Certs + lex.httpsOptions.SNICallback(sni, cb); + }; + server = https.createServer(opts.httpsOptions); + + server.on('error', function (err) { + if (opts.errorPort || opts.manualPort) { + showError(err, port); + process.exit(1); + return; + } + + opts.errorPort = err.toString(); + + return createServer(portFallback, null, content, opts).then(resolve); + }); + + server.listen(port, function () { + opts.port = port; + opts.redirectOptions.port = port; + + if (opts.livereload) { + opts.lrPort = opts.lrPort || lrPort; + var livereload = require('livereload'); + var server2 = livereload.createServer({ + https: opts.httpsOptions + , port: opts.lrPort + , exclusions: [ 'node_modules' ] + }); + + console.info("[livereload] watching " + opts.pubdir); + console.warn("WARNING: If CPU usage spikes to 100% it's because too many files are being watched"); + // TODO create map of directories to watch from opts.sites and iterate over it + server2.watch(opts.pubdir); + } + + // if we haven't disabled insecure port + if ('false' !== opts.insecurePort) { + // and both ports are the default + if ((httpsPort === opts.port && httpPort === opts.insecurePort) + // or other case + || (httpPort !== opts.insecurePort && opts.port !== opts.insecurePort) + ) { + return createInsecureServer(opts.insecurePort, null, opts).then(function (_server) { + insecureServer = _server; + resolve(); + }); + } + } + + opts.insecurePort = opts.port; + resolve(); return; - } + }); if ('function' === typeof app) { - app(req, res); - return; + app = app(directive); + } else if ('function' === typeof app.create) { + app = app.create(directive); } - res.end('not ready'); - }); + server.on('request', function (req, res) { + console.log('[' + req.method + '] ' + req.url); + if (!req.socket.encrypted && !/\/\.well-known\/acme-challenge\//.test(req.url)) { + opts.redirectApp(req, res); + return; + } - return PromiseA.resolve(app).then(function (_app) { - app = _app; + if ('function' === typeof app) { + app(req, res); + return; + } + + res.end('not ready'); + }); + + return PromiseA.resolve(app).then(function (_app) { + app = _app; + }); }); }); } diff --git a/package.json b/package.json index f3049c0..02abc86 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "greenlock": "git+https://git.daplie.com/Daplie/node-greenlock.git#master", "greenlock-express": "git+https://git.daplie.com/Daplie/greenlock-express.git#master", "httpolyglot": "^0.1.1", + "ipaddr.js": "git+https://github.com/whitequark/ipaddr.js.git#v1.3.0", "ipify": "^1.1.0", "js-yaml": "^3.8.1", "le-challenge-ddns": "git+https://git.daplie.com/Daplie/le-challenge-ddns.git#master", diff --git a/packages/assets/org.oauth3 b/packages/assets/org.oauth3 index 356a2d3..d646999 160000 --- a/packages/assets/org.oauth3 +++ b/packages/assets/org.oauth3 @@ -1 +1 @@ -Subproject commit 356a2d3131bcc6d0f6199a1d2039dcdcee0e3481 +Subproject commit d64699977e883871246d3fc062a384e88a554e2a