fix(SNI): disable SNI certificate autogeneration by default

This commit is contained in:
Andris Reinman 2024-04-29 13:17:51 +03:00
parent ec4a2a25d7
commit ecbdc9be5f
No known key found for this signature in database
GPG key ID: DC6C83F4D584D364
4 changed files with 55 additions and 10 deletions

View file

@ -19,7 +19,8 @@ keyExponent = 65537
[autogenerate]
# If enabled then automatically generates TLS certificates based on SNI servernames
enabled = true
enabled = false
[autogenerate.cnameMapping]
# Sudomain CNAME mapping
# "abc" = ["def.com"] means that if the SNI servername domain is "abc.{domain}"
@ -27,9 +28,10 @@ enabled = true
# If multiple CNAME targets are defined (eg ["def.com", "bef.com"], then at least 1 must match.
# Additionally, there must be at least 1 email account with "@{domain}" address.
# If there is no match, then TLS certificate is not generated.
imap = ["imap.example.com"]
smtp = ["smtp.example.com"]
pop3 = ["imap.example.com"]
# imap = ["imap.example.com"]
# smtp = ["smtp.example.com"]
# pop3 = ["imap.example.com"]
[agent]
# If enabled then starts a HTTP server that listens for ACME verification requests

View file

@ -631,6 +631,15 @@ indexes:
expires: 1
'_acme.lastRenewalCheck': 1
- collection: certs
index:
name: garbage_check
key:
acme: 1
updated: 1
expires: 1
autogenerated: 1
- collection: audits
index:
name: user_expire_time

View file

@ -19,7 +19,9 @@ const { promisify } = require('util');
const generateKeyPair = promisify(crypto.generateKeyPair);
const CERT_RENEW_TTL = 30 * 24 * 3600 * 1000;
const CERT_RENEW_DELAY = 24 * 3600 * 100;
const CERT_RENEW_DELAY = 24 * 3600 * 1000;
// delete uninitialized certificates after 1 day
const CERT_GARBAGE_TTL = 24 * 2300 * 1000;
class CertHandler {
constructor(options) {
@ -110,6 +112,22 @@ class CertHandler {
return query;
}
async clearGarbage() {
// delete expired and uninitialized SNI certificates
let r = await this.database.collection('certs').deleteMany({
acme: true,
updated: {
$lt: new Date(Date.now() + CERT_GARBAGE_TTL)
},
$or: [{ expires: { $exists: false } }, { expires: { $lt: new Date() } }],
autogenerated: true
});
if (r?.deletedCount) {
log.verbose('Certs', 'Deleted uninitialized and expired autogenerated certificates. count=%s', r?.deletedCount);
}
}
async getNextRenewal() {
let r = await this.database.collection('certs').findOneAndUpdate(
{
@ -391,7 +409,14 @@ class CertHandler {
{
servername
},
{ $set: certData, $inc: { v: 1 }, $setOnInsert: { servername, created: new Date() } },
{
$set: certData,
$inc: { v: 1 },
$setOnInsert: {
servername,
created: new Date()
}
},
{
upsert: true,
returnDocument: 'after'
@ -659,11 +684,14 @@ class CertHandler {
// not a FQDN
return false;
}
const subdomain = domain.substring(0, dotPos).toLowerCase().trim();
const maindomain = domain
.substring(dotPos + 1)
.toLowerCase()
.trim();
const maindomain = tools.normalizeDomain(domain.substring(dotPos + 1));
if (!this.acmeConfig.autogenerate?.cnameMapping?.hasOwnProperty(subdomain)) {
log.verbose('Certs', 'Skip ACME. reason="unsupported subdomain" action=precheck domain=%s', domain);
return false;
}
let subdomainTargets = [].concat(this.acmeConfig.autogenerate?.cnameMapping?.[subdomain] || []);
if (!subdomainTargets.length) {

View file

@ -6,6 +6,12 @@ const config = require('wild-config');
let run = async (task, data, options) => {
const { acquireCert, certHandler } = options;
try {
await certHandler.clearGarbage();
} catch (err) {
log.error('Tasks', 'task=acme-update id=%s action=clear-garbage error=%s', task._id, err.message);
}
let certData;
while ((certData = await certHandler.getNextRenewal())) {
let cert = await acquireCert(certData.servername, config.acme, certData, certHandler);