rework dns api to take domainObject

the DNS backends require many different params, it's just easier to
pass them all together and have backends do whatever.

For example, route53 API requires the fqdn. Some other backends require just the
"part" to insert.

* location - location in the database (where app is installed)
* zoneName - the dns zone name
* domain - domain in the database (where apps are installed into)
* name/getName() - this returns the name to insert in the DNS based on zoneName/location
* fqdn - the fully resolved location in zoneName

verifyDnsConfig also takes a domain object even if it's not in db just so that we can
test even existing domain objects, if required. The IP param is removed since it's not
required.

for caas, we also don't need the fqdn hack in dnsConfig anymore
This commit is contained in:
Girish Ramakrishnan
2019-01-04 18:44:54 -08:00
parent 16c1622b1f
commit 65210ea91d
18 changed files with 627 additions and 485 deletions

View File

@@ -397,8 +397,8 @@ function get(appId, callback) {
for (let d of domainObjects) { domainObjectMap[d.domain] = d; } for (let d of domainObjects) { domainObjectMap[d.domain] = d; }
app.iconUrl = getIconUrlSync(app); app.iconUrl = getIconUrlSync(app);
app.fqdn = domains.fqdn(app.location, app.domain, domainObjectMap[app.domain].config); app.fqdn = domains.fqdn(app.location, domainObjectMap[app.domain]);
app.alternateDomains.forEach(function (ad) { ad.fqdn = domains.fqdn(ad.subdomain, ad.domain, domainObjectMap[ad.domain].config); }); app.alternateDomains.forEach(function (ad) { ad.fqdn = domains.fqdn(ad.subdomain, domainObjectMap[ad.domain]); });
callback(null, app); callback(null, app);
}); });
@@ -431,8 +431,8 @@ function getByIpAddress(ip, callback) {
for (let d of domainObjects) { domainObjectMap[d.domain] = d; } for (let d of domainObjects) { domainObjectMap[d.domain] = d; }
app.iconUrl = getIconUrlSync(app); app.iconUrl = getIconUrlSync(app);
app.fqdn = domains.fqdn(app.location, app.domain, domainObjectMap[app.domain].config); app.fqdn = domains.fqdn(app.location, domainObjectMap[app.domain]);
app.alternateDomains.forEach(function (ad) { ad.fqdn = domains.fqdn(ad.subdomain, ad.domain, domainObjectMap[ad.domain].config); }); app.alternateDomains.forEach(function (ad) { ad.fqdn = domains.fqdn(ad.subdomain, domainObjectMap[ad.domain]); });
callback(null, app); callback(null, app);
}); });
@@ -457,8 +457,8 @@ function getAll(callback) {
async.eachSeries(apps, function (app, iteratorDone) { async.eachSeries(apps, function (app, iteratorDone) {
app.iconUrl = getIconUrlSync(app); app.iconUrl = getIconUrlSync(app);
app.fqdn = domains.fqdn(app.location, app.domain, domainObjectMap[app.domain].config); app.fqdn = domains.fqdn(app.location, domainObjectMap[app.domain]);
app.alternateDomains.forEach(function (ad) { ad.fqdn = domains.fqdn(ad.subdomain, ad.domain, domainObjectMap[ad.domain].config); }); app.alternateDomains.forEach(function (ad) { ad.fqdn = domains.fqdn(ad.subdomain, domainObjectMap[ad.domain]); });
iteratorDone(null, app); iteratorDone(null, app);
}, function (error) { }, function (error) {

View File

@@ -292,7 +292,7 @@ function setDashboardDomain(domain, callback) {
if (error && error.reason === DomainsError.NOT_FOUND) return callback(new CloudronError(CloudronError.BAD_FIELD, 'No such domain')); if (error && error.reason === DomainsError.NOT_FOUND) return callback(new CloudronError(CloudronError.BAD_FIELD, 'No such domain'));
if (error) return callback(new CloudronError(CloudronError.INTERNAL_ERROR, error)); if (error) return callback(new CloudronError(CloudronError.INTERNAL_ERROR, error));
const fqdn = domains.fqdn(constants.ADMIN_LOCATION, domainObject.domain, domainObject.config); const fqdn = domains.fqdn(constants.ADMIN_LOCATION, domainObject);
config.setAdminDomain(domain); config.setAdminDomain(domain);
config.setAdminLocation(constants.ADMIN_LOCATION); config.setAdminLocation(constants.ADMIN_LOCATION);

View File

@@ -4,35 +4,38 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
var assert = require('assert'), var assert = require('assert'),
config = require('../config.js'), config = require('../config.js'),
debug = require('debug')('box:dns/caas'), debug = require('debug')('box:dns/caas'),
domains = require('../domains.js'),
DomainsError = require('../domains.js').DomainsError, DomainsError = require('../domains.js').DomainsError,
superagent = require('superagent'), superagent = require('superagent'),
util = require('util'); util = require('util'),
waitForDns = require('./waitfordns.js');
function getFqdn(subdomain, domain) { function getFqdn(location, domain) {
assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof domain, 'string');
return (subdomain === '') ? domain : subdomain + '-' + domain; return (location === '') ? domain : location + '-' + domain;
} }
function add(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
var fqdn = subdomain !== '' && type === 'TXT' ? subdomain + '.' + dnsConfig.fqdn : getFqdn(subdomain, dnsConfig.fqdn); const dnsConfig = domainObject.config;
debug('add: %s for zone %s of type %s with values %j', subdomain, dnsConfig.fqdn, type, values); let fqdn = location !== '' && type === 'TXT' ? location + '.' + domainObject.domain : getFqdn(location, domainObject.domain);
debug('add: %s for zone %s of type %s with values %j', location, domainObject.domain, type, values);
var data = { var data = {
type: type, type: type,
@@ -54,16 +57,16 @@ function add(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
var fqdn = subdomain !== '' && type === 'TXT' ? subdomain + '.' + dnsConfig.fqdn : getFqdn(subdomain, dnsConfig.fqdn); const dnsConfig = domainObject.config;
const fqdn = location !== '' && type === 'TXT' ? location + '.' + domainObject.domain : getFqdn(location, domainObject.domain);
debug('get: zoneName: %s subdomain: %s type: %s fqdn: %s', dnsConfig.fqdn, subdomain, type, fqdn); debug('get: zoneName: %s subdomain: %s type: %s fqdn: %s', domainObject.domain, location, type, fqdn);
superagent superagent
.get(config.apiServerOrigin() + '/api/v1/domains/' + fqdn) .get(config.apiServerOrigin() + '/api/v1/domains/' + fqdn)
@@ -77,26 +80,15 @@ function get(dnsConfig, zoneName, subdomain, type, callback) {
}); });
} }
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
add(dnsConfig, zoneName, subdomain, type, values, callback); const dnsConfig = domainObject.config;
} debug('del: %s for zone %s of type %s with values %j', location, domainObject.domain, type, values);
function del(dnsConfig, zoneName, subdomain, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object');
assert.strictEqual(typeof zoneName, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string');
assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function');
debug('del: %s for zone %s of type %s with values %j', subdomain, dnsConfig.fqdn, type, values);
var data = { var data = {
type: type, type: type,
@@ -104,7 +96,7 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
}; };
superagent superagent
.del(config.apiServerOrigin() + '/api/v1/domains/' + getFqdn(subdomain, dnsConfig.fqdn)) .del(config.apiServerOrigin() + '/api/v1/domains/' + getFqdn(location, domainObject.domain))
.query({ token: dnsConfig.token }) .query({ token: dnsConfig.token })
.send(data) .send(data)
.timeout(30 * 1000) .timeout(30 * 1000)
@@ -119,29 +111,42 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const fqdn = domains.fqdn(location, domainObject);
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config;
if (!dnsConfig.token || typeof dnsConfig.token !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'token must be a non-empty string')); if (!dnsConfig.token || typeof dnsConfig.token !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'token must be a non-empty string'));
const ip = '127.0.0.1';
var credentials = { var credentials = {
token: dnsConfig.token, token: dnsConfig.token,
fqdn: domain,
hyphenatedSubdomains: true // this will ensure we always use them, regardless of passed-in configs hyphenatedSubdomains: true // this will ensure we always use them, regardless of passed-in configs
}; };
const testSubdomain = 'cloudrontestdns'; const location = 'cloudrontestdns';
upsert(credentials, zoneName, testSubdomain, 'A', [ ip ], function (error, changeId) { upsert(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record added with change id %s', changeId); debug('verifyDnsConfig: Test A record added');
del(credentials, zoneName, testSubdomain, 'A', [ ip ], function (error) { del(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record removed again'); debug('verifyDnsConfig: Test A record removed again');

View File

@@ -4,7 +4,7 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
@@ -12,9 +12,11 @@ var assert = require('assert'),
async = require('async'), async = require('async'),
debug = require('debug')('box:dns/cloudflare'), debug = require('debug')('box:dns/cloudflare'),
dns = require('../native-dns.js'), dns = require('../native-dns.js'),
domains = require('../domains.js'),
DomainsError = require('../domains.js').DomainsError, DomainsError = require('../domains.js').DomainsError,
superagent = require('superagent'), superagent = require('superagent'),
util = require('util'), util = require('util'),
waitForDns = require('./waitfordns.js'),
_ = require('underscore'); _ = require('underscore');
// we are using latest v4 stable API https://api.cloudflare.com/#getting-started-endpoints // we are using latest v4 stable API https://api.cloudflare.com/#getting-started-endpoints
@@ -54,16 +56,13 @@ function getZoneByName(dnsConfig, zoneName, callback) {
} }
// gets records filtered by zone, type and fqdn // gets records filtered by zone, type and fqdn
function getDnsRecords(dnsConfig, zoneId, zoneName, subdomain, type, callback) { function getDnsRecords(dnsConfig, zoneId, fqdn, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof dnsConfig, 'object');
assert.strictEqual(typeof zoneId, 'string'); assert.strictEqual(typeof zoneId, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof fqdn, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
var fqdn = subdomain === '' ? zoneName : subdomain + '.' + zoneName;
superagent.get(CLOUDFLARE_ENDPOINT + '/zones/' + zoneId + '/dns_records') superagent.get(CLOUDFLARE_ENDPOINT + '/zones/' + zoneId + '/dns_records')
.set('X-Auth-Key',dnsConfig.token) .set('X-Auth-Key',dnsConfig.token)
.set('X-Auth-Email',dnsConfig.email) .set('X-Auth-Email',dnsConfig.email)
@@ -79,24 +78,25 @@ function getDnsRecords(dnsConfig, zoneId, zoneName, subdomain, type, callback) {
}); });
} }
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
var fqdn = subdomain === '' ? zoneName : subdomain + '.' + zoneName; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
fqdn = domains.fqdn(location, domainObject);
debug('upsert: %s for zone %s of type %s with values %j', subdomain, zoneName, type, values); debug('upsert: %s for zone %s of type %s with values %j', fqdn, zoneName, type, values);
getZoneByName(dnsConfig, zoneName, function(error, result) { getZoneByName(dnsConfig, zoneName, function(error, result) {
if (error) return callback(error); if (error) return callback(error);
let zoneId = result.id; let zoneId = result.id;
getDnsRecords(dnsConfig, zoneId, zoneName, subdomain, type, function (error, dnsRecords) { getDnsRecords(dnsConfig, zoneId, fqdn, type, function (error, dnsRecords) {
if (error) return callback(error); if (error) return callback(error);
let i = 0; // // used to track available records to update instead of create let i = 0; // // used to track available records to update instead of create
@@ -119,7 +119,7 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
}; };
if (i >= dnsRecords.length) { // create a new record if (i >= dnsRecords.length) { // create a new record
debug(`upsert: Adding new record subdomain: ${subdomain}, zoneName: ${zoneName} proxied: false`); debug(`upsert: Adding new record fqdn: ${fqdn}, zoneName: ${zoneName} proxied: false`);
superagent.post(CLOUDFLARE_ENDPOINT + '/zones/' + zoneId + '/dns_records') superagent.post(CLOUDFLARE_ENDPOINT + '/zones/' + zoneId + '/dns_records')
.set('X-Auth-Key', dnsConfig.token) .set('X-Auth-Key', dnsConfig.token)
@@ -135,7 +135,7 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
} else { // replace existing record } else { // replace existing record
data.proxied = dnsRecords[i].proxied; // preserve proxied parameter data.proxied = dnsRecords[i].proxied; // preserve proxied parameter
debug(`upsert: Updating existing record subdomain: ${subdomain}, zoneName: ${zoneName} proxied: ${data.proxied}`); debug(`upsert: Updating existing record fqdn: ${fqdn}, zoneName: ${zoneName} proxied: ${data.proxied}`);
superagent.put(CLOUDFLARE_ENDPOINT + '/zones/' + zoneId + '/dns_records/' + dnsRecords[i].id) superagent.put(CLOUDFLARE_ENDPOINT + '/zones/' + zoneId + '/dns_records/' + dnsRecords[i].id)
.set('X-Auth-Key', dnsConfig.token) .set('X-Auth-Key', dnsConfig.token)
@@ -156,17 +156,20 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
getZoneByName(dnsConfig, zoneName, function(error, result){ const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
fqdn = domains.fqdn(location, domainObject);
getZoneByName(dnsConfig, zoneName, function(error, zone) {
if (error) return callback(error); if (error) return callback(error);
getDnsRecords(dnsConfig, result.id, zoneName, subdomain, type, function(error, result) { getDnsRecords(dnsConfig, zone.id, fqdn, type, function (error, result) {
if (error) return callback(error); if (error) return callback(error);
var tmp = result.map(function (record) { return record.content; }); var tmp = result.map(function (record) { return record.content; });
@@ -177,18 +180,21 @@ function get(dnsConfig, zoneName, subdomain, type, callback) {
}); });
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
getZoneByName(dnsConfig, zoneName, function(error, result){ const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
fqdn = domains.fqdn(location, domainObject);
getZoneByName(dnsConfig, zoneName, function(error, zone) {
if (error) return callback(error); if (error) return callback(error);
getDnsRecords(dnsConfig, result.id, zoneName, subdomain, type, function(error, result) { getDnsRecords(dnsConfig, zone.id, fqdn, type, function(error, result) {
if (error) return callback(error); if (error) return callback(error);
if (result.length === 0) return callback(null); if (result.length === 0) return callback(null);
@@ -221,16 +227,31 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const fqdn = domains.fqdn(location, domainObject);
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName;
if (!dnsConfig.token || typeof dnsConfig.token !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'token must be a non-empty string')); if (!dnsConfig.token || typeof dnsConfig.token !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'token must be a non-empty string'));
if (!dnsConfig.email || typeof dnsConfig.email !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'email must be a non-empty string')); if (!dnsConfig.email || typeof dnsConfig.email !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'email must be a non-empty string'));
const ip = '127.0.0.1';
var credentials = { var credentials = {
token: dnsConfig.token, token: dnsConfig.token,
email: dnsConfig.email email: dnsConfig.email
@@ -242,22 +263,22 @@ function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) {
if (error && error.code === 'ENOTFOUND') return callback(new DomainsError(DomainsError.BAD_FIELD, 'Unable to resolve nameservers for this domain')); if (error && error.code === 'ENOTFOUND') return callback(new DomainsError(DomainsError.BAD_FIELD, 'Unable to resolve nameservers for this domain'));
if (error || !nameservers) return callback(new DomainsError(DomainsError.BAD_FIELD, error ? error.message : 'Unable to get nameservers')); if (error || !nameservers) return callback(new DomainsError(DomainsError.BAD_FIELD, error ? error.message : 'Unable to get nameservers'));
getZoneByName(dnsConfig, zoneName, function(error, result) { getZoneByName(dnsConfig, zoneName, function(error, zone) {
if (error) return callback(error); if (error) return callback(error);
if (!_.isEqual(result.name_servers.sort(), nameservers.sort())) { if (!_.isEqual(zone.name_servers.sort(), nameservers.sort())) {
debug('verifyDnsConfig: %j and %j do not match', nameservers, result.name_servers); debug('verifyDnsConfig: %j and %j do not match', nameservers, zone.name_servers);
return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Cloudflare')); return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Cloudflare'));
} }
const testSubdomain = 'cloudrontestdns'; const location = 'cloudrontestdns';
upsert(credentials, zoneName, testSubdomain, 'A', [ ip ], function (error, changeId) { upsert(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record added with change id %s', changeId); debug('verifyDnsConfig: Test A record added');
del(dnsConfig, zoneName, testSubdomain, 'A', [ ip ], function (error) { del(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record removed again'); debug('verifyDnsConfig: Test A record removed again');

View File

@@ -4,7 +4,7 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
@@ -12,10 +12,12 @@ var assert = require('assert'),
async = require('async'), async = require('async'),
debug = require('debug')('box:dns/digitalocean'), debug = require('debug')('box:dns/digitalocean'),
dns = require('../native-dns.js'), dns = require('../native-dns.js'),
domains = require('../domains.js'),
DomainsError = require('../domains.js').DomainsError, DomainsError = require('../domains.js').DomainsError,
safe = require('safetydance'), safe = require('safetydance'),
superagent = require('superagent'), superagent = require('superagent'),
util = require('util'); util = require('util'),
waitForDns = require('./waitfordns.js');
var DIGITALOCEAN_ENDPOINT = 'https://api.digitalocean.com'; var DIGITALOCEAN_ENDPOINT = 'https://api.digitalocean.com';
@@ -23,10 +25,10 @@ function formatError(response) {
return util.format('DigitalOcean DNS error [%s] %j', response.statusCode, response.body); return util.format('DigitalOcean DNS error [%s] %j', response.statusCode, response.body);
} }
function getInternal(dnsConfig, zoneName, subdomain, type, callback) { function getInternal(dnsConfig, zoneName, name, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof dnsConfig, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof zoneName, 'string');
assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof name, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
@@ -45,7 +47,7 @@ function getInternal(dnsConfig, zoneName, subdomain, type, callback) {
if (result.statusCode !== 200) return callback(new DomainsError(DomainsError.EXTERNAL_ERROR, formatError(result))); if (result.statusCode !== 200) return callback(new DomainsError(DomainsError.EXTERNAL_ERROR, formatError(result)));
matchingRecords = matchingRecords.concat(result.body.domain_records.filter(function (record) { matchingRecords = matchingRecords.concat(result.body.domain_records.filter(function (record) {
return (record.type === type && record.name === subdomain); return (record.type === type && record.name === name);
})); }));
nextPage = (result.body.links && result.body.links.pages) ? result.body.links.pages.next : null; nextPage = (result.body.links && result.body.links.pages) ? result.body.links.pages.next : null;
@@ -61,19 +63,20 @@ function getInternal(dnsConfig, zoneName, subdomain, type, callback) {
}); });
} }
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
debug('upsert: %s for zone %s of type %s with values %j', subdomain, zoneName, type, values); debug('upsert: %s for zone %s of type %s with values %j', name, zoneName, type, values);
getInternal(dnsConfig, zoneName, subdomain, type, function (error, result) { getInternal(dnsConfig, zoneName, name, type, function (error, result) {
if (error) return callback(error); if (error) return callback(error);
// used to track available records to update instead of create // used to track available records to update instead of create
@@ -89,7 +92,7 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
var data = { var data = {
type: type, type: type,
name: subdomain, name: name,
data: value, data: value,
priority: priority, priority: priority,
ttl: 1 ttl: 1
@@ -133,16 +136,17 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
getInternal(dnsConfig, zoneName, subdomain, type, function (error, result) { getInternal(dnsConfig, zoneName, name, type, function (error, result) {
if (error) return callback(error); if (error) return callback(error);
// We only return the value string // We only return the value string
@@ -154,17 +158,18 @@ function get(dnsConfig, zoneName, subdomain, type, callback) {
}); });
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
getInternal(dnsConfig, zoneName, subdomain, type, function (error, result) { getInternal(dnsConfig, zoneName, name, type, function (error, result) {
if (error) return callback(error); if (error) return callback(error);
if (result.length === 0) return callback(null); if (result.length === 0) return callback(null);
@@ -193,15 +198,30 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const fqdn = domains.fqdn(location, domainObject);
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName;
if (!dnsConfig.token || typeof dnsConfig.token !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'token must be a non-empty string')); if (!dnsConfig.token || typeof dnsConfig.token !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'token must be a non-empty string'));
const ip = '127.0.0.1';
var credentials = { var credentials = {
token: dnsConfig.token token: dnsConfig.token
}; };
@@ -217,14 +237,14 @@ function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) {
return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Digital Ocean')); return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Digital Ocean'));
} }
const testSubdomain = 'cloudrontestdns'; const location = 'cloudrontestdns';
upsert(credentials, zoneName, testSubdomain, 'A', [ ip ], function (error, changeId) { upsert(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record added with change id %s', changeId); debug('verifyDnsConfig: Test A record added');
del(dnsConfig, zoneName, testSubdomain, 'A', [ ip ], function (error) { del(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record removed again'); debug('verifyDnsConfig: Test A record removed again');

View File

@@ -4,16 +4,18 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
var assert = require('assert'), var assert = require('assert'),
debug = require('debug')('box:dns/gandi'), debug = require('debug')('box:dns/gandi'),
dns = require('../native-dns.js'), dns = require('../native-dns.js'),
domains = require('../domains.js'),
DomainsError = require('../domains.js').DomainsError, DomainsError = require('../domains.js').DomainsError,
superagent = require('superagent'), superagent = require('superagent'),
util = require('util'); util = require('util'),
waitForDns = require('./waitfordns.js');
var GANDI_API = 'https://dns.api.gandi.net/api/v5'; var GANDI_API = 'https://dns.api.gandi.net/api/v5';
@@ -21,24 +23,25 @@ function formatError(response) {
return util.format(`Gandi DNS error [${response.statusCode}] ${response.body.message}`); return util.format(`Gandi DNS error [${response.statusCode}] ${response.body.message}`);
} }
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
debug(`upsert: ${subdomain} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`); debug(`upsert: ${name} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`);
var data = { var data = {
'rrset_ttl': 300, // this is the minimum allowed 'rrset_ttl': 300, // this is the minimum allowed
'rrset_values': values // for mx records, value is already of the '<priority> <server>' format 'rrset_values': values // for mx records, value is already of the '<priority> <server>' format
}; };
superagent.put(`${GANDI_API}/domains/${zoneName}/records/${subdomain}/${type}`) superagent.put(`${GANDI_API}/domains/${zoneName}/records/${name}/${type}`)
.set('X-Api-Key', dnsConfig.token) .set('X-Api-Key', dnsConfig.token)
.timeout(30 * 1000) .timeout(30 * 1000)
.send(data) .send(data)
@@ -52,18 +55,19 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
debug(`get: ${subdomain} in zone ${zoneName} of type ${type}`); debug(`get: ${name} in zone ${zoneName} of type ${type}`);
superagent.get(`${GANDI_API}/domains/${zoneName}/records/${subdomain}/${type}`) superagent.get(`${GANDI_API}/domains/${zoneName}/records/${name}/${type}`)
.set('X-Api-Key', dnsConfig.token) .set('X-Api-Key', dnsConfig.token)
.timeout(30 * 1000) .timeout(30 * 1000)
.end(function (error, result) { .end(function (error, result) {
@@ -78,19 +82,20 @@ function get(dnsConfig, zoneName, subdomain, type, callback) {
}); });
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
debug(`del: ${subdomain} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`); debug(`del: ${name} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`);
superagent.del(`${GANDI_API}/domains/${zoneName}/records/${subdomain}/${type}`) superagent.del(`${GANDI_API}/domains/${zoneName}/records/${name}/${type}`)
.set('X-Api-Key', dnsConfig.token) .set('X-Api-Key', dnsConfig.token)
.timeout(30 * 1000) .timeout(30 * 1000)
.end(function (error, result) { .end(function (error, result) {
@@ -105,19 +110,34 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const fqdn = domains.fqdn(location, domainObject);
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName;
if (!dnsConfig.token || typeof dnsConfig.token !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'token must be a non-empty string')); if (!dnsConfig.token || typeof dnsConfig.token !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'token must be a non-empty string'));
var credentials = { var credentials = {
token: dnsConfig.token token: dnsConfig.token
}; };
const ip = '127.0.0.1';
if (process.env.BOX_ENV === 'test') return callback(null, credentials); // this shouldn't be here if (process.env.BOX_ENV === 'test') return callback(null, credentials); // this shouldn't be here
dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) { dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) {
@@ -129,14 +149,14 @@ function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) {
return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Gandi')); return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Gandi'));
} }
const testSubdomain = 'cloudrontestdns'; const location = 'cloudrontestdns';
upsert(credentials, zoneName, testSubdomain, 'A', [ ip ], function (error, changeId) { upsert(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record added with change id %s', changeId); debug('verifyDnsConfig: Test A record added');
del(dnsConfig, zoneName, testSubdomain, 'A', [ ip ], function (error) { del(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record removed again'); debug('verifyDnsConfig: Test A record removed again');

View File

@@ -4,16 +4,18 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
var assert = require('assert'), var assert = require('assert'),
debug = require('debug')('box:dns/gcdns'), debug = require('debug')('box:dns/gcdns'),
dns = require('../native-dns.js'), dns = require('../native-dns.js'),
domains = require('../domains.js'),
DomainsError = require('../domains.js').DomainsError, DomainsError = require('../domains.js').DomainsError,
GCDNS = require('@google-cloud/dns'), GCDNS = require('@google-cloud/dns'),
util = require('util'), util = require('util'),
waitForDns = require('./waitfordns.js'),
_ = require('underscore'); _ = require('underscore');
function getDnsCredentials(dnsConfig) { function getDnsCredentials(dnsConfig) {
@@ -55,22 +57,23 @@ function getZoneByName(dnsConfig, zoneName, callback) {
}); });
} }
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
debug('add: %s for zone %s of type %s with values %j', subdomain, zoneName, type, values); const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
fqdn = domains.fqdn(location, domainObject);
debug('add: %s for zone %s of type %s with values %j', fqdn, zoneName, type, values);
getZoneByName(getDnsCredentials(dnsConfig), zoneName, function (error, zone) { getZoneByName(getDnsCredentials(dnsConfig), zoneName, function (error, zone) {
if (error) return callback(error); if (error) return callback(error);
var domain = (subdomain ? subdomain + '.' : '') + zoneName + '.'; zone.getRecords({ type: type, name: fqdn + '.' }, function (error, oldRecords) {
zone.getRecords({ type: type, name: domain }, function (error, oldRecords) {
if (error && error.code === 403) return callback(new DomainsError(DomainsError.ACCESS_DENIED, error.message)); if (error && error.code === 403) return callback(new DomainsError(DomainsError.ACCESS_DENIED, error.message));
if (error) { if (error) {
debug('upsert->zone.getRecords', error); debug('upsert->zone.getRecords', error);
@@ -78,12 +81,12 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
} }
var newRecord = zone.record(type, { var newRecord = zone.record(type, {
name: domain, name: fqdn + '.',
data: values, data: values,
ttl: 1 ttl: 1
}); });
zone.createChange({ delete: oldRecords, add: newRecord }, function(error, change) { zone.createChange({ delete: oldRecords, add: newRecord }, function(error /*, change */) {
if (error && error.code === 403) return callback(new DomainsError(DomainsError.ACCESS_DENIED, error.message)); if (error && error.code === 403) return callback(new DomainsError(DomainsError.ACCESS_DENIED, error.message));
if (error && error.code === 412) return callback(new DomainsError(DomainsError.STILL_BUSY, error.message)); if (error && error.code === 412) return callback(new DomainsError(DomainsError.STILL_BUSY, error.message));
if (error) { if (error) {
@@ -97,18 +100,21 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
fqdn = domains.fqdn(location, domainObject);
getZoneByName(getDnsCredentials(dnsConfig), zoneName, function (error, zone) { getZoneByName(getDnsCredentials(dnsConfig), zoneName, function (error, zone) {
if (error) return callback(error); if (error) return callback(error);
var params = { var params = {
name: (subdomain ? subdomain + '.' : '') + zoneName + '.', name: fqdn + '.',
type: type type: type
}; };
@@ -122,20 +128,21 @@ function get(dnsConfig, zoneName, subdomain, type, callback) {
}); });
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
fqdn = domains.fqdn(location, domainObject);
getZoneByName(getDnsCredentials(dnsConfig), zoneName, function (error, zone) { getZoneByName(getDnsCredentials(dnsConfig), zoneName, function (error, zone) {
if (error) return callback(error); if (error) return callback(error);
var domain = (subdomain ? subdomain + '.' : '') + zoneName + '.'; zone.getRecords({ type: type, name: fqdn + '.' }, function(error, oldRecords) {
zone.getRecords({ type: type, name: domain }, function(error, oldRecords) {
if (error && error.code === 403) return callback(new DomainsError(DomainsError.ACCESS_DENIED, error.message)); if (error && error.code === 403) return callback(new DomainsError(DomainsError.ACCESS_DENIED, error.message));
if (error) { if (error) {
debug('del->zone.getRecords', error); debug('del->zone.getRecords', error);
@@ -156,19 +163,35 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const fqdn = domains.fqdn(location, domainObject);
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName;
if (typeof dnsConfig.projectId !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'projectId must be a string')); if (typeof dnsConfig.projectId !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'projectId must be a string'));
if (!dnsConfig.credentials || typeof dnsConfig.credentials !== 'object') return callback(new DomainsError(DomainsError.BAD_FIELD, 'credentials must be an object')); if (!dnsConfig.credentials || typeof dnsConfig.credentials !== 'object') return callback(new DomainsError(DomainsError.BAD_FIELD, 'credentials must be an object'));
if (typeof dnsConfig.credentials.client_email !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'credentials.client_email must be a string')); if (typeof dnsConfig.credentials.client_email !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'credentials.client_email must be a string'));
if (typeof dnsConfig.credentials.private_key !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'credentials.private_key must be a string')); if (typeof dnsConfig.credentials.private_key !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'credentials.private_key must be a string'));
var credentials = getDnsCredentials(dnsConfig); var credentials = getDnsCredentials(dnsConfig);
const ip = '127.0.0.1';
if (process.env.BOX_ENV === 'test') return callback(null, credentials); // this shouldn't be here if (process.env.BOX_ENV === 'test') return callback(null, credentials); // this shouldn't be here
dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) { dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) {
@@ -184,14 +207,14 @@ function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) {
return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Google Cloud DNS')); return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Google Cloud DNS'));
} }
const testSubdomain = 'cloudrontestdns'; const location = 'cloudrontestdns';
upsert(credentials, zoneName, testSubdomain, 'A', [ ip ], function (error, changeId) { upsert(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record added with change id %s', changeId); debug('verifyDnsConfig: Test A record added');
del(dnsConfig, zoneName, testSubdomain, 'A', [ ip ], function (error) { del(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record removed again'); debug('verifyDnsConfig: Test A record removed again');

View File

@@ -4,16 +4,18 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
var assert = require('assert'), var assert = require('assert'),
debug = require('debug')('box:dns/godaddy'), debug = require('debug')('box:dns/godaddy'),
dns = require('../native-dns.js'), dns = require('../native-dns.js'),
domains = require('../domains.js'),
DomainsError = require('../domains.js').DomainsError, DomainsError = require('../domains.js').DomainsError,
superagent = require('superagent'), superagent = require('superagent'),
util = require('util'); util = require('util'),
waitForDns = require('./waitfordns.js');
// const GODADDY_API_OTE = 'https://api.ote-godaddy.com/v1/domains'; // const GODADDY_API_OTE = 'https://api.ote-godaddy.com/v1/domains';
const GODADDY_API = 'https://api.godaddy.com/v1/domains'; const GODADDY_API = 'https://api.godaddy.com/v1/domains';
@@ -27,17 +29,18 @@ function formatError(response) {
return util.format(`GoDaddy DNS error [${response.statusCode}] ${response.body.message}`); return util.format(`GoDaddy DNS error [${response.statusCode}] ${response.body.message}`);
} }
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
debug(`upsert: ${subdomain} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`); debug(`upsert: ${name} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`);
var records = [ ]; var records = [ ];
values.forEach(function (value) { values.forEach(function (value) {
@@ -53,7 +56,7 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
records.push(record); records.push(record);
}); });
superagent.put(`${GODADDY_API}/${zoneName}/records/${type}/${subdomain}`) superagent.put(`${GODADDY_API}/${zoneName}/records/${type}/${name}`)
.set('Authorization', `sso-key ${dnsConfig.apiKey}:${dnsConfig.apiSecret}`) .set('Authorization', `sso-key ${dnsConfig.apiKey}:${dnsConfig.apiSecret}`)
.timeout(30 * 1000) .timeout(30 * 1000)
.send(records) .send(records)
@@ -68,18 +71,19 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
debug(`get: ${subdomain} in zone ${zoneName} of type ${type}`); debug(`get: ${name} in zone ${zoneName} of type ${type}`);
superagent.get(`${GODADDY_API}/${zoneName}/records/${type}/${subdomain}`) superagent.get(`${GODADDY_API}/${zoneName}/records/${type}/${name}`)
.set('Authorization', `sso-key ${dnsConfig.apiKey}:${dnsConfig.apiSecret}`) .set('Authorization', `sso-key ${dnsConfig.apiKey}:${dnsConfig.apiSecret}`)
.timeout(30 * 1000) .timeout(30 * 1000)
.end(function (error, result) { .end(function (error, result) {
@@ -98,22 +102,23 @@ function get(dnsConfig, zoneName, subdomain, type, callback) {
}); });
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
debug(`get: ${subdomain} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`); debug(`get: ${name} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`);
if (type !== 'A' && type !== 'TXT') return callback(new DomainsError(DomainsError.EXTERNAL_ERROR, new Error('Record deletion is not supported by GoDaddy API'))); if (type !== 'A' && type !== 'TXT') return callback(new DomainsError(DomainsError.EXTERNAL_ERROR, new Error('Record deletion is not supported by GoDaddy API')));
// check if the record exists at all so that we don't insert the "Dead" record for no reason // check if the record exists at all so that we don't insert the "Dead" record for no reason
get(dnsConfig, zoneName, subdomain, type, function (error, values) { get(domainObject, location, type, function (error, values) {
if (error) return callback(error); if (error) return callback(error);
if (values.length === 0) return callback(); if (values.length === 0) return callback();
@@ -123,7 +128,7 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
data: type === 'A' ? GODADDY_INVALID_IP : GODADDY_INVALID_TXT data: type === 'A' ? GODADDY_INVALID_IP : GODADDY_INVALID_TXT
}]; }];
superagent.put(`${GODADDY_API}/${zoneName}/records/${type}/${subdomain}`) superagent.put(`${GODADDY_API}/${zoneName}/records/${type}/${name}`)
.set('Authorization', `sso-key ${dnsConfig.apiKey}:${dnsConfig.apiSecret}`) .set('Authorization', `sso-key ${dnsConfig.apiKey}:${dnsConfig.apiSecret}`)
.send(records) .send(records)
.timeout(30 * 1000) .timeout(30 * 1000)
@@ -140,16 +145,31 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const fqdn = domains.fqdn(location, domainObject);
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName;
if (!dnsConfig.apiKey || typeof dnsConfig.apiKey !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'apiKey must be a non-empty string')); if (!dnsConfig.apiKey || typeof dnsConfig.apiKey !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'apiKey must be a non-empty string'));
if (!dnsConfig.apiSecret || typeof dnsConfig.apiSecret !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'apiSecret must be a non-empty string')); if (!dnsConfig.apiSecret || typeof dnsConfig.apiSecret !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'apiSecret must be a non-empty string'));
const ip = '127.0.0.1';
var credentials = { var credentials = {
apiKey: dnsConfig.apiKey, apiKey: dnsConfig.apiKey,
apiSecret: dnsConfig.apiSecret apiSecret: dnsConfig.apiSecret
@@ -166,14 +186,14 @@ function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) {
return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to GoDaddy')); return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to GoDaddy'));
} }
const testSubdomain = 'cloudrontestdns'; const location = 'cloudrontestdns';
upsert(credentials, zoneName, testSubdomain, 'A', [ ip ], function (error, changeId) { upsert(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record added with change id %s', changeId); debug('verifyDnsConfig: Test A record added');
del(dnsConfig, zoneName, testSubdomain, 'A', [ ip ], function (error) { del(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record removed again'); debug('verifyDnsConfig: Test A record removed again');

View File

@@ -10,18 +10,16 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
var assert = require('assert'), var assert = require('assert'),
DomainsError = require('../domains.js').DomainsError,
util = require('util'); util = require('util');
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
@@ -31,10 +29,9 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) {
callback(new Error('not implemented')); callback(new Error('not implemented'));
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
@@ -43,10 +40,9 @@ function get(dnsConfig, zoneName, subdomain, type, callback) {
callback(new Error('not implemented')); callback(new Error('not implemented'));
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
@@ -56,11 +52,19 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
callback(new Error('not implemented')); callback(new Error('not implemented'));
} }
function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function');
callback();
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
// Result: dnsConfig object // Result: dnsConfig object

View File

@@ -4,43 +4,42 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
var assert = require('assert'), var assert = require('assert'),
debug = require('debug')('box:dns/manual'), debug = require('debug')('box:dns/manual'),
dns = require('../native-dns.js'), dns = require('../native-dns.js'),
domains = require('../domains.js'),
DomainsError = require('../domains.js').DomainsError, DomainsError = require('../domains.js').DomainsError,
util = require('util'); util = require('util'),
waitForDns = require('./waitfordns.js');
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
debug('upsert: %s for zone %s of type %s with values %j', subdomain, zoneName, type, values); debug('upsert: %s for zone %s of type %s with values %j', location, domainObject.zoneName, type, values);
return callback(null); return callback(null);
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
callback(null, [ ]); // returning ip confuses apptask into thinking the entry already exists callback(null, [ ]); // returning ip confuses apptask into thinking the entry already exists
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
@@ -48,13 +47,25 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
return callback(); return callback();
} }
function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const fqdn = domains.fqdn(location, domainObject);
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function');
const zoneName = domainObject.zoneName;
// Very basic check if the nameservers can be fetched // Very basic check if the nameservers can be fetched
dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) { dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) {
if (error && error.code === 'ENOTFOUND') return callback(new DomainsError(DomainsError.BAD_FIELD, 'Unable to resolve nameservers for this domain')); if (error && error.code === 'ENOTFOUND') return callback(new DomainsError(DomainsError.BAD_FIELD, 'Unable to resolve nameservers for this domain'));

View File

@@ -4,16 +4,19 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
var assert = require('assert'), var assert = require('assert'),
debug = require('debug')('box:dns/namecom'), debug = require('debug')('box:dns/namecom'),
dns = require('../native-dns.js'), dns = require('../native-dns.js'),
safe = require('safetydance'), domains = require('../domains.js'),
DomainsError = require('../domains.js').DomainsError, DomainsError = require('../domains.js').DomainsError,
superagent = require('superagent'); safe = require('safetydance'),
superagent = require('superagent'),
util = require('util'),
waitForDns = require('./waitfordns.js');
const NAMECOM_API = 'https://api.name.com/v4'; const NAMECOM_API = 'https://api.name.com/v4';
@@ -21,18 +24,18 @@ function formatError(response) {
return `Name.com DNS error [${response.statusCode}] ${response.text}`; return `Name.com DNS error [${response.statusCode}] ${response.text}`;
} }
function addRecord(dnsConfig, zoneName, subdomain, type, values, callback) { function addRecord(dnsConfig, zoneName, name, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof dnsConfig, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof zoneName, 'string');
assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof name, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(Array.isArray(values)); assert(Array.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
debug(`add: ${subdomain} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`); debug(`add: ${name} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`);
var data = { var data = {
host: subdomain, host: name,
type: type, type: type,
ttl: 300 // 300 is the lowest ttl: 300 // 300 is the lowest
}; };
@@ -57,19 +60,19 @@ function addRecord(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function updateRecord(dnsConfig, zoneName, recordId, subdomain, type, values, callback) { function updateRecord(dnsConfig, zoneName, recordId, name, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof dnsConfig, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof zoneName, 'string');
assert.strictEqual(typeof recordId, 'number'); assert.strictEqual(typeof recordId, 'number');
assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof name, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(Array.isArray(values)); assert(Array.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
debug(`update:${recordId} on ${subdomain} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`); debug(`update:${recordId} on ${name} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`);
var data = { var data = {
host: subdomain, host: name,
type: type, type: type,
ttl: 300 // 300 is the lowest ttl: 300 // 300 is the lowest
}; };
@@ -94,16 +97,14 @@ function updateRecord(dnsConfig, zoneName, recordId, subdomain, type, values, ca
}); });
} }
function getInternal(dnsConfig, zoneName, subdomain, type, callback) { function getInternal(dnsConfig, zoneName, name, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof dnsConfig, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof zoneName, 'string');
assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof name, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; debug(`getInternal: ${name} in zone ${zoneName} of type ${type}`);
debug(`getInternal: ${subdomain} in zone ${zoneName} of type ${type}`);
superagent.get(`${NAMECOM_API}/domains/${zoneName}/records`) superagent.get(`${NAMECOM_API}/domains/${zoneName}/records`)
.auth(dnsConfig.username, dnsConfig.token) .auth(dnsConfig.username, dnsConfig.token)
@@ -123,7 +124,7 @@ function getInternal(dnsConfig, zoneName, subdomain, type, callback) {
}); });
var results = result.body.records.filter(function (r) { var results = result.body.records.filter(function (r) {
return (r.host === subdomain && r.type === type); return (r.host === name && r.type === type);
}); });
debug('getInternal: %j', results); debug('getInternal: %j', results);
@@ -132,35 +133,39 @@ function getInternal(dnsConfig, zoneName, subdomain, type, callback) {
}); });
} }
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(Array.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
debug(`upsert: ${subdomain} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`); debug(`upsert: ${name} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`);
getInternal(dnsConfig, zoneName, subdomain, type, function (error, result) { getInternal(dnsConfig, zoneName, name, type, function (error, result) {
if (error) return callback(error); if (error) return callback(error);
if (result.length === 0) return addRecord(dnsConfig, zoneName, subdomain, type, values, callback); if (result.length === 0) return addRecord(dnsConfig, zoneName, name, type, values, callback);
return updateRecord(dnsConfig, zoneName, result[0].id, subdomain, type, values, callback); return updateRecord(dnsConfig, zoneName, result[0].id, name, type, values, callback);
}); });
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
getInternal(dnsConfig, zoneName, subdomain, type, function (error, result) { const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
getInternal(dnsConfig, zoneName, name, type, function (error, result) {
if (error) return callback(error); if (error) return callback(error);
var tmp = result.map(function (record) { return record.answer; }); var tmp = result.map(function (record) { return record.answer; });
@@ -171,19 +176,20 @@ function get(dnsConfig, zoneName, subdomain, type, callback) {
}); });
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(Array.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
subdomain = subdomain || '@'; const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
name = domains.getName(domainObject, location, type) || '@';
debug(`del: ${subdomain} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`); debug(`del: ${name} in zone ${zoneName} of type ${type} with values ${JSON.stringify(values)}`);
getInternal(dnsConfig, zoneName, subdomain, type, function (error, result) { getInternal(dnsConfig, zoneName, name, type, function (error, result) {
if (error) return callback(error); if (error) return callback(error);
if (result.length === 0) return callback(); if (result.length === 0) return callback();
@@ -201,13 +207,26 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const fqdn = domains.fqdn(location, domainObject);
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName;
if (typeof dnsConfig.username !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'username must be a string')); if (typeof dnsConfig.username !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'username must be a string'));
if (typeof dnsConfig.token !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'token must be a string')); if (typeof dnsConfig.token !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'token must be a string'));
@@ -216,6 +235,8 @@ function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) {
token: dnsConfig.token token: dnsConfig.token
}; };
const ip = '127.0.0.1';
if (process.env.BOX_ENV === 'test') return callback(null, credentials); // this shouldn't be here if (process.env.BOX_ENV === 'test') return callback(null, credentials); // this shouldn't be here
dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) { dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) {
@@ -227,14 +248,14 @@ function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) {
return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Name.com')); return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Name.com'));
} }
const testSubdomain = 'cloudrontestdns'; const location = 'cloudrontestdns';
upsert(credentials, zoneName, testSubdomain, 'A', [ ip ], function (error, changeId) { upsert(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record added with change id %s', changeId); debug('verifyDnsConfig: Test A record added');
del(dnsConfig, zoneName, testSubdomain, 'A', [ ip ], function (error) { del(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record removed again'); debug('verifyDnsConfig: Test A record removed again');

View File

@@ -4,7 +4,7 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: waitForDns, wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
@@ -12,33 +12,30 @@ var assert = require('assert'),
debug = require('debug')('box:dns/noop'), debug = require('debug')('box:dns/noop'),
util = require('util'); util = require('util');
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
debug('upsert: %s for zone %s of type %s with values %j', subdomain, zoneName, type, values); debug('upsert: %s for zone %s of type %s with values %j', location, domainObject.zoneName, type, values);
return callback(null); return callback(null);
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
callback(null, [ ]); // returning ip confuses apptask into thinking the entry already exists callback(null, [ ]); // returning ip confuses apptask into thinking the entry already exists
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
@@ -46,9 +43,9 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
return callback(); return callback();
} }
function waitForDns(domain, zoneName, type, value, options, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof value, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 } assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
@@ -57,11 +54,8 @@ function waitForDns(domain, zoneName, type, value, options, callback) {
callback(); callback();
} }
function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof domain, 'string');
assert.strictEqual(typeof zoneName, 'string');
assert.strictEqual(typeof ip, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
return callback(null, { }); return callback(null, { });

View File

@@ -4,19 +4,18 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig, verifyDnsConfig: verifyDnsConfig
// not part of "dns" interface
getHostedZone: getHostedZone
}; };
var assert = require('assert'), var assert = require('assert'),
AWS = require('aws-sdk'), AWS = require('aws-sdk'),
debug = require('debug')('box:dns/route53'), debug = require('debug')('box:dns/route53'),
dns = require('../native-dns.js'), dns = require('../native-dns.js'),
domains = require('../domains.js'),
DomainsError = require('../domains.js').DomainsError, DomainsError = require('../domains.js').DomainsError,
util = require('util'), util = require('util'),
waitForDns = require('./waitfordns.js'),
_ = require('underscore'); _ = require('underscore');
function getDnsCredentials(dnsConfig) { function getDnsCredentials(dnsConfig) {
@@ -82,20 +81,22 @@ function getHostedZone(dnsConfig, zoneName, callback) {
}); });
} }
function add(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
debug('add: %s for zone %s of type %s with values %j', subdomain, zoneName, type, values); const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
fqdn = domains.fqdn(location, domainObject);
debug('add: %s for zone %s of type %s with values %j', fqdn, zoneName, type, values);
getZoneByName(dnsConfig, zoneName, function (error, zone) { getZoneByName(dnsConfig, zoneName, function (error, zone) {
if (error) return callback(error); if (error) return callback(error);
var fqdn = subdomain === '' ? zoneName : subdomain + '.' + zoneName;
var records = values.map(function (v) { return { Value: v }; }); // for mx records, value is already of the '<priority> <server>' format var records = values.map(function (v) { return { Value: v }; }); // for mx records, value is already of the '<priority> <server>' format
var params = { var params = {
@@ -126,31 +127,23 @@ function add(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string');
assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function');
add(dnsConfig, zoneName, subdomain, type, values, callback);
}
function get(dnsConfig, zoneName, subdomain, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object');
assert.strictEqual(typeof zoneName, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
fqdn = domains.fqdn(location, domainObject);
getZoneByName(dnsConfig, zoneName, function (error, zone) { getZoneByName(dnsConfig, zoneName, function (error, zone) {
if (error) return callback(error); if (error) return callback(error);
var params = { var params = {
HostedZoneId: zone.Id, HostedZoneId: zone.Id,
MaxItems: '1', MaxItems: '1',
StartRecordName: (subdomain ? subdomain + '.' : '') + zoneName + '.', StartRecordName: fqdn + '.',
StartRecordType: type StartRecordType: type
}; };
@@ -169,18 +162,20 @@ function get(dnsConfig, zoneName, subdomain, type, callback) {
}); });
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName,
fqdn = domains.fqdn(location, domainObject);
getZoneByName(dnsConfig, zoneName, function (error, zone) { getZoneByName(dnsConfig, zoneName, function (error, zone) {
if (error) return callback(error); if (error) return callback(error);
var fqdn = subdomain === '' ? zoneName : subdomain + '.' + zoneName;
var records = values.map(function (v) { return { Value: v }; }); var records = values.map(function (v) { return { Value: v }; });
var resourceRecordSet = { var resourceRecordSet = {
@@ -226,13 +221,26 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
}); });
} }
function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const fqdn = domains.fqdn(location, domainObject);
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function');
const dnsConfig = domainObject.config,
zoneName = domainObject.zoneName;
if (!dnsConfig.accessKeyId || typeof dnsConfig.accessKeyId !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'accessKeyId must be a non-empty string')); if (!dnsConfig.accessKeyId || typeof dnsConfig.accessKeyId !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'accessKeyId must be a non-empty string'));
if (!dnsConfig.secretAccessKey || typeof dnsConfig.secretAccessKey !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'secretAccessKey must be a non-empty string')); if (!dnsConfig.secretAccessKey || typeof dnsConfig.secretAccessKey !== 'string') return callback(new DomainsError(DomainsError.BAD_FIELD, 'secretAccessKey must be a non-empty string'));
@@ -244,6 +252,8 @@ function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) {
listHostedZonesByName: true, // new/updated creds require this perm listHostedZonesByName: true, // new/updated creds require this perm
}; };
const ip = '127.0.0.1';
if (process.env.BOX_ENV === 'test') return callback(null, credentials); // this shouldn't be here if (process.env.BOX_ENV === 'test') return callback(null, credentials); // this shouldn't be here
dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) { dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) {
@@ -258,14 +268,14 @@ function verifyDnsConfig(dnsConfig, fqdn, zoneName, ip, callback) {
return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Route53')); return callback(new DomainsError(DomainsError.BAD_FIELD, 'Domain nameservers are not set to Route53'));
} }
const testSubdomain = 'cloudrontestdns'; const location = 'cloudrontestdns';
upsert(credentials, zoneName, testSubdomain, 'A', [ ip ], function (error, changeId) { upsert(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record added with change id %s', changeId); debug('verifyDnsConfig: Test A record added');
del(credentials, zoneName, testSubdomain, 'A', [ ip ], function (error) { del(domainObject, location, 'A', [ ip ], function (error) {
if (error) return callback(error); if (error) return callback(error);
debug('verifyDnsConfig: Test A record removed again'); debug('verifyDnsConfig: Test A record removed again');

View File

@@ -30,8 +30,8 @@ function resolveIp(hostname, options, callback) {
}); });
} }
function isChangeSynced(domain, type, value, nameserver, callback) { function isChangeSynced(hostname, type, value, nameserver, callback) {
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof hostname, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof value, 'string'); assert.strictEqual(typeof value, 'string');
assert.strictEqual(typeof nameserver, 'string'); assert.strictEqual(typeof nameserver, 'string');
@@ -46,16 +46,16 @@ function isChangeSynced(domain, type, value, nameserver, callback) {
async.every(nsIps, function (nsIp, iteratorCallback) { async.every(nsIps, function (nsIp, iteratorCallback) {
const resolveOptions = { server: nsIp, timeout: 5000 }; const resolveOptions = { server: nsIp, timeout: 5000 };
const resolver = type === 'A' ? resolveIp.bind(null, domain) : dns.resolve.bind(null, domain, 'TXT'); const resolver = type === 'A' ? resolveIp.bind(null, hostname) : dns.resolve.bind(null, hostname, 'TXT');
resolver(resolveOptions, function (error, answer) { resolver(resolveOptions, function (error, answer) {
if (error && error.code === 'TIMEOUT') { if (error && error.code === 'TIMEOUT') {
debug(`isChangeSynced: NS ${nameserver} (${nsIp}) timed out when resolving ${domain} (${type})`); debug(`isChangeSynced: NS ${nameserver} (${nsIp}) timed out when resolving ${hostname} (${type})`);
return iteratorCallback(null, true); // should be ok if dns server is down return iteratorCallback(null, true); // should be ok if dns server is down
} }
if (error) { if (error) {
debug(`isChangeSynced: NS ${nameserver} (${nsIp}) errored when resolve ${domain} (${type}): ${error}`); debug(`isChangeSynced: NS ${nameserver} (${nsIp}) errored when resolve ${hostname} (${type}): ${error}`);
return iteratorCallback(null, false); return iteratorCallback(null, false);
} }
@@ -66,7 +66,7 @@ function isChangeSynced(domain, type, value, nameserver, callback) {
match = answer.some(function (a) { return value === a.join(''); }); match = answer.some(function (a) { return value === a.join(''); });
} }
debug(`isChangeSynced: ${domain} (${type}) was resolved to ${answer} at NS ${nameserver} (${nsIp}). Expecting ${value}. Match ${match}`); debug(`isChangeSynced: ${hostname} (${type}) was resolved to ${answer} at NS ${nameserver} (${nsIp}). Expecting ${value}. Match ${match}`);
iteratorCallback(null, match); iteratorCallback(null, match);
}); });
@@ -76,26 +76,26 @@ function isChangeSynced(domain, type, value, nameserver, callback) {
} }
// check if IP change has propagated to every nameserver // check if IP change has propagated to every nameserver
function waitForDns(domain, zoneName, type, value, options, callback) { function waitForDns(hostname, zoneName, type, value, options, callback) {
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof hostname, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof zoneName, 'string');
assert(type === 'A' || type === 'TXT'); assert(type === 'A' || type === 'TXT');
assert.strictEqual(typeof value, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 } assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
debug('waitForDns: domain %s to be %s in zone %s.', domain, value, zoneName); debug('waitForDns: hostname %s to be %s in zone %s.', hostname, value, zoneName);
var attempt = 0; var attempt = 0;
async.retry(options, function (retryCallback) { async.retry(options, function (retryCallback) {
++attempt; ++attempt;
debug(`waitForDns (try ${attempt}): ${domain} to be ${value} in zone ${zoneName}`); debug(`waitForDns (try ${attempt}): ${hostname} to be ${value} in zone ${zoneName}`);
dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) { dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) {
if (error || !nameservers) return retryCallback(error || new DomainsError(DomainsError.EXTERNAL_ERROR, 'Unable to get nameservers')); if (error || !nameservers) return retryCallback(error || new DomainsError(DomainsError.EXTERNAL_ERROR, 'Unable to get nameservers'));
async.every(nameservers, isChangeSynced.bind(null, domain, type, value), function (error, synced) { async.every(nameservers, isChangeSynced.bind(null, hostname, type, value), function (error, synced) {
debug('waitForDns: %s %s ns: %j', domain, synced ? 'done' : 'not done', nameservers); debug('waitForDns: %s %s ns: %j', hostname, synced ? 'done' : 'not done', nameservers);
retryCallback(synced ? null : new DomainsError(DomainsError.EXTERNAL_ERROR, 'ETRYAGAIN')); retryCallback(synced ? null : new DomainsError(DomainsError.EXTERNAL_ERROR, 'ETRYAGAIN'));
}); });
@@ -103,7 +103,7 @@ function waitForDns(domain, zoneName, type, value, options, callback) {
}, function retryDone(error) { }, function retryDone(error) {
if (error) return callback(error); if (error) return callback(error);
debug(`waitForDns: ${domain} has propagated`); debug(`waitForDns: ${hostname} has propagated`);
callback(null); callback(null);
}); });

View File

@@ -4,44 +4,43 @@ exports = module.exports = {
upsert: upsert, upsert: upsert,
get: get, get: get,
del: del, del: del,
waitForDns: require('./waitfordns.js'), wait: wait,
verifyDnsConfig: verifyDnsConfig verifyDnsConfig: verifyDnsConfig
}; };
var assert = require('assert'), var assert = require('assert'),
debug = require('debug')('box:dns/manual'), debug = require('debug')('box:dns/manual'),
dns = require('../native-dns.js'), dns = require('../native-dns.js'),
domains = require('../domains.js'),
DomainsError = require('../domains.js').DomainsError, DomainsError = require('../domains.js').DomainsError,
sysinfo = require('../sysinfo.js'), sysinfo = require('../sysinfo.js'),
util = require('util'); util = require('util'),
waitForDns = require('./waitfordns.js');
function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { function upsert(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
debug('upsert: %s for zone %s of type %s with values %j', subdomain, zoneName, type, values); debug('upsert: %s for zone %s of type %s with values %j', location, domainObject.zoneName, type, values);
return callback(null); return callback(null);
} }
function get(dnsConfig, zoneName, subdomain, type, callback) { function get(domainObject, location, type, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
callback(null, [ ]); // returning ip confuses apptask into thinking the entry already exists callback(null, [ ]); // returning ip confuses apptask into thinking the entry already exists
} }
function del(dnsConfig, zoneName, subdomain, type, values, callback) { function del(domainObject, location, type, values, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof subdomain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
@@ -49,20 +48,33 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) {
return callback(); return callback();
} }
function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { function wait(domainObject, location, type, value, options, callback) {
assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof value, 'string');
assert(options && typeof options === 'object'); // { interval: 5000, times: 50000 }
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
const fqdn = domains.fqdn(location, domainObject);
waitForDns(fqdn, domainObject.zoneName, type, value, options, callback);
}
function verifyDnsConfig(domainObject, callback) {
assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof callback, 'function');
const zoneName = domainObject.zoneName;
// Very basic check if the nameservers can be fetched // Very basic check if the nameservers can be fetched
dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) { dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) {
if (error && error.code === 'ENOTFOUND') return callback(new DomainsError(DomainsError.BAD_FIELD, 'Unable to resolve nameservers for this domain')); if (error && error.code === 'ENOTFOUND') return callback(new DomainsError(DomainsError.BAD_FIELD, 'Unable to resolve nameservers for this domain'));
if (error || !nameservers) return callback(new DomainsError(DomainsError.BAD_FIELD, error ? error.message : 'Unable to get nameservers')); if (error || !nameservers) return callback(new DomainsError(DomainsError.BAD_FIELD, error ? error.message : 'Unable to get nameservers'));
const separator = dnsConfig.hyphenatedSubdomains ? '-' : '.'; const location = 'cloudrontestdns';
const fqdn = `cloudrontest${separator}${domain}`; const fqdn = domains.fqdn(location, domainObject);
dns.resolve(fqdn, 'A', { server: '127.0.0.1', timeout: 5000 }, function (error, result) { dns.resolve(fqdn, 'A', { server: '127.0.0.1', timeout: 5000 }, function (error, result) {
if (error && error.code === 'ENOTFOUND') return callback(new DomainsError(DomainsError.BAD_FIELD, `Unable to resolve ${fqdn}`)); if (error && error.code === 'ENOTFOUND') return callback(new DomainsError(DomainsError.BAD_FIELD, `Unable to resolve ${fqdn}`));
if (error || !result) return callback(new DomainsError(DomainsError.BAD_FIELD, error ? error.message : `Unable to resolve ${fqdn}`)); if (error || !result) return callback(new DomainsError(DomainsError.BAD_FIELD, error ? error.message : `Unable to resolve ${fqdn}`));

View File

@@ -10,6 +10,7 @@ module.exports = exports = {
isLocked: isLocked, isLocked: isLocked,
fqdn: fqdn, fqdn: fqdn,
getName: getName,
getDnsRecords: getDnsRecords, getDnsRecords: getDnsRecords,
upsertDnsRecords: upsertDnsRecords, upsertDnsRecords: upsertDnsRecords,
@@ -28,10 +29,7 @@ module.exports = exports = {
prepareDashboardDomain: prepareDashboardDomain, prepareDashboardDomain: prepareDashboardDomain,
DomainsError: DomainsError, DomainsError: DomainsError
// exported for testing
_getName: getName
}; };
var assert = require('assert'), var assert = require('assert'),
@@ -105,18 +103,18 @@ function parentDomain(domain) {
return domain.replace(/^\S+?\./, ''); // +? means non-greedy return domain.replace(/^\S+?\./, ''); // +? means non-greedy
} }
function verifyDnsConfig(dnsConfig, domain, zoneName, provider, ip, callback) { function verifyDnsConfig(dnsConfig, domain, zoneName, provider, callback) {
assert(dnsConfig && typeof dnsConfig === 'object'); // the dns config to test with assert(dnsConfig && typeof dnsConfig === 'object'); // the dns config to test with
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof domain, 'string');
assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof zoneName, 'string');
assert.strictEqual(typeof provider, 'string'); assert.strictEqual(typeof provider, 'string');
assert.strictEqual(typeof ip, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
var backend = api(provider); var backend = api(provider);
if (!backend) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Invalid provider')); if (!backend) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Invalid provider'));
api(provider).verifyDnsConfig(dnsConfig, domain, zoneName, ip, function (error, result) { const domainObject = { config: dnsConfig, domain: domain, zoneName: zoneName };
api(provider).verifyDnsConfig(domainObject, function (error, result) {
if (error && error.reason === DomainsError.ACCESS_DENIED) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Incorrect configuration. Access denied')); if (error && error.reason === DomainsError.ACCESS_DENIED) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Incorrect configuration. Access denied'));
if (error && error.reason === DomainsError.NOT_FOUND) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Zone not found')); if (error && error.reason === DomainsError.NOT_FOUND) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Zone not found'));
if (error && error.reason === DomainsError.EXTERNAL_ERROR) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Configuration error: ' + error.message)); if (error && error.reason === DomainsError.EXTERNAL_ERROR) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Configuration error: ' + error.message));
@@ -130,12 +128,8 @@ function verifyDnsConfig(dnsConfig, domain, zoneName, provider, ip, callback) {
}); });
} }
function fqdn(location, domain, dnsConfig) { function fqdn(location, domainObject) {
assert.strictEqual(typeof location, 'string'); return location + (location ? (domainObject.config.hyphenatedSubdomains ? '-' : '.') : '') + domainObject.domain;
assert.strictEqual(typeof domain, 'string');
assert.strictEqual(typeof dnsConfig, 'object');
return location + (location ? (dnsConfig.hyphenatedSubdomains ? '-' : '.') : '') + domain;
} }
// Hostname validation comes from RFC 1123 (section 2.1) // Hostname validation comes from RFC 1123 (section 2.1)
@@ -146,7 +140,7 @@ function validateHostname(location, domainObject) {
assert.strictEqual(typeof location, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof domainObject, 'object'); assert.strictEqual(typeof domainObject, 'object');
const hostname = fqdn(location, domainObject.domain, domainObject.config); const hostname = fqdn(location, domainObject);
const RESERVED_LOCATIONS = [ const RESERVED_LOCATIONS = [
constants.API_LOCATION, constants.API_LOCATION,
@@ -231,23 +225,19 @@ function add(domain, data, auditSource, callback) {
let error = validateTlsConfig(tlsConfig, provider); let error = validateTlsConfig(tlsConfig, provider);
if (error) return callback(error); if (error) return callback(error);
sysinfo.getPublicIp(function (error, ip) { verifyDnsConfig(config, domain, zoneName, provider, function (error, sanitizedConfig) {
if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, 'Error getting IP:' + error.message)); if (error) return callback(error);
verifyDnsConfig(config, domain, zoneName, provider, ip, function (error, sanitizedConfig) { domaindb.add(domain, { zoneName: zoneName, provider: provider, config: sanitizedConfig, tlsConfig: tlsConfig }, function (error) {
if (error) return callback(error); if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(new DomainsError(DomainsError.ALREADY_EXISTS));
if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error));
domaindb.add(domain, { zoneName: zoneName, provider: provider, config: sanitizedConfig, tlsConfig: tlsConfig }, function (error) { reverseProxy.setFallbackCertificate(domain, fallbackCertificate, function (error) {
if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(new DomainsError(DomainsError.ALREADY_EXISTS));
if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error)); if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error));
reverseProxy.setFallbackCertificate(domain, fallbackCertificate, function (error) { eventlog.add(eventlog.ACTION_DOMAIN_ADD, auditSource, { domain, zoneName, provider });
if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error));
eventlog.add(eventlog.ACTION_DOMAIN_ADD, auditSource, { domain, zoneName, provider }); callback();
callback();
});
}); });
}); });
}); });
@@ -325,25 +315,21 @@ function update(domain, data, auditSource, callback) {
error = validateTlsConfig(tlsConfig, provider); error = validateTlsConfig(tlsConfig, provider);
if (error) return callback(error); if (error) return callback(error);
sysinfo.getPublicIp(function (error, ip) { verifyDnsConfig(config, domain, zoneName, provider, function (error, sanitizedConfig) {
if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, 'Error getting IP:' + error.message)); if (error) return callback(error);
verifyDnsConfig(config, domain, zoneName, provider, ip, function (error, sanitizedConfig) { domaindb.update(domain, { zoneName: zoneName, provider: provider, config: sanitizedConfig, tlsConfig: tlsConfig }, function (error) {
if (error) return callback(error); if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new DomainsError(DomainsError.NOT_FOUND));
if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error));
domaindb.update(domain, { zoneName: zoneName, provider: provider, config: sanitizedConfig, tlsConfig: tlsConfig }, function (error) { if (!fallbackCertificate) return callback();
if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new DomainsError(DomainsError.NOT_FOUND));
reverseProxy.setFallbackCertificate(domain, fallbackCertificate, function (error) {
if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error)); if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error));
if (!fallbackCertificate) return callback(); eventlog.add(eventlog.ACTION_DOMAIN_UPDATE, auditSource, { domain, zoneName, provider });
reverseProxy.setFallbackCertificate(domain, fallbackCertificate, function (error) { callback();
if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error));
eventlog.add(eventlog.ACTION_DOMAIN_UPDATE, auditSource, { domain, zoneName, provider });
callback();
});
}); });
}); });
}); });
@@ -379,39 +365,36 @@ function clear(callback) {
} }
// returns the 'name' that needs to be inserted into zone // returns the 'name' that needs to be inserted into zone
function getName(domain, subdomain, type) { function getName(domain, location, type) {
// hack for supporting special caas domains. if we want to remove this, we have to fix the appstore domain API first
if (domain.provider === 'caas') return subdomain;
const part = domain.domain.slice(0, -domain.zoneName.length - 1); const part = domain.domain.slice(0, -domain.zoneName.length - 1);
if (subdomain === '') return part; if (location === '') return part;
if (!domain.config.hyphenatedSubdomains) return part ? `${subdomain}.${part}` : subdomain; if (!domain.config.hyphenatedSubdomains) return part ? `${location}.${part}` : location;
// hyphenatedSubdomains // hyphenatedSubdomains
if (type !== 'TXT') return `${subdomain}-${part}`; if (type !== 'TXT') return `${location}-${part}`;
if (subdomain.startsWith('_acme-challenge.')) { if (location.startsWith('_acme-challenge.')) {
return `${subdomain}-${part}`; return `${location}-${part}`;
} else if (subdomain === '_acme-challenge') { } else if (location === '_acme-challenge') {
const up = part.replace(/^[^.]*\.?/, ''); // this gets the domain one level up const up = part.replace(/^[^.]*\.?/, ''); // this gets the domain one level up
return up ? `${subdomain}.${up}` : subdomain; return up ? `${location}.${up}` : location;
} else { } else {
return `${subdomain}.${part}`; return `${location}.${part}`;
} }
} }
function getDnsRecords(subdomain, domain, type, callback) { function getDnsRecords(location, domain, type, callback) {
assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof domain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
get(domain, function (error, result) { get(domain, function (error, domainObject) {
if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error)); if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error));
api(result.provider).get(result.config, result.zoneName, getName(result, subdomain, type), type, function (error, values) { api(domainObject.provider).get(domainObject, location, type, function (error, values) {
if (error) return callback(error); if (error) return callback(error);
callback(null, values); callback(null, values);
@@ -420,19 +403,19 @@ function getDnsRecords(subdomain, domain, type, callback) {
} }
// note: for TXT records the values must be quoted // note: for TXT records the values must be quoted
function upsertDnsRecords(subdomain, domain, type, values, callback) { function upsertDnsRecords(location, domain, type, values, callback) {
assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof domain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
debug('upsertDNSRecord: %s on %s type %s values', subdomain, domain, type, values); debug('upsertDNSRecord: %s on %s type %s values', location, domain, type, values);
get(domain, function (error, result) { get(domain, function (error, domainObject) {
if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error)); if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error));
api(result.provider).upsert(result.config, result.zoneName, getName(result, subdomain, type), type, values, function (error) { api(domainObject.provider).upsert(domainObject, location, type, values, function (error) {
if (error) return callback(error); if (error) return callback(error);
callback(null); callback(null);
@@ -440,19 +423,19 @@ function upsertDnsRecords(subdomain, domain, type, values, callback) {
}); });
} }
function removeDnsRecords(subdomain, domain, type, values, callback) { function removeDnsRecords(location, domain, type, values, callback) {
assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof domain, 'string');
assert.strictEqual(typeof type, 'string'); assert.strictEqual(typeof type, 'string');
assert(util.isArray(values)); assert(util.isArray(values));
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
debug('removeDNSRecord: %s on %s type %s values', subdomain, domain, type, values); debug('removeDNSRecord: %s on %s type %s values', location, domain, type, values);
get(domain, function (error, result) { get(domain, function (error, domainObject) {
if (error) return callback(error); if (error) return callback(error);
api(result.provider).del(result.config, result.zoneName, getName(result, subdomain, type), type, values, function (error) { api(domainObject.provider).del(domainObject, location, type, values, function (error) {
if (error && error.reason !== DomainsError.NOT_FOUND) return callback(error); if (error && error.reason !== DomainsError.NOT_FOUND) return callback(error);
callback(null); callback(null);
@@ -460,8 +443,8 @@ function removeDnsRecords(subdomain, domain, type, values, callback) {
}); });
} }
function waitForDnsRecord(subdomain, domain, type, value, options, callback) { function waitForDnsRecord(location, domain, type, value, options, callback) {
assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof domain, 'string');
assert(type === 'A' || type === 'TXT'); assert(type === 'A' || type === 'TXT');
assert.strictEqual(typeof value, 'string'); assert.strictEqual(typeof value, 'string');
@@ -471,9 +454,7 @@ function waitForDnsRecord(subdomain, domain, type, value, options, callback) {
get(domain, function (error, domainObject) { get(domain, function (error, domainObject) {
if (error) return callback(error); if (error) return callback(error);
const hostname = fqdn(subdomain, domainObject.domain, domainObject.config); api(domainObject.provider).wait(domainObject, location, type, value, options, callback);
api(domainObject.provider).waitForDns(hostname, domainObject.zoneName, type, value, options, callback);
}); });
} }
@@ -520,7 +501,7 @@ function prepareDashboardDomain(domain, auditSource, progressCallback, callback)
(done) => { progressCallback({ percent: 40, message: 'Waiting for DNS' }); done(); }, (done) => { progressCallback({ percent: 40, message: 'Waiting for DNS' }); done(); },
waitForDnsRecord.bind(null, constants.ADMIN_LOCATION, domain, 'A', ip, { interval: 30000, times: 50000 }), waitForDnsRecord.bind(null, constants.ADMIN_LOCATION, domain, 'A', ip, { interval: 30000, times: 50000 }),
(done) => { progressCallback({ percent: 70, message: 'Getting certificate' }); done(); }, (done) => { progressCallback({ percent: 70, message: 'Getting certificate' }); done(); },
reverseProxy.ensureCertificate.bind(null, fqdn(constants.ADMIN_LOCATION, domainObject.domain, domainObject.config), domain, auditSource) reverseProxy.ensureCertificate.bind(null, fqdn(constants.ADMIN_LOCATION, domainObject), domain, auditSource)
], function (error) { ], function (error) {
if (error) return callback(error); if (error) return callback(error);

View File

@@ -168,7 +168,7 @@ function validateCertificate(location, domainObject, certificate) {
if (cert && !key) return new ReverseProxyError(ReverseProxyError.INVALID_CERT, 'missing key'); if (cert && !key) return new ReverseProxyError(ReverseProxyError.INVALID_CERT, 'missing key');
// -checkhost checks for SAN or CN exclusively. SAN takes precedence and if present, ignores the CN. // -checkhost checks for SAN or CN exclusively. SAN takes precedence and if present, ignores the CN.
const fqdn = domains.fqdn(location, domainObject.domain, domainObject.config); const fqdn = domains.fqdn(location, domainObject);
var result = safe.child_process.execSync(`openssl x509 -noout -checkhost "${fqdn}"`, { encoding: 'utf8', input: cert }); var result = safe.child_process.execSync(`openssl x509 -noout -checkhost "${fqdn}"`, { encoding: 'utf8', input: cert });
if (result === null) return new ReverseProxyError(ReverseProxyError.INVALID_CERT, 'Unable to get certificate subject:' + safe.error.message); if (result === null) return new ReverseProxyError(ReverseProxyError.INVALID_CERT, 'Unable to get certificate subject:' + safe.error.message);
@@ -278,7 +278,7 @@ function setAppCertificateSync(location, domainObject, certificate) {
assert.strictEqual(typeof domainObject, 'object'); assert.strictEqual(typeof domainObject, 'object');
assert.strictEqual(typeof certificate, 'object'); assert.strictEqual(typeof certificate, 'object');
let fqdn = domains.fqdn(location, domainObject.domain, domainObject.config); let fqdn = domains.fqdn(location, domainObject);
if (certificate.cert && certificate.key) { if (certificate.cert && certificate.key) {
if (!safe.fs.writeFileSync(path.join(paths.APP_CERTS_DIR, `${fqdn}.user.cert`), certificate.cert)) return safe.error; if (!safe.fs.writeFileSync(path.join(paths.APP_CERTS_DIR, `${fqdn}.user.cert`), certificate.cert)) return safe.error;
if (!safe.fs.writeFileSync(path.join(paths.APP_CERTS_DIR, `${fqdn}.user.key`), certificate.key)) return safe.error; if (!safe.fs.writeFileSync(path.join(paths.APP_CERTS_DIR, `${fqdn}.user.key`), certificate.key)) return safe.error;
@@ -412,7 +412,7 @@ function configureAdmin(domain, auditSource, callback) {
domains.get(domain, function (error, domainObject) { domains.get(domain, function (error, domainObject) {
if (error) return callback(error); if (error) return callback(error);
const adminFqdn = domains.fqdn(constants.ADMIN_LOCATION, domainObject.domain, domainObject.config); const adminFqdn = domains.fqdn(constants.ADMIN_LOCATION, domainObject);
ensureCertificate(adminFqdn, domainObject.domain, auditSource, function (error, bundle) { ensureCertificate(adminFqdn, domainObject.domain, auditSource, function (error, bundle) {
if (error) return callback(error); if (error) return callback(error);
@@ -429,7 +429,7 @@ function writeAdminConfig(domain, callback) {
domains.get(domain, function (error, domainObject) { domains.get(domain, function (error, domainObject) {
if (error) return callback(error); if (error) return callback(error);
const adminFqdn = domains.fqdn(constants.ADMIN_LOCATION, domainObject.domain, domainObject.config); const adminFqdn = domains.fqdn(constants.ADMIN_LOCATION, domainObject);
getCertificate(adminFqdn, domainObject.domain, function (error, bundle) { getCertificate(adminFqdn, domainObject.domain, function (error, bundle) {
if (error) return callback(error); if (error) return callback(error);

View File

@@ -90,7 +90,7 @@ describe('Certificates', function () {
}); });
it('does not allow cert without matching domain', function () { it('does not allow cert without matching domain', function () {
expect(reverseProxy.validateCertificate('', { domain: 'cloudron.io', config: {} }, { cert: validCert0, key: validKey0 })).to.be.an(Error); expect(reverseProxy.validateCertificate('', { domain: 'cloudron.io' }, { cert: validCert0, key: validKey0 })).to.be.an(Error);
expect(reverseProxy.validateCertificate('cloudron.io', foobarDomain, { cert: validCert0, key: validKey0 })).to.be.an(Error); expect(reverseProxy.validateCertificate('cloudron.io', foobarDomain, { cert: validCert0, key: validKey0 })).to.be.an(Error);
}); });