From c33457fff29cb6d44ecc8f24c624d3ddb93e8744 Mon Sep 17 00:00:00 2001 From: "Arena.ai Agent" Date: Fri, 2 Oct 2026 02:31:12 +0000 Subject: [PATCH] caddy: serve the Hub, RTL and Mempool sites to local clients only The Hub (sovransystemsos.local), Ride The Lightning (:3051) and Mempool (:60847) sites are meant for the home network. With ports 80/443 forwarded for public services, Caddy also receives requests from other clients, so these sites now check the client address as well as the Host header. A new snippet, sovran_lan_only, closes the connection unless the client is on this computer or the local network: private_ranges, 100.64.0.0/10 (Tailscale), 169.254.0.0/16, fe80::/10 and fc00::/7. IPv6 global addresses (2000::/3) are not filtered: computers on the network often connect over their own global address, which cannot be told apart from one on the internet by the address alone. Only the three local sites import the snippet; the domain sites for public services are unchanged. Clients with a public IPv4 address on the local network are no longer served on these sites. The Hub is still available on port 8937. Checked with Caddy 2.11.4 and the Caddyfile the generator writes: public IPv4 clients get the connection closed on all three sites, local clients are served, and the public domain sites answer as before. Add tests/test_caddy_lan_only.py and a note in SECURITY.md. --- SECURITY.md | 6 ++ modules/core/caddy.nix | 23 ++++++++ tests/test_caddy_lan_only.py | 110 +++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 tests/test_caddy_lan_only.py diff --git a/SECURITY.md b/SECURITY.md index 7ea8960..12559a7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -35,6 +35,12 @@ external networks and are outside a “fully offline” model. The local Hub currently uses HTTP. Authentication does not encrypt local network traffic, so use a trusted LAN and avoid public or guest Wi-Fi. +Caddy serves the Hub (`sovransystemsos.local`), Ride The Lightning (port 3051), +and Mempool (port 60847) only to this computer and to clients on your local +network (private, link-local, and VPN addresses), even when ports 80 and 443 are +forwarded to this computer for public services. Other IPv4 clients get the +connection closed. IPv6 global addresses are not filtered. + ### Public services and your home IP address Server + Desktop publishes services under your own domain. The Dynamic DNS diff --git a/modules/core/caddy.nix b/modules/core/caddy.nix index 5f6d9e3..d90c4e8 100755 --- a/modules/core/caddy.nix +++ b/modules/core/caddy.nix @@ -89,6 +89,26 @@ EOF EOF ''} + # ── LAN-only guard ────────────────────────────── + # The Hub, RTL and Mempool sites below are meant for this home network + # only. Forwarding ports 80/443 on the router also lets other clients + # reach Caddy, so these sites check where a request comes from, not just + # which Host it asks for. Anyone else gets the connection closed. + # private_ranges 10/8, 172.16/12, 192.168/16, 127/8, fd00::/8, ::1 + # 100.64.0.0/10 Tailscale and other VPN addresses + # 169.254.0.0/16, fe80::/10, fc00::/7 link-local and unique-local + # 2000::/3 IPv6 global addresses. Computers on this network + # often connect over their own global address, which + # looks the same as one from the internet, so IPv6 + # global addresses are not filtered. + cat >> /run/caddy/Caddyfile <<'EOF' + +(sovran_lan_only) { + @outside not remote_ip private_ranges 100.64.0.0/10 169.254.0.0/16 fe80::/10 fc00::/7 2000::/3 + abort @outside +} +EOF + # ── Matrix ────────────────────────────────────── if [ -n "$MATRIX" ]; then if [ -f /run/caddy/element-calling.snippet ]; then @@ -206,6 +226,7 @@ EOF cat >> /run/caddy/Caddyfile <> /run/caddy/Caddyfile <> /run/caddy/Caddyfile <