@push.rocks/smartacme 9.0.1 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/smartacme.classes.smartacme.d.ts +27 -3
- package/dist_ts/smartacme.classes.smartacme.js +113 -47
- package/package.json +2 -1
- package/readme.hints.md +15 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +3 -0
- package/ts/plugins.ts +2 -0
- package/ts/smartacme.classes.smartacme.ts +167 -55
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartacme',
|
|
6
|
-
version: '9.0
|
|
6
|
+
version: '9.1.0',
|
|
7
7
|
description: 'A TypeScript-based ACME client for LetsEncrypt certificate management with a focus on simplicity and power.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx1QkFBdUI7SUFDN0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDZHQUE2RztDQUMzSCxDQUFBIn0=
|
package/dist_ts/index.d.ts
CHANGED
package/dist_ts/plugins.d.ts
CHANGED
|
@@ -12,7 +12,8 @@ import * as smartnetwork from '@push.rocks/smartnetwork';
|
|
|
12
12
|
import * as smartunique from '@push.rocks/smartunique';
|
|
13
13
|
import * as smartstring from '@push.rocks/smartstring';
|
|
14
14
|
import * as smarttime from '@push.rocks/smarttime';
|
|
15
|
-
|
|
15
|
+
import * as taskbuffer from '@push.rocks/taskbuffer';
|
|
16
|
+
export { lik, smartdata, smartdelay, smartdnsClient, smartlog, smartnetwork, smartunique, smartstring, smarttime, taskbuffer, };
|
|
16
17
|
import * as tsclass from '@tsclass/tsclass';
|
|
17
18
|
export { tsclass };
|
|
18
19
|
import * as acme from './acme/index.js';
|
package/dist_ts/plugins.js
CHANGED
|
@@ -15,7 +15,8 @@ import * as smartnetwork from '@push.rocks/smartnetwork';
|
|
|
15
15
|
import * as smartunique from '@push.rocks/smartunique';
|
|
16
16
|
import * as smartstring from '@push.rocks/smartstring';
|
|
17
17
|
import * as smarttime from '@push.rocks/smarttime';
|
|
18
|
-
|
|
18
|
+
import * as taskbuffer from '@push.rocks/taskbuffer';
|
|
19
|
+
export { lik, smartdata, smartdelay, smartdnsClient, smartlog, smartnetwork, smartunique, smartstring, smarttime, taskbuffer, };
|
|
19
20
|
// @tsclass scope
|
|
20
21
|
import * as tsclass from '@tsclass/tsclass';
|
|
21
22
|
export { tsclass };
|
|
@@ -25,4 +26,4 @@ export { acme };
|
|
|
25
26
|
// local handlers for challenge types
|
|
26
27
|
import * as handlers from './handlers/index.js';
|
|
27
28
|
export { handlers };
|
|
28
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
29
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYztBQUNkLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBRTdCLE9BQU8sRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFFcEIsdUJBQXVCO0FBQ3ZCLE9BQU8sS0FBSyxVQUFVLE1BQU0sMkJBQTJCLENBQUM7QUFFeEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxDQUFDO0FBRXRCLG9CQUFvQjtBQUNwQixPQUFPLEtBQUssR0FBRyxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZDLE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFVBQVUsTUFBTSx3QkFBd0IsQ0FBQztBQUNyRCxPQUFPLEtBQUssY0FBYyxNQUFNLDZCQUE2QixDQUFDO0FBQzlELE9BQU8sS0FBSyxRQUFRLE1BQU0sc0JBQXNCLENBQUM7QUFDakQsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEtBQUssV0FBVyxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFDdkQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBRXJELE9BQU8sRUFDTCxHQUFHLEVBQ0gsU0FBUyxFQUNULFVBQVUsRUFDVixjQUFjLEVBQ2QsUUFBUSxFQUNSLFlBQVksRUFDWixXQUFXLEVBQ1gsV0FBVyxFQUNYLFNBQVMsRUFDVCxVQUFVLEdBQ1gsQ0FBQztBQUVGLGlCQUFpQjtBQUNqQixPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBRTVDLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQztBQUVuQix3Q0FBd0M7QUFDeEMsT0FBTyxLQUFLLElBQUksTUFBTSxpQkFBaUIsQ0FBQztBQUV4QyxPQUFPLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFDaEIscUNBQXFDO0FBQ3JDLE9BQU8sS0FBSyxRQUFRLE1BQU0scUJBQXFCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFFBQVEsRUFBRSxDQUFDIn0=
|
|
@@ -34,6 +34,21 @@ export interface ISmartAcmeOptions {
|
|
|
34
34
|
* Defaults to ['dns-01'] or first supported type from handlers.
|
|
35
35
|
*/
|
|
36
36
|
challengePriority?: string[];
|
|
37
|
+
/**
|
|
38
|
+
* Maximum number of concurrent ACME issuances across all domains.
|
|
39
|
+
* Defaults to 5.
|
|
40
|
+
*/
|
|
41
|
+
maxConcurrentIssuances?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Maximum ACME orders allowed within the sliding window.
|
|
44
|
+
* Defaults to 250 (conservative limit under Let's Encrypt's 300/3h).
|
|
45
|
+
*/
|
|
46
|
+
maxOrdersPerWindow?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Sliding window duration in milliseconds for rate limiting.
|
|
49
|
+
* Defaults to 3 hours (10_800_000 ms).
|
|
50
|
+
*/
|
|
51
|
+
orderWindowMs?: number;
|
|
37
52
|
}
|
|
38
53
|
/**
|
|
39
54
|
* class SmartAcme
|
|
@@ -57,9 +72,14 @@ export declare class SmartAcme {
|
|
|
57
72
|
private retryOptions;
|
|
58
73
|
private pendingChallenges;
|
|
59
74
|
private challengePriority;
|
|
60
|
-
private
|
|
75
|
+
private taskManager;
|
|
76
|
+
private certIssuanceTask;
|
|
61
77
|
private boundSigintHandler;
|
|
62
78
|
private boundSigtermHandler;
|
|
79
|
+
/**
|
|
80
|
+
* Exposes the aggregated task event stream for observing certificate issuance progress.
|
|
81
|
+
*/
|
|
82
|
+
get certIssuanceEvents(): plugins.taskbuffer.TaskManager['taskSubject'];
|
|
63
83
|
constructor(optionsArg: ISmartAcmeOptions);
|
|
64
84
|
/**
|
|
65
85
|
* starts the instance
|
|
@@ -87,8 +107,7 @@ export declare class SmartAcme {
|
|
|
87
107
|
* * if not in the database announce it
|
|
88
108
|
* * then get it from letsencrypt
|
|
89
109
|
* * store it
|
|
90
|
-
* *
|
|
91
|
-
* * retrieve it from the databse and return it
|
|
110
|
+
* * retrieve it from the database and return it
|
|
92
111
|
*
|
|
93
112
|
* @param domainArg
|
|
94
113
|
* @param options Optional configuration for certificate generation
|
|
@@ -96,4 +115,9 @@ export declare class SmartAcme {
|
|
|
96
115
|
getCertificateForDomain(domainArg: string, options?: {
|
|
97
116
|
includeWildcard?: boolean;
|
|
98
117
|
}): Promise<SmartacmeCert>;
|
|
118
|
+
/**
|
|
119
|
+
* Performs the actual ACME certificate issuance flow.
|
|
120
|
+
* Called by the certIssuanceTask's taskFunction.
|
|
121
|
+
*/
|
|
122
|
+
private performCertificateIssuance;
|
|
99
123
|
}
|
|
@@ -2,6 +2,12 @@ import * as plugins from './plugins.js';
|
|
|
2
2
|
import { SmartacmeCertMatcher } from './smartacme.classes.certmatcher.js';
|
|
3
3
|
import { commitinfo } from './00_commitinfo_data.js';
|
|
4
4
|
import { SmartacmeCert } from './smartacme.classes.cert.js';
|
|
5
|
+
const CERT_ISSUANCE_STEPS = [
|
|
6
|
+
{ name: 'prepare', description: 'Creating ACME order', percentage: 10 },
|
|
7
|
+
{ name: 'authorize', description: 'Solving ACME challenges', percentage: 40 },
|
|
8
|
+
{ name: 'finalize', description: 'Finalizing and getting cert', percentage: 30 },
|
|
9
|
+
{ name: 'store', description: 'Storing certificate', percentage: 20 },
|
|
10
|
+
];
|
|
5
11
|
/**
|
|
6
12
|
* class SmartAcme
|
|
7
13
|
* can be used for setting up communication with an ACME authority
|
|
@@ -13,6 +19,12 @@ import { SmartacmeCert } from './smartacme.classes.cert.js';
|
|
|
13
19
|
* ```
|
|
14
20
|
*/
|
|
15
21
|
export class SmartAcme {
|
|
22
|
+
/**
|
|
23
|
+
* Exposes the aggregated task event stream for observing certificate issuance progress.
|
|
24
|
+
*/
|
|
25
|
+
get certIssuanceEvents() {
|
|
26
|
+
return this.taskManager.taskSubject;
|
|
27
|
+
}
|
|
16
28
|
constructor(optionsArg) {
|
|
17
29
|
this.smartdns = new plugins.smartdnsClient.Smartdns({});
|
|
18
30
|
// track pending DNS challenges for graceful shutdown
|
|
@@ -41,8 +53,54 @@ export class SmartAcme {
|
|
|
41
53
|
optionsArg.challengePriority && optionsArg.challengePriority.length > 0
|
|
42
54
|
? optionsArg.challengePriority
|
|
43
55
|
: this.challengeHandlers.map((h) => h.getSupportedTypes()[0]);
|
|
44
|
-
//
|
|
45
|
-
this.
|
|
56
|
+
// ── TaskManager setup ──────────────────────────────────────────────────
|
|
57
|
+
this.taskManager = new plugins.taskbuffer.TaskManager();
|
|
58
|
+
// Constraint 1: Per-domain mutex — one issuance at a time per TLD, with result sharing
|
|
59
|
+
const certDomainMutex = new plugins.taskbuffer.TaskConstraintGroup({
|
|
60
|
+
name: 'cert-domain-mutex',
|
|
61
|
+
maxConcurrent: 1,
|
|
62
|
+
resultSharingMode: 'share-latest',
|
|
63
|
+
constraintKeyForExecution: (_task, input) => {
|
|
64
|
+
return input?.certDomainName ?? null;
|
|
65
|
+
},
|
|
66
|
+
shouldExecute: async (_task, input) => {
|
|
67
|
+
if (!input?.certDomainName || !this.certmanager)
|
|
68
|
+
return true;
|
|
69
|
+
// Safety net: if a valid cert is already cached, skip re-issuance
|
|
70
|
+
const existing = await this.certmanager.retrieveCertificate(input.certDomainName);
|
|
71
|
+
if (existing && !existing.shouldBeRenewed()) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
// Constraint 2: Global concurrency cap
|
|
78
|
+
const acmeGlobalConcurrency = new plugins.taskbuffer.TaskConstraintGroup({
|
|
79
|
+
name: 'acme-global-concurrency',
|
|
80
|
+
maxConcurrent: optionsArg.maxConcurrentIssuances ?? 5,
|
|
81
|
+
constraintKeyForExecution: () => 'global',
|
|
82
|
+
});
|
|
83
|
+
// Constraint 3: Account-level rate limiting
|
|
84
|
+
const acmeAccountRateLimit = new plugins.taskbuffer.TaskConstraintGroup({
|
|
85
|
+
name: 'acme-account-rate-limit',
|
|
86
|
+
rateLimit: {
|
|
87
|
+
maxPerWindow: optionsArg.maxOrdersPerWindow ?? 250,
|
|
88
|
+
windowMs: optionsArg.orderWindowMs ?? 10_800_000,
|
|
89
|
+
},
|
|
90
|
+
constraintKeyForExecution: () => 'account',
|
|
91
|
+
});
|
|
92
|
+
this.taskManager.addConstraintGroup(certDomainMutex);
|
|
93
|
+
this.taskManager.addConstraintGroup(acmeGlobalConcurrency);
|
|
94
|
+
this.taskManager.addConstraintGroup(acmeAccountRateLimit);
|
|
95
|
+
// Create the single reusable certificate issuance task
|
|
96
|
+
this.certIssuanceTask = new plugins.taskbuffer.Task({
|
|
97
|
+
name: 'cert-issuance',
|
|
98
|
+
steps: CERT_ISSUANCE_STEPS,
|
|
99
|
+
taskFunction: async (input) => {
|
|
100
|
+
return this.performCertificateIssuance(input);
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
this.taskManager.addTask(this.certIssuanceTask);
|
|
46
104
|
}
|
|
47
105
|
/**
|
|
48
106
|
* starts the instance
|
|
@@ -81,6 +139,8 @@ export class SmartAcme {
|
|
|
81
139
|
termsOfServiceAgreed: true,
|
|
82
140
|
contact: [`mailto:${this.options.accountEmail}`],
|
|
83
141
|
});
|
|
142
|
+
// Start the task manager
|
|
143
|
+
await this.taskManager.start();
|
|
84
144
|
// Setup graceful shutdown handlers (store references for removal in stop())
|
|
85
145
|
this.boundSigintHandler = () => this.handleSignal('SIGINT');
|
|
86
146
|
this.boundSigtermHandler = () => this.handleSignal('SIGTERM');
|
|
@@ -100,6 +160,8 @@ export class SmartAcme {
|
|
|
100
160
|
process.removeListener('SIGTERM', this.boundSigtermHandler);
|
|
101
161
|
this.boundSigtermHandler = null;
|
|
102
162
|
}
|
|
163
|
+
// Stop the task manager
|
|
164
|
+
await this.taskManager.stop();
|
|
103
165
|
// Destroy ACME HTTP transport (closes keep-alive sockets)
|
|
104
166
|
if (this.client) {
|
|
105
167
|
this.client.destroy();
|
|
@@ -183,8 +245,7 @@ export class SmartAcme {
|
|
|
183
245
|
* * if not in the database announce it
|
|
184
246
|
* * then get it from letsencrypt
|
|
185
247
|
* * store it
|
|
186
|
-
* *
|
|
187
|
-
* * retrieve it from the databse and return it
|
|
248
|
+
* * retrieve it from the database and return it
|
|
188
249
|
*
|
|
189
250
|
* @param domainArg
|
|
190
251
|
* @param options Optional configuration for certificate generation
|
|
@@ -206,32 +267,48 @@ export class SmartAcme {
|
|
|
206
267
|
}
|
|
207
268
|
// Retrieve any existing certificate record by base domain.
|
|
208
269
|
const retrievedCertificate = await this.certmanager.retrieveCertificate(certDomainName);
|
|
209
|
-
if (!retrievedCertificate
|
|
210
|
-
(await this.interestMap.checkInterest(certDomainName))) {
|
|
211
|
-
const existingCertificateInterest = this.interestMap.findInterest(certDomainName);
|
|
212
|
-
const certificate = existingCertificateInterest.interestFullfilled;
|
|
213
|
-
return certificate;
|
|
214
|
-
}
|
|
215
|
-
else if (retrievedCertificate && !retrievedCertificate.shouldBeRenewed()) {
|
|
270
|
+
if (retrievedCertificate && !retrievedCertificate.shouldBeRenewed()) {
|
|
216
271
|
return retrievedCertificate;
|
|
217
272
|
}
|
|
218
273
|
else if (retrievedCertificate && retrievedCertificate.shouldBeRenewed()) {
|
|
219
274
|
// Remove old certificate via certManager
|
|
220
275
|
await this.certmanager.deleteCertificate(certDomainName);
|
|
221
276
|
}
|
|
222
|
-
//
|
|
223
|
-
const
|
|
277
|
+
// Build issuance input and trigger the constrained task
|
|
278
|
+
const issuanceInput = {
|
|
279
|
+
certDomainName,
|
|
280
|
+
domainArg,
|
|
281
|
+
isWildcardRequest,
|
|
282
|
+
includeWildcard: options?.includeWildcard ?? false,
|
|
283
|
+
};
|
|
284
|
+
const result = await this.taskManager.triggerTaskConstrained(this.certIssuanceTask, issuanceInput);
|
|
285
|
+
// If we got a cert directly (either from execution or result sharing), return it
|
|
286
|
+
if (result != null) {
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
// If shouldExecute returned false (cert appeared in cache), read from cache
|
|
290
|
+
const cachedCert = await this.certmanager.retrieveCertificate(certDomainName);
|
|
291
|
+
if (cachedCert) {
|
|
292
|
+
return cachedCert;
|
|
293
|
+
}
|
|
294
|
+
throw new Error(`Certificate issuance failed for ${certDomainName}`);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Performs the actual ACME certificate issuance flow.
|
|
298
|
+
* Called by the certIssuanceTask's taskFunction.
|
|
299
|
+
*/
|
|
300
|
+
async performCertificateIssuance(input) {
|
|
301
|
+
const { certDomainName, isWildcardRequest, includeWildcard } = input;
|
|
302
|
+
// ── Step: prepare ─────────────────────────────────────────────────────
|
|
303
|
+
this.certIssuanceTask.notifyStep('prepare');
|
|
224
304
|
// Build identifiers array based on request
|
|
225
305
|
const identifiers = [];
|
|
226
306
|
if (isWildcardRequest) {
|
|
227
|
-
// If requesting a wildcard directly, only add the wildcard
|
|
228
307
|
identifiers.push({ type: 'dns', value: `*.${certDomainName}` });
|
|
229
308
|
}
|
|
230
309
|
else {
|
|
231
|
-
// Add the regular domain
|
|
232
310
|
identifiers.push({ type: 'dns', value: certDomainName });
|
|
233
|
-
|
|
234
|
-
if (options?.includeWildcard) {
|
|
311
|
+
if (includeWildcard) {
|
|
235
312
|
const hasDnsHandler = this.challengeHandlers.some((h) => h.getSupportedTypes().includes('dns-01'));
|
|
236
313
|
if (!hasDnsHandler) {
|
|
237
314
|
this.logger.log('warn', 'Wildcard certificate requested but no DNS-01 handler available. Skipping wildcard.');
|
|
@@ -245,6 +322,8 @@ export class SmartAcme {
|
|
|
245
322
|
const order = await this.retry(() => this.client.createOrder({
|
|
246
323
|
identifiers,
|
|
247
324
|
}), 'createOrder');
|
|
325
|
+
// ── Step: authorize ───────────────────────────────────────────────────
|
|
326
|
+
this.certIssuanceTask.notifyStep('authorize');
|
|
248
327
|
/* Get authorizations and select challenges */
|
|
249
328
|
const authorizations = await this.retry(() => this.client.getAuthorizations(order), 'getAuthorizations');
|
|
250
329
|
for (const authz of authorizations) {
|
|
@@ -268,15 +347,14 @@ export class SmartAcme {
|
|
|
268
347
|
}
|
|
269
348
|
const { type, handler } = selectedHandler;
|
|
270
349
|
// build handler input with keyAuthorization
|
|
271
|
-
let
|
|
350
|
+
let challengeInput;
|
|
272
351
|
// retrieve keyAuthorization for challenge
|
|
273
352
|
const keyAuth = await this.client.getChallengeKeyAuthorization(selectedChallengeArg);
|
|
274
353
|
if (type === 'dns-01') {
|
|
275
|
-
|
|
354
|
+
challengeInput = { type, hostName: `_acme-challenge.${authz.identifier.value}`, challenge: keyAuth };
|
|
276
355
|
}
|
|
277
356
|
else if (type === 'http-01') {
|
|
278
|
-
|
|
279
|
-
input = {
|
|
357
|
+
challengeInput = {
|
|
280
358
|
type,
|
|
281
359
|
token: selectedChallengeArg.token,
|
|
282
360
|
keyAuthorization: keyAuth,
|
|
@@ -284,25 +362,18 @@ export class SmartAcme {
|
|
|
284
362
|
};
|
|
285
363
|
}
|
|
286
364
|
else {
|
|
287
|
-
|
|
288
|
-
input = { type, keyAuthorization: keyAuth, ...selectedChallengeArg };
|
|
365
|
+
challengeInput = { type, keyAuthorization: keyAuth, ...selectedChallengeArg };
|
|
289
366
|
}
|
|
290
|
-
this.pendingChallenges.push(
|
|
367
|
+
this.pendingChallenges.push(challengeInput);
|
|
291
368
|
try {
|
|
292
|
-
|
|
293
|
-
await this.retry(() => handler.prepare(input), `${type}.prepare`);
|
|
294
|
-
// For DNS-01, wait for propagation before verification
|
|
369
|
+
await this.retry(() => handler.prepare(challengeInput), `${type}.prepare`);
|
|
295
370
|
if (type === 'dns-01') {
|
|
296
|
-
const dnsInput =
|
|
297
|
-
// Wait for authoritative DNS propagation before ACME verify
|
|
371
|
+
const dnsInput = challengeInput;
|
|
298
372
|
await this.retry(() => this.smartdns.checkUntilAvailable(dnsInput.hostName, 'TXT', dnsInput.challenge, 100, 5000), `${type}.propagation`);
|
|
299
|
-
// Extra cool-down to ensure ACME server sees the new TXT record
|
|
300
373
|
this.logger.log('info', 'Cooling down for 1 minute before ACME verification');
|
|
301
374
|
await plugins.smartdelay.delayFor(60000);
|
|
302
375
|
}
|
|
303
|
-
// Notify ACME server to complete the challenge
|
|
304
376
|
await this.retry(() => this.client.completeChallenge(selectedChallengeArg), `${type}.completeChallenge`);
|
|
305
|
-
// Wait for valid status (warnings on staging timeouts)
|
|
306
377
|
try {
|
|
307
378
|
await this.retry(() => this.client.waitForValidStatus(selectedChallengeArg), `${type}.waitForValidStatus`);
|
|
308
379
|
}
|
|
@@ -311,31 +382,28 @@ export class SmartAcme {
|
|
|
311
382
|
}
|
|
312
383
|
}
|
|
313
384
|
finally {
|
|
314
|
-
// Always cleanup resource
|
|
315
385
|
try {
|
|
316
|
-
await this.retry(() => handler.cleanup(
|
|
386
|
+
await this.retry(() => handler.cleanup(challengeInput), `${type}.cleanup`);
|
|
317
387
|
}
|
|
318
388
|
catch (err) {
|
|
319
389
|
await this.logger.log('error', `Error during ${type}.cleanup`, err);
|
|
320
390
|
}
|
|
321
391
|
finally {
|
|
322
|
-
this.pendingChallenges = this.pendingChallenges.filter((c) => c !==
|
|
392
|
+
this.pendingChallenges = this.pendingChallenges.filter((c) => c !== challengeInput);
|
|
323
393
|
}
|
|
324
394
|
}
|
|
325
395
|
}
|
|
326
|
-
|
|
396
|
+
// ── Step: finalize ────────────────────────────────────────────────────
|
|
397
|
+
this.certIssuanceTask.notifyStep('finalize');
|
|
327
398
|
const csrDomains = [];
|
|
328
399
|
let commonName;
|
|
329
400
|
if (isWildcardRequest) {
|
|
330
|
-
// For wildcard requests, use wildcard as common name
|
|
331
401
|
commonName = `*.${certDomainName}`;
|
|
332
|
-
csrDomains.push(certDomainName);
|
|
402
|
+
csrDomains.push(certDomainName);
|
|
333
403
|
}
|
|
334
404
|
else {
|
|
335
|
-
// For regular requests, use base domain as common name
|
|
336
405
|
commonName = certDomainName;
|
|
337
|
-
if (
|
|
338
|
-
// If wildcard was successfully added, include it as alt name
|
|
406
|
+
if (includeWildcard && identifiers.some(id => id.value === `*.${certDomainName}`)) {
|
|
339
407
|
csrDomains.push(`*.${certDomainName}`);
|
|
340
408
|
}
|
|
341
409
|
}
|
|
@@ -345,8 +413,8 @@ export class SmartAcme {
|
|
|
345
413
|
});
|
|
346
414
|
await this.retry(() => this.client.finalizeOrder(order, csr), 'finalizeOrder');
|
|
347
415
|
const cert = await this.retry(() => this.client.getCertificate(order), 'getCertificate');
|
|
348
|
-
|
|
349
|
-
|
|
416
|
+
// ── Step: store ───────────────────────────────────────────────────────
|
|
417
|
+
this.certIssuanceTask.notifyStep('store');
|
|
350
418
|
const certRecord = new SmartacmeCert({
|
|
351
419
|
id: plugins.smartunique.shortId(),
|
|
352
420
|
domainName: certDomainName,
|
|
@@ -358,9 +426,7 @@ export class SmartAcme {
|
|
|
358
426
|
});
|
|
359
427
|
await this.certmanager.storeCertificate(certRecord);
|
|
360
428
|
const newCertificate = await this.certmanager.retrieveCertificate(certDomainName);
|
|
361
|
-
|
|
362
|
-
currentDomainInterst.destroy();
|
|
363
|
-
return newCertificate;
|
|
429
|
+
return newCertificate ?? certRecord;
|
|
364
430
|
}
|
|
365
431
|
}
|
|
366
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBRXhDLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFzQzVEOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sT0FBTyxTQUFTO0lBK0JwQixZQUFZLFVBQTZCO1FBMUJqQyxhQUFRLEdBQUcsSUFBSSxPQUFPLENBQUMsY0FBYyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQWdCM0QscURBQXFEO1FBQzdDLHNCQUFpQixHQUE0QyxFQUFFLENBQUM7UUFLeEUseURBQXlEO1FBQ2pELHVCQUFrQixHQUF3QixJQUFJLENBQUM7UUFDL0Msd0JBQW1CLEdBQXdCLElBQUksQ0FBQztRQUd0RCxJQUFJLENBQUMsT0FBTyxHQUFHLFVBQVUsQ0FBQztRQUMxQixJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLG1CQUFtQixDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3hFLCtDQUErQztRQUMvQyxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQzVCLG1DQUFtQztRQUNuQyxJQUFJLENBQUMsWUFBWSxHQUFHO1lBQ2xCLE9BQU8sRUFBRSxVQUFVLENBQUMsWUFBWSxFQUFFLE9BQU8sSUFBSSxFQUFFO1lBQy9DLE1BQU0sRUFBRSxVQUFVLENBQUMsWUFBWSxFQUFFLE1BQU0sSUFBSSxDQUFDO1lBQzVDLFlBQVksRUFBRSxVQUFVLENBQUMsWUFBWSxFQUFFLFlB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
|
|
432
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBRXhDLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFXNUQsTUFBTSxtQkFBbUIsR0FBRztJQUMxQixFQUFFLElBQUksRUFBRSxTQUFTLEVBQUksV0FBVyxFQUFFLHFCQUFxQixFQUFVLFVBQVUsRUFBRSxFQUFFLEVBQUU7SUFDakYsRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSx5QkFBeUIsRUFBTSxVQUFVLEVBQUUsRUFBRSxFQUFFO0lBQ2pGLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRyxXQUFXLEVBQUUsNkJBQTZCLEVBQUUsVUFBVSxFQUFFLEVBQUUsRUFBRTtJQUNqRixFQUFFLElBQUksRUFBRSxPQUFPLEVBQU0sV0FBVyxFQUFFLHFCQUFxQixFQUFVLFVBQVUsRUFBRSxFQUFFLEVBQUU7Q0FDekUsQ0FBQztBQXFEWDs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLE9BQU8sU0FBUztJQWlDcEI7O09BRUc7SUFDSCxJQUFXLGtCQUFrQjtRQUMzQixPQUFPLElBQUksQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDO0lBQ3RDLENBQUM7SUFFRCxZQUFZLFVBQTZCO1FBbkNqQyxhQUFRLEdBQUcsSUFBSSxPQUFPLENBQUMsY0FBYyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQWdCM0QscURBQXFEO1FBQzdDLHNCQUFpQixHQUE0QyxFQUFFLENBQUM7UUFPeEUseURBQXlEO1FBQ2pE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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartacme",
|
|
3
|
-
"version": "9.0
|
|
3
|
+
"version": "9.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A TypeScript-based ACME client for LetsEncrypt certificate management with a focus on simplicity and power.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@peculiar/x509": "^1.14.3",
|
|
44
44
|
"@push.rocks/lik": "^6.2.2",
|
|
45
45
|
"@push.rocks/smartdata": "^7.0.15",
|
|
46
|
+
"@push.rocks/taskbuffer": "^6.1.0",
|
|
46
47
|
"@push.rocks/smartdelay": "^3.0.5",
|
|
47
48
|
"@push.rocks/smartdns": "^7.8.1",
|
|
48
49
|
"@push.rocks/smartlog": "^3.1.10",
|
package/readme.hints.md
CHANGED
|
@@ -28,6 +28,21 @@ Key files:
|
|
|
28
28
|
|
|
29
29
|
Usage in `ts/plugins.ts`: `import * as acme from './acme/index.js'` (replaces `acme-client`)
|
|
30
30
|
|
|
31
|
+
## Concurrency & Rate Limiting (taskbuffer integration)
|
|
32
|
+
|
|
33
|
+
As of v9.1.0, `@push.rocks/lik.InterestMap` was replaced with `@push.rocks/taskbuffer.TaskManager` for coordinating concurrent certificate requests. This provides:
|
|
34
|
+
|
|
35
|
+
- **Per-domain mutex** (`cert-domain-mutex`): Only one ACME issuance per TLD at a time, with `resultSharingMode: 'share-latest'` so queued callers get the same result without re-issuing.
|
|
36
|
+
- **Global concurrency cap** (`acme-global-concurrency`): Limits total parallel ACME operations (default 5, configurable via `maxConcurrentIssuances`).
|
|
37
|
+
- **Account-level rate limiting** (`acme-account-rate-limit`): Sliding-window rate limit (default 250 orders per 3 hours, configurable via `maxOrdersPerWindow`/`orderWindowMs`) to stay under Let's Encrypt limits.
|
|
38
|
+
- **Step-based progress**: The cert issuance task uses `notifyStep()` for prepare/authorize/finalize/store phases, observable via `smartAcme.certIssuanceEvents`.
|
|
39
|
+
|
|
40
|
+
Key implementation details:
|
|
41
|
+
- A single reusable `Task` named `cert-issuance` handles all domains via `triggerTaskConstrained()` with different inputs.
|
|
42
|
+
- The `shouldExecute` callback on the domain mutex checks the certmanager cache as a safety net.
|
|
43
|
+
- `TaskManager.start()` is called in `SmartAcme.start()` and `TaskManager.stop()` in `SmartAcme.stop()`.
|
|
44
|
+
- The "no cronjobs specified" log messages during tests come from taskbuffer's internal CronManager polling — harmless noise when no cron tasks are scheduled.
|
|
45
|
+
|
|
31
46
|
## Dependency Notes
|
|
32
47
|
|
|
33
48
|
- `acme-client` was replaced with custom implementation in `ts/acme/` + `@peculiar/x509` for CSR generation
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/index.ts
CHANGED
package/ts/plugins.ts
CHANGED
|
@@ -19,6 +19,7 @@ import * as smartnetwork from '@push.rocks/smartnetwork';
|
|
|
19
19
|
import * as smartunique from '@push.rocks/smartunique';
|
|
20
20
|
import * as smartstring from '@push.rocks/smartstring';
|
|
21
21
|
import * as smarttime from '@push.rocks/smarttime';
|
|
22
|
+
import * as taskbuffer from '@push.rocks/taskbuffer';
|
|
22
23
|
|
|
23
24
|
export {
|
|
24
25
|
lik,
|
|
@@ -30,6 +31,7 @@ export {
|
|
|
30
31
|
smartunique,
|
|
31
32
|
smartstring,
|
|
32
33
|
smarttime,
|
|
34
|
+
taskbuffer,
|
|
33
35
|
};
|
|
34
36
|
|
|
35
37
|
// @tsclass scope
|
|
@@ -4,6 +4,22 @@ import { SmartacmeCertMatcher } from './smartacme.classes.certmatcher.js';
|
|
|
4
4
|
import { commitinfo } from './00_commitinfo_data.js';
|
|
5
5
|
import { SmartacmeCert } from './smartacme.classes.cert.js';
|
|
6
6
|
|
|
7
|
+
// ── Types & constants for certificate issuance task ──────────────────────────
|
|
8
|
+
|
|
9
|
+
interface ICertIssuanceInput {
|
|
10
|
+
certDomainName: string;
|
|
11
|
+
domainArg: string;
|
|
12
|
+
isWildcardRequest: boolean;
|
|
13
|
+
includeWildcard: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const CERT_ISSUANCE_STEPS = [
|
|
17
|
+
{ name: 'prepare', description: 'Creating ACME order', percentage: 10 },
|
|
18
|
+
{ name: 'authorize', description: 'Solving ACME challenges', percentage: 40 },
|
|
19
|
+
{ name: 'finalize', description: 'Finalizing and getting cert', percentage: 30 },
|
|
20
|
+
{ name: 'store', description: 'Storing certificate', percentage: 20 },
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
7
23
|
/**
|
|
8
24
|
* the options for the class @see SmartAcme
|
|
9
25
|
*/
|
|
@@ -38,6 +54,21 @@ export interface ISmartAcmeOptions {
|
|
|
38
54
|
* Defaults to ['dns-01'] or first supported type from handlers.
|
|
39
55
|
*/
|
|
40
56
|
challengePriority?: string[];
|
|
57
|
+
/**
|
|
58
|
+
* Maximum number of concurrent ACME issuances across all domains.
|
|
59
|
+
* Defaults to 5.
|
|
60
|
+
*/
|
|
61
|
+
maxConcurrentIssuances?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Maximum ACME orders allowed within the sliding window.
|
|
64
|
+
* Defaults to 250 (conservative limit under Let's Encrypt's 300/3h).
|
|
65
|
+
*/
|
|
66
|
+
maxOrdersPerWindow?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Sliding window duration in milliseconds for rate limiting.
|
|
69
|
+
* Defaults to 3 hours (10_800_000 ms).
|
|
70
|
+
*/
|
|
71
|
+
orderWindowMs?: number;
|
|
41
72
|
}
|
|
42
73
|
|
|
43
74
|
/**
|
|
@@ -75,12 +106,21 @@ export class SmartAcme {
|
|
|
75
106
|
private pendingChallenges: plugins.tsclass.network.IDnsChallenge[] = [];
|
|
76
107
|
// priority order of challenge types
|
|
77
108
|
private challengePriority: string[];
|
|
78
|
-
//
|
|
79
|
-
private
|
|
109
|
+
// TaskManager for coordinating concurrent certificate requests
|
|
110
|
+
private taskManager: plugins.taskbuffer.TaskManager;
|
|
111
|
+
// Single reusable task for certificate issuance
|
|
112
|
+
private certIssuanceTask: plugins.taskbuffer.Task<undefined, typeof CERT_ISSUANCE_STEPS>;
|
|
80
113
|
// bound signal handlers so they can be removed on stop()
|
|
81
114
|
private boundSigintHandler: (() => void) | null = null;
|
|
82
115
|
private boundSigtermHandler: (() => void) | null = null;
|
|
83
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Exposes the aggregated task event stream for observing certificate issuance progress.
|
|
119
|
+
*/
|
|
120
|
+
public get certIssuanceEvents(): plugins.taskbuffer.TaskManager['taskSubject'] {
|
|
121
|
+
return this.taskManager.taskSubject;
|
|
122
|
+
}
|
|
123
|
+
|
|
84
124
|
constructor(optionsArg: ISmartAcmeOptions) {
|
|
85
125
|
this.options = optionsArg;
|
|
86
126
|
this.logger = plugins.smartlog.Smartlog.createForCommitinfo(commitinfo);
|
|
@@ -105,8 +145,60 @@ export class SmartAcme {
|
|
|
105
145
|
optionsArg.challengePriority && optionsArg.challengePriority.length > 0
|
|
106
146
|
? optionsArg.challengePriority
|
|
107
147
|
: this.challengeHandlers.map((h) => h.getSupportedTypes()[0]);
|
|
108
|
-
|
|
109
|
-
|
|
148
|
+
|
|
149
|
+
// ── TaskManager setup ──────────────────────────────────────────────────
|
|
150
|
+
this.taskManager = new plugins.taskbuffer.TaskManager();
|
|
151
|
+
|
|
152
|
+
// Constraint 1: Per-domain mutex — one issuance at a time per TLD, with result sharing
|
|
153
|
+
const certDomainMutex = new plugins.taskbuffer.TaskConstraintGroup({
|
|
154
|
+
name: 'cert-domain-mutex',
|
|
155
|
+
maxConcurrent: 1,
|
|
156
|
+
resultSharingMode: 'share-latest',
|
|
157
|
+
constraintKeyForExecution: (_task, input?: ICertIssuanceInput) => {
|
|
158
|
+
return input?.certDomainName ?? null;
|
|
159
|
+
},
|
|
160
|
+
shouldExecute: async (_task, input?: ICertIssuanceInput) => {
|
|
161
|
+
if (!input?.certDomainName || !this.certmanager) return true;
|
|
162
|
+
// Safety net: if a valid cert is already cached, skip re-issuance
|
|
163
|
+
const existing = await this.certmanager.retrieveCertificate(input.certDomainName);
|
|
164
|
+
if (existing && !existing.shouldBeRenewed()) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Constraint 2: Global concurrency cap
|
|
172
|
+
const acmeGlobalConcurrency = new plugins.taskbuffer.TaskConstraintGroup({
|
|
173
|
+
name: 'acme-global-concurrency',
|
|
174
|
+
maxConcurrent: optionsArg.maxConcurrentIssuances ?? 5,
|
|
175
|
+
constraintKeyForExecution: () => 'global',
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Constraint 3: Account-level rate limiting
|
|
179
|
+
const acmeAccountRateLimit = new plugins.taskbuffer.TaskConstraintGroup({
|
|
180
|
+
name: 'acme-account-rate-limit',
|
|
181
|
+
rateLimit: {
|
|
182
|
+
maxPerWindow: optionsArg.maxOrdersPerWindow ?? 250,
|
|
183
|
+
windowMs: optionsArg.orderWindowMs ?? 10_800_000,
|
|
184
|
+
},
|
|
185
|
+
constraintKeyForExecution: () => 'account',
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
this.taskManager.addConstraintGroup(certDomainMutex);
|
|
189
|
+
this.taskManager.addConstraintGroup(acmeGlobalConcurrency);
|
|
190
|
+
this.taskManager.addConstraintGroup(acmeAccountRateLimit);
|
|
191
|
+
|
|
192
|
+
// Create the single reusable certificate issuance task
|
|
193
|
+
this.certIssuanceTask = new plugins.taskbuffer.Task({
|
|
194
|
+
name: 'cert-issuance',
|
|
195
|
+
steps: CERT_ISSUANCE_STEPS,
|
|
196
|
+
taskFunction: async (input: ICertIssuanceInput) => {
|
|
197
|
+
return this.performCertificateIssuance(input);
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
this.taskManager.addTask(this.certIssuanceTask);
|
|
110
202
|
}
|
|
111
203
|
|
|
112
204
|
/**
|
|
@@ -149,6 +241,10 @@ export class SmartAcme {
|
|
|
149
241
|
termsOfServiceAgreed: true,
|
|
150
242
|
contact: [`mailto:${this.options.accountEmail}`],
|
|
151
243
|
});
|
|
244
|
+
|
|
245
|
+
// Start the task manager
|
|
246
|
+
await this.taskManager.start();
|
|
247
|
+
|
|
152
248
|
// Setup graceful shutdown handlers (store references for removal in stop())
|
|
153
249
|
this.boundSigintHandler = () => this.handleSignal('SIGINT');
|
|
154
250
|
this.boundSigtermHandler = () => this.handleSignal('SIGTERM');
|
|
@@ -169,6 +265,8 @@ export class SmartAcme {
|
|
|
169
265
|
process.removeListener('SIGTERM', this.boundSigtermHandler);
|
|
170
266
|
this.boundSigtermHandler = null;
|
|
171
267
|
}
|
|
268
|
+
// Stop the task manager
|
|
269
|
+
await this.taskManager.stop();
|
|
172
270
|
// Destroy ACME HTTP transport (closes keep-alive sockets)
|
|
173
271
|
if (this.client) {
|
|
174
272
|
this.client.destroy();
|
|
@@ -255,8 +353,7 @@ export class SmartAcme {
|
|
|
255
353
|
* * if not in the database announce it
|
|
256
354
|
* * then get it from letsencrypt
|
|
257
355
|
* * store it
|
|
258
|
-
* *
|
|
259
|
-
* * retrieve it from the databse and return it
|
|
356
|
+
* * retrieve it from the database and return it
|
|
260
357
|
*
|
|
261
358
|
* @param domainArg
|
|
262
359
|
* @param options Optional configuration for certificate generation
|
|
@@ -284,35 +381,59 @@ export class SmartAcme {
|
|
|
284
381
|
// Retrieve any existing certificate record by base domain.
|
|
285
382
|
const retrievedCertificate = await this.certmanager.retrieveCertificate(certDomainName);
|
|
286
383
|
|
|
287
|
-
if (
|
|
288
|
-
!retrievedCertificate &&
|
|
289
|
-
(await this.interestMap.checkInterest(certDomainName))
|
|
290
|
-
) {
|
|
291
|
-
const existingCertificateInterest = this.interestMap.findInterest(certDomainName);
|
|
292
|
-
const certificate = existingCertificateInterest.interestFullfilled;
|
|
293
|
-
return certificate;
|
|
294
|
-
} else if (retrievedCertificate && !retrievedCertificate.shouldBeRenewed()) {
|
|
384
|
+
if (retrievedCertificate && !retrievedCertificate.shouldBeRenewed()) {
|
|
295
385
|
return retrievedCertificate;
|
|
296
386
|
} else if (retrievedCertificate && retrievedCertificate.shouldBeRenewed()) {
|
|
297
387
|
// Remove old certificate via certManager
|
|
298
388
|
await this.certmanager.deleteCertificate(certDomainName);
|
|
299
389
|
}
|
|
300
390
|
|
|
301
|
-
//
|
|
302
|
-
const
|
|
391
|
+
// Build issuance input and trigger the constrained task
|
|
392
|
+
const issuanceInput: ICertIssuanceInput = {
|
|
393
|
+
certDomainName,
|
|
394
|
+
domainArg,
|
|
395
|
+
isWildcardRequest,
|
|
396
|
+
includeWildcard: options?.includeWildcard ?? false,
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const result = await this.taskManager.triggerTaskConstrained(
|
|
400
|
+
this.certIssuanceTask,
|
|
401
|
+
issuanceInput,
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
// If we got a cert directly (either from execution or result sharing), return it
|
|
405
|
+
if (result != null) {
|
|
406
|
+
return result;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// If shouldExecute returned false (cert appeared in cache), read from cache
|
|
410
|
+
const cachedCert = await this.certmanager.retrieveCertificate(certDomainName);
|
|
411
|
+
if (cachedCert) {
|
|
412
|
+
return cachedCert;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
throw new Error(`Certificate issuance failed for ${certDomainName}`);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Performs the actual ACME certificate issuance flow.
|
|
420
|
+
* Called by the certIssuanceTask's taskFunction.
|
|
421
|
+
*/
|
|
422
|
+
private async performCertificateIssuance(input: ICertIssuanceInput): Promise<SmartacmeCert> {
|
|
423
|
+
const { certDomainName, isWildcardRequest, includeWildcard } = input;
|
|
424
|
+
|
|
425
|
+
// ── Step: prepare ─────────────────────────────────────────────────────
|
|
426
|
+
this.certIssuanceTask.notifyStep('prepare');
|
|
303
427
|
|
|
304
428
|
// Build identifiers array based on request
|
|
305
|
-
const identifiers = [];
|
|
306
|
-
|
|
429
|
+
const identifiers: Array<{ type: 'dns'; value: string }> = [];
|
|
430
|
+
|
|
307
431
|
if (isWildcardRequest) {
|
|
308
|
-
// If requesting a wildcard directly, only add the wildcard
|
|
309
432
|
identifiers.push({ type: 'dns', value: `*.${certDomainName}` });
|
|
310
433
|
} else {
|
|
311
|
-
// Add the regular domain
|
|
312
434
|
identifiers.push({ type: 'dns', value: certDomainName });
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (options?.includeWildcard) {
|
|
435
|
+
|
|
436
|
+
if (includeWildcard) {
|
|
316
437
|
const hasDnsHandler = this.challengeHandlers.some((h) =>
|
|
317
438
|
h.getSupportedTypes().includes('dns-01'),
|
|
318
439
|
);
|
|
@@ -329,6 +450,9 @@ export class SmartAcme {
|
|
|
329
450
|
identifiers,
|
|
330
451
|
}), 'createOrder');
|
|
331
452
|
|
|
453
|
+
// ── Step: authorize ───────────────────────────────────────────────────
|
|
454
|
+
this.certIssuanceTask.notifyStep('authorize');
|
|
455
|
+
|
|
332
456
|
/* Get authorizations and select challenges */
|
|
333
457
|
const authorizations = await this.retry(() => this.client.getAuthorizations(order), 'getAuthorizations');
|
|
334
458
|
|
|
@@ -352,45 +476,37 @@ export class SmartAcme {
|
|
|
352
476
|
}
|
|
353
477
|
const { type, handler } = selectedHandler;
|
|
354
478
|
// build handler input with keyAuthorization
|
|
355
|
-
let
|
|
479
|
+
let challengeInput: any;
|
|
356
480
|
// retrieve keyAuthorization for challenge
|
|
357
481
|
const keyAuth = await this.client.getChallengeKeyAuthorization(selectedChallengeArg);
|
|
358
482
|
if (type === 'dns-01') {
|
|
359
|
-
|
|
483
|
+
challengeInput = { type, hostName: `_acme-challenge.${authz.identifier.value}`, challenge: keyAuth };
|
|
360
484
|
} else if (type === 'http-01') {
|
|
361
|
-
|
|
362
|
-
input = {
|
|
485
|
+
challengeInput = {
|
|
363
486
|
type,
|
|
364
487
|
token: (selectedChallengeArg as any).token,
|
|
365
488
|
keyAuthorization: keyAuth,
|
|
366
489
|
webPath: `/.well-known/acme-challenge/${(selectedChallengeArg as any).token}`,
|
|
367
490
|
};
|
|
368
491
|
} else {
|
|
369
|
-
|
|
370
|
-
input = { type, keyAuthorization: keyAuth, ...selectedChallengeArg };
|
|
492
|
+
challengeInput = { type, keyAuthorization: keyAuth, ...selectedChallengeArg };
|
|
371
493
|
}
|
|
372
|
-
this.pendingChallenges.push(
|
|
494
|
+
this.pendingChallenges.push(challengeInput);
|
|
373
495
|
try {
|
|
374
|
-
|
|
375
|
-
await this.retry(() => handler.prepare(input), `${type}.prepare`);
|
|
376
|
-
// For DNS-01, wait for propagation before verification
|
|
496
|
+
await this.retry(() => handler.prepare(challengeInput), `${type}.prepare`);
|
|
377
497
|
if (type === 'dns-01') {
|
|
378
|
-
const dnsInput =
|
|
379
|
-
// Wait for authoritative DNS propagation before ACME verify
|
|
498
|
+
const dnsInput = challengeInput as { hostName: string; challenge: string };
|
|
380
499
|
await this.retry(
|
|
381
500
|
() => this.smartdns.checkUntilAvailable(dnsInput.hostName, 'TXT', dnsInput.challenge, 100, 5000),
|
|
382
501
|
`${type}.propagation`,
|
|
383
502
|
);
|
|
384
|
-
// Extra cool-down to ensure ACME server sees the new TXT record
|
|
385
503
|
this.logger.log('info', 'Cooling down for 1 minute before ACME verification');
|
|
386
504
|
await plugins.smartdelay.delayFor(60000);
|
|
387
505
|
}
|
|
388
|
-
// Notify ACME server to complete the challenge
|
|
389
506
|
await this.retry(
|
|
390
507
|
() => this.client.completeChallenge(selectedChallengeArg),
|
|
391
508
|
`${type}.completeChallenge`,
|
|
392
509
|
);
|
|
393
|
-
// Wait for valid status (warnings on staging timeouts)
|
|
394
510
|
try {
|
|
395
511
|
await this.retry(
|
|
396
512
|
() => this.client.waitForValidStatus(selectedChallengeArg),
|
|
@@ -404,34 +520,32 @@ export class SmartAcme {
|
|
|
404
520
|
);
|
|
405
521
|
}
|
|
406
522
|
} finally {
|
|
407
|
-
// Always cleanup resource
|
|
408
523
|
try {
|
|
409
|
-
await this.retry(() => handler.cleanup(
|
|
524
|
+
await this.retry(() => handler.cleanup(challengeInput), `${type}.cleanup`);
|
|
410
525
|
} catch (err) {
|
|
411
526
|
await this.logger.log('error', `Error during ${type}.cleanup`, err);
|
|
412
527
|
} finally {
|
|
413
|
-
this.pendingChallenges = this.pendingChallenges.filter((c) => c !==
|
|
528
|
+
this.pendingChallenges = this.pendingChallenges.filter((c) => c !== challengeInput);
|
|
414
529
|
}
|
|
415
530
|
}
|
|
416
531
|
}
|
|
417
532
|
|
|
418
|
-
|
|
419
|
-
|
|
533
|
+
// ── Step: finalize ────────────────────────────────────────────────────
|
|
534
|
+
this.certIssuanceTask.notifyStep('finalize');
|
|
535
|
+
|
|
536
|
+
const csrDomains: string[] = [];
|
|
420
537
|
let commonName: string;
|
|
421
|
-
|
|
538
|
+
|
|
422
539
|
if (isWildcardRequest) {
|
|
423
|
-
// For wildcard requests, use wildcard as common name
|
|
424
540
|
commonName = `*.${certDomainName}`;
|
|
425
|
-
csrDomains.push(certDomainName);
|
|
541
|
+
csrDomains.push(certDomainName);
|
|
426
542
|
} else {
|
|
427
|
-
// For regular requests, use base domain as common name
|
|
428
543
|
commonName = certDomainName;
|
|
429
|
-
if (
|
|
430
|
-
// If wildcard was successfully added, include it as alt name
|
|
544
|
+
if (includeWildcard && identifiers.some(id => id.value === `*.${certDomainName}`)) {
|
|
431
545
|
csrDomains.push(`*.${certDomainName}`);
|
|
432
546
|
}
|
|
433
547
|
}
|
|
434
|
-
|
|
548
|
+
|
|
435
549
|
const [key, csr] = await plugins.acme.AcmeCrypto.createCsr({
|
|
436
550
|
commonName,
|
|
437
551
|
altNames: csrDomains,
|
|
@@ -440,9 +554,9 @@ export class SmartAcme {
|
|
|
440
554
|
await this.retry(() => this.client.finalizeOrder(order, csr), 'finalizeOrder');
|
|
441
555
|
const cert = await this.retry(() => this.client.getCertificate(order), 'getCertificate');
|
|
442
556
|
|
|
443
|
-
|
|
557
|
+
// ── Step: store ───────────────────────────────────────────────────────
|
|
558
|
+
this.certIssuanceTask.notifyStep('store');
|
|
444
559
|
|
|
445
|
-
// Store the new certificate record
|
|
446
560
|
const certRecord = new SmartacmeCert({
|
|
447
561
|
id: plugins.smartunique.shortId(),
|
|
448
562
|
domainName: certDomainName,
|
|
@@ -455,9 +569,7 @@ export class SmartAcme {
|
|
|
455
569
|
await this.certmanager.storeCertificate(certRecord);
|
|
456
570
|
|
|
457
571
|
const newCertificate = await this.certmanager.retrieveCertificate(certDomainName);
|
|
458
|
-
|
|
459
|
-
currentDomainInterst.destroy();
|
|
460
|
-
return newCertificate;
|
|
572
|
+
return newCertificate ?? certRecord;
|
|
461
573
|
}
|
|
462
574
|
|
|
463
575
|
}
|