diff --git a/src/docker.js b/src/docker.js index 8e3930ca9..7443b3574 100644 --- a/src/docker.js +++ b/src/docker.js @@ -204,18 +204,18 @@ async function getAddonMounts(app) { break; } case 'tls': { - const bundle = await reverseProxy.getCertificatePath(app.fqdn, app.domain); + const certificatePath = await reverseProxy.getCertificatePath(app.fqdn, app.domain); mounts.push({ Target: '/etc/certs/tls_cert.pem', - Source: bundle.certFilePath, + Source: certificatePath.certFilePath, Type: 'bind', ReadOnly: true }); mounts.push({ Target: '/etc/certs/tls_key.pem', - Source: bundle.keyFilePath, + Source: certificatePath.keyFilePath, Type: 'bind', ReadOnly: true }); diff --git a/src/mail.js b/src/mail.js index fa7fe6f23..1bdb673cc 100644 --- a/src/mail.js +++ b/src/mail.js @@ -698,15 +698,15 @@ async function configureMail(mailFqdn, mailDomain, serviceConfig) { const memory = system.getMemoryAllocation(memoryLimit); const cloudronToken = hat(8 * 128), relayToken = hat(8 * 128); - const bundle = await reverseProxy.getCertificatePath(mailFqdn, mailDomain); + const certificatePath = await reverseProxy.getCertificatePath(mailFqdn, mailDomain); 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); - if (!safe.child_process.execSync(`cp ${bundle.keyFilePath} ${mailKeyFilePath}`)) throw new BoxError(BoxError.FS_ERROR, 'Could not create key file:' + safe.error.message); + if (!safe.child_process.execSync(`cp ${certificatePath.certFilePath} ${mailCertFilePath}`)) throw new BoxError(BoxError.FS_ERROR, 'Could not create cert file:' + safe.error.message); + if (!safe.child_process.execSync(`cp ${certificatePath.keyFilePath} ${mailKeyFilePath}`)) throw new BoxError(BoxError.FS_ERROR, 'Could not create key 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(mailKeyFilePath, 0o644)) throw new BoxError(BoxError.FS_ERROR, `Could not chmod key file: ${safe.error.message}`); diff --git a/src/reverseproxy.js b/src/reverseproxy.js index 5e1c583e4..d39b390ca 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -140,12 +140,12 @@ function providerMatchesSync(domainObject, certFilePath, apiOptions) { // note: https://tools.ietf.org/html/rfc4346#section-7.4.2 (certificate_list) requires that the // servers certificate appears first (and not the intermediate cert) -function validateCertificate(subdomain, domainObject, bundle) { +function validateCertificate(subdomain, domainObject, certificate) { assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof domainObject, 'object'); - assert(bundle && typeof bundle, 'object'); + assert(certificate && typeof certificate, 'object'); - const { cert, key } = bundle; + const { cert, key } = certificate; // check for empty cert and key strings if (!cert && key) return new BoxError(BoxError.BAD_FIELD, 'missing cert'); @@ -215,13 +215,13 @@ async function generateFallbackCertificate(domain) { return { cert, key }; } -async function setFallbackCertificate(domain, bundle) { +async function setFallbackCertificate(domain, certificate) { assert.strictEqual(typeof domain, 'string'); - assert(bundle && typeof bundle === 'object'); + assert(certificate && typeof certificate === 'object'); debug(`setFallbackCertificate: setting certs for domain ${domain}`); - if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, `${domain}.host.cert`), bundle.cert)) throw new BoxError(BoxError.FS_ERROR, safe.error.message); - if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, `${domain}.host.key`), bundle.key)) throw new BoxError(BoxError.FS_ERROR, safe.error.message); + if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, `${domain}.host.cert`), certificate.cert)) throw new BoxError(BoxError.FS_ERROR, safe.error.message); + if (!safe.fs.writeFileSync(path.join(paths.NGINX_CERT_DIR, `${domain}.host.key`), certificate.key)) throw new BoxError(BoxError.FS_ERROR, safe.error.message); // TODO: maybe the cert is being used by the mail container await reload(); @@ -285,29 +285,29 @@ async function getCertificatePath(fqdn, domain) { const domainObject = await domains.get(domain); - const bundlePath = getUserCertificatePathSync(fqdn); // user cert always wins - if (fs.existsSync(bundlePath.certFilePath) && fs.existsSync(bundlePath.keyFilePath)) return bundlePath; + const userPath = getUserCertificatePathSync(fqdn); // user cert always wins + if (fs.existsSync(userPath.certFilePath) && fs.existsSync(userPath.keyFilePath)) return userPath; if (domainObject.tlsConfig.provider === 'fallback') return getFallbackCertificatePathSync(domain); - const acmeBundlePath = getAcmeCertificatePathSync(fqdn, domainObject); - if (fs.existsSync(acmeBundlePath.certFilePath) && fs.existsSync(acmeBundlePath.keyFilePath)) return acmeBundlePath; + const acmePath = getAcmeCertificatePathSync(fqdn, domainObject); + if (fs.existsSync(acmePath.certFilePath) && fs.existsSync(acmePath.keyFilePath)) return acmePath; return getFallbackCertificatePathSync(domain); } -async function checkAppCertificate(fqdn, domainObject) { +async function checkUserCertificate(fqdn, domainObject) { assert.strictEqual(typeof fqdn, 'string'); // this can contain wildcard domain (for alias domains) assert.strictEqual(typeof domainObject, 'object'); const subdomain = fqdn.substr(0, fqdn.length - domainObject.domain.length - 1); - const bundle = await apps.getCertificate(subdomain, domainObject.domain); - if (!bundle) return null; + const userCertificate = await apps.getCertificate(subdomain, domainObject.domain); + if (!userCertificate) return null; const { certFilePath, keyFilePath } = getUserCertificatePathSync(fqdn); - if (!safe.fs.writeFileSync(certFilePath, bundle.cert)) throw new BoxError(BoxError.FS_ERROR, `Failed to write certificate: ${safe.error.message}`); - if (!safe.fs.writeFileSync(keyFilePath, bundle.key)) throw new BoxError(BoxError.FS_ERROR, `Failed to write key: ${safe.error.message}`); + if (!safe.fs.writeFileSync(certFilePath, userCertificate.cert)) throw new BoxError(BoxError.FS_ERROR, `Failed to write certificate: ${safe.error.message}`); + if (!safe.fs.writeFileSync(keyFilePath, userCertificate.key)) throw new BoxError(BoxError.FS_ERROR, `Failed to write key: ${safe.error.message}`); return { certFilePath, keyFilePath }; } @@ -359,24 +359,24 @@ async function ensureCertificate(subdomain, domain, auditSource) { const domainObject = await domains.get(domain); - const bundle = await checkAppCertificate(subdomain, domainObject); - if (bundle) return { bundle, renewed: false }; + const userPath = await checkUserCertificate(subdomain, domainObject); + if (userPath) return { certificatePath: userPath, renewed: false }; if (domainObject.tlsConfig.provider === 'fallback') { debug(`ensureCertificate: ${subdomain} will use fallback certs`); - return { bundle: getFallbackCertificatePathSync(domain), renewed: false }; + return { certificatePath: getFallbackCertificatePathSync(domain), renewed: false }; } const { acmeApi, apiOptions } = await getAcmeApi(domainObject); let notAfter = null; - const [, currentBundle] = await safe(checkAcmeCertificate(subdomain, domainObject)); - if (currentBundle) { - debug(`ensureCertificate: ${subdomain} certificate already exists at ${currentBundle.keyFilePath}`); - notAfter = getExpiryDate(currentBundle.certFilePath); + const [, acmePath] = await safe(checkAcmeCertificate(subdomain, domainObject)); + if (acmePath) { + debug(`ensureCertificate: ${subdomain} certificate already exists at ${acmePath.keyFilePath}`); + notAfter = getExpiryDate(acmePath.certFilePath); const isExpiring = (notAfter - new Date()) <= (30 * 24 * 60 * 60 * 1000); // expiring in a month - if (!isExpiring && providerMatchesSync(domainObject, currentBundle.certFilePath, apiOptions)) return { bundle: currentBundle, renewed: false }; + if (!isExpiring && providerMatchesSync(domainObject, acmePath.certFilePath, apiOptions)) return { certificatePath: acmePath, renewed: false }; debug(`ensureCertificate: ${subdomain} cert requires renewal`); } else { debug(`ensureCertificate: ${subdomain} cert does not exist`); @@ -388,37 +388,37 @@ async function ensureCertificate(subdomain, domain, auditSource) { let [error] = await safe(acmeApi.getCertificate(subdomain, domain, acmePaths, apiOptions)); debug(`ensureCertificate: error: ${error ? error.message : 'null'} cert: ${acmePaths.certFilePath || 'null'}`); - await safe(eventlog.add(currentBundle ? eventlog.ACTION_CERTIFICATE_RENEWAL : eventlog.ACTION_CERTIFICATE_NEW, auditSource, { domain: subdomain, errorMessage: error ? error.message : '', notAfter })); + await safe(eventlog.add(acmePath ? eventlog.ACTION_CERTIFICATE_RENEWAL : eventlog.ACTION_CERTIFICATE_NEW, auditSource, { domain: subdomain, errorMessage: error ? error.message : '', notAfter })); - if (error && currentBundle && (notAfter - new Date() > 0)) { // still some life left in this certificate - debug('ensureCertificate: continue using existing bundle since renewal failed'); - return { bundle: currentBundle, renewed: false }; + if (error && acmePath && (notAfter - new Date() > 0)) { // still some life left in this certificate + debug('ensureCertificate: continue using existing certificate since renewal failed'); + return { certificatePath: acmePath, renewed: false }; } if (!error) { [error] = await safe(updateCertBlobs(subdomain, domainObject)); - if (!error) return { bundle: { certFilePath: acmePaths.certFilePath, keyFilePath: acmePaths.keyFilePath }, renewed: true }; + if (!error) return { certificatePath: { certFilePath: acmePaths.certFilePath, keyFilePath: acmePaths.keyFilePath }, renewed: true }; } debug(`ensureCertificate: renewal of ${subdomain} failed. using fallback certificates for ${domain}`); - return { bundle: getFallbackCertificatePathSync(domain), renewed: false }; + return { certificatePath: getFallbackCertificatePathSync(domain), renewed: false }; } -async function writeDashboardNginxConfig(fqdn, bundlePath) { +async function writeDashboardNginxConfig(fqdn, certificatePath) { assert.strictEqual(typeof fqdn, 'string'); - assert.strictEqual(typeof bundlePath, 'object'); + assert.strictEqual(typeof certificatePath, 'object'); const data = { sourceDir: path.resolve(__dirname, '..'), vhost: fqdn, hasIPv6: sysinfo.hasIPv6(), endpoint: 'dashboard', - certFilePath: bundlePath.certFilePath, - keyFilePath: bundlePath.keyFilePath, + certFilePath: certificatePath.certFilePath, + keyFilePath: certificatePath.keyFilePath, robotsTxtQuoted: JSON.stringify('User-agent: *\nDisallow: /\n'), proxyAuth: { enabled: false, id: null, location: nginxLocation('/') }, - ocsp: await isOcspEnabled(bundlePath.certFilePath) + ocsp: await isOcspEnabled(certificatePath.certFilePath) }; const nginxConf = ejs.render(NGINX_APPCONFIG_EJS, data); const nginxConfigFilename = path.join(paths.NGINX_APPCONFIG_DIR, `${fqdn}.conf`); @@ -434,9 +434,9 @@ async function writeDashboardConfig(domainObject) { debug(`writeDashboardConfig: writing admin config for ${domainObject.domain}`); const dashboardFqdn = dns.fqdn(constants.DASHBOARD_LOCATION, domainObject); - const bundle = await getCertificatePath(dashboardFqdn, domainObject.domain); + const certificatePath = await getCertificatePath(dashboardFqdn, domainObject.domain); - await writeDashboardNginxConfig(dashboardFqdn, bundle); + await writeDashboardNginxConfig(dashboardFqdn, certificatePath); } function getNginxConfigFilename(app, fqdn, type) { @@ -457,11 +457,11 @@ function getNginxConfigFilename(app, fqdn, type) { return path.join(paths.NGINX_APPCONFIG_DIR, `${app.id}${nginxConfigFilenameSuffix}.conf`); } -async function writeAppNginxConfig(app, fqdn, type, bundlePath) { +async function writeAppNginxConfig(app, fqdn, type, certificatePath) { assert.strictEqual(typeof app, 'object'); assert.strictEqual(typeof fqdn, 'string'); assert.strictEqual(typeof type, 'string'); - assert.strictEqual(typeof bundlePath, 'object'); + assert.strictEqual(typeof certificatePath, 'object'); const data = { sourceDir: path.resolve(__dirname, '..'), @@ -471,14 +471,14 @@ async function writeAppNginxConfig(app, fqdn, type, bundlePath) { port: null, endpoint: null, redirectTo: null, - certFilePath: bundlePath.certFilePath, - keyFilePath: bundlePath.keyFilePath, + certFilePath: certificatePath.certFilePath, + keyFilePath: certificatePath.keyFilePath, robotsTxtQuoted: null, cspQuoted: null, hideHeaders: [], proxyAuth: { enabled: false }, upstreamUri: '', // only for endpoint === external - ocsp: await isOcspEnabled(bundlePath.certFilePath) + ocsp: await isOcspEnabled(certificatePath.certFilePath) }; if (type === apps.LOCATION_TYPE_PRIMARY || type === apps.LOCATION_TYPE_ALIAS || type === apps.LOCATION_TYPE_SECONDARY) { @@ -549,8 +549,8 @@ async function writeAppConfigs(app) { if (!safe.fs.unlinkSync(keyFilePath)) debug(`Error removing key: ${safe.error.message}`); } - const bundle = await getCertificatePath(appDomain.fqdn, appDomain.domain); - await writeAppNginxConfig(app, appDomain.fqdn, appDomain.type, bundle); + const certificatePath = await getCertificatePath(appDomain.fqdn, appDomain.domain); + await writeAppNginxConfig(app, appDomain.fqdn, appDomain.type, certificatePath); } } @@ -619,7 +619,7 @@ async function renewCerts(options, auditSource, progressCallback) { progressCallback({ percent: progress, message: `Ensuring certs of ${appDomain.fqdn}` }); progress += Math.round(100/appDomains.length); - const { bundle, renewed } = await ensureCertificate(appDomain.fqdn, appDomain.domain, auditSource); + const { certificatePath, renewed } = await ensureCertificate(appDomain.fqdn, appDomain.domain, auditSource); if (renewed) renewedCerts.push(appDomain.fqdn); @@ -627,15 +627,15 @@ async function renewCerts(options, auditSource, progressCallback) { // hack to check if the app's cert changed or not. this doesn't handle prod/staging le change since they use same file name let currentNginxConfig = safe.fs.readFileSync(appDomain.nginxConfigFilename, 'utf8') || ''; - if (currentNginxConfig.includes(bundle.certFilePath)) continue; + if (currentNginxConfig.includes(certificatePath.certFilePath)) continue; - debug(`renewCerts: creating new nginx config since ${appDomain.nginxConfigFilename} does not have ${bundle.certFilePath}`); + debug(`renewCerts: creating new nginx config since ${appDomain.nginxConfigFilename} does not have ${certificatePath.certFilePath}`); // reconfigure since the cert changed if (appDomain.type === 'webadmin' || appDomain.type === 'webadmin+mail') { - await writeDashboardNginxConfig(settings.dashboardFqdn(), bundle); + await writeDashboardNginxConfig(settings.dashboardFqdn(), certificatePath); } else { - await writeAppNginxConfig(appDomain.app, appDomain.fqdn, appDomain.type, bundle); + await writeAppNginxConfig(appDomain.app, appDomain.fqdn, appDomain.type, certificatePath); } } diff --git a/src/userdirectory.js b/src/userdirectory.js index 4df520e4b..a2de383cf 100644 --- a/src/userdirectory.js +++ b/src/userdirectory.js @@ -278,11 +278,11 @@ async function start() { const domainObject = await domains.get(settings.dashboardDomain()); const dashboardFqdn = dns.fqdn(constants.DASHBOARD_LOCATION, domainObject); - const bundle = await reverseproxy.getCertificatePath(dashboardFqdn, domainObject.domain); + const certificatePath = await reverseproxy.getCertificatePath(dashboardFqdn, domainObject.domain); gServer = ldap.createServer({ - certificate: fs.readFileSync(bundle.certFilePath, 'utf8'), - key: fs.readFileSync(bundle.keyFilePath, 'utf8'), + certificate: fs.readFileSync(certificatePath.certFilePath, 'utf8'), + key: fs.readFileSync(certificatePath.keyFilePath, 'utf8'), log: logger });