Files
buildfor_life_repair/docs/caddy-reverse-proxy.md
T
grabowski 392747e639
Deploy to LXC / deploy (push) Successful in 19s
Update Caddy guide: upstream TLS, NetBird, Tor, Yggdrasil
Rewritten for the actual architecture: separate upstream Caddy handles
TLS for public domain, LXC Caddy only does HTTP. Added NetBird
interface binding, explicit per-interface blocks, upstream Caddy
config snippet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 14:08:28 +07:00

232 lines
5.7 KiB
Markdown

# 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 {scheme}
}
}
(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
```