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
feat(dstack-ingress): delegate every record, not just the challenge
Delegation moved the ACME challenge out of the served domain's zone, but
left three records behind that the operator still had to create by hand --
and one of them, the app-address TXT, carries the app id, which changes
whenever the compose changes. So "set it up once" was never true: edit the
compose, and someone has to go back to the production zone.
Alias all three names into the delegation zone instead. The operator
creates three CNAMEs before deploying and is then done, permanently:
svc.example.com CNAME svc.example.com.deleg.net
_dstack-app-address.svc.example.com CNAME _dstack-app-address.svc.example.com.deleg.net
_acme-challenge.svc.example.com CNAME _acme-challenge.svc.example.com.deleg.net
The container publishes what they point at, including the accounturi CAA
it previously could only ask for.
The gateway pointer becomes an address record rather than a CNAME, which
is the one real cost here. Let's Encrypt follows a CNAME when it looks up
CAA and honours what it finds at the target -- verified against the
staging CA -- but a CNAME excludes every other type at that name, so a
CNAME there would leave nowhere to put the CAA. It does not climb past
the target either: a CAA at the delegation zone apex is ignored, also
verified. An address record is what lets one name carry both. The
container re-resolves GATEWAY_DOMAIN every pass, so a gateway that moves
is picked up within RENEW_INTERVAL, where a CNAME used to follow it
immediately.
Wildcards keep the operator-managed CAA. RFC 8659 evaluates
*.example.com at example.com, which is the operator's own name and cannot
be aliased away.
Renewal passes still verify only the challenge alias. The other two are
about serving, and dns-01 never connects here, so blocking a renewal on
them would turn a routing problem into an expired certificate. The first
pass checks all three, because that is setup.
Everything they point at is published by the container, so this is the whole of
224
+
the operator's job — the app id can change afterwards and no DNS edit follows.
225
+
223
226
Then start the container with a real provider token and `ACME_STAGING=true`, and
224
227
watch for: the three records verifying, `Executing (challenge-delegation):` with
225
228
`--manual`and both hooks, the certificate arriving, and the challenge TXT being
@@ -251,7 +254,9 @@ These fail fast and are cheap, so run them on every change:
251
254
| Scenario | Expected |
252
255
|---|---|
253
256
| tls-alpn-01 + a wildcard domain | Refused before any ACME call, citing RFC 8737 |
257
+
| Delegation, CAA in the delegated zone changed to forbid the CA | Blocked before any ACME attempt — proves the published CAA is reachable through the CNAME |
254
258
| Delegation with no CAA record at all | Blocked — unlike normal issuance, where "no CAA" means unrestricted and passes |
259
+
| Delegation, later pass, gateway CNAME broken | Renewal proceeds — that record is for serving, and dns-01 does not use it |
255
260
| A CAA record with `validationmethods=dns-01`, mode tls-alpn-01 | Blocked with the restriction quoted back |
256
261
| TXT holding the wrong value | Reported as `want <x>, saw <y>`, not "missing" |
257
262
| An instance ID the gateway does not know | The CA reports a connection error — the gateway will not route to it |
0 commit comments