diff --git a/migrations/20211012020255-mail-add-dkimKeyJson.js b/migrations/20211012020255-mail-add-dkimKeyJson.js new file mode 100644 index 000000000..57688fd11 --- /dev/null +++ b/migrations/20211012020255-mail-add-dkimKeyJson.js @@ -0,0 +1,42 @@ +'use strict'; + +const async = require('async'), + fs = require('fs'), + path = require('path'); + +const MAIL_DATA_DIR = '/home/yellowtent/boxdata/mail'; +const DKIM_DIR = `${MAIL_DATA_DIR}/dkim`; + +exports.up = function(db, callback) { + db.runSql('ALTER TABLE mail ADD COLUMN dkimKeyJson MEDIUMTEXT', function (error) { + if (error) return callback(error); + + fs.readdir(DKIM_DIR, function (error, filenames) { + if (error && error.code === 'ENOENT') return callback(); + if (error) return callback(error); + + async.eachSeries(filenames, function (filename, iteratorCallback) { + const domain = filename; + const publicKey = fs.readFileSync(path.join(MAIL_DATA_DIR, domain, 'public'), 'utf8'); + const privateKey = fs.readFileSync(path.join(MAIL_DATA_DIR, domain, 'public'), 'utf8'); + + const dkimKey = { + publicKey, + privateKey + }; + + db.runSql('UPDATE mail SET dkimKeyJson=? WHERE domain=?', [ JSON.stringify(dkimKey), domain ], iteratorCallback); + }, function (error) { + if (error) return callback(error); + + fs.rmdir(DKIM_DIR, { recursive: true }, callback); + }); + }); + }); +}; + +exports.down = function(db, callback) { + async.series([ + db.runSql.run(db, 'ALTER TABLE mail DROP COLUMN dkimKeyJson') + ], callback); +}; diff --git a/migrations/schema.sql b/migrations/schema.sql index 828bc3b90..eb17b0ac4 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -180,6 +180,7 @@ CREATE TABLE IF NOT EXISTS mail( relayJson TEXT, bannerJson TEXT, + dkimKeyJson MEDIUMTEXT, dkimSelector VARCHAR(128) NOT NULL DEFAULT "cloudron", FOREIGN KEY(domain) REFERENCES domains(domain), diff --git a/setup/start.sh b/setup/start.sh index e90955a6e..35c037cd7 100755 --- a/setup/start.sh +++ b/setup/start.sh @@ -52,7 +52,8 @@ mkdir -p "${PLATFORM_DATA_DIR}/mysql" mkdir -p "${PLATFORM_DATA_DIR}/postgresql" mkdir -p "${PLATFORM_DATA_DIR}/mongodb" mkdir -p "${PLATFORM_DATA_DIR}/redis" -mkdir -p "${PLATFORM_DATA_DIR}/addons/mail/banner" +mkdir -p "${PLATFORM_DATA_DIR}/addons/mail/banner" \ + "${PLATFORM_DATA_DIR}/addons/mail/dkim" mkdir -p "${PLATFORM_DATA_DIR}/collectd/collectd.conf.d" mkdir -p "${PLATFORM_DATA_DIR}/logrotate.d" mkdir -p "${PLATFORM_DATA_DIR}/acme" diff --git a/src/domains.js b/src/domains.js index 3e4578f5c..064f49e3f 100644 --- a/src/domains.js +++ b/src/domains.js @@ -147,6 +147,8 @@ async function add(domain, data, auditSource) { let error = validateTlsConfig(tlsConfig, provider); if (error) throw error; + const dkimKey = await mail.generateDkimKey(); + if (!dkimSelector) { // create a unique suffix. this lets one add this domain can be added in another cloudron instance and not have their dkim selector conflict const suffix = crypto.createHash('sha256').update(settings.dashboardDomain()).digest('hex').substr(0, 6); @@ -159,7 +161,7 @@ async function add(domain, data, auditSource) { let queries = [ { query: 'INSERT INTO domains (domain, zoneName, provider, configJson, tlsConfigJson, fallbackCertificateJson) VALUES (?, ?, ?, ?, ?, ?)', args: [ domain, zoneName, provider, JSON.stringify(result.sanitizedConfig), JSON.stringify(tlsConfig), JSON.stringify(fallbackCertificate) ] }, - { query: 'INSERT INTO mail (domain, dkimSelector) VALUES (?, ?)', args: [ domain, dkimSelector || 'cloudron' ] }, + { query: 'INSERT INTO mail (domain, dkimKeyJson, dkimSelector) VALUES (?, ?, ?)', args: [ domain, JSON.stringify(dkimKey), dkimSelector || 'cloudron' ] }, ]; [error] = await safe(database.transaction(queries)); diff --git a/src/mail.js b/src/mail.js index 75605cc64..919a29113 100644 --- a/src/mail.js +++ b/src/mail.js @@ -13,6 +13,7 @@ exports = module.exports = { getDomain, clearDomains, + generateDkimKey, onDomainAdded, onDomainRemoved, @@ -64,7 +65,6 @@ exports = module.exports = { TYPE_ALIAS: 'alias', _delByDomain: delByDomain, - _readDkimPublicKeySync: readDkimPublicKeySync, _updateDomain: updateDomain }; @@ -72,6 +72,7 @@ const assert = require('assert'), BoxError = require('./boxerror.js'), cloudron = require('./cloudron.js'), constants = require('./constants.js'), + crypto = require('crypto'), database = require('./database.js'), debug = require('debug')('box:mail'), dns = require('./dns.js'), @@ -84,6 +85,7 @@ const assert = require('assert'), mysql = require('mysql'), net = require('net'), nodemailer = require('nodemailer'), + os = require('os'), path = require('path'), paths = require('./paths.js'), reverseProxy = require('./reverseproxy.js'), @@ -105,7 +107,7 @@ const DNS_OPTIONS = { timeout: 5000 }; const REMOVE_MAILBOX_CMD = path.join(__dirname, 'scripts/rmmailbox.sh'); const MAILBOX_FIELDS = [ 'name', 'type', 'ownerId', 'ownerType', 'aliasName', 'aliasDomain', 'creationTime', 'membersJson', 'membersOnly', 'domain', 'active', 'enablePop3' ].join(','); -const MAILDB_FIELDS = [ 'domain', 'enabled', 'mailFromValidation', 'catchAllJson', 'relayJson', 'dkimSelector', 'bannerJson' ].join(','); +const MAILDB_FIELDS = [ 'domain', 'enabled', 'mailFromValidation', 'catchAllJson', 'relayJson', 'dkimKeyJson', 'dkimSelector', 'bannerJson' ].join(','); function postProcessMailbox(data) { data.members = safe.JSON.parse(data.membersJson) || [ ]; @@ -143,6 +145,9 @@ function postProcessDomain(data) { data.banner = safe.JSON.parse(data.bannerJson) || { text: null, html: null }; delete data.bannerJson; + data.dkimKey = safe.JSON.parse(data.dkimKeyJson) || null; + delete data.dkimKeyJson; + return data; } @@ -259,13 +264,9 @@ async function checkDkim(mailDomain) { errorMessage: '' }; - const dkimKey = readDkimPublicKeySync(domain); - if (!dkimKey) { - dkim.errorMessage = `Failed to read dkim public key of ${domain}`; - return dkim; - } + const publicKey = mailDomain.dkimKey.publicKey.split('\n').slice(1, -2).join(''); // remove header, footer and new lines - dkim.expected = 'v=DKIM1; t=s; p=' + dkimKey; + dkim.expected = `v=DKIM1; t=s; p=${publicKey}`; const [error, txtRecords] = await safe(dns.promises.resolve(dkim.domain, dkim.type, DNS_OPTIONS)); if (error) { @@ -276,7 +277,7 @@ async function checkDkim(mailDomain) { if (txtRecords.length !== 0) { dkim.value = txtRecords[0].join(''); const actual = txtToDict(dkim.value); - dkim.status = actual.p === dkimKey; + dkim.status = actual.p === publicKey; } return dkim; @@ -620,13 +621,13 @@ async function createMailConfig(mailFqdn, mailDomain) { const mailInDomains = mailDomains.filter(function (d) { return d.enabled; }).map(function (d) { return d.domain; }).join(','); // mail_domain is used for SRS - if (!safe.fs.writeFileSync(path.join(paths.ADDON_CONFIG_DIR, 'mail/mail.ini'), + if (!safe.fs.writeFileSync(`${paths.MAIL_CONFIG_DIR}/mail.ini`, `mail_in_domains=${mailInDomains}\nmail_out_domains=${mailOutDomains}\nmail_server_name=${mailFqdn}\nmail_domain=${mailDomain}\n\n`, 'utf8')) { throw new BoxError(BoxError.FS_ERROR, `Could not create mail var file: ${safe.error.message}`); } // enable_outbound makes plugin forward email for relayed mail. non-relayed mail always hits LMTP plugin first - if (!safe.fs.writeFileSync(path.join(paths.ADDON_CONFIG_DIR, 'mail/smtp_forward.ini'), 'enable_outbound=false\ndomain_selector=mail_from\n', 'utf8')) { + if (!safe.fs.writeFileSync(`${paths.MAIL_CONFIG_DIR}/smtp_forward.ini`, 'enable_outbound=false\ndomain_selector=mail_from\n', 'utf8')) { throw new BoxError(BoxError.FS_ERROR, `Could not create smtp forward file: ${safe.error.message}`); } @@ -635,13 +636,21 @@ async function createMailConfig(mailFqdn, mailDomain) { const catchAll = domain.catchAll.map(function (c) { return `${c}@${domain.domain}`; }).join(','); const mailFromValidation = domain.mailFromValidation; - if (!safe.fs.appendFileSync(path.join(paths.ADDON_CONFIG_DIR, 'mail/mail.ini'), + if (!safe.fs.appendFileSync(`${paths.MAIL_CONFIG_DIR}/mail.ini`, `[${domain.domain}]\ncatch_all=${catchAll}\nmail_from_validation=${mailFromValidation}\n\n`, 'utf8')) { throw new BoxError(BoxError.FS_ERROR, `Could not create mail var file: ${safe.error.message}`); } - if (!safe.fs.writeFileSync(`${paths.ADDON_CONFIG_DIR}/mail/banner/${domain.domain}.text`, domain.banner.text || '')) throw new BoxError(BoxError.FS_ERROR, `Could not create text banner file: ${safe.error.message}`); - if (!safe.fs.writeFileSync(`${paths.ADDON_CONFIG_DIR}/mail/banner/${domain.domain}.html`, domain.banner.html || '')) throw new BoxError(BoxError.FS_ERROR, `Could not create html banner file: ${safe.error.message}`); + if (!safe.fs.writeFileSync(`${paths.MAIL_CONFIG_DIR}/banner/${domain.domain}.text`, domain.banner.text || '')) throw new BoxError(BoxError.FS_ERROR, `Could not create text banner file: ${safe.error.message}`); + if (!safe.fs.writeFileSync(`${paths.MAIL_CONFIG_DIR}/banner/${domain.domain}.html`, domain.banner.html || '')) throw new BoxError(BoxError.FS_ERROR, `Could not create html banner file: ${safe.error.message}`); + + safe.fs.mkdirSync(`${paths.MAIL_CONFIG_DIR}/dkim/${domain.domain}`, { recursive: true }); + if (!safe.fs.writeFileSync(`${paths.MAIL_CONFIG_DIR}/dkim/${domain.domain}/public`, domain.dkimKey.publicKey)) throw new BoxError(BoxError.FS_ERROR, `Could not create public key file: ${safe.error.message}`); + if (!safe.fs.writeFileSync(`${paths.MAIL_CONFIG_DIR}/dkim/${domain.domain}/private`, domain.dkimKey.privateKey)) throw new BoxError(BoxError.FS_ERROR, `Could not create private key file: ${safe.error.message}`); + if (!safe.fs.writeFileSync(`${paths.MAIL_CONFIG_DIR}/dkim/${domain.domain}/selector`, domain.dkimSelector)) throw new BoxError(BoxError.FS_ERROR, `Could not create selector file: ${safe.error.message}`); + + // if the 'yellowtent' user of OS and the 'cloudron' user of mail container don't match, the keys become inaccessible by mail code + if (!safe.fs.chmodSync(`${paths.MAIL_CONFIG_DIR}/mail/${domain.domain}/private`, 0o644)) return new BoxError(BoxError.FS_ERROR, safe.error); const relay = domain.relay; @@ -654,7 +663,7 @@ async function createMailConfig(mailFqdn, mailDomain) { if (!enableRelay) continue; - if (!safe.fs.appendFileSync(paths.ADDON_CONFIG_DIR + '/mail/smtp_forward.ini', + if (!safe.fs.appendFileSync(paths.MAIL_CONFIG_DIR + '/smtp_forward.ini', `[${domain.domain}]\nenable_outbound=true\nhost=${host}\nport=${port}\nenable_tls=true\nauth_type=${authType}\nauth_user=${username}\nauth_pass=${password}\n\n`, 'utf8')) { throw new BoxError(BoxError.FS_ERROR, `Could not create mail var file: ${safe.error.message}`); } @@ -680,9 +689,9 @@ async function configureMail(mailFqdn, mailDomain, serviceConfig) { const bundle = await reverseProxy.getCertificatePath(mailFqdn, mailDomain); - const dhparamsFilePath = path.join(paths.ADDON_CONFIG_DIR, 'mail/dhparams.pem'); - const mailCertFilePath = path.join(paths.ADDON_CONFIG_DIR, 'mail/tls_cert.pem'); - const mailKeyFilePath = path.join(paths.ADDON_CONFIG_DIR, 'mail/tls_key.pem'); + const dhparamsFilePath = `${paths.MAIL_CONFIG_DIR}/dhparams.pem`; + const mailCertFilePath = `${paths.MAIL_CONFIG_DIR}/tls_cert.pem`; + const mailKeyFilePath = `${paths.MAIL_CONFIG_DIR}/tls_key.pem`; if (!safe.child_process.execSync(`cp ${paths.DHPARAMS_FILE} ${dhparamsFilePath}`)) throw new BoxError(BoxError.FS_ERROR, 'Could not copy dhparams:' + safe.error.message); if (!safe.child_process.execSync(`cp ${bundle.certFilePath} ${mailCertFilePath}`)) throw new BoxError(BoxError.FS_ERROR, 'Could not create cert file:' + safe.error.message); @@ -711,7 +720,7 @@ async function configureMail(mailFqdn, mailDomain, serviceConfig) { -e CLOUDRON_MAIL_TOKEN="${cloudronToken}" \ -e CLOUDRON_RELAY_TOKEN="${relayToken}" \ -v "${paths.MAIL_DATA_DIR}:/app/data" \ - -v "${paths.PLATFORM_DATA_DIR}/addons/mail:/etc/mail" \ + -v "${paths.MAIL_CONFIG_DIR}:/etc/mail" \ ${ports} \ --label isCloudronManaged=true \ ${readOnly} -v /run -v /tmp ${tag} ${cmd}`; @@ -842,58 +851,23 @@ async function txtRecordsWithSpf(domain, mailFqdn) { return txtRecords; } -function ensureDkimKeySync(mailDomain) { - assert.strictEqual(typeof mailDomain, 'object'); - - const domain = mailDomain.domain; - const dkimPath = path.join(paths.MAIL_DATA_DIR, `dkim/${domain}`); - const dkimPrivateKeyFile = path.join(dkimPath, 'private'); - const dkimPublicKeyFile = path.join(dkimPath, 'public'); - const dkimSelectorFile = path.join(dkimPath, 'selector'); - - if (safe.fs.existsSync(dkimPublicKeyFile) && - safe.fs.existsSync(dkimPublicKeyFile) && - safe.fs.existsSync(dkimPublicKeyFile)) { - debug(`Reusing existing DKIM keys for ${domain}`); - return null; - } - - debug(`Generating new DKIM keys for ${domain}`); - - if (!safe.fs.mkdirSync(dkimPath) && safe.error.code !== 'EEXIST') { - debug('Error creating dkim.', safe.error); - return new BoxError(BoxError.FS_ERROR, safe.error); - } +async function generateDkimKey() { + const publicKeyFilePath = path.join(os.tmpdir(), `dkim-${crypto.randomBytes(4).readUInt32LE(0)}.public`); + const privateKeyFilePath = path.join(os.tmpdir(), `dkim-${crypto.randomBytes(4).readUInt32LE(0)}.private`); // https://www.unlocktheinbox.com/dkim-key-length-statistics/ and https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-authentication-dkim-easy.html for key size - if (!safe.child_process.execSync('openssl genrsa -out ' + dkimPrivateKeyFile + ' 1024')) return new BoxError(BoxError.OPENSSL_ERROR, safe.error); - if (!safe.child_process.execSync('openssl rsa -in ' + dkimPrivateKeyFile + ' -out ' + dkimPublicKeyFile + ' -pubout -outform PEM')) return new BoxError(BoxError.OPENSSL_ERROR, safe.error); + if (!safe.child_process.execSync(`openssl genrsa -out ${privateKeyFilePath} 1024`)) return new BoxError(BoxError.OPENSSL_ERROR, safe.error); + if (!safe.child_process.execSync(`openssl rsa -in ${privateKeyFilePath} -out ${publicKeyFilePath} -pubout -outform PEM`)) return new BoxError(BoxError.OPENSSL_ERROR, safe.error); - if (!safe.fs.writeFileSync(dkimSelectorFile, mailDomain.dkimSelector, 'utf8')) return new BoxError(BoxError.FS_ERROR, safe.error); + const publicKey = safe.fs.readFileSync(publicKeyFilePath, 'utf8'); + if (!publicKey) throw new BoxError(BoxError.FS_ERROR, safe.error.message); + safe.fs.unlinkSync(publicKeyFilePath); - // if the 'yellowtent' user of OS and the 'cloudron' user of mail container don't match, the keys become inaccessible by mail code - if (!safe.fs.chmodSync(dkimPrivateKeyFile, 0o644)) return new BoxError(BoxError.FS_ERROR, safe.error); + const privateKey = safe.fs.readFileSync(privateKeyFilePath, 'utf8'); + if (!privateKey) throw new BoxError(BoxError.FS_ERROR, safe.error.message); + safe.fs.unlinkSync(privateKeyFilePath); - return null; -} - -function readDkimPublicKeySync(domain) { - assert.strictEqual(typeof domain, 'string'); - - var dkimPath = path.join(paths.MAIL_DATA_DIR, `dkim/${domain}`); - var dkimPublicKeyFile = path.join(dkimPath, 'public'); - - var publicKey = safe.fs.readFileSync(dkimPublicKeyFile, 'utf8'); - - if (publicKey === null) { - debug('Error reading dkim public key.', safe.error); - return null; - } - - // remove header, footer and new lines - publicKey = publicKey.split('\n').slice(1, -2).join(''); - - return publicKey; + return { publicKey, privateKey }; } async function upsertDnsRecords(domain, mailFqdn) { @@ -905,18 +879,14 @@ async function upsertDnsRecords(domain, mailFqdn) { const mailDomain = await getDomain(domain); if (!mailDomain) throw new BoxError(BoxError.NOT_FOUND, 'mail domain not found'); - let error = ensureDkimKeySync(mailDomain); - if (error) throw error; - if (process.env.BOX_ENV === 'test') return; - const dkimKey = readDkimPublicKeySync(domain); - if (!dkimKey) throw new BoxError(BoxError.FS_ERROR, 'Failed to read dkim public key'); + const publicKey = mailDomain.dkimKey.publicKey.split('\n').slice(1, -2).join(''); // remove header, footer and new lines // t=s limits the domainkey to this domain and not it's subdomains - const dkimRecord = { subdomain: `${mailDomain.dkimSelector}._domainkey`, domain: domain, type: 'TXT', values: [ `"v=DKIM1; t=s; p=${dkimKey}"` ] }; + const dkimRecord = { subdomain: `${mailDomain.dkimSelector}._domainkey`, domain: domain, type: 'TXT', values: [ `"v=DKIM1; t=s; p=${publicKey}"` ] }; - let records = []; + const records = []; records.push(dkimRecord); if (mailDomain.enabled) records.push({ subdomain: '', domain: domain, type: 'MX', values: [ '10 ' + mailFqdn + '.' ] }); @@ -997,7 +967,7 @@ async function onDomainAdded(domain) { if (!settings.mailFqdn()) return; // mail domain is not set yet (when provisioning) - await upsertDnsRecords(domain, settings.mailFqdn()); // do this first to ensure DKIM keys + await upsertDnsRecords(domain, settings.mailFqdn()); await restartMailIfActivated(); } diff --git a/src/paths.js b/src/paths.js index 3027379fa..235911bfc 100644 --- a/src/paths.js +++ b/src/paths.js @@ -28,6 +28,7 @@ exports = module.exports = { ACME_CHALLENGES_DIR: path.join(baseDir(), 'platformdata/acme'), ADDON_CONFIG_DIR: path.join(baseDir(), 'platformdata/addons'), + MAIL_CONFIG_DIR: path.join(baseDir(), 'platformdata/addons/mail'), COLLECTD_APPCONFIG_DIR: path.join(baseDir(), 'platformdata/collectd/collectd.conf.d'), LOGROTATE_CONFIG_DIR: path.join(baseDir(), 'platformdata/logrotate.d'), NGINX_CONFIG_DIR: path.join(baseDir(), 'platformdata/nginx'), diff --git a/src/routes/test/mail-test.js b/src/routes/test/mail-test.js index 84b16ab3f..5d255150a 100644 --- a/src/routes/test/mail-test.js +++ b/src/routes/test/mail-test.js @@ -15,7 +15,13 @@ const common = require('./common.js'), describe('Mail API', function () { const { setup, cleanup, serverUrl, owner, dashboardDomain } = common; - before(setup); + let publicKey; + before(async () => { + await setup(); + const d = await mail.getDomain(dashboardDomain); + publicKey = d.dkimKey.publicKey.split('\n').slice(1, -2).join(''); // remove header, footer and new lines + }); + after(cleanup); describe('crud', function () { @@ -111,7 +117,7 @@ describe('Mail API', function () { expect(response.body.dns.dkim.domain).to.eql(dkimDomain); expect(response.body.dns.dkim.type).to.eql('TXT'); expect(response.body.dns.dkim.value).to.eql(null); - expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + mail._readDkimPublicKeySync(dashboardDomain)); + expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + publicKey); expect(response.body.dns.dkim.status).to.eql(false); expect(response.body.dns.spf).to.be.an('object'); @@ -159,7 +165,7 @@ describe('Mail API', function () { expect(response.body.dns.spf.value).to.eql(null); expect(response.body.dns.dkim).to.be.an('object'); - expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + mail._readDkimPublicKeySync(dashboardDomain)); + expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + publicKey); expect(response.body.dns.dkim.status).to.eql(false); expect(response.body.dns.dkim.value).to.eql(null); @@ -184,7 +190,7 @@ describe('Mail API', function () { dnsAnswerQueue[mxDomain].MX = [ { priority: '20', exchange: settings.mailFqdn() }, { priority: '10', exchange: 'some.other.server' } ]; dnsAnswerQueue[dmarcDomain].TXT = [['v=DMARC2; p=reject; pct=100']]; - dnsAnswerQueue[dkimDomain].TXT = [['v=DKIM2; t=s; p=' + mail._readDkimPublicKeySync(dashboardDomain)]]; + dnsAnswerQueue[dkimDomain].TXT = [['v=DKIM2; t=s; p=' + publicKey]]; dnsAnswerQueue[spfDomain].TXT = [['v=spf1 a:random.com ~all']]; const response = await superagent.get(`${serverUrl}/api/v1/mail/${dashboardDomain}` + '/status') @@ -198,9 +204,9 @@ describe('Mail API', function () { expect(response.body.dns.spf.value).to.eql('v=spf1 a:random.com ~all'); expect(response.body.dns.dkim).to.be.an('object'); - expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + mail._readDkimPublicKeySync(dashboardDomain)); + expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + publicKey); expect(response.body.dns.dkim.status).to.eql(true); // as long as p= matches we are good - expect(response.body.dns.dkim.value).to.eql('v=DKIM2; t=s; p=' + mail._readDkimPublicKeySync(dashboardDomain)); + expect(response.body.dns.dkim.value).to.eql('v=DKIM2; t=s; p=' + publicKey); expect(response.body.dns.dmarc).to.be.an('object'); expect(response.body.dns.dmarc.expected).to.eql('v=DMARC1; p=reject; pct=100'); @@ -259,7 +265,7 @@ describe('Mail API', function () { dnsAnswerQueue[mxDomain].MX = [ { priority: '10', exchange: settings.mailFqdn() } ]; dnsAnswerQueue[dmarcDomain].TXT = [['v=DMARC1; p=reject; pct=100']]; - dnsAnswerQueue[dkimDomain].TXT = [['v=DKIM1; t=s; p=', mail._readDkimPublicKeySync(dashboardDomain) ]]; + dnsAnswerQueue[dkimDomain].TXT = [['v=DKIM1; t=s; p=', publicKey ]]; dnsAnswerQueue[spfDomain].TXT = [['v=spf1 a:' + settings.dashboardFqdn() + ' ~all']]; const response = await superagent.get(`${serverUrl}/api/v1/mail/${dashboardDomain}` + '/status') @@ -270,8 +276,8 @@ describe('Mail API', function () { expect(response.body.dns.dkim).to.be.an('object'); expect(response.body.dns.dkim.domain).to.eql(dkimDomain); expect(response.body.dns.dkim.type).to.eql('TXT'); - expect(response.body.dns.dkim.value).to.eql('v=DKIM1; t=s; p=' + mail._readDkimPublicKeySync(dashboardDomain)); - expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + mail._readDkimPublicKeySync(dashboardDomain)); + expect(response.body.dns.dkim.value).to.eql('v=DKIM1; t=s; p=' + publicKey); + expect(response.body.dns.dkim.expected).to.eql('v=DKIM1; t=s; p=' + publicKey); expect(response.body.dns.dkim.status).to.eql(true); expect(response.body.dns.spf).to.be.an('object');