Update autorenew and acme handling to allow passing challenge priority, and use dns-01 for autorenewal

develop
Thomas Lynch 9 months ago
parent f5d1054a5b
commit e483597b27
  1. 62
      acme.js
  2. 12
      autorenew/main.js

@ -4,6 +4,9 @@ const fs = require('fs').promises;
const acme = require('acme-client');
const dev = process.env.NODE_ENV !== 'production';
const redis = require('./redis.js');
const redlock = require('./redlock.js');
const psl = require('psl');
/**
* Function used to satisfy an ACME challenge
@ -27,17 +30,29 @@ async function challengeCreateFn(authz, challenge, keyAuthorization) {
/* dns-01 */
else if (challenge.type === 'dns-01') {
const dnsRecord = `_acme-challenge.${authz.identifier.value}`;
const recordValue = keyAuthorization;
console.log(`Creating TXT record for ${authz.identifier.value}: ${dnsRecord}`);
const record = { ttl: 300, text: recordValue, l: true, t: true };
let recordSetRaw = await redis.hget(`dns:${authz.identifier.value}.`, '_acme-challenge');
if (!recordSetRaw) {
recordSetRaw = {};
const parsed = psl.parse(authz.identifier.value);
const domain = parsed.domain;
let subdomain = `_acme-challenge`;
if (parsed.subdomain && parsed.subdomain.length > 0) {
subdomain += `.${parsed.subdomain}`;
}
const lock = await redlock.acquire([`lock:${domain}:${subdomain}`], 10000);
try {
const recordValue = keyAuthorization;
console.log(`Creating TXT record for "${subdomain}.${domain}" with value "${recordValue}"`);
const record = { ttl: 300, text: recordValue, l: true, t: true };
let recordSetRaw = await redis.hget(`dns:${domain}.`, subdomain);
if (!recordSetRaw) {
recordSetRaw = {};
}
recordSetRaw['txt'] = [record];
await redis.hset(`dns:${domain}.`, subdomain, recordSetRaw);
console.log(`Created TXT record for "${subdomain}.${domain}" with value "${recordValue}"`);
} catch(e) {
console.error(e);
} finally {
await lock.release();
}
recordSetRaw['txt'] = [record];
await redis.hset(`dns:${authz.identifier.value}.`, '_acme-challenge', recordSetRaw);
console.log(`Created TXT record "${dnsRecord}" with value "${recordValue}"`);
}
}
@ -63,11 +78,23 @@ async function challengeRemoveFn(authz, challenge, keyAuthorization) {
/* dns-01 */
else if (challenge.type === 'dns-01') {
const dnsRecord = `_acme-challenge.${authz.identifier.value}`;
const recordValue = keyAuthorization;
console.log(`Removing TXT record for ${authz.identifier.value}: ${dnsRecord}`);
await redis.hdel(`dns:${authz.identifier.value}.`, '_acme-challenge');
console.log(`Removed TXT record "${dnsRecord}" with value "${recordValue}"`);
const parsed = psl.parse(authz.identifier.value);
const domain = parsed.domain;
let subdomain = `_acme-challenge`;
if (parsed.subdomain && parsed.subdomain.length > 0) {
subdomain += `.${parsed.subdomain}`;
}
const lock = await redlock.acquire([`lock:${domain}:${subdomain}`], 10000);
try {
const recordValue = keyAuthorization;
console.log(`Removing TXT record "${subdomain}.${domain}" with value "${recordValue}"`);
await redis.hdel(`dns:${domain}.`, subdomain);
console.log(`Removed TXT record "${subdomain}.${domain}" with value "${recordValue}"`);
} catch(e) {
console.error(e);
} finally {
await lock.release();
}
}
}
@ -85,7 +112,7 @@ module.exports = {
});
},
generate: async function(domain, altnames, email) {
generate: async function(domain, altnames, email, challengePriority=['http-01', 'dns-01']) {
/* Create CSR */
const [key, csr] = await acme.crypto.createCsr({
commonName: domain,
@ -98,7 +125,8 @@ module.exports = {
termsOfServiceAgreed: true,
skipChallengeVerification: true,
challengeCreateFn,
challengeRemoveFn
challengeRemoveFn,
challengePriority,
});
/* Done */
const haproxyCert = `${cert.toString()}\n${key.toString()}`;

@ -24,6 +24,7 @@ async function main() {
function getCertsOlderThan(days=60) {
return db.db.collection('certs')
.find({
_id: 'basedflare.com',
date: {
'$lt': new Date(new Date().setDate(new Date().getDate()-days))
},
@ -47,7 +48,7 @@ async function postFileAll(path, options, file, fdOptions) {
async function updateCert(dbCert) {
const { subject, altnames, email } = dbCert;
console.log('Renew cert request:', subject, altnames, email);
const { csr, key, cert, haproxyCert, date } = await acme.generate(subject, altnames, email);
const { csr, key, cert, haproxyCert, date } = await acme.generate(subject, altnames, email, ['dns-01', 'http-01']);
const { message, description, file, storage_name: storageName } = await postFileAll('/v2/services/haproxy/storage/ssl_certificates', {
method: 'POST',
headers: {
@ -75,11 +76,11 @@ async function updateCert(dbCert) {
}
await db.db.collection('certs')
.updateOne({
_id: subject,
'_id': subject,
}, {
$set: update,
'$set': update,
}, {
upsert: true,
'upsert': true,
});
}
@ -89,13 +90,14 @@ async function loop() {
for (let c of expiringCerts) {
console.log('Renewing cert that expires', new Date(new Date(c.date).setDate(new Date(c.date).getDate()+90)), 'for', c.subject, c.altnames.toString());
await updateCert(c);
process.exit(0);
};
} catch(e) {
console.error(e);
console.log('Sleeping for', 60000);
setTimeout(loop, 60000);
return;
}
console.log('Sleeping for', 3600000);
setTimeout(loop, 3600000);
}

Loading…
Cancel
Save