Skip to content

Commit c272920

Browse files
docs: document CNAME delegation (proxy) lookup
Add a CNAME Delegation section to docsource/configuration.md describing why challenge names are delegated to an isolated validation zone, how the CnameResolver follows a multi-level CNAME chain to its terminus, how the DNS provider plugin is selected against the resolved target (enabling cross-provider delegation), the loop/depth safety guards, and private-zone resolution via DnsVerificationServer. Update the enrollment flow summary to include the CNAME resolution step.
1 parent 936bcdc commit c272920

1 file changed

Lines changed: 56 additions & 3 deletions

File tree

docsource/configuration.md

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ Additional DNS providers can be added by extending the included `IDnsProvider` i
3939
2. Plugin initializes ACME client and creates a new order.
4040
3. For each domain:
4141
a. Retrieve DNS-01 challenge.
42-
b. Use the configured DNS provider to publish challenge record.
43-
c. Wait for DNS propagation and validate record.
44-
d. Notify ACME provider to trigger validation.
42+
b. Resolve any CNAME delegation: follow the CNAME chain from `_acme-challenge.<domain>` to its terminal target (see CNAME Delegation below).
43+
c. Select the DNS provider plugin for the zone that owns the (resolved) record name and publish the challenge TXT record there.
44+
d. Wait for DNS propagation and validate record.
45+
e. Notify ACME provider to trigger validation.
4546
4. Once all challenges are valid, finalize the order using CSR.
4647
5. Download the signed certificate from ACME provider.
4748
6. Return PEM certificate to the Gateway.
@@ -86,6 +87,58 @@ This logic is handled by the `DnsVerificationHelper` class and ensures a high-co
8687

8788
</details>
8889

90+
<details>
91+
<summary><strong>🔗 CNAME Delegation (Proxy) Lookup</strong></summary>
92+
93+
Many organizations do not want ACME automation to hold write access to their production DNS zone. The industry-standard pattern is to **delegate just the ACME challenge name** to a separate, isolated validation zone using a `CNAME` record. The plugin supports this transparently.
94+
95+
#### Why delegate?
96+
97+
A `CNAME` at `_acme-challenge.<domain>` points challenge validation at another zone. ACME automation then only needs write access to that isolated zone — never the production zone. A `CNAME` also cannot coexist with any other record type at the same name (RFC 1034), so the TXT record **must** be created at the CNAME's target, not at the original challenge name.
98+
99+
#### How the plugin resolves it
100+
101+
Before publishing the challenge record, the plugin runs the `CnameResolver`, which:
102+
103+
1. Issues a DNS `CNAME` query for `_acme-challenge.<domain>`.
104+
2. **Follows the chain to its terminus.** Delegation can be nested multiple levels deep (`A → B → C → …`); the resolver re-queries at each hop and stops only when it reaches a name that has no further `CNAME`. That terminal name is where the TXT record is created.
105+
3. Returns the original name unchanged when **no** `CNAME` exists — so non-delegated domains behave exactly as before (fully backwards compatible).
106+
107+
Safety guards: the resolver detects loops (a name that reappears in the chain) and enforces a maximum depth of **10 hops**, logging a warning and stopping at the last good name rather than looping forever.
108+
109+
#### Provider selection follows the delegation
110+
111+
The DNS provider plugin is resolved against the **name where the record actually lands**:
112+
113+
- **No delegation** → the provider is selected for the certificate domain (e.g. `www.example.com`).
114+
- **Delegated** → the provider is selected for the **resolved terminal target** (e.g. `abc123.acme-validation.net`).
115+
116+
This means a challenge delegated into a zone hosted by a *different* DNS provider is routed to the plugin that owns that zone. Propagation checks and cleanup also operate on the resolved name.
117+
118+
> ℹ️ Provider selection uses the AnyCA Gateway's Domain Validation configuration, which matches a configured (optionally wildcard) domain pattern one label at a time. Ensure the **delegation target's zone** is covered by a Domain Validation entry — e.g. a target of `abc123.acme-validation.net` needs a validator whose domain matches `*.acme-validation.net`.
119+
120+
#### Example (multi-level delegation across providers)
121+
122+
```text
123+
Cert domain: www.example.com (production zone, e.g. GoDaddy)
124+
Challenge name: _acme-challenge.www.example.com
125+
126+
DNS records (static, created once):
127+
_acme-challenge.www.example.com CNAME hop1.example.com (GoDaddy)
128+
hop1.example.com CNAME hop2.example.com (GoDaddy)
129+
hop2.example.com CNAME val.acme-zone.net (points into the validation zone)
130+
131+
Resolution + placement:
132+
_acme-challenge.www.example.com → hop1 → hop2 → val.acme-zone.net (terminal)
133+
Provider plugin selected for: acme-zone.net (the zone that owns val.acme-zone.net)
134+
TXT record created at: val.acme-zone.net
135+
ACME CA queries _acme-challenge.www.example.com, follows the CNAMEs, finds the TXT ✅
136+
```
137+
138+
> ℹ️ **Private/internal delegation zones:** set `DnsVerificationServer` to your authoritative DNS server IP. The `CnameResolver` honors it for the CNAME lookups; otherwise public resolvers (Google, Cloudflare, Quad9) are used.
139+
140+
</details>
141+
89142
<details>
90143
<summary><strong>🔑 Credential Flow</strong></summary>
91144

0 commit comments

Comments
 (0)