@push.rocks/smartacme 5.0.0 → 6.0.1
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/LICENSE +1 -1
- package/dist_ts/00_commitinfo_data.d.ts +1 -1
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/handlers/Dns01Handler.d.ts +13 -0
- package/dist_ts/handlers/Dns01Handler.js +24 -0
- package/dist_ts/handlers/Http01Handler.d.ts +34 -0
- package/dist_ts/handlers/Http01Handler.js +32 -0
- package/dist_ts/handlers/IChallengeHandler.d.ts +22 -0
- package/dist_ts/handlers/IChallengeHandler.js +2 -0
- package/dist_ts/handlers/index.d.ts +3 -0
- package/dist_ts/handlers/index.js +4 -0
- package/dist_ts/smartacme.classes.smartacme.d.ts +32 -4
- package/dist_ts/smartacme.classes.smartacme.js +137 -39
- package/dist_ts/smartacme.plugins.d.ts +4 -2
- package/dist_ts/smartacme.plugins.js +6 -3
- package/package.json +20 -20
- package/readme.md +23 -53
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/handlers/Dns01Handler.ts +40 -0
- package/ts/handlers/Http01Handler.ts +54 -0
- package/ts/handlers/IChallengeHandler.ts +22 -0
- package/ts/handlers/index.ts +4 -0
- package/ts/index.ts +1 -1
- package/ts/smartacme.classes.certmanager.ts +1 -1
- package/ts/smartacme.classes.smartacme.ts +167 -51
- package/ts/smartacme.plugins.ts +5 -2
package/LICENSE
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* autocreated commitinfo by @
|
|
2
|
+
* autocreated commitinfo by @push.rocks/commitinfo
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartacme',
|
|
6
|
-
version: '
|
|
6
|
+
version: '6.0.1',
|
|
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=
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as plugins from '../smartacme.plugins.js';
|
|
2
|
+
import type { IChallengeHandler } from './IChallengeHandler.js';
|
|
3
|
+
/**
|
|
4
|
+
* DNS-01 challenge handler using CloudflareAccount and Smartdns.
|
|
5
|
+
*/
|
|
6
|
+
export declare class Dns01Handler implements IChallengeHandler<plugins.tsclass.network.IDnsChallenge> {
|
|
7
|
+
private cf;
|
|
8
|
+
private smartdns;
|
|
9
|
+
constructor(cloudflareAccount: any, smartdnsInstance?: plugins.smartdnsClient.Smartdns);
|
|
10
|
+
getSupportedTypes(): string[];
|
|
11
|
+
prepare(ch: plugins.tsclass.network.IDnsChallenge): Promise<void>;
|
|
12
|
+
cleanup(ch: plugins.tsclass.network.IDnsChallenge): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as plugins from '../smartacme.plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* DNS-01 challenge handler using CloudflareAccount and Smartdns.
|
|
4
|
+
*/
|
|
5
|
+
export class Dns01Handler {
|
|
6
|
+
constructor(cloudflareAccount, smartdnsInstance) {
|
|
7
|
+
this.cf = cloudflareAccount;
|
|
8
|
+
this.smartdns = smartdnsInstance ?? new plugins.smartdnsClient.Smartdns({});
|
|
9
|
+
}
|
|
10
|
+
getSupportedTypes() {
|
|
11
|
+
return ['dns-01'];
|
|
12
|
+
}
|
|
13
|
+
async prepare(ch) {
|
|
14
|
+
// set DNS TXT record
|
|
15
|
+
await this.cf.convenience.acmeSetDnsChallenge(ch);
|
|
16
|
+
// wait for DNS propagation
|
|
17
|
+
await this.smartdns.checkUntilAvailable(ch.hostName, 'TXT', ch.challenge, 100, 5000);
|
|
18
|
+
}
|
|
19
|
+
async cleanup(ch) {
|
|
20
|
+
// remove DNS TXT record
|
|
21
|
+
await this.cf.convenience.acmeRemoveDnsChallenge(ch);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRG5zMDFIYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvaGFuZGxlcnMvRG5zMDFIYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0seUJBQXlCLENBQUM7QUFHbkQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sWUFBWTtJQUl2QixZQUNFLGlCQUFzQixFQUN0QixnQkFBa0Q7UUFFbEQsSUFBSSxDQUFDLEVBQUUsR0FBRyxpQkFBaUIsQ0FBQztRQUM1QixJQUFJLENBQUMsUUFBUSxHQUFHLGdCQUFnQixJQUFJLElBQUksT0FBTyxDQUFDLGNBQWMsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDOUUsQ0FBQztJQUVNLGlCQUFpQjtRQUN0QixPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDcEIsQ0FBQztJQUVNLEtBQUssQ0FBQyxPQUFPLENBQUMsRUFBeUM7UUFDNUQscUJBQXFCO1FBQ3JCLE1BQU0sSUFBSSxDQUFDLEVBQUUsQ0FBQyxXQUFXLENBQUMsbUJBQW1CLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDbEQsMkJBQTJCO1FBQzNCLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxtQkFBbUIsQ0FDckMsRUFBRSxDQUFDLFFBQVEsRUFDWCxLQUFLLEVBQ0wsRUFBRSxDQUFDLFNBQVMsRUFDWixHQUFHLEVBQ0gsSUFBSSxDQUNMLENBQUM7SUFDSixDQUFDO0lBRU0sS0FBSyxDQUFDLE9BQU8sQ0FBQyxFQUF5QztRQUM1RCx3QkFBd0I7UUFDeEIsTUFBTSxJQUFJLENBQUMsRUFBRSxDQUFDLFdBQVcsQ0FBQyxzQkFBc0IsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUN2RCxDQUFDO0NBQ0YifQ==
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { IChallengeHandler } from './IChallengeHandler.js';
|
|
2
|
+
/**
|
|
3
|
+
* HTTP-01 ACME challenge handler using file-system webroot.
|
|
4
|
+
* Writes and removes the challenge file under <webroot>/.well-known/acme-challenge/.
|
|
5
|
+
*/
|
|
6
|
+
export interface Http01HandlerOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Directory that serves HTTP requests for /.well-known/acme-challenge
|
|
9
|
+
*/
|
|
10
|
+
webroot: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class Http01Handler implements IChallengeHandler<{
|
|
13
|
+
type: string;
|
|
14
|
+
token: string;
|
|
15
|
+
keyAuthorization: string;
|
|
16
|
+
webPath: string;
|
|
17
|
+
}> {
|
|
18
|
+
private webroot;
|
|
19
|
+
constructor(options: Http01HandlerOptions);
|
|
20
|
+
getSupportedTypes(): string[];
|
|
21
|
+
prepare(ch: {
|
|
22
|
+
token: string;
|
|
23
|
+
keyAuthorization: string;
|
|
24
|
+
webPath: string;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
verify(ch: {
|
|
27
|
+
webPath: string;
|
|
28
|
+
keyAuthorization: string;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
cleanup(ch: {
|
|
31
|
+
token: string;
|
|
32
|
+
webPath: string;
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
export class Http01Handler {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.webroot = options.webroot;
|
|
6
|
+
}
|
|
7
|
+
getSupportedTypes() {
|
|
8
|
+
return ['http-01'];
|
|
9
|
+
}
|
|
10
|
+
async prepare(ch) {
|
|
11
|
+
const relWebPath = ch.webPath.replace(/^\/+/, '');
|
|
12
|
+
const filePath = path.join(this.webroot, relWebPath);
|
|
13
|
+
const dir = path.dirname(filePath);
|
|
14
|
+
await fs.mkdir(dir, { recursive: true });
|
|
15
|
+
await fs.writeFile(filePath, ch.keyAuthorization, 'utf8');
|
|
16
|
+
}
|
|
17
|
+
async verify(ch) {
|
|
18
|
+
// Optional: implement HTTP polling if desired
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
async cleanup(ch) {
|
|
22
|
+
const relWebPath = ch.webPath.replace(/^\/+/, '');
|
|
23
|
+
const filePath = path.join(this.webroot, relWebPath);
|
|
24
|
+
try {
|
|
25
|
+
await fs.unlink(filePath);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// ignore missing file
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSHR0cDAxSGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2hhbmRsZXJzL0h0dHAwMUhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsSUFBSSxFQUFFLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDcEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFjN0IsTUFBTSxPQUFPLGFBQWE7SUFReEIsWUFBWSxPQUE2QjtRQUN2QyxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUM7SUFDakMsQ0FBQztJQUVNLGlCQUFpQjtRQUN0QixPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDckIsQ0FBQztJQUVNLEtBQUssQ0FBQyxPQUFPLENBQUMsRUFBZ0U7UUFDbkYsTUFBTSxVQUFVLEdBQUcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ2xELE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztRQUNyRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ25DLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUN6QyxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxnQkFBZ0IsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUM1RCxDQUFDO0lBRU0sS0FBSyxDQUFDLE1BQU0sQ0FBQyxFQUFpRDtRQUNuRSw4Q0FBOEM7UUFDOUMsT0FBTztJQUNULENBQUM7SUFFTSxLQUFLLENBQUMsT0FBTyxDQUFDLEVBQXNDO1FBQ3pELE1BQU0sVUFBVSxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNsRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDckQsSUFBSSxDQUFDO1lBQ0gsTUFBTSxFQUFFLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzVCLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxzQkFBc0I7UUFDeEIsQ0FBQztJQUNILENBQUM7Q0FDRiJ9
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable interface for ACME challenge handlers.
|
|
3
|
+
* Supports DNS-01, HTTP-01, TLS-ALPN-01, or custom challenge types.
|
|
4
|
+
*/
|
|
5
|
+
export interface IChallengeHandler<T> {
|
|
6
|
+
/**
|
|
7
|
+
* ACME challenge types this handler supports (e.g. ['dns-01']).
|
|
8
|
+
*/
|
|
9
|
+
getSupportedTypes(): string[];
|
|
10
|
+
/**
|
|
11
|
+
* Prepare the challenge: set DNS record, start HTTP/TLS server, etc.
|
|
12
|
+
*/
|
|
13
|
+
prepare(ch: T): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Optional extra verify step (HTTP GET, ALPN handshake).
|
|
16
|
+
*/
|
|
17
|
+
verify?(ch: T): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Clean up resources: remove DNS record, stop server.
|
|
20
|
+
*/
|
|
21
|
+
cleanup(ch: T): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Removed legacy handler adapter
|
|
2
|
+
export { Dns01Handler } from './Dns01Handler.js';
|
|
3
|
+
export { Http01Handler } from './Http01Handler.js';
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9oYW5kbGVycy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxpQ0FBaUM7QUFDakMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2pELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQyJ9
|
|
@@ -7,9 +7,29 @@ export interface ISmartAcmeOptions {
|
|
|
7
7
|
accountPrivateKey?: string;
|
|
8
8
|
accountEmail: string;
|
|
9
9
|
mongoDescriptor: plugins.smartdata.IMongoDescriptor;
|
|
10
|
-
setChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
11
|
-
removeChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
12
10
|
environment: 'production' | 'integration';
|
|
11
|
+
/**
|
|
12
|
+
* Optional retry/backoff configuration for transient failures
|
|
13
|
+
*/
|
|
14
|
+
retryOptions?: {
|
|
15
|
+
/** number of retry attempts */
|
|
16
|
+
retries?: number;
|
|
17
|
+
/** backoff multiplier */
|
|
18
|
+
factor?: number;
|
|
19
|
+
/** initial delay in milliseconds */
|
|
20
|
+
minTimeoutMs?: number;
|
|
21
|
+
/** maximum delay cap in milliseconds */
|
|
22
|
+
maxTimeoutMs?: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Pluggable ACME challenge handlers (DNS-01, HTTP-01, TLS-ALPN-01, etc.)
|
|
26
|
+
*/
|
|
27
|
+
challengeHandlers?: plugins.handlers.IChallengeHandler<any>[];
|
|
28
|
+
/**
|
|
29
|
+
* Order of challenge types to try (e.g. ['http-01','dns-01']).
|
|
30
|
+
* Defaults to ['dns-01'] or first supported type from handlers.
|
|
31
|
+
*/
|
|
32
|
+
challengePriority?: string[];
|
|
13
33
|
}
|
|
14
34
|
/**
|
|
15
35
|
* class SmartAcme
|
|
@@ -27,10 +47,12 @@ export declare class SmartAcme {
|
|
|
27
47
|
private smartdns;
|
|
28
48
|
logger: plugins.smartlog.Smartlog;
|
|
29
49
|
private privateKey;
|
|
30
|
-
private setChallenge;
|
|
31
|
-
private removeChallenge;
|
|
32
50
|
private certmanager;
|
|
33
51
|
private certmatcher;
|
|
52
|
+
private retryOptions;
|
|
53
|
+
private pendingChallenges;
|
|
54
|
+
private challengeHandlers;
|
|
55
|
+
private challengePriority;
|
|
34
56
|
constructor(optionsArg: ISmartAcmeOptions);
|
|
35
57
|
/**
|
|
36
58
|
* starts the instance
|
|
@@ -40,6 +62,12 @@ export declare class SmartAcme {
|
|
|
40
62
|
*/
|
|
41
63
|
start(): Promise<void>;
|
|
42
64
|
stop(): Promise<void>;
|
|
65
|
+
/** Retry helper with exponential backoff */
|
|
66
|
+
private retry;
|
|
67
|
+
/** Clean up pending challenges and shut down */
|
|
68
|
+
private handleShutdown;
|
|
69
|
+
/** Handle process signals for graceful shutdown */
|
|
70
|
+
private handleSignal;
|
|
43
71
|
/**
|
|
44
72
|
* gets a certificate
|
|
45
73
|
* it runs through the following steps
|
|
@@ -15,9 +15,30 @@ import { commitinfo } from './00_commitinfo_data.js';
|
|
|
15
15
|
*/
|
|
16
16
|
export class SmartAcme {
|
|
17
17
|
constructor(optionsArg) {
|
|
18
|
-
this.smartdns = new plugins.
|
|
18
|
+
this.smartdns = new plugins.smartdnsClient.Smartdns({});
|
|
19
|
+
// track pending DNS challenges for graceful shutdown
|
|
20
|
+
this.pendingChallenges = [];
|
|
19
21
|
this.options = optionsArg;
|
|
20
22
|
this.logger = plugins.smartlog.Smartlog.createForCommitinfo(commitinfo);
|
|
23
|
+
// enable console output for structured logging
|
|
24
|
+
this.logger.enableConsole();
|
|
25
|
+
// initialize retry/backoff options
|
|
26
|
+
this.retryOptions = {
|
|
27
|
+
retries: optionsArg.retryOptions?.retries ?? 3,
|
|
28
|
+
factor: optionsArg.retryOptions?.factor ?? 2,
|
|
29
|
+
minTimeoutMs: optionsArg.retryOptions?.minTimeoutMs ?? 1000,
|
|
30
|
+
maxTimeoutMs: optionsArg.retryOptions?.maxTimeoutMs ?? 30000,
|
|
31
|
+
};
|
|
32
|
+
// initialize challenge handlers (must provide at least one)
|
|
33
|
+
if (!optionsArg.challengeHandlers || optionsArg.challengeHandlers.length === 0) {
|
|
34
|
+
throw new Error('You must provide at least one ACME challenge handler via options.challengeHandlers');
|
|
35
|
+
}
|
|
36
|
+
this.challengeHandlers = optionsArg.challengeHandlers;
|
|
37
|
+
// initialize challenge priority
|
|
38
|
+
this.challengePriority =
|
|
39
|
+
optionsArg.challengePriority && optionsArg.challengePriority.length > 0
|
|
40
|
+
? optionsArg.challengePriority
|
|
41
|
+
: this.challengeHandlers.map((h) => h.getSupportedTypes()[0]);
|
|
21
42
|
}
|
|
22
43
|
/**
|
|
23
44
|
* starts the instance
|
|
@@ -28,8 +49,6 @@ export class SmartAcme {
|
|
|
28
49
|
async start() {
|
|
29
50
|
this.privateKey =
|
|
30
51
|
this.options.accountPrivateKey || (await plugins.acme.forge.createPrivateKey()).toString();
|
|
31
|
-
this.setChallenge = this.options.setChallenge;
|
|
32
|
-
this.removeChallenge = this.options.removeChallenge;
|
|
33
52
|
// CertMangaer
|
|
34
53
|
this.certmanager = new SmartacmeCertManager(this, {
|
|
35
54
|
mongoDescriptor: this.options.mongoDescriptor,
|
|
@@ -54,10 +73,63 @@ export class SmartAcme {
|
|
|
54
73
|
termsOfServiceAgreed: true,
|
|
55
74
|
contact: [`mailto:${this.options.accountEmail}`],
|
|
56
75
|
});
|
|
76
|
+
// Setup graceful shutdown handlers
|
|
77
|
+
process.on('SIGINT', () => this.handleSignal('SIGINT'));
|
|
78
|
+
process.on('SIGTERM', () => this.handleSignal('SIGTERM'));
|
|
57
79
|
}
|
|
58
80
|
async stop() {
|
|
59
81
|
await this.certmanager.smartdataDb.close();
|
|
60
82
|
}
|
|
83
|
+
/** Retry helper with exponential backoff */
|
|
84
|
+
async retry(operation, operationName = 'operation') {
|
|
85
|
+
let attempt = 0;
|
|
86
|
+
let delay = this.retryOptions.minTimeoutMs;
|
|
87
|
+
while (true) {
|
|
88
|
+
try {
|
|
89
|
+
return await operation();
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
attempt++;
|
|
93
|
+
if (attempt > this.retryOptions.retries) {
|
|
94
|
+
await this.logger.log('error', `Operation ${operationName} failed after ${attempt} attempts`, err);
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
await this.logger.log('warn', `Operation ${operationName} failed on attempt ${attempt}, retrying in ${delay}ms`, err);
|
|
98
|
+
await plugins.smartdelay.delayFor(delay);
|
|
99
|
+
delay = Math.min(delay * this.retryOptions.factor, this.retryOptions.maxTimeoutMs);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Clean up pending challenges and shut down */
|
|
104
|
+
async handleShutdown() {
|
|
105
|
+
for (const input of [...this.pendingChallenges]) {
|
|
106
|
+
const type = input.type;
|
|
107
|
+
const handler = this.challengeHandlers.find((h) => h.getSupportedTypes().includes(type));
|
|
108
|
+
if (handler) {
|
|
109
|
+
try {
|
|
110
|
+
await handler.cleanup(input);
|
|
111
|
+
await this.logger.log('info', `Removed pending ${type} challenge during shutdown`, input);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
await this.logger.log('error', `Failed to remove pending ${type} challenge during shutdown`, err);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
await this.logger.log('warn', `No handler for pending challenge type '${type}' during shutdown; skipping cleanup`, input);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
this.pendingChallenges = [];
|
|
122
|
+
await this.stop();
|
|
123
|
+
}
|
|
124
|
+
/** Handle process signals for graceful shutdown */
|
|
125
|
+
handleSignal(sig) {
|
|
126
|
+
this.logger.log('info', `Received signal ${sig}, shutting down gracefully`);
|
|
127
|
+
this.handleShutdown()
|
|
128
|
+
.then(() => process.exit(0))
|
|
129
|
+
.catch((err) => {
|
|
130
|
+
this.logger.log('error', 'Error during shutdown', err).then(() => process.exit(1));
|
|
131
|
+
});
|
|
132
|
+
}
|
|
61
133
|
/**
|
|
62
134
|
* gets a certificate
|
|
63
135
|
* it runs through the following steps
|
|
@@ -89,50 +161,76 @@ export class SmartAcme {
|
|
|
89
161
|
}
|
|
90
162
|
// lets make sure others get the same interest
|
|
91
163
|
const currentDomainInterst = await this.certmanager.interestMap.addInterest(certDomainName);
|
|
92
|
-
/* Place new order */
|
|
93
|
-
const order = await this.client.createOrder({
|
|
164
|
+
/* Place new order with retry */
|
|
165
|
+
const order = await this.retry(() => this.client.createOrder({
|
|
94
166
|
identifiers: [
|
|
95
167
|
{ type: 'dns', value: certDomainName },
|
|
96
168
|
{ type: 'dns', value: `*.${certDomainName}` },
|
|
97
169
|
],
|
|
98
|
-
});
|
|
170
|
+
}), 'createOrder');
|
|
99
171
|
/* Get authorizations and select challenges */
|
|
100
|
-
const authorizations = await this.client.getAuthorizations(order);
|
|
172
|
+
const authorizations = await this.retry(() => this.client.getAuthorizations(order), 'getAuthorizations');
|
|
101
173
|
for (const authz of authorizations) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
174
|
+
await this.logger.log('debug', 'Authorization received', authz);
|
|
175
|
+
// select a handler based on configured priority
|
|
176
|
+
let selectedHandler = null;
|
|
177
|
+
let selectedChallengeArg = null;
|
|
178
|
+
for (const type of this.challengePriority) {
|
|
179
|
+
const candidate = authz.challenges.find((c) => c.type === type);
|
|
180
|
+
if (!candidate)
|
|
181
|
+
continue;
|
|
182
|
+
const handler = this.challengeHandlers.find((h) => h.getSupportedTypes().includes(type));
|
|
183
|
+
if (handler) {
|
|
184
|
+
selectedHandler = { type, handler };
|
|
185
|
+
selectedChallengeArg = candidate;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (!selectedHandler) {
|
|
190
|
+
throw new Error(`No challenge handler for domain ${authz.identifier.value}: supported types [${this.challengePriority.join(',')}]`);
|
|
191
|
+
}
|
|
192
|
+
const { type, handler } = selectedHandler;
|
|
193
|
+
// build handler input with keyAuthorization
|
|
194
|
+
let input;
|
|
195
|
+
// retrieve keyAuthorization for challenge
|
|
196
|
+
const keyAuth = await this.client.getChallengeKeyAuthorization(selectedChallengeArg);
|
|
197
|
+
if (type === 'dns-01') {
|
|
198
|
+
input = { type, hostName: `_acme-challenge.${authz.identifier.value}`, challenge: keyAuth };
|
|
199
|
+
}
|
|
200
|
+
else if (type === 'http-01') {
|
|
201
|
+
// HTTP-01 requires serving token at webPath
|
|
202
|
+
input = {
|
|
203
|
+
type,
|
|
204
|
+
token: selectedChallengeArg.token,
|
|
205
|
+
keyAuthorization: keyAuth,
|
|
206
|
+
webPath: `/.well-known/acme-challenge/${selectedChallengeArg.token}`,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
// generic challenge input: include raw challenge properties
|
|
211
|
+
input = { type, keyAuthorization: keyAuth, ...selectedChallengeArg };
|
|
212
|
+
}
|
|
213
|
+
this.pendingChallenges.push(input);
|
|
109
214
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
await
|
|
119
|
-
/* Verify that challenge is satisfied */
|
|
120
|
-
await this.client.verifyChallenge(authz, dnsChallenge);
|
|
121
|
-
/* Notify ACME provider that challenge is satisfied */
|
|
122
|
-
await this.client.completeChallenge(dnsChallenge);
|
|
123
|
-
/* Wait for ACME provider to respond with valid status */
|
|
124
|
-
await this.client.waitForValidStatus(dnsChallenge);
|
|
215
|
+
await this.retry(() => handler.prepare(input), `${type}.prepare`);
|
|
216
|
+
if (handler.verify) {
|
|
217
|
+
await this.retry(() => handler.verify(input), `${type}.verify`);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
await this.retry(() => this.client.verifyChallenge(authz, selectedChallengeArg), `${type}.verifyChallenge`);
|
|
221
|
+
}
|
|
222
|
+
await this.retry(() => this.client.completeChallenge(selectedChallengeArg), `${type}.completeChallenge`);
|
|
223
|
+
await this.retry(() => this.client.waitForValidStatus(selectedChallengeArg), `${type}.waitForValidStatus`);
|
|
125
224
|
}
|
|
126
225
|
finally {
|
|
127
|
-
/* Clean up challenge response */
|
|
128
226
|
try {
|
|
129
|
-
await this.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
});
|
|
227
|
+
await this.retry(() => handler.cleanup(input), `${type}.cleanup`);
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
await this.logger.log('error', `Error during ${type}.cleanup`, err);
|
|
133
231
|
}
|
|
134
|
-
|
|
135
|
-
|
|
232
|
+
finally {
|
|
233
|
+
this.pendingChallenges = this.pendingChallenges.filter((c) => c !== input);
|
|
136
234
|
}
|
|
137
235
|
}
|
|
138
236
|
}
|
|
@@ -141,8 +239,8 @@ export class SmartAcme {
|
|
|
141
239
|
commonName: `*.${certDomainName}`,
|
|
142
240
|
altNames: [certDomainName],
|
|
143
241
|
});
|
|
144
|
-
await this.client.finalizeOrder(order, csr);
|
|
145
|
-
const cert = await this.client.getCertificate(order);
|
|
242
|
+
await this.retry(() => this.client.finalizeOrder(order, csr), 'finalizeOrder');
|
|
243
|
+
const cert = await this.retry(() => this.client.getCertificate(order), 'getCertificate');
|
|
146
244
|
/* Done */
|
|
147
245
|
await this.certmanager.storeCertificate({
|
|
148
246
|
id: plugins.smartunique.shortId(),
|
|
@@ -165,4 +263,4 @@ export class SmartAcme {
|
|
|
165
263
|
return SmartacmeCert.getInstances({});
|
|
166
264
|
}
|
|
167
265
|
}
|
|
168
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sd0JBQXdCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQzVELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQWNyRDs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLE9BQU8sU0FBUztJQW1CcEIsWUFBWSxVQUE2QjtRQWRqQyxhQUFRLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQWVuRCxJQUFJLENBQUMsT0FBTyxHQUFHLFVBQVUsQ0FBQztRQUMxQixJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLG1CQUFtQixDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQzFFLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxLQUFLO1FBQ2hCLElBQUksQ0FBQyxVQUFVO1lBQ2IsSUFBSSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsSUFBSSxDQUFDLE1BQU0sT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQzdGLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUM7UUFDOUMsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQztRQUVwRCxjQUFj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
|
|
266
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sd0JBQXdCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQzVELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQzFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQW1DckQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxPQUFPLFNBQVM7SUF3QnBCLFlBQVksVUFBNkI7UUFuQmpDLGFBQVEsR0FBRyxJQUFJLE9BQU8sQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBWTNELHFEQUFxRDtRQUM3QyxzQkFBaUIsR0FBNEMsRUFBRSxDQUFDO1FBT3RFLElBQUksQ0FBQyxPQUFPLEdBQUcsVUFBVSxDQUFDO1FBQzFCLElBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsbUJBQW1CLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDeEUsK0NBQStDO1FBQy9DLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDNUIsbUNBQW1DO1FBQ25DLElBQUksQ0FBQyxZQUFZLEdBQUc7WUFDbEIsT0FBTyxFQUFFLFVBQVUsQ0FBQyxZQUFZLEVBQUUsT0FBTyxJQUFJLENBQUM7WUFDOUMsTUFBTSxFQUFFLFVBQVUsQ0FBQyxZQUFZLEVBQUUsTUFBTSxJQUFJLENBQUM7WUFDNUMsWUFBWSxFQUFFLFVBQVUsQ0FBQyxZQUFZLEVBQUUsWUFBWSxJQUFJLElBQUk7WUFDM0QsWUFBWSxF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
|
|
@@ -3,15 +3,17 @@ export { typedserver };
|
|
|
3
3
|
import * as lik from '@push.rocks/lik';
|
|
4
4
|
import * as smartdata from '@push.rocks/smartdata';
|
|
5
5
|
import * as smartdelay from '@push.rocks/smartdelay';
|
|
6
|
-
import * as
|
|
6
|
+
import * as smartdnsClient from '@push.rocks/smartdns/client';
|
|
7
7
|
import * as smartlog from '@push.rocks/smartlog';
|
|
8
8
|
import * as smartpromise from '@push.rocks/smartpromise';
|
|
9
9
|
import * as smartrequest from '@push.rocks/smartrequest';
|
|
10
10
|
import * as smartunique from '@push.rocks/smartunique';
|
|
11
11
|
import * as smartstring from '@push.rocks/smartstring';
|
|
12
12
|
import * as smarttime from '@push.rocks/smarttime';
|
|
13
|
-
export { lik, smartdata, smartdelay,
|
|
13
|
+
export { lik, smartdata, smartdelay, smartdnsClient, smartlog, smartpromise, smartrequest, smartunique, smartstring, smarttime, };
|
|
14
14
|
import * as tsclass from '@tsclass/tsclass';
|
|
15
15
|
export { tsclass };
|
|
16
16
|
import * as acme from 'acme-client';
|
|
17
17
|
export { acme };
|
|
18
|
+
import * as handlers from './handlers/index.js';
|
|
19
|
+
export { handlers };
|
|
@@ -5,18 +5,21 @@ export { typedserver };
|
|
|
5
5
|
import * as lik from '@push.rocks/lik';
|
|
6
6
|
import * as smartdata from '@push.rocks/smartdata';
|
|
7
7
|
import * as smartdelay from '@push.rocks/smartdelay';
|
|
8
|
-
import * as
|
|
8
|
+
import * as smartdnsClient from '@push.rocks/smartdns/client';
|
|
9
9
|
import * as smartlog from '@push.rocks/smartlog';
|
|
10
10
|
import * as smartpromise from '@push.rocks/smartpromise';
|
|
11
11
|
import * as smartrequest from '@push.rocks/smartrequest';
|
|
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
|
-
export { lik, smartdata, smartdelay,
|
|
15
|
+
export { lik, smartdata, smartdelay, smartdnsClient, smartlog, smartpromise, smartrequest, smartunique, smartstring, smarttime, };
|
|
16
16
|
// @tsclass scope
|
|
17
17
|
import * as tsclass from '@tsclass/tsclass';
|
|
18
18
|
export { tsclass };
|
|
19
19
|
// third party scope
|
|
20
20
|
import * as acme from 'acme-client';
|
|
21
21
|
export { acme };
|
|
22
|
-
|
|
22
|
+
// local handlers for challenge types
|
|
23
|
+
import * as handlers from './handlers/index.js';
|
|
24
|
+
export { handlers };
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLnBsdWdpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGFjbWUucGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxtQkFBbUI7QUFDbkIsT0FBTyxLQUFLLFdBQVcsTUFBTSx5QkFBeUIsQ0FBQztBQUV2RCxPQUFPLEVBQUUsV0FBVyxFQUFFLENBQUM7QUFFdkIsbUJBQW1CO0FBQ25CLE9BQU8sS0FBSyxHQUFHLE1BQU0saUJBQWlCLENBQUM7QUFDdkMsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUNuRCxPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBQ3JELE9BQU8sS0FBSyxjQUFjLE1BQU0sNkJBQTZCLENBQUM7QUFDOUQsT0FBTyxLQUFLLFFBQVEsTUFBTSxzQkFBc0IsQ0FBQztBQUNqRCxPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxZQUFZLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxLQUFLLFdBQVcsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEtBQUssV0FBVyxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFFbkQsT0FBTyxFQUNMLEdBQUcsRUFDSCxTQUFTLEVBQ1QsVUFBVSxFQUNWLGNBQWMsRUFDZCxRQUFRLEVBQ1IsWUFBWSxFQUNaLFlBQVksRUFDWixXQUFXLEVBQ1gsV0FBVyxFQUNYLFNBQVMsR0FDVixDQUFDO0FBRUYsaUJBQWlCO0FBQ2pCLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFFNUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxDQUFDO0FBRW5CLG9CQUFvQjtBQUNwQixPQUFPLEtBQUssSUFBSSxNQUFNLGFBQWEsQ0FBQztBQUVwQyxPQUFPLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFDaEIscUNBQXFDO0FBQ3JDLE9BQU8sS0FBSyxRQUFRLE1BQU0scUJBQXFCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFFBQVEsRUFBRSxDQUFDIn0=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartacme",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
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",
|
|
@@ -27,35 +27,35 @@
|
|
|
27
27
|
"certificate renewal",
|
|
28
28
|
"wildcard certificates"
|
|
29
29
|
],
|
|
30
|
-
"author": "
|
|
30
|
+
"author": "Task Venture Capital GmbH",
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"bugs": {
|
|
33
|
-
"url": "https://
|
|
33
|
+
"url": "https://code.foss.global/push.rocks/smartacme/issues"
|
|
34
34
|
},
|
|
35
|
-
"homepage": "https://code.foss.global/push.rocks/smartacme",
|
|
35
|
+
"homepage": "https://code.foss.global/push.rocks/smartacme#readme",
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@api.global/typedserver": "^3.0.
|
|
38
|
-
"@push.rocks/lik": "^6.
|
|
39
|
-
"@push.rocks/smartdata": "^5.
|
|
37
|
+
"@api.global/typedserver": "^3.0.74",
|
|
38
|
+
"@push.rocks/lik": "^6.2.2",
|
|
39
|
+
"@push.rocks/smartdata": "^5.15.1",
|
|
40
40
|
"@push.rocks/smartdelay": "^3.0.5",
|
|
41
|
-
"@push.rocks/smartdns": "^
|
|
41
|
+
"@push.rocks/smartdns": "^6.2.2",
|
|
42
42
|
"@push.rocks/smartlog": "^3.0.7",
|
|
43
|
-
"@push.rocks/smartpromise": "^4.
|
|
44
|
-
"@push.rocks/smartrequest": "^2.0
|
|
43
|
+
"@push.rocks/smartpromise": "^4.2.3",
|
|
44
|
+
"@push.rocks/smartrequest": "^2.1.0",
|
|
45
45
|
"@push.rocks/smartstring": "^4.0.15",
|
|
46
|
-
"@push.rocks/smarttime": "^4.
|
|
46
|
+
"@push.rocks/smarttime": "^4.1.1",
|
|
47
47
|
"@push.rocks/smartunique": "^3.0.9",
|
|
48
|
-
"@tsclass/tsclass": "^
|
|
49
|
-
"acme-client": "^4.
|
|
48
|
+
"@tsclass/tsclass": "^9.0.0",
|
|
49
|
+
"acme-client": "^5.4.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@apiclient.xyz/cloudflare": "^6.
|
|
53
|
-
"@git.zone/tsbuild": "^2.
|
|
54
|
-
"@git.zone/tsrun": "^1.
|
|
55
|
-
"@git.zone/tstest": "^1.0.
|
|
56
|
-
"@push.rocks/qenv": "^6.0
|
|
57
|
-
"@push.rocks/tapbundle": "^5.
|
|
58
|
-
"@types/node": "^
|
|
52
|
+
"@apiclient.xyz/cloudflare": "^6.3.2",
|
|
53
|
+
"@git.zone/tsbuild": "^2.3.2",
|
|
54
|
+
"@git.zone/tsrun": "^1.3.3",
|
|
55
|
+
"@git.zone/tstest": "^1.0.96",
|
|
56
|
+
"@push.rocks/qenv": "^6.1.0",
|
|
57
|
+
"@push.rocks/tapbundle": "^5.6.3",
|
|
58
|
+
"@types/node": "^22.15.2"
|
|
59
59
|
},
|
|
60
60
|
"files": [
|
|
61
61
|
"ts/**/*",
|
package/readme.md
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
```markdown
|
|
2
1
|
# @push.rocks/smartacme
|
|
3
2
|
|
|
4
3
|
A TypeScript-based ACME client with an easy yet powerful interface for LetsEncrypt certificate management.
|
|
@@ -40,35 +39,40 @@ Ensure your project includes the necessary TypeScript configuration and dependen
|
|
|
40
39
|
|
|
41
40
|
### Creating a SmartAcme Instance
|
|
42
41
|
|
|
43
|
-
Start by importing the `SmartAcme` class
|
|
42
|
+
Start by importing the `SmartAcme` class and any built-in handlers you plan to use. For example, to use DNS-01 via Cloudflare:
|
|
44
43
|
|
|
45
44
|
```typescript
|
|
46
45
|
import { SmartAcme } from '@push.rocks/smartacme';
|
|
46
|
+
import * as cloudflare from '@apiclient.xyz/cloudflare';
|
|
47
|
+
import { Dns01Handler } from '@push.rocks/smartacme/ts/handlers/Dns01Handler.js';
|
|
48
|
+
|
|
49
|
+
// Create a Cloudflare account client with your API token
|
|
50
|
+
const cfAccount = new cloudflare.CloudflareAccount('YOUR_CF_TOKEN');
|
|
47
51
|
|
|
52
|
+
// Instantiate SmartAcme with one or more ACME challenge handlers
|
|
48
53
|
const smartAcmeInstance = new SmartAcme({
|
|
49
|
-
accountEmail: 'youremail@example.com',
|
|
50
|
-
accountPrivateKey: null, // Private key for the account (optional, if not provided it will be generated)
|
|
54
|
+
accountEmail: 'youremail@example.com',
|
|
51
55
|
mongoDescriptor: {
|
|
52
56
|
mongoDbUrl: 'mongodb://yourmongoURL',
|
|
53
57
|
mongoDbName: 'yourDbName',
|
|
54
58
|
mongoDbPass: 'yourDbPassword',
|
|
55
59
|
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
environment: 'integration', // 'production' to request real certificates
|
|
61
|
+
retryOptions: {}, // optional retry/backoff settings
|
|
62
|
+
challengeHandlers: [
|
|
63
|
+
new Dns01Handler(cfAccount),
|
|
64
|
+
// you can add more handlers, e.g. Http01Handler
|
|
65
|
+
],
|
|
66
|
+
challengePriority: ['dns-01'], // optional ordering of challenge types
|
|
63
67
|
});
|
|
64
68
|
```
|
|
65
69
|
|
|
66
70
|
### Initializing SmartAcme
|
|
67
71
|
|
|
68
|
-
Before proceeding to request certificates,
|
|
72
|
+
Before proceeding to request certificates, start your SmartAcme instance:
|
|
69
73
|
|
|
70
74
|
```typescript
|
|
71
|
-
await smartAcmeInstance.
|
|
75
|
+
await smartAcmeInstance.start();
|
|
72
76
|
```
|
|
73
77
|
|
|
74
78
|
### Obtaining a Certificate for a Domain
|
|
@@ -83,34 +87,7 @@ console.log('Certificate:', myCert);
|
|
|
83
87
|
|
|
84
88
|
### Automating DNS Challenges
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
import * as cloudflare from '@apiclient.xyz/cloudflare';
|
|
90
|
-
import { Qenv } from '@push.rocks/qenv';
|
|
91
|
-
|
|
92
|
-
const testQenv = new Qenv('./', './.nogit/');
|
|
93
|
-
const testCloudflare = new cloudflare.CloudflareAccount(testQenv.getEnvVarOnDemand('CF_TOKEN'));
|
|
94
|
-
|
|
95
|
-
const smartAcmeInstance = new SmartAcme({
|
|
96
|
-
accountEmail: 'domains@example.com',
|
|
97
|
-
accountPrivateKey: null,
|
|
98
|
-
mongoDescriptor: {
|
|
99
|
-
mongoDbName: testQenv.getEnvVarRequired('MONGODB_DATABASE'),
|
|
100
|
-
mongoDbPass: testQenv.getEnvVarRequired('MONGODB_PASSWORD'),
|
|
101
|
-
mongoDbUrl: testQenv.getEnvVarRequired('MONGODB_URL'),
|
|
102
|
-
},
|
|
103
|
-
removeChallenge: async (dnsChallenge) => {
|
|
104
|
-
testCloudflare.convenience.acmeRemoveDnsChallenge(dnsChallenge);
|
|
105
|
-
},
|
|
106
|
-
setChallenge: async (dnsChallenge) => {
|
|
107
|
-
testCloudflare.convenience.acmeSetDnsChallenge(dnsChallenge);
|
|
108
|
-
},
|
|
109
|
-
environment: 'integration',
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
await smartAcmeInstance.init();
|
|
113
|
-
```
|
|
90
|
+
SmartAcme uses pluggable ACME challenge handlers (see built-in handlers below) to automate domain validation. You configure handlers via the `challengeHandlers` array when creating the instance, and SmartAcme will invoke each handler’s `prepare`, optional `verify`, and `cleanup` methods during the ACME order flow.
|
|
114
91
|
|
|
115
92
|
### Managing Certificates
|
|
116
93
|
|
|
@@ -130,7 +107,7 @@ When creating an instance of `SmartAcme`, you can specify an `environment` optio
|
|
|
130
107
|
|
|
131
108
|
### Complete Example
|
|
132
109
|
|
|
133
|
-
Below is a complete example demonstrating how to use `@push.rocks/smartacme` to obtain and manage an ACME certificate with Let's Encrypt:
|
|
110
|
+
Below is a complete example demonstrating how to use `@push.rocks/smartacme` to obtain and manage an ACME certificate with Let's Encrypt using a DNS-01 handler:
|
|
134
111
|
|
|
135
112
|
```typescript
|
|
136
113
|
import { SmartAcme } from '@push.rocks/smartacme';
|
|
@@ -143,22 +120,16 @@ const cloudflareAccount = new cloudflare.CloudflareAccount(qenv.getEnvVarOnDeman
|
|
|
143
120
|
async function main() {
|
|
144
121
|
const smartAcmeInstance = new SmartAcme({
|
|
145
122
|
accountEmail: 'youremail@example.com',
|
|
146
|
-
accountPrivateKey: null,
|
|
147
123
|
mongoDescriptor: {
|
|
148
124
|
mongoDbUrl: qenv.getEnvVarRequired('MONGODB_URL'),
|
|
149
125
|
mongoDbName: qenv.getEnvVarRequired('MONGODB_DATABASE'),
|
|
150
126
|
mongoDbPass: qenv.getEnvVarRequired('MONGODB_PASSWORD'),
|
|
151
127
|
},
|
|
152
|
-
setChallenge: async (dnsChallenge) => {
|
|
153
|
-
await cloudflareAccount.convenience.acmeSetDnsChallenge(dnsChallenge);
|
|
154
|
-
},
|
|
155
|
-
removeChallenge: async (dnsChallenge) => {
|
|
156
|
-
await cloudflareAccount.convenience.acmeRemoveDnsChallenge(dnsChallenge);
|
|
157
|
-
},
|
|
158
128
|
environment: 'integration',
|
|
129
|
+
challengeHandlers: [ new Dns01Handler(cloudflareAccount) ],
|
|
159
130
|
});
|
|
160
131
|
|
|
161
|
-
await smartAcmeInstance.
|
|
132
|
+
await smartAcmeInstance.start();
|
|
162
133
|
|
|
163
134
|
const myDomain = 'example.com';
|
|
164
135
|
const myCert = await smartAcmeInstance.getCertificateForDomain(myDomain);
|
|
@@ -265,11 +236,10 @@ tap.start();
|
|
|
265
236
|
This comprehensive guide ensures you can set up, manage, and test ACME certificates efficiently and effectively using `@push.rocks/smartacme`.
|
|
266
237
|
|
|
267
238
|
---
|
|
268
|
-
```
|
|
269
239
|
|
|
270
240
|
## License and Legal Information
|
|
271
241
|
|
|
272
|
-
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
242
|
+
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
273
243
|
|
|
274
244
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
275
245
|
|
|
@@ -279,7 +249,7 @@ This project is owned and maintained by Task Venture Capital GmbH. The names and
|
|
|
279
249
|
|
|
280
250
|
### Company Information
|
|
281
251
|
|
|
282
|
-
Task Venture Capital GmbH
|
|
252
|
+
Task Venture Capital GmbH
|
|
283
253
|
Registered at District court Bremen HRB 35230 HB, Germany
|
|
284
254
|
|
|
285
255
|
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* autocreated commitinfo by @
|
|
2
|
+
* autocreated commitinfo by @push.rocks/commitinfo
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartacme',
|
|
6
|
-
version: '
|
|
6
|
+
version: '6.0.1',
|
|
7
7
|
description: 'A TypeScript-based ACME client for LetsEncrypt certificate management with a focus on simplicity and power.'
|
|
8
8
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as plugins from '../smartacme.plugins.js';
|
|
2
|
+
import type { IChallengeHandler } from './IChallengeHandler.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* DNS-01 challenge handler using CloudflareAccount and Smartdns.
|
|
6
|
+
*/
|
|
7
|
+
export class Dns01Handler implements IChallengeHandler<plugins.tsclass.network.IDnsChallenge> {
|
|
8
|
+
private cf: any;
|
|
9
|
+
private smartdns: plugins.smartdnsClient.Smartdns;
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
cloudflareAccount: any,
|
|
13
|
+
smartdnsInstance?: plugins.smartdnsClient.Smartdns,
|
|
14
|
+
) {
|
|
15
|
+
this.cf = cloudflareAccount;
|
|
16
|
+
this.smartdns = smartdnsInstance ?? new plugins.smartdnsClient.Smartdns({});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public getSupportedTypes(): string[] {
|
|
20
|
+
return ['dns-01'];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public async prepare(ch: plugins.tsclass.network.IDnsChallenge): Promise<void> {
|
|
24
|
+
// set DNS TXT record
|
|
25
|
+
await this.cf.convenience.acmeSetDnsChallenge(ch);
|
|
26
|
+
// wait for DNS propagation
|
|
27
|
+
await this.smartdns.checkUntilAvailable(
|
|
28
|
+
ch.hostName,
|
|
29
|
+
'TXT',
|
|
30
|
+
ch.challenge,
|
|
31
|
+
100,
|
|
32
|
+
5000,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public async cleanup(ch: plugins.tsclass.network.IDnsChallenge): Promise<void> {
|
|
37
|
+
// remove DNS TXT record
|
|
38
|
+
await this.cf.convenience.acmeRemoveDnsChallenge(ch);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import type { IChallengeHandler } from './IChallengeHandler.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* HTTP-01 ACME challenge handler using file-system webroot.
|
|
7
|
+
* Writes and removes the challenge file under <webroot>/.well-known/acme-challenge/.
|
|
8
|
+
*/
|
|
9
|
+
export interface Http01HandlerOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Directory that serves HTTP requests for /.well-known/acme-challenge
|
|
12
|
+
*/
|
|
13
|
+
webroot: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class Http01Handler implements IChallengeHandler<{
|
|
17
|
+
type: string;
|
|
18
|
+
token: string;
|
|
19
|
+
keyAuthorization: string;
|
|
20
|
+
webPath: string;
|
|
21
|
+
}> {
|
|
22
|
+
private webroot: string;
|
|
23
|
+
|
|
24
|
+
constructor(options: Http01HandlerOptions) {
|
|
25
|
+
this.webroot = options.webroot;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public getSupportedTypes(): string[] {
|
|
29
|
+
return ['http-01'];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public async prepare(ch: { token: string; keyAuthorization: string; webPath: string }): Promise<void> {
|
|
33
|
+
const relWebPath = ch.webPath.replace(/^\/+/, '');
|
|
34
|
+
const filePath = path.join(this.webroot, relWebPath);
|
|
35
|
+
const dir = path.dirname(filePath);
|
|
36
|
+
await fs.mkdir(dir, { recursive: true });
|
|
37
|
+
await fs.writeFile(filePath, ch.keyAuthorization, 'utf8');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public async verify(ch: { webPath: string; keyAuthorization: string }): Promise<void> {
|
|
41
|
+
// Optional: implement HTTP polling if desired
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public async cleanup(ch: { token: string; webPath: string }): Promise<void> {
|
|
46
|
+
const relWebPath = ch.webPath.replace(/^\/+/, '');
|
|
47
|
+
const filePath = path.join(this.webroot, relWebPath);
|
|
48
|
+
try {
|
|
49
|
+
await fs.unlink(filePath);
|
|
50
|
+
} catch {
|
|
51
|
+
// ignore missing file
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable interface for ACME challenge handlers.
|
|
3
|
+
* Supports DNS-01, HTTP-01, TLS-ALPN-01, or custom challenge types.
|
|
4
|
+
*/
|
|
5
|
+
export interface IChallengeHandler<T> {
|
|
6
|
+
/**
|
|
7
|
+
* ACME challenge types this handler supports (e.g. ['dns-01']).
|
|
8
|
+
*/
|
|
9
|
+
getSupportedTypes(): string[];
|
|
10
|
+
/**
|
|
11
|
+
* Prepare the challenge: set DNS record, start HTTP/TLS server, etc.
|
|
12
|
+
*/
|
|
13
|
+
prepare(ch: T): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Optional extra verify step (HTTP GET, ALPN handshake).
|
|
16
|
+
*/
|
|
17
|
+
verify?(ch: T): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Clean up resources: remove DNS record, stop server.
|
|
20
|
+
*/
|
|
21
|
+
cleanup(ch: T): Promise<void>;
|
|
22
|
+
}
|
package/ts/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './smartacme.classes.smartacme.js';
|
|
2
|
-
export { SmartacmeCert as Cert } from './smartacme.classes.cert.js';
|
|
2
|
+
export { SmartacmeCert as Cert } from './smartacme.classes.cert.js';
|
|
@@ -11,9 +11,30 @@ export interface ISmartAcmeOptions {
|
|
|
11
11
|
accountPrivateKey?: string;
|
|
12
12
|
accountEmail: string;
|
|
13
13
|
mongoDescriptor: plugins.smartdata.IMongoDescriptor;
|
|
14
|
-
setChallenge
|
|
15
|
-
removeChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
14
|
+
// Removed legacy setChallenge/removeChallenge in favor of `challengeHandlers`
|
|
16
15
|
environment: 'production' | 'integration';
|
|
16
|
+
/**
|
|
17
|
+
* Optional retry/backoff configuration for transient failures
|
|
18
|
+
*/
|
|
19
|
+
retryOptions?: {
|
|
20
|
+
/** number of retry attempts */
|
|
21
|
+
retries?: number;
|
|
22
|
+
/** backoff multiplier */
|
|
23
|
+
factor?: number;
|
|
24
|
+
/** initial delay in milliseconds */
|
|
25
|
+
minTimeoutMs?: number;
|
|
26
|
+
/** maximum delay cap in milliseconds */
|
|
27
|
+
maxTimeoutMs?: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Pluggable ACME challenge handlers (DNS-01, HTTP-01, TLS-ALPN-01, etc.)
|
|
31
|
+
*/
|
|
32
|
+
challengeHandlers?: plugins.handlers.IChallengeHandler<any>[];
|
|
33
|
+
/**
|
|
34
|
+
* Order of challenge types to try (e.g. ['http-01','dns-01']).
|
|
35
|
+
* Defaults to ['dns-01'] or first supported type from handlers.
|
|
36
|
+
*/
|
|
37
|
+
challengePriority?: string[];
|
|
17
38
|
}
|
|
18
39
|
|
|
19
40
|
/**
|
|
@@ -30,24 +51,50 @@ export class SmartAcme {
|
|
|
30
51
|
private options: ISmartAcmeOptions;
|
|
31
52
|
|
|
32
53
|
// the acme client
|
|
33
|
-
private client:
|
|
34
|
-
private smartdns = new plugins.
|
|
54
|
+
private client: plugins.acme.Client;
|
|
55
|
+
private smartdns = new plugins.smartdnsClient.Smartdns({});
|
|
35
56
|
public logger: plugins.smartlog.Smartlog;
|
|
36
57
|
|
|
37
58
|
// the account private key
|
|
38
59
|
private privateKey: string;
|
|
39
60
|
|
|
40
|
-
// challenge fullfillment
|
|
41
|
-
private setChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
42
|
-
private removeChallenge: (dnsChallengeArg: plugins.tsclass.network.IDnsChallenge) => Promise<any>;
|
|
43
61
|
|
|
44
62
|
// certmanager
|
|
45
63
|
private certmanager: SmartacmeCertManager;
|
|
46
64
|
private certmatcher: SmartacmeCertMatcher;
|
|
65
|
+
// retry/backoff configuration (resolved with defaults)
|
|
66
|
+
private retryOptions: { retries: number; factor: number; minTimeoutMs: number; maxTimeoutMs: number };
|
|
67
|
+
// track pending DNS challenges for graceful shutdown
|
|
68
|
+
private pendingChallenges: plugins.tsclass.network.IDnsChallenge[] = [];
|
|
69
|
+
// configured pluggable ACME challenge handlers
|
|
70
|
+
private challengeHandlers: plugins.handlers.IChallengeHandler<any>[];
|
|
71
|
+
// priority order of challenge types
|
|
72
|
+
private challengePriority: string[];
|
|
47
73
|
|
|
48
74
|
constructor(optionsArg: ISmartAcmeOptions) {
|
|
49
75
|
this.options = optionsArg;
|
|
50
76
|
this.logger = plugins.smartlog.Smartlog.createForCommitinfo(commitinfo);
|
|
77
|
+
// enable console output for structured logging
|
|
78
|
+
this.logger.enableConsole();
|
|
79
|
+
// initialize retry/backoff options
|
|
80
|
+
this.retryOptions = {
|
|
81
|
+
retries: optionsArg.retryOptions?.retries ?? 3,
|
|
82
|
+
factor: optionsArg.retryOptions?.factor ?? 2,
|
|
83
|
+
minTimeoutMs: optionsArg.retryOptions?.minTimeoutMs ?? 1000,
|
|
84
|
+
maxTimeoutMs: optionsArg.retryOptions?.maxTimeoutMs ?? 30000,
|
|
85
|
+
};
|
|
86
|
+
// initialize challenge handlers (must provide at least one)
|
|
87
|
+
if (!optionsArg.challengeHandlers || optionsArg.challengeHandlers.length === 0) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
'You must provide at least one ACME challenge handler via options.challengeHandlers',
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
this.challengeHandlers = optionsArg.challengeHandlers;
|
|
93
|
+
// initialize challenge priority
|
|
94
|
+
this.challengePriority =
|
|
95
|
+
optionsArg.challengePriority && optionsArg.challengePriority.length > 0
|
|
96
|
+
? optionsArg.challengePriority
|
|
97
|
+
: this.challengeHandlers.map((h) => h.getSupportedTypes()[0]);
|
|
51
98
|
}
|
|
52
99
|
|
|
53
100
|
/**
|
|
@@ -59,8 +106,6 @@ export class SmartAcme {
|
|
|
59
106
|
public async start() {
|
|
60
107
|
this.privateKey =
|
|
61
108
|
this.options.accountPrivateKey || (await plugins.acme.forge.createPrivateKey()).toString();
|
|
62
|
-
this.setChallenge = this.options.setChallenge;
|
|
63
|
-
this.removeChallenge = this.options.removeChallenge;
|
|
64
109
|
|
|
65
110
|
// CertMangaer
|
|
66
111
|
this.certmanager = new SmartacmeCertManager(this, {
|
|
@@ -88,11 +133,65 @@ export class SmartAcme {
|
|
|
88
133
|
termsOfServiceAgreed: true,
|
|
89
134
|
contact: [`mailto:${this.options.accountEmail}`],
|
|
90
135
|
});
|
|
136
|
+
// Setup graceful shutdown handlers
|
|
137
|
+
process.on('SIGINT', () => this.handleSignal('SIGINT'));
|
|
138
|
+
process.on('SIGTERM', () => this.handleSignal('SIGTERM'));
|
|
91
139
|
}
|
|
92
140
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
141
|
+
public async stop() {
|
|
142
|
+
await this.certmanager.smartdataDb.close();
|
|
143
|
+
}
|
|
144
|
+
/** Retry helper with exponential backoff */
|
|
145
|
+
private async retry<T>(operation: () => Promise<T>, operationName: string = 'operation'): Promise<T> {
|
|
146
|
+
let attempt = 0;
|
|
147
|
+
let delay = this.retryOptions.minTimeoutMs;
|
|
148
|
+
while (true) {
|
|
149
|
+
try {
|
|
150
|
+
return await operation();
|
|
151
|
+
} catch (err) {
|
|
152
|
+
attempt++;
|
|
153
|
+
if (attempt > this.retryOptions.retries) {
|
|
154
|
+
await this.logger.log('error', `Operation ${operationName} failed after ${attempt} attempts`, err);
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
await this.logger.log('warn', `Operation ${operationName} failed on attempt ${attempt}, retrying in ${delay}ms`, err);
|
|
158
|
+
await plugins.smartdelay.delayFor(delay);
|
|
159
|
+
delay = Math.min(delay * this.retryOptions.factor, this.retryOptions.maxTimeoutMs);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** Clean up pending challenges and shut down */
|
|
164
|
+
private async handleShutdown(): Promise<void> {
|
|
165
|
+
for (const input of [...this.pendingChallenges]) {
|
|
166
|
+
const type: string = (input as any).type;
|
|
167
|
+
const handler = this.challengeHandlers.find((h) => h.getSupportedTypes().includes(type));
|
|
168
|
+
if (handler) {
|
|
169
|
+
try {
|
|
170
|
+
await handler.cleanup(input);
|
|
171
|
+
await this.logger.log('info', `Removed pending ${type} challenge during shutdown`, input);
|
|
172
|
+
} catch (err) {
|
|
173
|
+
await this.logger.log('error', `Failed to remove pending ${type} challenge during shutdown`, err);
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
await this.logger.log(
|
|
177
|
+
'warn',
|
|
178
|
+
`No handler for pending challenge type '${type}' during shutdown; skipping cleanup`,
|
|
179
|
+
input,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
this.pendingChallenges = [];
|
|
184
|
+
await this.stop();
|
|
185
|
+
}
|
|
186
|
+
/** Handle process signals for graceful shutdown */
|
|
187
|
+
private handleSignal(sig: string): void {
|
|
188
|
+
this.logger.log('info', `Received signal ${sig}, shutting down gracefully`);
|
|
189
|
+
this.handleShutdown()
|
|
190
|
+
.then(() => process.exit(0))
|
|
191
|
+
.catch((err) => {
|
|
192
|
+
this.logger.log('error', 'Error during shutdown', err).then(() => process.exit(1));
|
|
193
|
+
});
|
|
194
|
+
}
|
|
96
195
|
|
|
97
196
|
/**
|
|
98
197
|
* gets a certificate
|
|
@@ -128,54 +227,71 @@ export class SmartAcme {
|
|
|
128
227
|
// lets make sure others get the same interest
|
|
129
228
|
const currentDomainInterst = await this.certmanager.interestMap.addInterest(certDomainName);
|
|
130
229
|
|
|
131
|
-
/* Place new order */
|
|
132
|
-
const order = await this.client.createOrder({
|
|
230
|
+
/* Place new order with retry */
|
|
231
|
+
const order = await this.retry(() => this.client.createOrder({
|
|
133
232
|
identifiers: [
|
|
134
233
|
{ type: 'dns', value: certDomainName },
|
|
135
234
|
{ type: 'dns', value: `*.${certDomainName}` },
|
|
136
235
|
],
|
|
137
|
-
});
|
|
236
|
+
}), 'createOrder');
|
|
138
237
|
|
|
139
238
|
/* Get authorizations and select challenges */
|
|
140
|
-
const authorizations = await this.client.getAuthorizations(order);
|
|
239
|
+
const authorizations = await this.retry(() => this.client.getAuthorizations(order), 'getAuthorizations');
|
|
141
240
|
|
|
142
241
|
for (const authz of authorizations) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
242
|
+
await this.logger.log('debug', 'Authorization received', authz);
|
|
243
|
+
// select a handler based on configured priority
|
|
244
|
+
let selectedHandler: { type: string; handler: plugins.handlers.IChallengeHandler<any> } | null = null;
|
|
245
|
+
let selectedChallengeArg: any = null;
|
|
246
|
+
for (const type of this.challengePriority) {
|
|
247
|
+
const candidate = authz.challenges.find((c: any) => c.type === type);
|
|
248
|
+
if (!candidate) continue;
|
|
249
|
+
const handler = this.challengeHandlers.find((h) => h.getSupportedTypes().includes(type));
|
|
250
|
+
if (handler) {
|
|
251
|
+
selectedHandler = { type, handler };
|
|
252
|
+
selectedChallengeArg = candidate;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (!selectedHandler) {
|
|
257
|
+
throw new Error(`No challenge handler for domain ${authz.identifier.value}: supported types [${this.challengePriority.join(',')}]`);
|
|
258
|
+
}
|
|
259
|
+
const { type, handler } = selectedHandler;
|
|
260
|
+
// build handler input with keyAuthorization
|
|
261
|
+
let input: any;
|
|
262
|
+
// retrieve keyAuthorization for challenge
|
|
263
|
+
const keyAuth = await this.client.getChallengeKeyAuthorization(selectedChallengeArg);
|
|
264
|
+
if (type === 'dns-01') {
|
|
265
|
+
input = { type, hostName: `_acme-challenge.${authz.identifier.value}`, challenge: keyAuth };
|
|
266
|
+
} else if (type === 'http-01') {
|
|
267
|
+
// HTTP-01 requires serving token at webPath
|
|
268
|
+
input = {
|
|
269
|
+
type,
|
|
270
|
+
token: (selectedChallengeArg as any).token,
|
|
271
|
+
keyAuthorization: keyAuth,
|
|
272
|
+
webPath: `/.well-known/acme-challenge/${(selectedChallengeArg as any).token}`,
|
|
273
|
+
};
|
|
274
|
+
} else {
|
|
275
|
+
// generic challenge input: include raw challenge properties
|
|
276
|
+
input = { type, keyAuthorization: keyAuth, ...selectedChallengeArg };
|
|
277
|
+
}
|
|
278
|
+
this.pendingChallenges.push(input);
|
|
151
279
|
try {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
await this.
|
|
159
|
-
|
|
160
|
-
await plugins.smartdelay.delayFor(60000);
|
|
161
|
-
|
|
162
|
-
/* Verify that challenge is satisfied */
|
|
163
|
-
await this.client.verifyChallenge(authz, dnsChallenge);
|
|
164
|
-
|
|
165
|
-
/* Notify ACME provider that challenge is satisfied */
|
|
166
|
-
await this.client.completeChallenge(dnsChallenge);
|
|
167
|
-
|
|
168
|
-
/* Wait for ACME provider to respond with valid status */
|
|
169
|
-
await this.client.waitForValidStatus(dnsChallenge);
|
|
280
|
+
await this.retry(() => handler.prepare(input), `${type}.prepare`);
|
|
281
|
+
if (handler.verify) {
|
|
282
|
+
await this.retry(() => handler.verify!(input), `${type}.verify`);
|
|
283
|
+
} else {
|
|
284
|
+
await this.retry(() => this.client.verifyChallenge(authz, selectedChallengeArg), `${type}.verifyChallenge`);
|
|
285
|
+
}
|
|
286
|
+
await this.retry(() => this.client.completeChallenge(selectedChallengeArg), `${type}.completeChallenge`);
|
|
287
|
+
await this.retry(() => this.client.waitForValidStatus(selectedChallengeArg), `${type}.waitForValidStatus`);
|
|
170
288
|
} finally {
|
|
171
|
-
/* Clean up challenge response */
|
|
172
289
|
try {
|
|
173
|
-
await this.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
console.log(e);
|
|
290
|
+
await this.retry(() => handler.cleanup(input), `${type}.cleanup`);
|
|
291
|
+
} catch (err) {
|
|
292
|
+
await this.logger.log('error', `Error during ${type}.cleanup`, err);
|
|
293
|
+
} finally {
|
|
294
|
+
this.pendingChallenges = this.pendingChallenges.filter((c) => c !== input);
|
|
179
295
|
}
|
|
180
296
|
}
|
|
181
297
|
}
|
|
@@ -186,8 +302,8 @@ export class SmartAcme {
|
|
|
186
302
|
altNames: [certDomainName],
|
|
187
303
|
});
|
|
188
304
|
|
|
189
|
-
await this.client.finalizeOrder(order, csr);
|
|
190
|
-
const cert = await this.client.getCertificate(order);
|
|
305
|
+
await this.retry(() => this.client.finalizeOrder(order, csr), 'finalizeOrder');
|
|
306
|
+
const cert = await this.retry(() => this.client.getCertificate(order), 'getCertificate');
|
|
191
307
|
|
|
192
308
|
/* Done */
|
|
193
309
|
|
package/ts/smartacme.plugins.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { typedserver };
|
|
|
7
7
|
import * as lik from '@push.rocks/lik';
|
|
8
8
|
import * as smartdata from '@push.rocks/smartdata';
|
|
9
9
|
import * as smartdelay from '@push.rocks/smartdelay';
|
|
10
|
-
import * as
|
|
10
|
+
import * as smartdnsClient from '@push.rocks/smartdns/client';
|
|
11
11
|
import * as smartlog from '@push.rocks/smartlog';
|
|
12
12
|
import * as smartpromise from '@push.rocks/smartpromise';
|
|
13
13
|
import * as smartrequest from '@push.rocks/smartrequest';
|
|
@@ -19,7 +19,7 @@ export {
|
|
|
19
19
|
lik,
|
|
20
20
|
smartdata,
|
|
21
21
|
smartdelay,
|
|
22
|
-
|
|
22
|
+
smartdnsClient,
|
|
23
23
|
smartlog,
|
|
24
24
|
smartpromise,
|
|
25
25
|
smartrequest,
|
|
@@ -37,3 +37,6 @@ export { tsclass };
|
|
|
37
37
|
import * as acme from 'acme-client';
|
|
38
38
|
|
|
39
39
|
export { acme };
|
|
40
|
+
// local handlers for challenge types
|
|
41
|
+
import * as handlers from './handlers/index.js';
|
|
42
|
+
export { handlers };
|