You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds an opt-in `ACME_CHALLENGE_ALIAS` mode so the DNS token can be scoped to a
delegated zone instead of the served domain's own (often shared, production)
zone. Closes the least-privilege gap described in the issue.
- certman.py: when ACME_CHALLENGE_ALIAS is set, run certbot via `--manual` with
auth/cleanup hooks instead of the provider's DNS plugin; `renew` reuses the
hooks saved in the renewal config.
- acme-dns-alias-hook.sh: writes/removes the `_acme-challenge` TXT in the
delegated zone (reusing dnsman.py / the existing provider abstraction).
- dnsman.py + base.py: add `unset_txt` for challenge cleanup.
- entrypoint.sh: in delegation mode the served-zone records (alias, app-address
TXT, CAA) can't be written by our token, so print the exact records the
operator must set statically, and for CAA verify presence (via DoH) and warn
loudly if missing — instead of silently dropping the accounturi lock.
- README: document the mode, required static records, and the CAA security note.
Non-breaking: unset ACME_CHALLENGE_ALIAS = current behavior.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xa1zFJs3FVP8UTsSWuS3Yj
Copy file name to clipboardExpand all lines: custom-domain/dstack-ingress/README.md
+30Lines changed: 30 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -179,9 +179,39 @@ environment:
179
179
| `EVIDENCE_SERVER` | `true` | Serve evidence files at `/evidences/` on the TLS port |
180
180
| `EVIDENCE_PORT` | `80` | Internal port for evidence HTTP server |
181
181
| `ALPN` | | TLS ALPN protocols (e.g. `h2,http/1.1`). Only set if backends support h2c |
182
+
| `ACME_CHALLENGE_ALIAS` | | Delegate the ACME DNS-01 challenge to this zone (see below) so the DNS token needs no access to the served domain's own zone |
183
+
| `ACME_CHALLENGE_PROPAGATION_SECONDS` | `30` | Wait after writing the delegated challenge TXT before validation (only used with `ACME_CHALLENGE_ALIAS`) |
182
184
183
185
For DNS provider credentials, see [DNS_PROVIDERS.md](DNS_PROVIDERS.md).
184
186
187
+
### ACME challenge delegation (least-privilege DNS token)
188
+
189
+
By default the DNS token must be able to edit the **served domain's own zone**
190
+
(to write `_acme-challenge`, and — with `SET_CAA` — the CAA record). If the
191
+
served name lives under a shared production zone (e.g. `svc.example.com` under
192
+
`example.com`), that token can edit every record in the zone, which may be more
193
+
privilege than you want.
194
+
195
+
Set `ACME_CHALLENGE_ALIAS=<delegation-zone>` to answer the DNS-01 challenge in a
196
+
separate zone that your token controls, so the token never touches the served
197
+
domain's zone. In this mode dstack-ingress **only** manages the challenge TXT in
198
+
the delegation zone; you set the following records **once, statically**, in the
199
+
served domain's production zone (the container prints the exact values on start):
> **Security note.** The `accounturi` CAA restricts issuance to this enclave's
209
+
> ACME account. In delegation mode dstack-ingress cannot set it for you (no
210
+
> token for the served zone), so it prints the record and verifies (via DoH)
211
+
> that it is present, warning loudly if not. Without this CAA, anyone who can
212
+
> satisfy the delegated challenge could obtain a certificate for the domain.
213
+
> Provide the token scoped only to `<delegation-zone>`.
214
+
185
215
## Evidence & Attestation
186
216
187
217
Evidence files are served at `https://your-domain.com/evidences/` by default (via payload inspection in HAProxy's TCP mode). They can also be accessed by the backend application through the shared `/evidences` volume.
0 commit comments