Skip to content

Commit

Permalink
fix(SNI): disable SNI certificate autogeneration by default
Browse files Browse the repository at this point in the history
  • Loading branch information
andris9 committed Apr 29, 2024
1 parent ec4a2a2 commit ecbdc9b
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 10 deletions.
10 changes: 6 additions & 4 deletions config/acme.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ 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}"
# then there must be a CNAME record for this domain that points to "def.com".
# 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
Expand Down
9 changes: 9 additions & 0 deletions indexes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 34 additions & 6 deletions lib/cert-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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(
{
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions lib/tasks/acme-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit ecbdc9b

Please sign in to comment.