deploy: auto-reconcile public DNS CNAMEs via Cloudflare
castle deploy now manages the public zone's tunnel CNAMEs to match the current set of public: true services — creating missing records and deleting removed ones — instead of only printing manual 'cloudflared tunnel route dns' hints. New core/generators/dns.py reconciles via the Cloudflare API (stdlib only). It only ever touches records whose content is <tunnel_id>.cfargotunnel.com, so hand-managed records in the same zone are never altered. deploy._write_tunnel_config calls it in both branches (delete of the last public service cleans up its CNAME). Without a token it's a no-op and the manual route-once hints are surfaced as before. Token: ~/.castle/secrets/CLOUDFLARE_PUBLIC_DNS_TOKEN, a single DNS:Edit permission scoped to the public zone (the 'Edit zone DNS' template) — confirmed sufficient: that one permission resolves the zone by name and edits records, no separate Zone:Read needed. Separate from CLOUDFLARE_API_TOKEN (ACME, internal zone). castle doctor gains a read-only probe that WARNs when the token is absent (CNAMEs stay manual) and FAILs when it can't reach the zone/records.
This commit is contained in:
@@ -89,12 +89,10 @@ proxy: true # required — the service must be routed
|
||||
public: true # also expose at <name>.pub.payne.io via the tunnel
|
||||
```
|
||||
|
||||
Then deploy and route DNS (a CNAME `<name>.pub.payne.io → <tunnel>.cfargotunnel.com`,
|
||||
created once per public host — `castle deploy` prints the exact command):
|
||||
Then just deploy:
|
||||
|
||||
```bash
|
||||
castle deploy
|
||||
cloudflared tunnel route dns <tunnel-id> <name>.pub.payne.io
|
||||
```
|
||||
|
||||
`castle deploy` regenerates `~/.castle/artifacts/specs/cloudflared.yml` from the
|
||||
@@ -102,6 +100,35 @@ current set of public services and restarts `castle-tunnel`. Flip `public` back
|
||||
`false` (or remove it) and redeploy to un-expose — the hostname drops out of the
|
||||
ingress immediately.
|
||||
|
||||
### DNS: automatic (with a token) or manual
|
||||
|
||||
Each public host needs a CNAME `<name>.pub.payne.io → <tunnel>.cfargotunnel.com`.
|
||||
Castle can manage these for you. Create a Cloudflare API token with a single
|
||||
**`DNS:Edit`** permission scoped to the **public zone** — this is exactly
|
||||
Cloudflare's built-in *"Edit zone DNS"* template. That one permission is
|
||||
sufficient: it resolves the zone by name *and* creates/deletes records (no
|
||||
separate `Zone:Read` is needed — verified against `domain0.org`). An account-owned
|
||||
token (`cfat_…`) works (that's what this was confirmed with). Drop it into the
|
||||
secret store:
|
||||
|
||||
```bash
|
||||
printf %s "<token>" > ~/.castle/secrets/CLOUDFLARE_PUBLIC_DNS_TOKEN
|
||||
chmod 600 ~/.castle/secrets/CLOUDFLARE_PUBLIC_DNS_TOKEN
|
||||
```
|
||||
|
||||
With it present, every `castle deploy` **reconciles** the public CNAMEs against the
|
||||
current public set — creating missing ones and deleting removed ones — and prints a
|
||||
one-line summary. It only ever touches records pointing at *this* tunnel
|
||||
(`<tunnel_id>.cfargotunnel.com`), so hand-managed records in the same zone are safe.
|
||||
This token is separate from `CLOUDFLARE_API_TOKEN` (which is the ACME token for the
|
||||
internal zone); the public zone is usually a different zone/account.
|
||||
|
||||
Without the token, `castle deploy` instead prints the exact command to run per host:
|
||||
|
||||
```bash
|
||||
cloudflared tunnel route dns <tunnel-id> <name>.pub.payne.io
|
||||
```
|
||||
|
||||
## The part that isn't the tunnel
|
||||
|
||||
Reachability is the easy half. Anything public also needs, per service:
|
||||
|
||||
Reference in New Issue
Block a user