@serve.zone/dcrouter 17.10.2 → 18.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/deno.json +1 -1
- package/dist_serve/bundle.js +360 -360
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/acme/acme-failure-classification.d.ts +64 -0
- package/dist_ts/acme/acme-failure-classification.js +114 -0
- package/dist_ts/acme/classes.smartacme-lifecycle.d.ts +42 -0
- package/dist_ts/acme/classes.smartacme-lifecycle.js +75 -4
- package/dist_ts/acme/index.d.ts +1 -0
- package/dist_ts/acme/index.js +2 -1
- package/dist_ts/classes.dcrouter.d.ts +33 -9
- package/dist_ts/classes.dcrouter.js +145 -11
- package/dist_ts/config/classes.route-config-manager.d.ts +56 -0
- package/dist_ts/config/classes.route-config-manager.js +161 -1
- package/dist_ts/db/documents/classes.dns-authority.doc.d.ts +30 -0
- package/dist_ts/db/documents/classes.dns-authority.doc.js +108 -0
- package/dist_ts/db/documents/index.d.ts +1 -0
- package/dist_ts/db/documents/index.js +2 -1
- package/dist_ts/dns/classes.dns-server-runtime.d.ts +109 -1
- package/dist_ts/dns/classes.dns-server-runtime.js +212 -34
- package/dist_ts/dns/domain-ownership.d.ts +111 -0
- package/dist_ts/dns/domain-ownership.js +152 -0
- package/dist_ts/dns/index.d.ts +2 -0
- package/dist_ts/dns/index.js +3 -1
- package/dist_ts/dns/manager.dns-authority.d.ts +143 -0
- package/dist_ts/dns/manager.dns-authority.js +481 -0
- package/dist_ts/dns/manager.dns.d.ts +121 -12
- package/dist_ts/dns/manager.dns.js +298 -28
- package/dist_ts/email/classes.accepted-email-spool.d.ts +15 -0
- package/dist_ts/email/classes.accepted-email-spool.js +102 -23
- package/dist_ts/email/classes.smartmta-blob-storage-manager.js +12 -3
- package/dist_ts/errors/error.codes.d.ts +4 -0
- package/dist_ts/errors/error.codes.js +5 -1
- package/dist_ts/opsserver/classes.opsserver.d.ts +1 -0
- package/dist_ts/opsserver/classes.opsserver.js +3 -1
- package/dist_ts/opsserver/handlers/acme-config.handler.js +6 -1
- package/dist_ts/opsserver/handlers/certificate.handler.d.ts +16 -0
- package/dist_ts/opsserver/handlers/certificate.handler.js +96 -10
- package/dist_ts/opsserver/handlers/config.handler.js +4 -2
- package/dist_ts/opsserver/handlers/dns-authority.handler.d.ts +20 -0
- package/dist_ts/opsserver/handlers/dns-authority.handler.js +108 -0
- package/dist_ts/opsserver/handlers/dns-provider.handler.js +5 -1
- package/dist_ts/opsserver/handlers/domain.handler.js +9 -1
- package/dist_ts/opsserver/handlers/gatewayclient.handler.js +2 -2
- package/dist_ts/opsserver/handlers/index.d.ts +1 -0
- package/dist_ts/opsserver/handlers/index.js +2 -1
- package/dist_ts_interfaces/data/dns-authority.d.ts +98 -0
- package/dist_ts_interfaces/data/dns-authority.js +26 -0
- package/dist_ts_interfaces/data/index.d.ts +1 -0
- package/dist_ts_interfaces/data/index.js +2 -1
- package/dist_ts_interfaces/data/route-management.d.ts +9 -2
- package/dist_ts_interfaces/data/route-management.js +3 -1
- package/dist_ts_interfaces/requests/certificate.d.ts +23 -0
- package/dist_ts_interfaces/requests/certificate.js +1 -1
- package/dist_ts_interfaces/requests/dns-authority.d.ts +80 -0
- package/dist_ts_interfaces/requests/dns-authority.js +3 -0
- package/dist_ts_interfaces/requests/index.d.ts +1 -0
- package/dist_ts_interfaces/requests/index.js +2 -1
- package/dist_ts_oci_container/index.js +9 -4
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/package.json +1 -1
- package/readme.hints.md +412 -0
- package/readme.md +48 -6
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/acme/acme-failure-classification.ts +201 -0
- package/ts/acme/classes.smartacme-lifecycle.ts +104 -3
- package/ts/acme/index.ts +1 -0
- package/ts/classes.dcrouter.ts +193 -23
- package/ts/config/classes.route-config-manager.ts +197 -0
- package/ts/db/documents/classes.dns-authority.doc.ts +49 -0
- package/ts/db/documents/index.ts +1 -0
- package/ts/dns/classes.dns-server-runtime.ts +257 -38
- package/ts/dns/domain-ownership.ts +272 -0
- package/ts/dns/index.ts +2 -0
- package/ts/dns/manager.dns-authority.ts +558 -0
- package/ts/dns/manager.dns.ts +373 -27
- package/ts/email/classes.accepted-email-spool.ts +98 -21
- package/ts/email/classes.smartmta-blob-storage-manager.ts +10 -2
- package/ts/errors/error.codes.ts +4 -0
- package/ts/opsserver/classes.opsserver.ts +2 -0
- package/ts/opsserver/handlers/acme-config.handler.ts +7 -0
- package/ts/opsserver/handlers/certificate.handler.ts +103 -8
- package/ts/opsserver/handlers/config.handler.ts +3 -1
- package/ts/opsserver/handlers/dns-authority.handler.ts +142 -0
- package/ts/opsserver/handlers/dns-provider.handler.ts +6 -0
- package/ts/opsserver/handlers/domain.handler.ts +12 -0
- package/ts/opsserver/handlers/gatewayclient.handler.ts +1 -1
- package/ts/opsserver/handlers/index.ts +1 -0
- package/ts/readme.md +1 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/dcrouter',
|
|
6
|
-
version: '
|
|
6
|
+
version: '18.0.1',
|
|
7
7
|
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
|
8
8
|
};
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxzQkFBc0I7SUFDNUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLDBFQUEwRTtDQUN4RixDQUFBIn0=
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { PlatformError, type IErrorContext } from '../errors/base.errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* ACME failure classification.
|
|
4
|
+
*
|
|
5
|
+
* dcrouter has two independent ACME retry budgets, and neither used to look at
|
|
6
|
+
* *why* a failure happened:
|
|
7
|
+
*
|
|
8
|
+
* 1. `SmartAcmeLifecycle` — SmartAcme provider startup. 5 s→1 h exponential
|
|
9
|
+
* backoff with jitter, hard cap of 20 attempts, then permanent give-up.
|
|
10
|
+
* Only a SmartProxy rebuild or a process restart re-arms it.
|
|
11
|
+
* 2. `CertProvisionScheduler` — per-domain certificate provisioning.
|
|
12
|
+
* `min(failures², 24 h)` backoff, **no cap**, re-armed by time forever.
|
|
13
|
+
* This is the budget that reached 31–45 failures on the broken domains.
|
|
14
|
+
*
|
|
15
|
+
* Retrying is correct for rate limits, DNS propagation and transport faults. It
|
|
16
|
+
* is never correct for a configuration cause: a hostname with no managed domain
|
|
17
|
+
* cannot acquire one by waiting, so every one of those attempts was a silent
|
|
18
|
+
* no-op that also kept the real reason out of the operator's view. Permanent
|
|
19
|
+
* causes must therefore skip the budget entirely and surface attributably.
|
|
20
|
+
*
|
|
21
|
+
* Unclassified causes stay transient on purpose. Guessing "permanent" would
|
|
22
|
+
* strand recoverable domains, so the default preserves existing retry behaviour;
|
|
23
|
+
* only causes we can positively recognise are treated as terminal.
|
|
24
|
+
*/
|
|
25
|
+
export type TAcmeFailureReason =
|
|
26
|
+
/** Ownership of the hostname could not be proven (no DomainDoc / no provider zone / not delegation-verified). */
|
|
27
|
+
'domain-ownership-unverified'
|
|
28
|
+
/** The DNS-01 dispatcher found no managed zone able to hold the challenge record. */
|
|
29
|
+
| 'no-managed-dns-zone'
|
|
30
|
+
/** No challenge handler / provider is wired for this domain at all. */
|
|
31
|
+
| 'no-challenge-handler'
|
|
32
|
+
/** The ACME account itself is misconfigured (email, terms, key, directory URL). */
|
|
33
|
+
| 'acme-account-configuration'
|
|
34
|
+
/** CAA forbids our issuer — only the domain holder can change this. */
|
|
35
|
+
| 'caa-forbids-issuance'
|
|
36
|
+
/** ACME server rate limit — retrying is exactly right. */
|
|
37
|
+
| 'rate-limited'
|
|
38
|
+
/** Challenge not yet visible, propagation delay, transport fault. */
|
|
39
|
+
| 'transient'
|
|
40
|
+
/** Nothing recognised. Treated as transient so recoverable causes keep retrying. */
|
|
41
|
+
| 'unclassified';
|
|
42
|
+
export interface IAcmeFailureClassification {
|
|
43
|
+
reason: TAcmeFailureReason;
|
|
44
|
+
/** True when no retry can resolve the cause, so the retry budget must not be consumed. */
|
|
45
|
+
permanent: boolean;
|
|
46
|
+
message: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Classify an ACME/provisioning failure into a retryable or terminal cause.
|
|
50
|
+
* Structured errors win over text matching: a `DomainOwnershipError` and any
|
|
51
|
+
* NON_RECOVERABLE CONFIGURATION `PlatformError` are permanent by declaration.
|
|
52
|
+
*/
|
|
53
|
+
export declare const classifyAcmeFailure: (errorArg: unknown) => IAcmeFailureClassification;
|
|
54
|
+
/**
|
|
55
|
+
* Terminal ACME failure. HIGH severity so the automatic PlatformError log lands
|
|
56
|
+
* at `error` rather than being lost in provisioning warn noise, and
|
|
57
|
+
* NON_RECOVERABLE so `isRetryable()` and every downstream retry layer agree that
|
|
58
|
+
* this must not be retried.
|
|
59
|
+
*/
|
|
60
|
+
export declare class AcmePermanentFailureError extends PlatformError {
|
|
61
|
+
readonly classification: IAcmeFailureClassification;
|
|
62
|
+
constructor(classification: IAcmeFailureClassification, operation: string, component: string, context?: IErrorContext);
|
|
63
|
+
protected createWithContext(context: IErrorContext): PlatformError;
|
|
64
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { PlatformError } from '../errors/base.errors.js';
|
|
2
|
+
import { DCR_ACME_PERMANENT_FAILURE, ErrorCategory, ErrorRecoverability, ErrorSeverity, } from '../errors/error.codes.js';
|
|
3
|
+
import { DomainOwnershipError } from '../dns/domain-ownership.js';
|
|
4
|
+
/**
|
|
5
|
+
* Ordered most-specific-first. Each pattern must only match text that uniquely
|
|
6
|
+
* identifies the cause — a false "permanent" verdict silently stops legitimate
|
|
7
|
+
* retries, which is a worse failure than an extra retry.
|
|
8
|
+
*/
|
|
9
|
+
const reasonPatterns = [
|
|
10
|
+
{
|
|
11
|
+
// Thrown by DnsManager.buildAcmeConvenientDnsProvider() when no DomainDoc covers the FQDN.
|
|
12
|
+
reason: 'no-managed-dns-zone',
|
|
13
|
+
permanent: true,
|
|
14
|
+
patterns: [
|
|
15
|
+
/no managed domain found for/i,
|
|
16
|
+
/add the domain in domains before issuing certificates/i,
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
reason: 'no-challenge-handler',
|
|
21
|
+
permanent: true,
|
|
22
|
+
patterns: [
|
|
23
|
+
/no (?:challenge )?handler (?:found |available )?for/i,
|
|
24
|
+
/no dns-01 (?:handler|provider)/i,
|
|
25
|
+
/domain is not supported by any challenge handler/i,
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
reason: 'caa-forbids-issuance',
|
|
30
|
+
permanent: true,
|
|
31
|
+
patterns: [
|
|
32
|
+
/caa record(?:s)? (?:for [^\s]+ )?(?:prevent|forbid|do not allow)/i,
|
|
33
|
+
/urn:ietf:params:acme:error:caa/i,
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
reason: 'acme-account-configuration',
|
|
38
|
+
permanent: true,
|
|
39
|
+
patterns: [
|
|
40
|
+
/urn:ietf:params:acme:error:invalidemail/i,
|
|
41
|
+
/urn:ietf:params:acme:error:accountdoesnotexist/i,
|
|
42
|
+
/urn:ietf:params:acme:error:unsupportedcontact/i,
|
|
43
|
+
/must agree to (?:the )?terms of service/i,
|
|
44
|
+
/accountemail is required/i,
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
reason: 'rate-limited',
|
|
49
|
+
permanent: false,
|
|
50
|
+
patterns: [
|
|
51
|
+
/urn:ietf:params:acme:error:ratelimited/i,
|
|
52
|
+
/too many (?:certificates|requests|failed authorizations)/i,
|
|
53
|
+
/rate ?limit/i,
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
const extractMessage = (errorArg) => {
|
|
58
|
+
if (errorArg instanceof Error)
|
|
59
|
+
return errorArg.message;
|
|
60
|
+
if (typeof errorArg === 'string')
|
|
61
|
+
return errorArg;
|
|
62
|
+
if (errorArg && typeof errorArg === 'object' && 'message' in errorArg) {
|
|
63
|
+
return String(errorArg.message);
|
|
64
|
+
}
|
|
65
|
+
return String(errorArg);
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Classify an ACME/provisioning failure into a retryable or terminal cause.
|
|
69
|
+
* Structured errors win over text matching: a `DomainOwnershipError` and any
|
|
70
|
+
* NON_RECOVERABLE CONFIGURATION `PlatformError` are permanent by declaration.
|
|
71
|
+
*/
|
|
72
|
+
export const classifyAcmeFailure = (errorArg) => {
|
|
73
|
+
const message = extractMessage(errorArg);
|
|
74
|
+
if (errorArg instanceof DomainOwnershipError) {
|
|
75
|
+
return { reason: 'domain-ownership-unverified', permanent: true, message };
|
|
76
|
+
}
|
|
77
|
+
if (errorArg instanceof PlatformError
|
|
78
|
+
&& errorArg.category === ErrorCategory.CONFIGURATION
|
|
79
|
+
&& errorArg.recoverability === ErrorRecoverability.NON_RECOVERABLE) {
|
|
80
|
+
return { reason: 'acme-account-configuration', permanent: true, message };
|
|
81
|
+
}
|
|
82
|
+
for (const candidate of reasonPatterns) {
|
|
83
|
+
if (candidate.patterns.some((pattern) => pattern.test(message))) {
|
|
84
|
+
return { reason: candidate.reason, permanent: candidate.permanent, message };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { reason: 'unclassified', permanent: false, message };
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Terminal ACME failure. HIGH severity so the automatic PlatformError log lands
|
|
91
|
+
* at `error` rather than being lost in provisioning warn noise, and
|
|
92
|
+
* NON_RECOVERABLE so `isRetryable()` and every downstream retry layer agree that
|
|
93
|
+
* this must not be retried.
|
|
94
|
+
*/
|
|
95
|
+
export class AcmePermanentFailureError extends PlatformError {
|
|
96
|
+
classification;
|
|
97
|
+
constructor(classification, operation, component, context = {}) {
|
|
98
|
+
super(`${operation} failed permanently (${classification.reason}): ${classification.message}`, DCR_ACME_PERMANENT_FAILURE, ErrorSeverity.HIGH, ErrorCategory.CONFIGURATION, ErrorRecoverability.NON_RECOVERABLE, {
|
|
99
|
+
component,
|
|
100
|
+
operation,
|
|
101
|
+
userMessage: `${operation} cannot succeed until its configuration is fixed (${classification.reason}): ${classification.message}`,
|
|
102
|
+
...context,
|
|
103
|
+
data: {
|
|
104
|
+
acmeFailureReason: classification.reason,
|
|
105
|
+
...context.data,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
this.classification = classification;
|
|
109
|
+
}
|
|
110
|
+
createWithContext(context) {
|
|
111
|
+
return new AcmePermanentFailureError(this.classification, this.context.operation || 'operation', this.context.component || 'acme', context);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS1mYWlsdXJlLWNsYXNzaWZpY2F0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvYWNtZS9hY21lLWZhaWx1cmUtY2xhc3NpZmljYXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGFBQWEsRUFBc0IsTUFBTSwwQkFBMEIsQ0FBQztBQUM3RSxPQUFPLEVBQ0wsMEJBQTBCLEVBQzFCLGFBQWEsRUFDYixtQkFBbUIsRUFDbkIsYUFBYSxHQUNkLE1BQU0sMEJBQTBCLENBQUM7QUFDbEMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUF5RGxFOzs7O0dBSUc7QUFDSCxNQUFNLGNBQWMsR0FBcUI7SUFDdkM7UUFDRSwyRkFBMkY7UUFDM0YsTUFBTSxFQUFFLHFCQUFxQjtRQUM3QixTQUFTLEVBQUUsSUFBSTtRQUNmLFFBQVEsRUFBRTtZQUNSLDhCQUE4QjtZQUM5Qix3REFBd0Q7U0FDekQ7S0FDRjtJQUNEO1FBQ0UsTUFBTSxFQUFFLHNCQUFzQjtRQUM5QixTQUFTLEVBQUUsSUFBSTtRQUNmLFFBQVEsRUFBRTtZQUNSLHNEQUFzRDtZQUN0RCxpQ0FBaUM7WUFDakMsbURBQW1EO1NBQ3BEO0tBQ0Y7SUFDRDtRQUNFLE1BQU0sRUFBRSxzQkFBc0I7UUFDOUIsU0FBUyxFQUFFLElBQUk7UUFDZixRQUFRLEVBQUU7WUFDUixtRUFBbUU7WUFDbkUsaUNBQWlDO1NBQ2xDO0tBQ0Y7SUFDRDtRQUNFLE1BQU0sRUFBRSw0QkFBNEI7UUFDcEMsU0FBUyxFQUFFLElBQUk7UUFDZixRQUFRLEVBQUU7WUFDUiwwQ0FBMEM7WUFDMUMsaURBQWlEO1lBQ2pELGdEQUFnRDtZQUNoRCwwQ0FBMEM7WUFDMUMsMkJBQTJCO1NBQzVCO0tBQ0Y7SUFDRDtRQUNFLE1BQU0sRUFBRSxjQUFjO1FBQ3RCLFNBQVMsRUFBRSxLQUFLO1FBQ2hCLFFBQVEsRUFBRTtZQUNSLHlDQUF5QztZQUN6Qywy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
|
|
@@ -1,9 +1,38 @@
|
|
|
1
1
|
import type { DcRouter } from '../classes.dcrouter.js';
|
|
2
|
+
import { type TAcmeFailureReason } from './acme-failure-classification.js';
|
|
3
|
+
/** Terminal state of the SmartAcme provider startup budget. */
|
|
4
|
+
export interface IAcmeStartFailure {
|
|
5
|
+
reason: TAcmeFailureReason;
|
|
6
|
+
/** True when no further retry can resolve the cause. */
|
|
7
|
+
permanent: boolean;
|
|
8
|
+
/** Attempts consumed when the budget ended. */
|
|
9
|
+
attempts: number;
|
|
10
|
+
message: string;
|
|
11
|
+
at: number;
|
|
12
|
+
/** What an operator has to do to re-arm startup. */
|
|
13
|
+
rearmedBy: string;
|
|
14
|
+
}
|
|
15
|
+
/** Attempts allowed for transient SmartAcme startup failures before giving up. */
|
|
16
|
+
export declare const smartAcmeStartMaxAttempts = 20;
|
|
2
17
|
/**
|
|
3
18
|
* Background start/retry/stop lifecycle for the DcRouter-owned SmartAcme
|
|
4
19
|
* instance. SmartAcme startup can hit ACME rate limits, so startup runs in
|
|
5
20
|
* the background with generation-guarded exponential retry, and certificate
|
|
6
21
|
* provisioning is re-triggered once DNS-01 becomes ready.
|
|
22
|
+
*
|
|
23
|
+
* Retry semantics, which are NOT shared with the per-domain
|
|
24
|
+
* `CertProvisionScheduler` budget:
|
|
25
|
+
* - increments once per failed `smartAcme.start()`;
|
|
26
|
+
* - 5 s → 1 h exponential backoff with ±20% jitter;
|
|
27
|
+
* - capped at `smartAcmeStartMaxAttempts` transient attempts;
|
|
28
|
+
* - reset by `startInBackground()`, `stop()`, and a successful start;
|
|
29
|
+
* - re-armed only by `startInBackground()`, i.e. a SmartProxy rebuild, an
|
|
30
|
+
* explicit `rearm()` after a configuration change, or a process restart.
|
|
31
|
+
* Nothing re-arms it on a timer, so the terminal state is recorded in
|
|
32
|
+
* `startFailure` and logged at `error` instead of scrolling past as a warning.
|
|
33
|
+
*
|
|
34
|
+
* A permanent cause never enters the backoff at all: retrying a misconfigured
|
|
35
|
+
* ACME account cannot fix it, and spending the budget on it hides the reason.
|
|
7
36
|
*/
|
|
8
37
|
export declare class SmartAcmeLifecycle {
|
|
9
38
|
private dcRouterRef;
|
|
@@ -11,12 +40,25 @@ export declare class SmartAcmeLifecycle {
|
|
|
11
40
|
ready: boolean;
|
|
12
41
|
/** Tracks whether the taskbuffer SmartAcme service is started, so SmartProxy rebuilds can re-kick startup. */
|
|
13
42
|
serviceStarted: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Set when the startup budget ended — either immediately for a permanent cause
|
|
45
|
+
* or after the transient attempt cap. Cleared on every (re-)arm and on success.
|
|
46
|
+
*/
|
|
47
|
+
startFailure?: IAcmeStartFailure;
|
|
14
48
|
private startGeneration;
|
|
15
49
|
private startPromise?;
|
|
16
50
|
private retryTimer?;
|
|
17
51
|
private retryAttempt;
|
|
18
52
|
constructor(dcRouterRef: DcRouter);
|
|
19
53
|
startInBackground(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Re-arm startup after a configuration change that can plausibly fix a
|
|
56
|
+
* previously terminal cause (e.g. the ACME account settings were corrected).
|
|
57
|
+
* Without this, an exhausted or permanently-failed budget could only be reset
|
|
58
|
+
* by a SmartProxy rebuild or a full restart — and a dcrouter restart is a
|
|
59
|
+
* measured 30–60 s of total public outage.
|
|
60
|
+
*/
|
|
61
|
+
rearm(reasonArg: string): boolean;
|
|
20
62
|
stop(): Promise<void>;
|
|
21
63
|
private scheduleStart;
|
|
22
64
|
private runStartAttempt;
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import * as plugins from '../plugins.js';
|
|
2
2
|
import { logger } from '../logger.js';
|
|
3
|
+
import { AcmePermanentFailureError, classifyAcmeFailure, } from './acme-failure-classification.js';
|
|
4
|
+
/** Attempts allowed for transient SmartAcme startup failures before giving up. */
|
|
5
|
+
export const smartAcmeStartMaxAttempts = 20;
|
|
3
6
|
/**
|
|
4
7
|
* Background start/retry/stop lifecycle for the DcRouter-owned SmartAcme
|
|
5
8
|
* instance. SmartAcme startup can hit ACME rate limits, so startup runs in
|
|
6
9
|
* the background with generation-guarded exponential retry, and certificate
|
|
7
10
|
* provisioning is re-triggered once DNS-01 becomes ready.
|
|
11
|
+
*
|
|
12
|
+
* Retry semantics, which are NOT shared with the per-domain
|
|
13
|
+
* `CertProvisionScheduler` budget:
|
|
14
|
+
* - increments once per failed `smartAcme.start()`;
|
|
15
|
+
* - 5 s → 1 h exponential backoff with ±20% jitter;
|
|
16
|
+
* - capped at `smartAcmeStartMaxAttempts` transient attempts;
|
|
17
|
+
* - reset by `startInBackground()`, `stop()`, and a successful start;
|
|
18
|
+
* - re-armed only by `startInBackground()`, i.e. a SmartProxy rebuild, an
|
|
19
|
+
* explicit `rearm()` after a configuration change, or a process restart.
|
|
20
|
+
* Nothing re-arms it on a timer, so the terminal state is recorded in
|
|
21
|
+
* `startFailure` and logged at `error` instead of scrolling past as a warning.
|
|
22
|
+
*
|
|
23
|
+
* A permanent cause never enters the backoff at all: retrying a misconfigured
|
|
24
|
+
* ACME account cannot fix it, and spending the budget on it hides the reason.
|
|
8
25
|
*/
|
|
9
26
|
export class SmartAcmeLifecycle {
|
|
10
27
|
dcRouterRef;
|
|
@@ -12,6 +29,11 @@ export class SmartAcmeLifecycle {
|
|
|
12
29
|
ready = false;
|
|
13
30
|
/** Tracks whether the taskbuffer SmartAcme service is started, so SmartProxy rebuilds can re-kick startup. */
|
|
14
31
|
serviceStarted = false;
|
|
32
|
+
/**
|
|
33
|
+
* Set when the startup budget ended — either immediately for a permanent cause
|
|
34
|
+
* or after the transient attempt cap. Cleared on every (re-)arm and on success.
|
|
35
|
+
*/
|
|
36
|
+
startFailure;
|
|
15
37
|
startGeneration = 0;
|
|
16
38
|
startPromise;
|
|
17
39
|
retryTimer;
|
|
@@ -27,13 +49,33 @@ export class SmartAcmeLifecycle {
|
|
|
27
49
|
const generation = ++this.startGeneration;
|
|
28
50
|
this.ready = false;
|
|
29
51
|
this.retryAttempt = 0;
|
|
52
|
+
this.startFailure = undefined;
|
|
30
53
|
this.clearRetryTimer();
|
|
31
54
|
this.scheduleStart(generation, 0);
|
|
32
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Re-arm startup after a configuration change that can plausibly fix a
|
|
58
|
+
* previously terminal cause (e.g. the ACME account settings were corrected).
|
|
59
|
+
* Without this, an exhausted or permanently-failed budget could only be reset
|
|
60
|
+
* by a SmartProxy rebuild or a full restart — and a dcrouter restart is a
|
|
61
|
+
* measured 30–60 s of total public outage.
|
|
62
|
+
*/
|
|
63
|
+
rearm(reasonArg) {
|
|
64
|
+
if (!this.dcRouterRef.smartAcme || !this.serviceStarted) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (this.ready && !this.startFailure) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
logger.log('info', `Re-arming SmartAcme DNS-01 provider startup: ${reasonArg}`);
|
|
71
|
+
this.startInBackground();
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
33
74
|
async stop() {
|
|
34
75
|
this.startGeneration++;
|
|
35
76
|
this.ready = false;
|
|
36
77
|
this.retryAttempt = 0;
|
|
78
|
+
this.startFailure = undefined;
|
|
37
79
|
this.clearRetryTimer();
|
|
38
80
|
const smartAcme = this.dcRouterRef.smartAcme;
|
|
39
81
|
if (!smartAcme) {
|
|
@@ -82,6 +124,7 @@ export class SmartAcmeLifecycle {
|
|
|
82
124
|
}
|
|
83
125
|
this.ready = true;
|
|
84
126
|
this.retryAttempt = 0;
|
|
127
|
+
this.startFailure = undefined;
|
|
85
128
|
logger.log('info', 'SmartAcme DNS-01 provider is now ready');
|
|
86
129
|
this.retriggerCertificateProvisioning();
|
|
87
130
|
}
|
|
@@ -93,9 +136,37 @@ export class SmartAcmeLifecycle {
|
|
|
93
136
|
await smartAcme.stop().catch((stopErr) => {
|
|
94
137
|
logger.log('warn', `Failed to clean up SmartAcme after startup failure: ${stopErr.message}`);
|
|
95
138
|
});
|
|
139
|
+
// Classify before spending any of the budget: a permanent cause is not made
|
|
140
|
+
// truer by 20 more attempts, and every attempt spent on it is an hour in
|
|
141
|
+
// which nothing tells the operator what is actually wrong.
|
|
142
|
+
const classification = classifyAcmeFailure(err);
|
|
143
|
+
if (classification.permanent) {
|
|
144
|
+
this.startFailure = {
|
|
145
|
+
reason: classification.reason,
|
|
146
|
+
permanent: true,
|
|
147
|
+
attempts: this.retryAttempt,
|
|
148
|
+
message: classification.message,
|
|
149
|
+
at: Date.now(),
|
|
150
|
+
rearmedBy: 'fixing the ACME configuration (which calls rearm()), a SmartProxy rebuild, or a process restart',
|
|
151
|
+
};
|
|
152
|
+
// Constructing this logs at `error` with the code and category attached.
|
|
153
|
+
new AcmePermanentFailureError(classification, 'SmartAcme DNS-01 provider startup', 'smartacme-lifecycle', { data: { attempts: this.retryAttempt, rearmedBy: this.startFailure.rearmedBy } });
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
96
156
|
this.retryAttempt++;
|
|
97
|
-
if (this.retryAttempt >
|
|
98
|
-
|
|
157
|
+
if (this.retryAttempt > smartAcmeStartMaxAttempts) {
|
|
158
|
+
this.startFailure = {
|
|
159
|
+
reason: classification.reason,
|
|
160
|
+
permanent: false,
|
|
161
|
+
attempts: this.retryAttempt - 1,
|
|
162
|
+
message: classification.message,
|
|
163
|
+
at: Date.now(),
|
|
164
|
+
rearmedBy: 'a SmartProxy rebuild, an explicit rearm() after a configuration change, or a process restart',
|
|
165
|
+
};
|
|
166
|
+
logger.log('error', `SmartAcme DNS-01 provider gave up after ${smartAcmeStartMaxAttempts} startup attempts `
|
|
167
|
+
+ `(${classification.reason}): ${classification.message}. `
|
|
168
|
+
+ 'Nothing re-arms this on a timer — DNS-01 stays unavailable and every affected route falls back to a '
|
|
169
|
+
+ `no-op http-01 path until ${this.startFailure.rearmedBy}.`, { acmeFailureReason: classification.reason, attempts: this.startFailure.attempts });
|
|
99
170
|
return;
|
|
100
171
|
}
|
|
101
172
|
const baseDelayMs = 5000;
|
|
@@ -103,7 +174,7 @@ export class SmartAcmeLifecycle {
|
|
|
103
174
|
const delayMs = Math.min(baseDelayMs * Math.pow(2, this.retryAttempt - 1), maxDelayMs);
|
|
104
175
|
const jitter = 0.8 + Math.random() * 0.4;
|
|
105
176
|
const actualDelayMs = Math.floor(delayMs * jitter);
|
|
106
|
-
logger.log('warn', `SmartAcme DNS-01 provider startup failed: ${err.message}; retrying in ${actualDelayMs}ms (attempt ${this.retryAttempt}
|
|
177
|
+
logger.log('warn', `SmartAcme DNS-01 provider startup failed: ${err.message}; retrying in ${actualDelayMs}ms (attempt ${this.retryAttempt}/${smartAcmeStartMaxAttempts})`);
|
|
107
178
|
this.scheduleStart(generation, actualDelayMs);
|
|
108
179
|
}
|
|
109
180
|
finally {
|
|
@@ -141,4 +212,4 @@ export class SmartAcmeLifecycle {
|
|
|
141
212
|
}
|
|
142
213
|
}
|
|
143
214
|
}
|
|
144
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
215
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zbWFydGFjbWUtbGlmZWN5Y2xlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvYWNtZS9jbGFzc2VzLnNtYXJ0YWNtZS1saWZlY3ljbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUV0QyxPQUFPLEVBQ0wseUJBQXlCLEVBQ3pCLG1CQUFtQixHQUVwQixNQUFNLGtDQUFrQyxDQUFDO0FBZTFDLGtGQUFrRjtBQUNsRixNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyxFQUFFLENBQUM7QUFFNUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQkc7QUFDSCxNQUFNLE9BQU8sa0JBQWtCO0lBZ0JUO0lBZnBCLGlFQUFpRTtJQUMxRCxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLDhHQUE4RztJQUN2RyxjQUFjLEdBQUcsS0FBSyxDQUFDO0lBQzlCOzs7T0FHRztJQUNJLFlBQVksQ0FBcUI7SUFFaEMsZUFBZSxHQUFHLENBQUMsQ0FBQztJQUNwQixZQUFZLENBQWlCO0lBQzdCLFVBQVUsQ0FBaUM7SUFDM0MsWUFBWSxHQUFHLENBQUMsQ0FBQztJQUV6QixZQUFvQixXQUFxQjtRQUFyQixnQkFBVyxHQUFYLFdBQVcsQ0FBVTtJQUFHLENBQUM7SUFFdEMsaUJBQWlCO1FBQ3RCLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1lBQ25CLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxVQUFVLEdBQUcsRUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDO1FBQzFDLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ25CLElBQUksQ0FBQyxZQUFZLEdBQUcsQ0FBQyxDQUFDO1FBQ3RCLElBQUks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
|
package/dist_ts/acme/index.d.ts
CHANGED
package/dist_ts/acme/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export * from './manager.acme-config.js';
|
|
2
2
|
export * from './classes.smartacme-lifecycle.js';
|
|
3
|
-
|
|
3
|
+
export * from './acme-failure-classification.js';
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9hY21lL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsMEJBQTBCLENBQUM7QUFDekMsY0FBYyxrQ0FBa0MsQ0FBQztBQUNqRCxjQUFjLGtDQUFrQyxDQUFDIn0=
|
|
@@ -12,6 +12,7 @@ import { RouteConfigManager, ApiTokenManager, GatewayClientManager, ReferenceRes
|
|
|
12
12
|
import { SecurityPolicyManager, RoutePolicyAugmenter } from './security/index.js';
|
|
13
13
|
import { type IHttp3Config } from './http3/index.js';
|
|
14
14
|
import { DnsManager } from './dns/manager.dns.js';
|
|
15
|
+
import { DnsAuthorityManager } from './dns/manager.dns-authority.js';
|
|
15
16
|
import { DnsServerRuntime } from './dns/classes.dns-server-runtime.js';
|
|
16
17
|
import { GatewayRouteDnsReconciler } from './dns/classes.gateway-route-dns-reconciler.js';
|
|
17
18
|
import { AcmeConfigManager } from './acme/manager.acme-config.js';
|
|
@@ -61,16 +62,14 @@ export interface IDcRouterOptions {
|
|
|
61
62
|
/**
|
|
62
63
|
* The nameserver domains (e.g., ['ns1.example.com', 'ns2.example.com'])
|
|
63
64
|
* These will automatically get A records pointing to publicIp or proxyIps[0]
|
|
64
|
-
*
|
|
65
|
+
* A zone becomes authoritative by having its public NS records observed
|
|
66
|
+
* naming one of these — that is the only way into the authority set.
|
|
67
|
+
*
|
|
68
|
+
* There is deliberately no `dnsScopes` counterpart. Which zones dcrouter
|
|
69
|
+
* answers for is database state carrying its own delegation evidence, not
|
|
70
|
+
* deployment configuration; see ts/dns/manager.dns-authority.ts.
|
|
65
71
|
*/
|
|
66
72
|
dnsNsDomains?: string[];
|
|
67
|
-
/**
|
|
68
|
-
* Domains this DNS server is authoritative for (e.g., ['example.com', 'mail.example.org'])
|
|
69
|
-
* NS records will be auto-generated for these domains
|
|
70
|
-
* Any DNS record outside these scopes will trigger a warning
|
|
71
|
-
* Email domains with `internal-dns` mode must be included here
|
|
72
|
-
*/
|
|
73
|
-
dnsScopes?: string[];
|
|
74
73
|
/** Explicit UDP bind address for the embedded DNS server. Defaults to auto-detection. */
|
|
75
74
|
dnsBindInterface?: string;
|
|
76
75
|
/**
|
|
@@ -88,7 +87,7 @@ export interface IDcRouterOptions {
|
|
|
88
87
|
publicIp?: string;
|
|
89
88
|
/**
|
|
90
89
|
* DNS records to register
|
|
91
|
-
* Must be within
|
|
90
|
+
* Must be within a delegation-verified zone (or receive warning)
|
|
92
91
|
* Only need A, CNAME, TXT, MX records (NS records auto-generated, SOA handled by smartdns)
|
|
93
92
|
* Can use `useIngressProxy: false` to expose real server IP (defaults to true)
|
|
94
93
|
*/
|
|
@@ -246,6 +245,8 @@ export declare class DcRouter {
|
|
|
246
245
|
referenceResolver?: ReferenceResolver;
|
|
247
246
|
targetProfileManager?: TargetProfileManager;
|
|
248
247
|
dnsManager?: DnsManager;
|
|
248
|
+
/** Owns which zones dcrouter may answer for authoritatively, and proves it. */
|
|
249
|
+
dnsAuthorityManager?: DnsAuthorityManager;
|
|
249
250
|
gatewayRouteDnsReconciler?: GatewayRouteDnsReconciler;
|
|
250
251
|
opsEventManager?: OpsEventManager;
|
|
251
252
|
acmeConfigManager?: AcmeConfigManager;
|
|
@@ -373,6 +374,29 @@ export declare class DcRouter {
|
|
|
373
374
|
/** Serialized edge mutation on the RemoteIngress hub (delegates to the hub lifecycle). */
|
|
374
375
|
mutateRemoteIngressEdges<T>(mutation: (manager: RemoteIngressManager) => Promise<T>, syncAllowedEdges?: boolean): Promise<T>;
|
|
375
376
|
updateRemoteIngressHubSettings(updates: TRemoteIngressHubSettingsUpdate, updatedBy: string): Promise<IRemoteIngressHubSettings>;
|
|
377
|
+
/**
|
|
378
|
+
* Re-derive every piece of runtime state that depends on the effective DNS
|
|
379
|
+
* authority set, after a zone gained or lost delegation-verified authority.
|
|
380
|
+
*
|
|
381
|
+
* Deliberately reuses the existing machinery rather than paralleling it:
|
|
382
|
+
* `reconcileAuthoritativeZones()` drives the same `runtimeRegistrations`
|
|
383
|
+
* registry that domain deletion uses, and the overlay and route-warning
|
|
384
|
+
* refreshes are the same calls the domain mutation paths make. Throws on the
|
|
385
|
+
* first failure so `DnsAuthorityManager` can roll the change back rather than
|
|
386
|
+
* leave the router half-converted.
|
|
387
|
+
*/
|
|
388
|
+
reconcileDnsAuthority(reasonArg: string): Promise<void>;
|
|
389
|
+
/**
|
|
390
|
+
* Re-derive the private-route DNS overlay from the currently applied route set.
|
|
391
|
+
*
|
|
392
|
+
* The overlay is derived from routes but gated on domain ownership, so a domain
|
|
393
|
+
* mutation can invalidate it without any route changing. Deleting a DomainDoc
|
|
394
|
+
* is the case that matters: without this the overlay keeps answering `aa` for a
|
|
395
|
+
* hostname whose zone we no longer manage until some unrelated route apply
|
|
396
|
+
* happens. Named here so the mutation site can trigger its own invalidation
|
|
397
|
+
* instead of forcing a full route re-apply.
|
|
398
|
+
*/
|
|
399
|
+
resyncPrivateRouteDnsOverlay(reasonArg: string): Promise<void>;
|
|
376
400
|
/** Restart SmartProxy after RemoteIngress hub settings changed listener wiring. Called by RemoteIngressHubLifecycle. */
|
|
377
401
|
restartSmartProxyForRemoteIngressSettings(): Promise<void>;
|
|
378
402
|
/** Bootstrap routes the RemoteIngress hub uses to derive edge ports before the DB route set is applied. */
|