BREAKING CHANGE(smartacme): Make wildcard certificates opt-in to fix HTTP-01 only configurations

This commit is contained in:
2025-05-19 10:01:31 +00:00
parent dcc89f0088
commit 086eea1aa2
8 changed files with 359 additions and 11 deletions

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartacme',
version: '7.3.4',
version: '8.0.0',
description: 'A TypeScript-based ACME client for LetsEncrypt certificate management with a focus on simplicity and power.'
}

View File

@ -221,8 +221,12 @@ export class SmartAcme {
* * retrieve it from the databse and return it
*
* @param domainArg
* @param options Optional configuration for certificate generation
*/
public async getCertificateForDomain(domainArg: string): Promise<SmartacmeCert> {
public async getCertificateForDomain(
domainArg: string,
options?: { includeWildcard?: boolean }
): Promise<SmartacmeCert> {
// Determine if this is a wildcard request (e.g., '*.example.com').
const isWildcardRequest = domainArg.startsWith('*.');
// Determine the base domain for certificate retrieval/issuance.
@ -259,12 +263,32 @@ export class SmartAcme {
// lets make sure others get the same interest
const currentDomainInterst = await this.interestMap.addInterest(certDomainName);
// Build identifiers array based on request
const identifiers = [];
if (isWildcardRequest) {
// If requesting a wildcard directly, only add the wildcard
identifiers.push({ type: 'dns', value: `*.${certDomainName}` });
} else {
// Add the regular domain
identifiers.push({ type: 'dns', value: certDomainName });
// Only add wildcard if explicitly requested
if (options?.includeWildcard) {
const hasDnsHandler = this.challengeHandlers.some((h) =>
h.getSupportedTypes().includes('dns-01'),
);
if (!hasDnsHandler) {
this.logger.log('warn', 'Wildcard certificate requested but no DNS-01 handler available. Skipping wildcard.');
} else {
identifiers.push({ type: 'dns', value: `*.${certDomainName}` });
}
}
}
/* Place new order with retry */
const order = await this.retry(() => this.client.createOrder({
identifiers: [
{ type: 'dns', value: certDomainName },
{ type: 'dns', value: `*.${certDomainName}` },
],
identifiers,
}), 'createOrder');
/* Get authorizations and select challenges */
@ -359,9 +383,25 @@ export class SmartAcme {
}
/* Finalize order */
const csrDomains = [];
let commonName: string;
if (isWildcardRequest) {
// For wildcard requests, use wildcard as common name
commonName = `*.${certDomainName}`;
csrDomains.push(certDomainName); // Add base domain as alt name
} else {
// For regular requests, use base domain as common name
commonName = certDomainName;
if (options?.includeWildcard && identifiers.some(id => id.value === `*.${certDomainName}`)) {
// If wildcard was successfully added, include it as alt name
csrDomains.push(`*.${certDomainName}`);
}
}
const [key, csr] = await plugins.acme.forge.createCsr({
commonName: `*.${certDomainName}`,
altNames: [certDomainName],
commonName,
altNames: csrDomains,
});
await this.retry(() => this.client.finalizeOrder(order, csr), 'finalizeOrder');