diff --git a/src/cloudron.js b/src/cloudron.js index 7aa944d63..5f334c9ae 100644 --- a/src/cloudron.js +++ b/src/cloudron.js @@ -287,10 +287,11 @@ async function updateDashboardDomain(domain, auditSource) { safe(services.rebuildService('turn', auditSource), { debug }); // to update the realm variable } -async function renewCerts(auditSource) { +async function renewCerts(options, auditSource) { + assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof auditSource, 'object'); - const taskId = await tasks.add(tasks.TASK_CHECK_CERTS, [ auditSource ]); + const taskId = await tasks.add(tasks.TASK_CHECK_CERTS, [ options, auditSource ]); tasks.startTask(taskId, {}); return taskId; } diff --git a/src/cron.js b/src/cron.js index 60e17a80e..3058551e0 100644 --- a/src/cron.js +++ b/src/cron.js @@ -148,7 +148,7 @@ async function startJobs() { // randomized per Cloudron based on hourlySeed gJobs.certificateRenew = new CronJob({ cronTime: `00 10 ${hour} * * *`, - onTick: async () => await safe(cloudron.renewCerts(AuditSource.CRON), { debug }), + onTick: async () => await safe(cloudron.renewCerts({}, AuditSource.CRON), { debug }), start: true }); diff --git a/src/domains.js b/src/domains.js index 3e72d406c..10d044b64 100644 --- a/src/domains.js +++ b/src/domains.js @@ -243,6 +243,7 @@ async function setConfig(domain, data, auditSource) { if (result.affectedRows === 0) throw new BoxError(BoxError.NOT_FOUND, 'Domain not found'); if (fallbackCertificate) await reverseProxy.setFallbackCertificate(domain, fallbackCertificate); + if (!_.isEqual(domainObject.tlsConfig, tlsConfig.provider)) await reverseProxy.handleCertificateProviderChanged(domain); await eventlog.add(eventlog.ACTION_DOMAIN_UPDATE, auditSource, { domain, zoneName, provider }); } diff --git a/src/reverseproxy.js b/src/reverseproxy.js index 52912fb04..e4705c131 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -26,6 +26,8 @@ exports = module.exports = { removeAppConfigs, restoreFallbackCertificates, + + handleCertificateProviderChanged }; const acme2 = require('./acme2.js'), @@ -596,7 +598,8 @@ async function cleanupCerts(locations, auditSource, progressCallback) { debug('cleanupCerts: done'); } -async function checkCerts(auditSource, progressCallback) { +async function checkCerts(options, auditSource, progressCallback) { + assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof progressCallback, 'function'); @@ -611,13 +614,18 @@ async function checkCerts(auditSource, progressCallback) { await ensureCertificates(locations, auditSource, progressCallback); - progressCallback( { message: 'Rebuilding app configs' }); - for (const app of allApps) { - await writeAppConfigs(app); + if (options.rebuild || fs.existsSync(paths.REVERSE_PROXY_REBUILD_FILE)) { + progressCallback( { message: 'Rebuilding app configs' }); + for (const app of allApps) { + await writeAppConfigs(app); + } + await writeDashboardConfig(settings.dashboardDomain()); + safe.fs.unlinkSync(paths.REVERSE_PROXY_REBUILD_FILE); } - await writeDashboardConfig(settings.dashboardDomain()); + + // let other parts of code know about any cert changes. apptask can trigger a renewal, provider can change, for example await mail.handleCertChanged(); - await shell.promises.sudo('rebuildConfigs', [ RESTART_SERVICE_CMD, 'box' ], {}); + await shell.promises.sudo('rebuildConfigs', [ RESTART_SERVICE_CMD, 'box' ], {}); // directory server for (const app of allApps) { if (app.manifest.addons?.tls) await setupTlsAddon(app); } @@ -679,3 +687,9 @@ async function writeDefaultConfig(options) { await reload(); } + +async function handleCertificateProviderChanged(domain) { + assert.strictEqual(typeof domain, 'string'); + + safe.fs.appendFileSync(paths.REVERSE_PROXY_REBUILD_FILE, `${domain}\n`, 'utf8'); +} diff --git a/src/routes/cloudron.js b/src/routes/cloudron.js index dc8573d5f..e730c6eb2 100644 --- a/src/routes/cloudron.js +++ b/src/routes/cloudron.js @@ -300,7 +300,9 @@ async function prepareDashboardDomain(req, res, next) { } async function renewCerts(req, res, next) { - const [error, taskId] = await safe(cloudron.renewCerts(AuditSource.fromRequest(req))); + if ('rebuild' in req.body && typeof req.body !== 'boolean') return next(new HttpError(400, 'rebuild must be a boolean')); + + const [error, taskId] = await safe(cloudron.renewCerts(req.body, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(202, { taskId }));