# Caddy Reverse Proxy Setup The LXC runs a local Caddy that accepts traffic from all networks and proxies to the SvelteKit app. TLS termination for the public domain happens on a separate upstream Caddy — this instance only handles plain HTTP. ## Architecture ``` ┌─────────────────────────────────┐ Internet ──► Upstream Caddy (TLS) ──────┤ │ collection.newedge.house │ │ │ LXC: Caddy (:80) ──► :3000 │ NetBird ──► nb-ip:80 ──────────────────┤ (SvelteKit app) │ │ │ Yggdrasil──► [200:...]:80 ──────────────┤ │ │ │ Tor ──► .onion ──► tor ──► :8880 ──┤ │ └─────────────────────────────────┘ ``` All routes inject `Host: collection.newedge.house` so SvelteKit CSRF passes. ## 1. Install Caddy and Tor ```bash # Caddy apt install -y debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list apt update apt install caddy # Tor apt install tor ``` ## 2. Configure Tor hidden service Edit `/etc/tor/torrc`: ``` HiddenServiceDir /var/lib/tor/bflr/ HiddenServicePort 80 127.0.0.1:8880 ``` ```bash systemctl restart tor cat /var/lib/tor/bflr/hostname # → your .onion address ``` ## 3. Get your interface addresses ```bash # Yggdrasil IPv6 yggdrasilctl getSelf # → 200:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx # NetBird IP ip addr show wt0 # → 100.x.x.x (or whatever your NetBird interface is called) # LAN IP ip addr show eth0 # → 192.168.x.x ``` ## 4. Caddyfile Edit `/etc/caddy/Caddyfile`: ```caddy # ─── Shared snippets ──────────────────────────────────────────────── (proxy) { reverse_proxy 127.0.0.1:3000 { header_up Host collection.newedge.house header_up X-Real-IP {remote_host} header_up X-Forwarded-For {remote_host} header_up X-Forwarded-Proto https } } (common) { encode gzip request_body { max_size 50MB } } # ─── Public traffic (HTTP from upstream Caddy, TLS already terminated) ─ :80 { import common import proxy } # ─── Tor hidden service (Tor connects to localhost:8880) ───────────── :8880 { import common import proxy bind 127.0.0.1 } # ─── Yggdrasil (bind to Yggdrasil IPv6 only) ──────────────────────── # Replace with your address from: yggdrasilctl getSelf http://[200:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]:80 { import common import proxy } # ─── NetBird (bind to NetBird interface IP only) ───────────────────── # Replace with your address from: ip addr show wt0 http://100.x.x.x:80 { import common import proxy } ``` **Replace:** - `200:xxxx:...` with your Yggdrasil address - `100.x.x.x` with your NetBird IP **Note:** The `:80` block catches all traffic including from the upstream Caddy. The Yggdrasil and NetBird blocks are explicit bindings so Caddy listens on those interfaces. If they conflict with `:80`, remove the `:80` block and add explicit LAN binding instead: ```caddy # If you need separate LAN binding instead of catch-all :80 http://192.168.x.x:80 { import common import proxy } ``` ## 5. Upstream Caddy (the TLS termination server) On your upstream Caddy that handles public TLS, add: ```caddy collection.newedge.house { reverse_proxy LXC_IP:80 { header_up X-Forwarded-Proto https } } ``` Replace `LXC_IP` with the LXC's LAN or Tailscale/NetBird IP as seen from the upstream server. ## 6. App config The app only listens on localhost: ```bash # /home/bflr/buildfor_life_repair/.env HOST=127.0.0.1 PORT=3000 ORIGIN=https://collection.newedge.house BASE_URL=https://collection.newedge.house ``` systemd service: ```ini Environment=HOST=127.0.0.1 Environment=PORT=3000 ``` ## 7. Start everything ```bash systemctl daemon-reload systemctl restart bflr systemctl enable --now caddy systemctl restart tor ``` ## 8. Verify each path ```bash # Via upstream (public domain) curl -s -o /dev/null -w "%{http_code}" https://collection.newedge.house/login # Direct LAN curl -s -o /dev/null -w "%{http_code}" http://LXC_LAN_IP/login # NetBird curl -s -o /dev/null -w "%{http_code}" http://100.x.x.x/login # Yggdrasil (from a peer) curl -s -o /dev/null -w "%{http_code}" http://[200:xxxx:...]/login # Tor (from Tor Browser) # http://your-onion-address.onion/login # Local test curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/login ``` ## Troubleshooting ```bash # Caddy systemctl status caddy journalctl -u caddy -f caddy validate --config /etc/caddy/Caddyfile # Tor systemctl status tor cat /var/lib/tor/bflr/hostname # Yggdrasil yggdrasilctl getSelf yggdrasilctl getPeers # NetBird netbird status # App systemctl status bflr journalctl -u bflr -f # Test CSRF manually curl -H "Host: collection.newedge.house" http://127.0.0.1:3000/login ```