63 lines
4.2 KiB
Markdown
63 lines
4.2 KiB
Markdown
---
|
|
created: '2026-06-07'
|
|
path: Sources/Homelab
|
|
project: traefik-deployment
|
|
tags:
|
|
- tailscale
|
|
- traefik
|
|
- tls
|
|
- dns
|
|
- knot
|
|
type: session-notes
|
|
---
|
|
|
|
## Outcome
|
|
|
|
Laptop (`truckslaptop`) can now reach the Traefik dashboard remotely over the tailnet at `https://traefik.taila7f44e.ts.net/dashboard/` with a valid cert and clean padlock — HTTP 200 confirmed. This is a parallel access path that does not touch the existing on-LAN `herbylab.dev` + wildcard-cert setup.
|
|
|
|
Access model is two-path, toggled by hand:
|
|
- **On LAN:** `tailscale set --accept-dns=false` → normal resolver → Pi-hole → Knot → `*.herbylab.dev` names, wildcard cert. Unchanged.
|
|
- **Remote:** `tailscale set --accept-dns=true` on the laptop → MagicDNS resolves `.ts.net` names → reach services by their Tailscale node name.
|
|
|
|
## Topics Covered
|
|
|
|
- Why MagicDNS short names worked via `tailscale ip` but `.ts.net` FQDNs didn't resolve in `curl`/system resolver.
|
|
- The cert and `Host()`-routing implications of using `.ts.net` names instead of `herbylab.dev`.
|
|
- Generating a static Tailscale cert on the Traefik box and wiring it into the file provider alongside the Cloudflare DNS-01 auto-generated wildcard.
|
|
- Decision to keep subnet routing / split-horizon Knot off the table; accept two naming schemes by location.
|
|
|
|
## Key Learnings
|
|
|
|
- **MagicDNS needs a global nameserver to recurse.** With the tailnet's Global Nameservers empty, MagicDNS (`100.100.100.100` / `fd7a:115c:a1e0::53`) answers authoritatively for the tailnet but returns SERVFAIL ("recursion requested but not available") for public names. This broke `tailscale cert`'s reach to Let's Encrypt when `--accept-dns=true` was set on the Traefik box. Fix: either set `--accept-dns=false` so the box uses its own recursing resolver, or add a Global Nameserver (e.g. `1.1.1.1` or Pi-hole's tailnet IP) in the admin console. The cert command does not need MagicDNS — it only needs the box to reach Let's Encrypt over normal DNS.
|
|
- **`tailscale set --accept-dns` is global, not per-network.** No native per-SSID switching; the two-path model is a manual toggle (acceptable here, can be automated later via a NetworkManager dispatcher hook).
|
|
- **`resolvectl` failing with "unknown unit" = systemd-resolved not running.** On the laptop, Tailscale writes `resolv.conf` directly instead. Works fine; just know that's the mechanism rather than the resolved stub.
|
|
- **Two cert mechanisms coexist in the file provider.** The wildcard is auto-generated by the `cloudflare` resolver (`certificates: []` is empty on purpose, Traefik fetches/renews via DNS-01). The `.ts.net` cert is a static hand-generated file pair declared explicitly under `tls.certificates`. Different mechanisms, both valid, separate dynamic files.
|
|
- **`405` to a `curl -sI` (HEAD) against `api@internal` is a success signal** — TLS matched and routing worked; the dashboard just rejects HEAD. Confirm with a GET to `/dashboard/`.
|
|
- **The `.ts.net` route is per-service forever.** One static cert covers the box; each service needs its own `Host(`x.taila7f44e.ts.net`)` router. The trade for skipping subnet routing / split-horizon Knot.
|
|
|
|
## Config landed
|
|
|
|
Cert files: `/opt/traefik/dynamic/certs/traefik.taila7f44e.ts.net.{crt,key}` (in-container: `/etc/traefik/dynamic/certs/`).
|
|
|
|
New dynamic file `/opt/traefik/dynamic/tailscale.yml`:
|
|
```yaml
|
|
tls:
|
|
certificates:
|
|
- certFile: /etc/traefik/dynamic/certs/traefik.taila7f44e.ts.net.crt
|
|
keyFile: /etc/traefik/dynamic/certs/traefik.taila7f44e.ts.net.key
|
|
http:
|
|
routers:
|
|
tailscale-dashboard:
|
|
rule: "Host(`traefik.taila7f44e.ts.net`)"
|
|
service: api@internal
|
|
tls: {}
|
|
entryPoints:
|
|
- websecure
|
|
```
|
|
|
|
## Follow-ons
|
|
|
|
- [ ] Decide whether to add a tailnet Global Nameserver (Pi-hole tailnet IP or `1.1.1.1`) so MagicDNS recurses — would let `--accept-dns=true` stay on without breaking public resolution on any box.
|
|
- [ ] Optional: NetworkManager dispatcher script to auto-toggle `--accept-dns` on home-network connect, removing the manual flip.
|
|
- [ ] Renewal story for the static `.ts.net` cert — `tailscale cert` does not auto-renew like the Cloudflare resolver does; needs a timer or manual refresh before the ~90-day expiry.
|
|
- [ ] Add `.ts.net` routers for any other service wanted remotely (per-service `Host()` matchers). |