From 6b2b91ba26dd39139da8d0db7380b9d3e9dbd717 Mon Sep 17 00:00:00 2001 From: tigerbot Date: Wed, 18 Oct 2017 12:06:01 -0600 Subject: [PATCH] updated the documentation and validation for DDNS settings --- README.md | 47 ++++++++++++++++++++---- bin/goldilocks.js | 2 +- etc/goldilocks/goldilocks.example.yml | 18 +++++++--- lib/admin/config.js | 52 ++++++++++++++++++++++----- 4 files changed, 98 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 54b7931..e78e7da 100644 --- a/README.md +++ b/README.md @@ -403,17 +403,50 @@ tunnel_server: - 'api.tunnel.example.com' ``` -### tunnel +### DDNS -The tunnel client is meant to be run from behind a firewalls, carrier-grade NAT, -or otherwise inaccessible devices to allow them to be accessed publicly on the -internet. +The DDNS module watches the network environment of the unit and makes sure the +device is always accessible on the internet using the domains listed in the +config. If the device has a public address or if it can automatically set up +port forwarding the device will periodically check its public address to ensure +the DNS records always point to it. Otherwise it will to connect to a tunnel +server and set the DNS records to point to that server. -### ddns +The `loopback` setting specifies how the unit will check its public IP address +and whether connections can reach it. Currently only `tunnel@oauth3.org` is +supported. If the loopback setting is not defined it will default to using +`oauth3.org`. -TODO +The `tunnel` setting can be used to specify how to connect to the tunnel. +Currently only `tunnel@oauth3.org` is supported. The token specified in the +`tunnel` setting will be used to acquire the tokens that are used directly with +the tunnel server. If the tunnel setting is not defined it will default to try +using the tokens in the modules for the relevant domains. -### mdns +If a particular DDNS module has been disabled the device will still try to set +up port forwarding (and connect to a tunnel if that doesn't work), but the DNS +records will not be updated to point to the device. This is to allow a setup to +be tested before transitioning services between devices. + +```yaml +ddns: + disabled: false + loopback: + type: 'tunnel@oauth3.org' + domain: oauth3.org + tunnel: + type: 'tunnel@oauth3.org' + token: user_token_id + modules: + - type: 'dns@oauth3.org' + token: user_token_id + domains: + - www.example.com + - api.example.com + - test.example.com +``` + +### mDNS enabled by default diff --git a/bin/goldilocks.js b/bin/goldilocks.js index 516f6d7..c65ede3 100755 --- a/bin/goldilocks.js +++ b/bin/goldilocks.js @@ -311,7 +311,6 @@ function fillConfig(config, args) { config.debug = config.debug || args.debug; config.socks5 = config.socks5 || { enabled: false }; - config.ddns = config.ddns || { enabled: false }; // Use Object.assign to copy any real config values over the default values so we can // easily make sure all the fields we need exist . @@ -338,6 +337,7 @@ function fillConfig(config, args) { fillComponent('tcp', true); fillComponent('http', false); fillComponent('tls', false); + fillComponent('ddns', false); config.device = { hostname: require('os').hostname() }; diff --git a/etc/goldilocks/goldilocks.example.yml b/etc/goldilocks/goldilocks.example.yml index 2545f4d..2d1747d 100644 --- a/etc/goldilocks/goldilocks.example.yml +++ b/etc/goldilocks/goldilocks.example.yml @@ -91,8 +91,16 @@ tunnel_server: - 'tunnel.localhost.com' ddns: - enabled: true - domains: - - www.example.com - - api.example.com - - test.example.com + loopback: + type: 'tunnel@oauth3.org' + domain: oauth3.org + tunnel: + type: 'tunnel@oauth3.org' + token: user_token_id + modules: + - type: 'dns@oauth3.org' + token: user_token_id + domains: + - www.example.com + - api.example.com + - test.example.com diff --git a/lib/admin/config.js b/lib/admin/config.js index 682052c..3001f6f 100644 --- a/lib/admin/config.js +++ b/lib/admin/config.js @@ -48,6 +48,16 @@ var moduleSchemas = { , challenge_type: { type: 'string' } } } + + // the dns control modules for DDNS +, dns_oauth3_org: { + name: 'dns@oauth3.org' + , type: 'object' + , required: [ 'token' ] + , properties: { + token: { type: 'string' } + } + } }; // forward is basically the same as proxy, but specifies the relevant incoming port(s). // only allows for the raw transport layers (TCP/UDP) @@ -57,6 +67,10 @@ moduleSchemas.forward.properties.ports = { type: 'array', items: portSchema }; Object.keys(moduleSchemas).forEach(function (name) { var schema = moduleSchemas[name]; + if (schema.name) { + name = schema.name; + delete schema.name; + } schema.id = '/modules/'+name; schema.required = ['id', 'type'].concat(schema.required || []); schema.properties.id = { type: 'string' }; @@ -72,12 +86,13 @@ var moduleRefs = { , tls: [ 'proxy', 'acme' ].map(toSchemaRef) , tcp: [ 'forward' ].map(toSchemaRef) , udp: [ 'forward' ].map(toSchemaRef) +, ddns: [ 'dns@oauth3.org' ].map(toSchemaRef) }; function addDomainRequirement(itemSchema) { itemSchema.required = (itemSchema.required || []).concat('domains'); itemSchema.properties = itemSchema.properties || {}; - itemSchema.domains = { type: 'array', items: { type: 'string' }, minLength: 1}; + itemSchema.properties.domains = { type: 'array', items: { type: 'string' }, minLength: 1}; return itemSchema; } @@ -93,6 +108,7 @@ var domainSchema = { , properties: { tls: { type: 'array', items: { oneOf: moduleRefs.tls }} , http: { type: 'array', items: { oneOf: moduleRefs.http }} + , ddns: { type: 'array', items: { oneOf: moduleRefs.ddns }} } , additionalProperties: false } @@ -158,7 +174,23 @@ var mdnsSchema = { var ddnsSchema = { type: 'object' , properties: { - enabled: { type: 'boolean' } + loopback: { + type: 'object' + , required: [ 'type', 'domain' ] + , properties: { + type: { type: 'string', const: 'tunnel@oauth3.org' } + , domain: { type: 'string'} + } + } + , tunnel: { + type: 'object' + , required: [ 'type', 'token' ] + , properties: { + type: { type: 'string', const: 'tunnel@oauth3.org' } + , token: { type: 'string'} + } + } + , modules: { type: 'array', items: { oneOf: moduleRefs.ddns }} } }; var socks5Schema = { @@ -265,6 +297,7 @@ class DomainList extends IdList { dom.modules = { http: new ModuleList((dom.modules || {}).http) , tls: new ModuleList((dom.modules || {}).tls) + , ddns: new ModuleList((dom.modules || {}).ddns) }; }); } @@ -280,14 +313,16 @@ class DomainList extends IdList { var modLists = { http: new ModuleList() , tls: new ModuleList() + , ddns: new ModuleList() }; // We add these after instead of in the constructor to run the validation and manipulation // in the ModList add function since these are all new modules. - if (dom.modules && Array.isArray(dom.modules.http)) { - dom.modules.http.forEach(modLists.http.add, modLists.http); - } - if (dom.modules && Array.isArray(dom.modules.tls)) { - dom.modules.tls.forEach(modLists.tls.add, modLists.tls); + if (dom.modules) { + Object.keys(modLists).forEach(function (key) { + if (Array.isArray(dom.modules[key])) { + dom.modules[key].forEach(modLists[key].add, modLists[key]); + } + }); } dom.id = require('crypto').randomBytes(4).toString('hex'); @@ -306,6 +341,7 @@ class ConfigChanger { this.tls.modules = new ModuleList(this.tls.modules); this.tcp.modules = new ModuleList(this.tcp.modules); this.udp.modules = new ModuleList(this.udp.modules); + this.ddns.modules = new ModuleList(this.ddns.modules); } update(update) { @@ -314,7 +350,7 @@ class ConfigChanger { if (update.domains) { update.domains.forEach(self.domains.add, self.domains); } - [ 'http', 'tls', 'tcp', 'udp' ].forEach(function (name) { + [ 'http', 'tls', 'tcp', 'udp', 'ddns' ].forEach(function (name) { if (update[name] && update[name].modules) { update[name].modules.forEach(self[name].modules.add, self[name].modules); delete update[name].modules;