Fixed CA handling for SNI certificates

This commit is contained in:
Andris Reinman 2022-08-16 15:22:47 +03:00
parent d8150b7d2b
commit 3f738389ba
No known key found for this signature in database
GPG key ID: DC6C83F4D584D364
5 changed files with 35 additions and 22 deletions

8
api.js
View file

@ -145,10 +145,7 @@ if (config.api.secure && certOptions.key) {
let httpsServerOptions = {}; let httpsServerOptions = {};
httpsServerOptions.key = certOptions.key; httpsServerOptions.key = certOptions.key;
if (certOptions.ca) { httpsServerOptions.cert = tools.buildCertChain(certOptions.cert, certOptions.ca);
httpsServerOptions.ca = certOptions.ca;
}
httpsServerOptions.cert = certOptions.cert;
let defaultSecureContext = tls.createSecureContext(httpsServerOptions); let defaultSecureContext = tls.createSecureContext(httpsServerOptions);
@ -534,10 +531,7 @@ module.exports = done => {
namespace: 'mail' namespace: 'mail'
}); });
if (config.acme && config.acme.agent && config.acme.agent.enabled) {
acmeRoutes(db, server, { disableRedirect: true }); acmeRoutes(db, server, { disableRedirect: true });
}
usersRoutes(db, server, userHandler, settingsHandler); usersRoutes(db, server, userHandler, settingsHandler);
addressesRoutes(db, server, userHandler, settingsHandler); addressesRoutes(db, server, userHandler, settingsHandler);
mailboxesRoutes(db, server, mailboxHandler); mailboxesRoutes(db, server, mailboxHandler);

View file

@ -54,7 +54,8 @@ const tlsDefaults = {
'-----END CERTIFICATE-----', '-----END CERTIFICATE-----',
honorCipherOrder: true, honorCipherOrder: true,
requestOCSP: false, requestOCSP: false,
sessionIdContext: crypto.createHash('sha1').update(process.argv.join(' ')).digest('hex').slice(0, 32) sessionIdContext: crypto.createHash('sha1').update(process.argv.join(' ')).digest('hex').slice(0, 32),
minVersion: 'TLSv1'
}; };
/** /**

View file

@ -295,8 +295,6 @@ class CertHandler {
certData.cert = cert; certData.cert = cert;
} }
certData.ca = [].concat(ca || []);
if (primaryCert) { if (primaryCert) {
try { try {
const parsedCert = forge.pki.certificateFromPem(primaryCert); const parsedCert = forge.pki.certificateFromPem(primaryCert);
@ -558,7 +556,11 @@ class CertHandler {
// key might be encrypted // key might be encrypted
let privateKey = await decrypt(certData.privateKey, this.secret, this.cipher); let privateKey = await decrypt(certData.privateKey, this.secret, this.cipher);
let serviceCtxOpts = { key: privateKey, cert: certData.cert, ca: certData.ca }; let serviceCtxOpts = {
key: privateKey,
cert: tools.buildCertChain(certData.cert, certData.ca)
};
for (let key of ['dhparam']) { for (let key of ['dhparam']) {
if (serverOptions[key]) { if (serverOptions[key]) {
serviceCtxOpts[key] = serverOptions[key]; serviceCtxOpts[key] = serverOptions[key];

View file

@ -4,6 +4,7 @@ const config = require('wild-config');
const fs = require('fs'); const fs = require('fs');
const db = require('./db'); const db = require('./db');
const CertHandler = require('./cert-handler'); const CertHandler = require('./cert-handler');
const { buildCertChain } = require('./tools');
const certs = new Map(); const certs = new Map();
const servers = []; const servers = [];
@ -92,12 +93,7 @@ module.exports.loadTLSOptions = (serverOptions, name) => {
if (serverCerts) { if (serverCerts) {
serverOptions.key = serverCerts.key; serverOptions.key = serverCerts.key;
serverOptions.cert = buildCertChain(serverCerts.cert, serverCerts.ca);
if (serverCerts.ca) {
serverOptions.ca = serverCerts.ca;
}
serverOptions.cert = serverCerts.cert;
if (serverCerts.dhparam) { if (serverCerts.dhparam) {
serverOptions.dhparam = serverCerts.dhparam; serverOptions.dhparam = serverCerts.dhparam;
@ -130,13 +126,12 @@ config.on('reload', () => {
let certOptions = {}; let certOptions = {};
if (serverCerts) { if (serverCerts) {
certOptions.key = serverCerts.key; certOptions.key = serverCerts.key;
if (serverCerts.ca) { certOptions.cert = buildCertChain(serverCerts.cert, serverCerts.ca);
certOptions.ca = serverCerts.ca;
}
certOptions.cert = serverCerts.cert;
if (serverCerts.dhparam) { if (serverCerts.dhparam) {
certOptions.dhparam = serverCerts.dhparam; certOptions.dhparam = serverCerts.dhparam;
} }
entry.server.updateSecureContext(certOptions); entry.server.updateSecureContext(certOptions);
} }
}); });

View file

@ -1,3 +1,4 @@
/* eslint no-control-regex: 0 */
'use strict'; 'use strict';
const os = require('os'); const os = require('os');
@ -586,6 +587,24 @@ function roundTime(seconds) {
return `${seconds} ${seconds === 1 ? 'second' : 'seconds'}`; return `${seconds} ${seconds === 1 ? 'second' : 'seconds'}`;
} }
function parsePemBundle(bundle) {
bundle = (bundle || '').toString().split(/\r?\n/).join('\x00');
let matches = bundle.match(/[-]{3,}BEGIN [^-]+[-]{3,}.*?[-]{3,}END [^-]+[-]{3,}/g);
if (matches) {
matches = Array.from(matches).map(cert => cert.replace(/\x00/g, '\n') + '\n');
}
return matches;
}
function buildCertChain(cert, ca) {
return [cert]
.concat(ca || [])
.flatMap(ca => ca)
.map(ca => ca.trim() + '\n')
.filter(ca => ca.trim())
.join('\n');
}
module.exports = { module.exports = {
normalizeAddress, normalizeAddress,
normalizeDomain, normalizeDomain,
@ -609,6 +628,8 @@ module.exports = {
formatFingerprint, formatFingerprint,
getEnabled2fa, getEnabled2fa,
roundTime, roundTime,
parsePemBundle,
buildCertChain,
formatMetaData: metaData => { formatMetaData: metaData => {
if (typeof metaData === 'string') { if (typeof metaData === 'string') {