beefcake: advertise 192.168.0.0/24 so tailnet clients can reach LAN-only services #542

Closed
lytedev wants to merge 1 commit from beefcake-advertise-lan into main
Owner

Summary

Phones on cellular using beefcake as exit node could reach lobste.rs and other internet hosts (after #540 landed the NAT fix), but silently failed to reach internal-only services that DNS resolves to LAN IPs — git.lyte.dev → 192.168.0.9, for example.

Root cause: Tailscale clients exclude RFC1918 destinations from exit-node routing unless the subnet is advertised as a route by some peer. So the phone resolves git.lyte.dev → 192.168.0.9, has no route for that IP, drops the packet. Works on wifi only because the phone is on the LAN directly.

Fix: advertise 192.168.0.0/24 from beefcake. Tailnet clients now have a route for the home subnet via beefcake → LAN-only services reachable from anywhere on the tailnet, exit-node or not.

Test plan

  • Deploy beefcake (nixos-rebuild switch).
  • Approve the new route in headscale: sudo headscale nodes approve-routes -i 8 -r 0.0.0.0/0,::/0,192.168.0.0/24 (the existing 0.0.0.0/0 + ::/0 routes must be re-listed because approve-routes is a SET operation, not an add).
  • Verify beefcake announces the route: tailscale status --json | jq .Self.AllowedIPs should include 192.168.0.0/24.
  • Verify a peer received it: from dragon, tailscale status --json | jq '.Peer | to_entries[] | select(.value.HostName=="beefcake") | .value.AllowedIPs' should include 192.168.0.0/24.
  • From phone on cellular with beefcake as exit node: open https://git.lyte.dev/ — page loads.
  • Spot-check: curl http://lobste.rs/ from phone still works (didn't break general exit-node behavior).

Security

No ACL change. Only tag:admindevice has the *:* rule that grants reachability to arbitrary destinations, so group:family / group:friends still can't reach LAN devices through this advertised route — they remain limited to the explicit beefcake:80,443[,445] rules. Stacks on top of #540 cleanly (different lines of the same file).

## Summary Phones on cellular using beefcake as exit node could reach lobste.rs and other internet hosts (after #540 landed the NAT fix), but silently failed to reach internal-only services that DNS resolves to LAN IPs — git.lyte.dev → 192.168.0.9, for example. Root cause: Tailscale clients **exclude RFC1918 destinations from exit-node routing** unless the subnet is advertised as a route by some peer. So the phone resolves git.lyte.dev → 192.168.0.9, has no route for that IP, drops the packet. Works on wifi only because the phone is on the LAN directly. Fix: advertise 192.168.0.0/24 from beefcake. Tailnet clients now have a route for the home subnet via beefcake → LAN-only services reachable from anywhere on the tailnet, exit-node or not. ## Test plan - [ ] Deploy beefcake (`nixos-rebuild switch`). - [ ] Approve the new route in headscale: `sudo headscale nodes approve-routes -i 8 -r 0.0.0.0/0,::/0,192.168.0.0/24` (the existing 0.0.0.0/0 + ::/0 routes must be re-listed because approve-routes is a SET operation, not an add). - [ ] Verify beefcake announces the route: `tailscale status --json | jq .Self.AllowedIPs` should include `192.168.0.0/24`. - [ ] Verify a peer received it: from dragon, `tailscale status --json | jq '.Peer | to_entries[] | select(.value.HostName=="beefcake") | .value.AllowedIPs'` should include `192.168.0.0/24`. - [ ] From phone on cellular with beefcake as exit node: open `https://git.lyte.dev/` — page loads. - [ ] Spot-check: `curl http://lobste.rs/` from phone still works (didn't break general exit-node behavior). ## Security No ACL change. Only `tag:admindevice` has the `*:*` rule that grants reachability to arbitrary destinations, so `group:family` / `group:friends` still can't reach LAN devices through this advertised route — they remain limited to the explicit `beefcake:80,443[,445]` rules. Stacks on top of #540 cleanly (different lines of the same file).
beefcake: advertise 192.168.0.0/24 subnet route for tailnet LAN access
All checks were successful
/ check-format (push) Successful in 8s
/ build (push) Successful in 12m23s
7aa82c2d77
Tailscale clients exclude RFC1918 destinations from exit-node routing
unless the subnet is advertised as a route by some peer. Result: phones
on cellular using beefcake as exit node can reach lobste.rs and other
internet hosts, but silently fail to reach LAN-only services that DNS
resolves to 192.168.0.x (git.lyte.dev for example).

Advertising 192.168.0.0/24 from beefcake gives every tailnet client a
route into the home LAN via the exit node, fixing this for all clients
in one shot. Headscale still has to approve the route — run
\`headscale nodes approve-routes -i 8 -r 0.0.0.0/0,::/0,192.168.0.0/24\`
once after deploy.

Security: existing ACL only grants tag:admindevice the \`*:*\` rule
that covers arbitrary IPs, so family/friends groups still can't reach
LAN devices through this route.
lytedev closed this pull request 2026-05-23 20:48:47 -05:00
All checks were successful
/ check-format (push) Successful in 8s
Required
Details
/ build (push) Successful in 12m23s
Required
Details

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lytedev/nix!542
No description provided.