116 lines
4.3 KiB
Markdown
116 lines
4.3 KiB
Markdown
---
|
|
project: dns-knot-pihole-end-to-end
|
|
type: session-notes
|
|
status: completed
|
|
path: Tech/Sessions
|
|
tags:
|
|
- homelab
|
|
- dns
|
|
- knot
|
|
- pihole
|
|
- openwrt
|
|
- traefik
|
|
created: 2026-05-08
|
|
updated: 2026-05-08
|
|
---
|
|
# DNS — Knot + Pi-hole End-to-End
|
|
|
|
## Outcome
|
|
|
|
Phase 1 of the Traefik project closed. Internal DNS architecture from
|
|
the homelab DNS decision doc is live: Knot DNS authoritative for
|
|
`herbylab.dev` on LXC 103, Pi-hole conditionally forwarding the zone,
|
|
OpenWrt passing answers through. All six initial records resolve
|
|
cleanly from a normal LAN client through the full chain.
|
|
|
|
## Architecture (as built)
|
|
|
|
|
|
Client → OpenWrt (10.0.11.1) → Pi-hole (10.0.11.50)
|
|
├─ herbylab.dev → Knot (10.0.11.10)
|
|
└─ everything else → Unbound → root
|
|
|
|
|
|
## Records served
|
|
|
|
| Name | IP | VLAN |
|
|
|---|---|---|
|
|
| ns1 | 10.0.11.10 | Lab |
|
|
| immich | 10.0.11.62 | Lab |
|
|
| pihole | 10.0.11.50 | Lab |
|
|
| plex | 10.0.11.54 | Lab |
|
|
| pve | 10.0.21.188 | Private |
|
|
| traefik | 10.0.21.181 | Private |
|
|
|
|
Migrated from `.lan` to `.dev` to enable LE wildcard via Cloudflare
|
|
DNS-01 in Phase 2.
|
|
|
|
## Key Learnings
|
|
|
|
- **`knotc reload` does not re-bind sockets.** It reloads the config
|
|
file but listening sockets only get re-evaluated on full daemon
|
|
restart. If `listen:` directives change (or were missing on first
|
|
start), `systemctl restart knot` is required. Symptom: zone loads,
|
|
`zone-status` reports correctly, but `dig` returns "connection
|
|
refused."
|
|
- **Knot config order matters for references.** `acl` blocks must be
|
|
defined before `zone` blocks that reference them — YAML validation is
|
|
single-pass, no forward references. Same pattern likely applies to
|
|
other ID-based references.
|
|
- **Fresh PVE LXCs inherit Tailscale resolvers from the host.**
|
|
`/etc/resolv.conf` gets populated with `100.100.100.100` by default.
|
|
If the LXC isn't on the tailnet, name resolution silently fails until
|
|
pointed at a reachable resolver. Permanent fix: configure DNS in the
|
|
LXC's PVE config or, for a DNS server LXC, point it at itself once
|
|
it's serving.
|
|
- **OpenWrt rebind protection silently filters RFC1918 answers.** When
|
|
dnsmasq has `rebind_protection='1'`, any DNS answer pointing to a
|
|
private IP gets stripped — query returns NOERROR with ANSWER:0 and EDE
|
|
15 (Blocked). The fix is whitelisting the domain via `rebind_domain`.
|
|
Easy to miss because the block happens *after* the upstream answers
|
|
correctly, so direct queries to Pi-hole or Knot work fine while
|
|
client-path queries fail.
|
|
- **EDE 15 (Blocked) doesn't always mean a blocklist match.** It's a
|
|
generic "this answer was filtered" code. Pi-hole's blocklists,
|
|
OpenWrt's rebind protection, and several other filters all use it.
|
|
Pi-hole's query log saying "Allowed" while `dig` reports EDE 15 is the
|
|
tell — block is happening downstream of Pi-hole.
|
|
- **Pi-hole v6 renamed conditional forwarding.** It's now under
|
|
reverse-server config with format
|
|
`,,,`. Same plumbing, different
|
|
UI surface.
|
|
- **Direct-vs-chain queries are the diagnostic shortcut.** When a DNS
|
|
chain fails, querying each link directly with `dig @`
|
|
immediately localizes the broken hop. Saved real time tonight
|
|
isolating the OpenWrt rebind issue.
|
|
|
|
## Decisions Locked
|
|
|
|
- Zone file lives at `/var/lib/knot/herbylab.dev.zone` on LXC 103,
|
|
owned by `knot:knot`
|
|
- Single NS record (`ns1`) for now; secondary will be added when NAS
|
|
instance comes up
|
|
- TTL strategy: 300 during buildout (current), raise to 3600 once stable
|
|
- SOA serial format: `YYYYMMDDNN` (current: `2026050801`)
|
|
- ACL allows transfer/notify from localhost only — will expand when
|
|
secondary is added
|
|
- Conditional forwarding scoped to `10.0.0.0/8` in Pi-hole —
|
|
VLAN/firewall layer enforces actual access boundaries
|
|
|
|
## Open Items
|
|
|
|
- [ ] LXC `/etc/resolv.conf` is a manual edit pointing at Pi-hole —
|
|
repoint at itself (10.0.11.10) with Pi-hole as fallback, make
|
|
persistent
|
|
- [ ] Knot zone file into git as source of truth
|
|
- [ ] Ansible role for Knot primary deployment
|
|
- [ ] Knot secondary on the NAS (after primary proven stable)
|
|
- [ ] Second Pi-hole instance for redundancy
|
|
- [ ] Pi-hole sync method decision (gravity-sync vs Teleporter)
|
|
- [ ] Move Traefik VM to Lab VLAN (currently on Private from original
|
|
bridge setup)
|
|
- [ ] Raise TTLs to 3600 once buildout settles
|
|
|
|
## Next Session
|
|
|
|
Phase 2 — wildcard cert via Traefik + Cloudflare DNS-01. DNS layer is done. |