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.
This commit is contained in:
Arena.ai Agent
2026-10-01 21:43:47 -05:00
committed by naturallaw777
parent 34cfba4282
commit c33457fff2
3 changed files with 139 additions and 0 deletions
+23
View File
@@ -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 <<EOF
http://sovransystemsos.local {
import sovran_lan_only
reverse_proxy localhost:8937
header {
Clear-Site-Data "\"cache\""
@@ -220,6 +241,7 @@ EOF
cat >> /run/caddy/Caddyfile <<EOF
:3051 {
import sovran_lan_only
reverse_proxy :3050
encode gzip zstd
}
@@ -229,6 +251,7 @@ EOF
cat >> /run/caddy/Caddyfile <<EOF
:60847 {
import sovran_lan_only
reverse_proxy :60845
encode gzip zstd
}