diff --git a/src/cloudron.js b/src/cloudron.js index b3d44648f..846855947 100644 --- a/src/cloudron.js +++ b/src/cloudron.js @@ -166,19 +166,24 @@ function onDomainConfigured(callback) { ], callback); } -function dnsSetup(dnsConfig, domain, callback) { +function dnsSetup(dnsConfig, domain, zoneName, callback) { assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof callback, 'function'); if (config.fqdn()) return callback(new CloudronError(CloudronError.ALREADY_SETUP)); - settings.setDnsConfig(dnsConfig, domain, function (error) { + if (!zoneName) zoneName = tld.getDomain(domain) || ''; + + debug('dnsSetup: Setting up Cloudron with domain %s and zone %s', domain, zoneName); + + settings.setDnsConfig(dnsConfig, domain, zoneName, function (error) { if (error && error.reason === SettingsError.BAD_FIELD) return callback(new CloudronError(CloudronError.BAD_FIELD, error.message)); if (error) return callback(new CloudronError(CloudronError.INTERNAL_ERROR, error)); config.setFqdn(domain); // set fqdn only after dns config is valid, otherwise cannot re-setup if we failed - config.setZoneName(tld.getDomain(domain)); + config.setZoneName(zoneName); async.series([ // do not block onDomainConfigured, @@ -857,9 +862,9 @@ function migrate(options, callback) { if (!options.domain) return doMigrate(options, callback); - var dnsConfig = _.pick(options, 'domain', 'provider', 'accessKeyId', 'secretAccessKey', 'region', 'endpoint', 'token'); + var dnsConfig = _.pick(options, 'domain', 'provider', 'accessKeyId', 'secretAccessKey', 'region', 'endpoint', 'token', 'zoneName'); - settings.setDnsConfig(dnsConfig, options.domain, function (error) { + settings.setDnsConfig(dnsConfig, options.domain, options.zoneName || '', function (error) { if (error && error.reason === SettingsError.BAD_FIELD) return callback(new CloudronError(CloudronError.BAD_FIELD, error.message)); if (error) return callback(new CloudronError(CloudronError.INTERNAL_ERROR, error)); diff --git a/src/dns/caas.js b/src/dns/caas.js index 27fb6048e..5a52846bd 100644 --- a/src/dns/caas.js +++ b/src/dns/caas.js @@ -112,9 +112,10 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) { }); } -function verifyDnsConfig(dnsConfig, domain, ip, callback) { +function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof callback, 'function'); diff --git a/src/dns/digitalocean.js b/src/dns/digitalocean.js index 79fe9b958..1badb5c5d 100644 --- a/src/dns/digitalocean.js +++ b/src/dns/digitalocean.js @@ -180,9 +180,10 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) { }); } -function verifyDnsConfig(dnsConfig, domain, ip, callback) { +function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof callback, 'function'); @@ -193,7 +194,7 @@ function verifyDnsConfig(dnsConfig, domain, ip, callback) { if (process.env.BOX_ENV === 'test') return callback(null, credentials); // this shouldn't be here - dns.resolveNs(domain, function (error, nameservers) { + dns.resolveNs(zoneName, function (error, nameservers) { if (error && error.code === 'ENOTFOUND') return callback(new SubdomainError(SubdomainError.BAD_FIELD, 'Unable to resolve nameservers for this domain')); if (error || !nameservers) return callback(new SubdomainError(SubdomainError.BAD_FIELD, error ? error.message : 'Unable to get nameservers')); @@ -202,7 +203,9 @@ function verifyDnsConfig(dnsConfig, domain, ip, callback) { return callback(new SubdomainError(SubdomainError.BAD_FIELD, 'Domain nameservers are not set to Digital Ocean')); } - upsert(credentials, domain, constants.ADMIN_LOCATION, 'A', [ ip ], function (error, changeId) { + const name = constants.ADMIN_LOCATION + (domain === zoneName ? '' : '.' + domain.slice(0, - zoneName.length - 1)); + + upsert(credentials, zoneName, name, 'A', [ ip ], function (error, changeId) { if (error) return callback(error); debug('verifyDnsConfig: A record added with change id %s', changeId); diff --git a/src/dns/interface.js b/src/dns/interface.js index 3e441c89f..ac6b5ae40 100644 --- a/src/dns/interface.js +++ b/src/dns/interface.js @@ -56,9 +56,10 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) { callback(new Error('not implemented')); } -function verifyDnsConfig(dnsConfig, domain, ip, callback) { +function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof callback, 'function'); diff --git a/src/dns/manual.js b/src/dns/manual.js index ba67d4842..9e1b4d1ae 100644 --- a/src/dns/manual.js +++ b/src/dns/manual.js @@ -51,15 +51,16 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) { return callback(); } -function verifyDnsConfig(dnsConfig, domain, ip, callback) { +function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof callback, 'function'); var adminDomain = constants.ADMIN_LOCATION + '.' + domain; - dns.resolveNs(domain, function (error, nameservers) { + dns.resolveNs(zoneName, function (error, nameservers) { if (error || !nameservers) return callback(new SubdomainError(SubdomainError.BAD_FIELD, 'Unable to get nameservers')); async.every(nameservers, function (nameserver, everyNsCallback) { diff --git a/src/dns/noop.js b/src/dns/noop.js index 239659364..eb26106c4 100644 --- a/src/dns/noop.js +++ b/src/dns/noop.js @@ -57,9 +57,10 @@ function waitForDns(domain, zoneName, value, type, options, callback) { callback(); } -function verifyDnsConfig(dnsConfig, domain, ip, callback) { +function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof callback, 'function'); diff --git a/src/dns/route53.js b/src/dns/route53.js index b741635ab..e083638d7 100644 --- a/src/dns/route53.js +++ b/src/dns/route53.js @@ -218,9 +218,10 @@ function del(dnsConfig, zoneName, subdomain, type, values, callback) { }); } -function verifyDnsConfig(dnsConfig, domain, ip, callback) { +function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof callback, 'function'); @@ -234,11 +235,11 @@ function verifyDnsConfig(dnsConfig, domain, ip, callback) { if (process.env.BOX_ENV === 'test') return callback(null, credentials); // this shouldn't be here - dns.resolveNs(domain, function (error, nameservers) { + dns.resolveNs(zoneName, function (error, nameservers) { if (error && error.code === 'ENOTFOUND') return callback(new SubdomainError(SubdomainError.BAD_FIELD, 'Unable to resolve nameservers for this domain')); if (error || !nameservers) return callback(new SubdomainError(SubdomainError.BAD_FIELD, error ? error.message : 'Unable to get nameservers')); - getHostedZone(credentials, domain, function (error, zone) { + getHostedZone(credentials, zoneName, function (error, zone) { if (error) return callback(error); if (!_.isEqual(zone.DelegationSet.NameServers.sort(), nameservers.sort())) { @@ -246,7 +247,9 @@ function verifyDnsConfig(dnsConfig, domain, ip, callback) { return callback(new SubdomainError(SubdomainError.BAD_FIELD, 'Domain nameservers are not set to Route53')); } - upsert(credentials, domain, constants.ADMIN_LOCATION, 'A', [ ip ], function (error, changeId) { + const name = constants.ADMIN_LOCATION + (domain === zoneName ? '' : '.' + domain.slice(0, - zoneName.length - 1)); + + upsert(credentials, zoneName, name, 'A', [ ip ], function (error, changeId) { if (error) return callback(error); debug('verifyDnsConfig: A record added with change id %s', changeId); diff --git a/src/routes/cloudron.js b/src/routes/cloudron.js index cb61be5c7..e25a86e1c 100644 --- a/src/routes/cloudron.js +++ b/src/routes/cloudron.js @@ -80,7 +80,9 @@ function dnsSetup(req, res, next) { if (typeof req.body.provider !== 'string') return next(new HttpError(400, 'provider is required')); if (typeof req.body.domain !== 'string' || !req.body.domain) return next(new HttpError(400, 'domain is required')); - cloudron.dnsSetup(req.body, req.body.domain.toLowerCase(), function (error) { + if ('zoneName' in req.body && typeof req.body.zoneName !== 'string') return next(new HttpError(400, 'zoneName must be a string')); + + cloudron.dnsSetup(req.body, req.body.domain.toLowerCase(), req.body.zoneName || '', function (error) { if (error && error.reason === CloudronError.ALREADY_SETUP) return next(new HttpError(409, error.message)); if (error && error.reason === CloudronError.BAD_FIELD) return next(new HttpError(400, error.message)); if (error) return next(new HttpError(500, error)); @@ -159,6 +161,8 @@ function migrate(req, res, next) { if (typeof req.body.provider !== 'string') return next(new HttpError(400, 'provider must be string')); } + if ('zoneName' in req.body && typeof req.body.zoneName !== 'string') return next(new HttpError(400, 'zoneName must be string')); + debug('Migration requested domain:%s size:%s region:%s', req.body.domain, req.body.size, req.body.region); var options = _.pick(req.body, 'domain', 'size', 'region'); diff --git a/src/routes/settings.js b/src/routes/settings.js index 992d45e06..5e4e324d7 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -199,7 +199,7 @@ function setDnsConfig(req, res, next) { if (typeof req.body.provider !== 'string') return next(new HttpError(400, 'provider is required')); - settings.setDnsConfig(req.body, config.fqdn(), function (error) { + settings.setDnsConfig(req.body, config.fqdn(), config.zoneName(), function (error) { if (error && error.reason === SettingsError.BAD_FIELD) return next(new HttpError(400, error.message)); if (error) return next(new HttpError(500, error)); diff --git a/src/settings.js b/src/settings.js index 5851311d8..88f1feed4 100644 --- a/src/settings.js +++ b/src/settings.js @@ -498,15 +498,16 @@ function getDnsConfig(callback) { }); } -function setDnsConfig(dnsConfig, domain, callback) { +function setDnsConfig(dnsConfig, domain, zoneName, callback) { assert.strictEqual(typeof dnsConfig, 'object'); assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof callback, 'function'); sysinfo.getPublicIp(function (error, ip) { if (error) return callback(new SettingsError(SettingsError.INTERNAL_ERROR, 'Error getting IP:' + error.message)); - subdomains.verifyDnsConfig(dnsConfig, domain, ip, function (error, result) { + subdomains.verifyDnsConfig(dnsConfig, domain, zoneName, ip, function (error, result) { if (error && error.reason === SubdomainError.ACCESS_DENIED) return callback(new SettingsError(SettingsError.BAD_FIELD, 'Error adding A record. Access denied')); if (error && error.reason === SubdomainError.NOT_FOUND) return callback(new SettingsError(SettingsError.BAD_FIELD, 'Zone not found')); if (error && error.reason === SubdomainError.EXTERNAL_ERROR) return callback(new SettingsError(SettingsError.BAD_FIELD, 'Error adding A record:' + error.message)); diff --git a/src/subdomains.js b/src/subdomains.js index 919e4bb53..fb83d97da 100644 --- a/src/subdomains.js +++ b/src/subdomains.js @@ -130,15 +130,16 @@ function waitForDns(domain, value, type, options, callback) { }); } -function verifyDnsConfig(dnsConfig, domain, ip, callback) { +function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { assert(dnsConfig && typeof dnsConfig === 'object'); // the dns config to test with assert(typeof dnsConfig.provider === 'string'); assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof zoneName, 'string'); assert.strictEqual(typeof ip, 'string'); assert.strictEqual(typeof callback, 'function'); var backend = api(dnsConfig.provider); if (!backend) return callback(new SubdomainError(SubdomainError.INVALID_PROVIDER)); - api(dnsConfig.provider).verifyDnsConfig(dnsConfig, domain, ip, callback); + api(dnsConfig.provider).verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback); }