do not use bundle terminology
apparently, bundle is also like a cert chain
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user