initial ipv6 support

this adds and waits for AAAA records based on setting. we have to wait
for both A and AAAA because we don't know if the user is accessing via
IPv4 or IPv6. For Let's Encrypt, IPv6 is preferred (but not sure if it
retries if IPv6 is unreachable).

part of #264
This commit is contained in:
Girish Ramakrishnan
2022-01-06 17:02:16 -08:00
parent 7d7539f931
commit d65ac353fe
6 changed files with 82 additions and 32 deletions
+20 -5
View File
@@ -34,6 +34,7 @@ const apps = require('./apps.js'),
debug = require('debug')('box:dns'),
dns = require('dns'),
domains = require('./domains.js'),
ipaddr = require('ipaddr.js'),
mail = require('./mail.js'),
promiseRetry = require('./promise-retry.js'),
safe = require('safetydance'),
@@ -135,14 +136,22 @@ async function checkDnsRecords(location, domain) {
assert.strictEqual(typeof location, 'string');
assert.strictEqual(typeof domain, 'string');
const values = await getDnsRecords(location, domain, 'A');
const ipv4Records = await getDnsRecords(location, domain, 'A');
const ipv4 = await sysinfo.getServerIPv4();
const ip = await sysinfo.getServerIPv4();
// if empty OR exactly one record with the ip, we don't need to overwrite
if (ipv4Records.length !== 0 && (ipv4Records.length !== 1 || ipv4Records[0] !== ipv4)) return { needsOverwrite: true };
if (values.length === 0) return { needsOverwrite: false }; // does not exist
if (values[0] === ip) return { needsOverwrite: false }; // exists but in sync
const ipv6Enabled = await settings.getIPv6Config();
if (ipv6Enabled) {
const ipv6Records = await getDnsRecords(location, domain, 'AAAA');
const ipv6 = await sysinfo.getServerIPv6();
return { needsOverwrite: true };
// if empty OR exactly one record with the ip, we don't need to overwrite
if (ipv6Records.length !== 0 && (ipv6Records.length !== 1 || ipaddr.parse(ipv6Records[0]).toRFC5952String() !== ipv6)) return { needsOverwrite: true };
}
return { needsOverwrite: false }; // one record exists and in sync
}
// note: for TXT records the values must be quoted
@@ -227,12 +236,15 @@ async function registerLocations(locations, options, progressCallback) {
debug(`registerLocations: Will register ${JSON.stringify(locations)} with options ${JSON.stringify(options)}`);
const ipv4 = await sysinfo.getServerIPv4();
const ipv6Enabled = await settings.getIPv6Config();
const ipv6 = ipv6Enabled ? await sysinfo.getServerIPv6() : null;
for (const location of locations) {
progressCallback({ message: `Registering location: ${location.subdomain ? (location.subdomain + '.') : ''}${location.domain}` });
await promiseRetry({ times: 200, interval: 5000, debug, retry: (error) => error.retryable }, async function () {
await registerLocation(location, options, 'A', ipv4);
if (ipv6Enabled) await registerLocation(location, options, 'AAAA', ipv6);
});
}
}
@@ -252,12 +264,15 @@ async function unregisterLocations(locations, progressCallback) {
assert.strictEqual(typeof progressCallback, 'function');
const ipv4 = await sysinfo.getServerIPv4();
const ipv6Enabled = await settings.getIPv6Config();
const ipv6 = ipv6Enabled ? await sysinfo.getServerIPv6() : null;
for (const location of locations) {
progressCallback({ message: `Unregistering location: ${location.subdomain ? (location.subdomain + '.') : ''}${location.domain}` });
await promiseRetry({ times: 30, interval: 5000, debug, retry: (error) => error.retryable }, async function () {
await unregisterLocation(location, 'A', ipv4);
if (ipv6Enabled) await unregisterLocation(location, 'AAAA', ipv6);
});
}
}