chore: rewrite Tailscale dashboard security with serve approach#90
Conversation
- Add Approach 2 (funnel) for machines missing Tailscale network extension - Document why serve fails when network extension isn't installed (DNS resolves to public ingress IPs) - Add troubleshooting section for common issues (etag mismatch, DNS, bind:loopback) - Update machine-setup.md gateway section with remote dashboard access checklist - Document funnel persistence (no launchd needed - Tailscale manages it)
- Document /etc/resolver/ts.net DNS fix for Homebrew-installed Tailscale (utun interface can't intercept DNS without network extension) - Rewrite security doc around serve (tailnet-only) instead of funnel - Update machine-setup.md with DNS fix as step 1 - Add troubleshooting for DNS resolution failures - Security: loopback bind + token auth + device auth + serve = 4 layers - Funnel moved to fallback section with explicit warnings
Homebrew Tailscale uses utun (no network extension), so *.ts.net DNS queries aren't intercepted. The fix is /etc/resolver/ts.net pointing to 100.100.100.100. Must be applied on every machine, not just server. Document covers why it breaks, how to fix, how to verify (don't use dig), and common mistakes.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 430430489a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| 3. **Use a gateway password** as an explicit multi-factor check instead of just an | ||
| opaque token. | ||
| ```bash | ||
| echo "nameserver 100.100.100.100" | sudo tee /etc/resolver/ts.net |
There was a problem hiding this comment.
Create resolver directory before writing ts.net resolver
The fix command writes directly to /etc/resolver/ts.net, but on clean macOS installs /etc/resolver often does not exist, so this step fails and leaves DNS unconfigured. In that state, the later tailscale serve setup won’t persist (the exact failure this doc is trying to prevent). Add sudo mkdir -p /etc/resolver before the tee command.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch — added sudo mkdir -p /etc/resolver in all three docs (security, troubleshooting, machine-setup). Fixed in 994a75c.
| 3. **Verify:** `curl -sS https://<hostname>.ts.net/ | head -1` — should return | ||
| `HTTP/2 200` |
There was a problem hiding this comment.
Verify HTTPS with response headers, not page body
This verification command checks the first line of the response body but the text says it should return HTTP/2 200, which is a status line only present in headers. Without -i, -I, or a -w status formatter, users will see HTML content instead of status and may incorrectly conclude the setup is broken.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed — changed to curl -sS -o /dev/null -w "%{http_code}" https://<hostname>.ts.net/ which correctly shows the HTTP status code. Fixed in 994a75c.
| curl -sS --max-time 5 https://<hostname>.ts.net/ -o /dev/null -w "%{remote_ip}\n" | ||
|
|
||
| # Node.js (uses system resolver) | ||
| node -e "require('dns').resolve4('<hostname>.ts.net', (e,a) => console.log(a))" |
There was a problem hiding this comment.
Use dns.lookup for system resolver validation
The troubleshooting section says this Node.js command uses the system resolver, but require('dns').resolve4(...) uses DNS queries directly rather than OS name-resolution behavior. That can produce 209.x results even when /etc/resolver/ts.net is correctly configured, making this check misleading in exactly the scenario being diagnosed.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Correct — dns.resolve4 makes direct DNS queries bypassing /etc/resolver. Changed to dns.lookup which uses getaddrinfo (system resolver). Fixed in 994a75c.
- Add `mkdir -p /etc/resolver` before writing resolver file (all 3 docs) - Fix curl verify command to show HTTP status code instead of body - Fix Node.js DNS example to use `dns.lookup` (system resolver) not `dns.resolve4` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
tailscale serve+/etc/resolverapproach (replaces manual TLS cert management)Test plan
🤖 Generated with Claude Code