# LAN DNS The homelab gives every service a name under `franksdank.cloud`. Users type a name; they never type a port number. This page covers how names reach users: the dnsmasq server, the G36 router hand-off, and the split between LAN and WAN resolution. See [[Caddy]] for the reverse proxy that serves the names, and [[Network Architecture]] for the full topology. ## Components | Component | Where | Role | |-----------|-------|------| | dnsmasq | Dankclaw quadlet (`dankclaw/dnsmasq/`) | Answers the flat service names for LAN clients | | SURFboard G36 | 192.168.0.1 | Hands dnsmasq to clients as the DHCP DNS server | | Porkbun | Registrar for `franksdank.cloud` | Answers the same names for the WAN | | Caddy | Dankclaw (192.168.0.143:443) | Serves every name over HTTPS | ## How a name reaches a client ```mermaid sequenceDiagram participant C as LAN client (laptop, phone) participant G as G36 router (192.168.0.1) participant D as dnsmasq (192.168.0.143:53) participant P as Porkbun (WAN) participant X as Caddy (192.168.0.143:443) C->>G: DHCP request G-->>C: lease + DNS = 192.168.0.143 C->>D: dig romm.franksdank.cloud D-->>C: 192.168.0.143 Note over D: addn-hosts answer
(dankclaw/dnsmasq/addn-hosts) C->>X: https://romm.franksdank.cloud X-->>C: RomM (192.168.0.235:8085 upstream) C->>P: dig franksdank.cloud (unknown name) P-->>C: 71.227.227.172 ``` dnsmasq answers only the names in `addn-hosts`. Every other query goes to the upstream resolvers `1.1.1.1` and `1.0.0.1`. The zone apex and unknown subdomains fall through to Porkbun. This avoids a wildcard override. ## Why split DNS The G36 has no NAT loopback (hairpin). A LAN client that resolves a name to the WAN IP and then connects to it would time out. Split DNS removes the hairpin: LAN clients get the LAN IP, WAN clients get the public IP. Both use the same name and the same Let's Encrypt certificate. See [[Caddy]] for the TLS side. ## dnsmasq quadlet - **Location:** `dankclaw/dnsmasq/` — `dnsmasq.container` (quadlet), `dnsmasq.conf`, `addn-hosts`, `Containerfile` - **Port:** 53/udp + 53/tcp on 192.168.0.143 - **Host prerequisite 1:** `sysctl net.ipv4.ip_unprivileged_port_start=53` (persisted in `/etc/sysctl.d/99-dnsmasq.conf`) - **Host prerequisite 2:** `DNSStubListener=no` in `/etc/systemd/resolved.conf` (the resolved stub would otherwise hold port 53 inside the container netns) - **Rebuild + restart after a change:** ``` podman build -t dnsmasq:latest -f dankclaw/dnsmasq/Containerfile . systemctl --user daemon-reload systemctl --user restart dnsmasq ``` Add a new service name in two places: 1. `dankclaw/dnsmasq/addn-hosts` — `192.168.0.143 .franksdank.cloud` 2. Porkbun — A record ` 71.227.227.172` (so WAN clients and the DNS-01 challenge work) Then add the vhost to [[Caddy]] and run `./scripts/deploy.sh dankclaw`. ## G36 router DNS hand-off The router must tell DHCP clients to use dnsmasq. Until that step is done, new clients get the router's own DNS relay and the flat names do not resolve for them. Existing clients that already use dnsmasq are unaffected. **Manual step (web GUI at `https://192.168.0.1`):** 1. Open **Gateway → Connection → Local IP Network → IPv4**. 2. Uncheck **DNS Proxy** (disable the router DNS relay). 3. Set **DNS** to **Statically configured**. 4. Set **Primary DNS** to `192.168.0.143`. 5. Leave the secondary field empty or set `0.0.0.0`. 6. Apply, then re-lease DHCP on each client (`sudo dhclient -r && sudo dhclient`). The router admin interface has no programmatic API in this firmware. The web GUI is the only control path. Keep the credentials out of this repo; the repo tracks no router secrets. Verify after the change: ``` # on a LAN client resolv.conf # nameserver 192.168.0.143 dig romm.franksdank.cloud +short # expected: 192.168.0.143 ``` ## Dankclaw host DNS The Dankclaw host itself resolves the flat names through dnsmasq: - `/etc/systemd/resolved.conf`: `DNS=192.168.0.143 192.168.0.1` (dnsmasq first, router as fallback) Without this, the host would resolve the names through the router to the WAN IP and time out (no hairpin). ## WAN records (Porkbun) The Porkbun zone `franksdank.cloud` carries an A record for every vhost plus the apex, all pointing at `71.227.227.172`. TTL is 300. The zone has no wildcard record: unknown subdomains return NXDOMAIN. Caddy's DNS-01 challenges add and remove `_acme-challenge.*` TXT records through the same API during issuance. See [[Caddy]] for the TLS flow. `franksdank.casa` is not in this Porkbun account. The `plex.franksdank.casa` vhost stays plain HTTP for that reason. The canonical Plex name is `plex.franksdank.cloud` (HTTPS). ## Failure modes | Symptom | First check | |---------|-------------| | Name does not resolve on a new client | `resolv.conf` shows 192.168.0.1, not .143 — the G36 hand-off step is missing | | Name resolves to 71.227.x and times out on the LAN | Client used the router DNS — same root cause | | `dig @192.168.0.143` fails entirely | dnsmasq quadlet down: `systemctl --user status dnsmasq` | | HTTPS works from the host but not the WAN | No port forward on the G36 for that service (manual router step) | | Cert renewal fails in Caddy logs | Porkbun API credentials in the Dankclaw `.env`; test with the Porkbun API `/ping` endpoint | ## See Also - [[Caddy]] — TLS, vhost list, DNS-01 - [[Network Architecture]] — topology, access policy - [[Dankclaw Stack]] — the host that carries dnsmasq and Caddy