Add Caddy reverse proxy guide for internal, Tor, and Yggdrasil access
Deploy to LXC / deploy (push) Successful in 20s
Deploy to LXC / deploy (push) Successful in 20s
Caddyfile config with shared proxy snippet that sets Host header for CSRF compatibility. Handles: - Public domain with auto HTTPS (Let's Encrypt) - LAN/internal on port 80 - Tor hidden service via localhost:8880 - Yggdrasil IPv6 on port 80 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
# Caddy Reverse Proxy Setup
|
||||
|
||||
Caddy sits in front of the SvelteKit app and handles all access methods:
|
||||
- Internal network (LAN/Tailscale)
|
||||
- Tor hidden service
|
||||
- Yggdrasil mesh network
|
||||
- Public domain with automatic HTTPS
|
||||
|
||||
## 1. Install Caddy
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
## 2. Install Tor
|
||||
|
||||
```bash
|
||||
apt install tor
|
||||
```
|
||||
|
||||
Edit `/etc/tor/torrc`:
|
||||
|
||||
```
|
||||
HiddenServiceDir /var/lib/tor/bflr/
|
||||
HiddenServicePort 80 127.0.0.1:8880
|
||||
```
|
||||
|
||||
Restart and get your .onion address:
|
||||
|
||||
```bash
|
||||
systemctl restart tor
|
||||
cat /var/lib/tor/bflr/hostname
|
||||
```
|
||||
|
||||
## 3. Configure Caddy
|
||||
|
||||
Edit `/etc/caddy/Caddyfile`:
|
||||
|
||||
```caddy
|
||||
# ─── Shared config ───────────────────────────────────────────────────
|
||||
|
||||
(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 domain (automatic HTTPS via Let's Encrypt) ───────────────
|
||||
|
||||
collection.newedge.house {
|
||||
import common
|
||||
reverse_proxy 127.0.0.1:3000 {
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
# ─── Internal / LAN access (HTTP on port 80) ─────────────────────────
|
||||
|
||||
:80 {
|
||||
import common
|
||||
import proxy
|
||||
}
|
||||
|
||||
# ─── Tor hidden service (HTTP on port 8880, Tor connects here) ───────
|
||||
|
||||
:8880 {
|
||||
import common
|
||||
import proxy
|
||||
bind 127.0.0.1
|
||||
}
|
||||
|
||||
# ─── Yggdrasil (HTTP on port 80, bind to Yggdrasil IPv6) ─────────────
|
||||
# Replace with your actual Yggdrasil address from: yggdrasilctl getSelf
|
||||
|
||||
http://[200:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]:80 {
|
||||
import common
|
||||
import proxy
|
||||
}
|
||||
```
|
||||
|
||||
**Important:** Replace `200:xxxx:...` with your actual Yggdrasil IPv6 address.
|
||||
|
||||
The `(proxy)` snippet sets `Host: collection.newedge.house` on all non-public routes so SvelteKit's CSRF check passes regardless of how you access the app.
|
||||
|
||||
## 4. Update the SvelteKit service
|
||||
|
||||
Change the app to only listen on localhost since Caddy handles external access:
|
||||
|
||||
```bash
|
||||
nano /home/bflr/buildfor_life_repair/.env
|
||||
```
|
||||
|
||||
```
|
||||
HOST=127.0.0.1
|
||||
PORT=3000
|
||||
```
|
||||
|
||||
Update systemd if HOST is set there too:
|
||||
|
||||
```bash
|
||||
nano /etc/systemd/system/bflr.service
|
||||
```
|
||||
|
||||
```ini
|
||||
Environment=HOST=127.0.0.1
|
||||
```
|
||||
|
||||
## 5. Firewall
|
||||
|
||||
Only Caddy needs external access:
|
||||
|
||||
```bash
|
||||
# Allow HTTP/HTTPS from anywhere
|
||||
ufw allow 80/tcp
|
||||
ufw allow 443/tcp
|
||||
|
||||
# Block direct app access from outside
|
||||
ufw deny 3000/tcp
|
||||
```
|
||||
|
||||
## 6. Start everything
|
||||
|
||||
```bash
|
||||
systemctl daemon-reload
|
||||
systemctl restart bflr
|
||||
systemctl enable --now caddy
|
||||
systemctl restart tor
|
||||
```
|
||||
|
||||
## 7. Verify
|
||||
|
||||
```bash
|
||||
# Public domain
|
||||
curl -s -o /dev/null -w "%{http_code}" https://collection.newedge.house/login
|
||||
|
||||
# Internal
|
||||
curl -s -o /dev/null -w "%{http_code}" http://localhost/login
|
||||
|
||||
# Tor (from Tor Browser)
|
||||
# http://your-onion-address.onion/login
|
||||
|
||||
# Yggdrasil (from a Yggdrasil peer)
|
||||
# http://[200:xxxx:...]/login
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
```
|
||||
Internet ──► collection.newedge.house:443 ──► Caddy ──► :3000 (app)
|
||||
LAN ──► server-ip:80 ──► Caddy ──► :3000 (app)
|
||||
Tor ──► .onion:80 ──► tor ──► :8880 ──► Caddy ──► :3000 (app)
|
||||
Yggdrasil──► [200:...]:80 ──► Caddy ──► :3000 (app)
|
||||
```
|
||||
|
||||
All routes set `Host: collection.newedge.house` so SvelteKit CSRF passes.
|
||||
Caddy handles TLS for the public domain automatically via Let's Encrypt.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
```bash
|
||||
# Check Caddy status
|
||||
systemctl status caddy
|
||||
journalctl -u caddy -f
|
||||
|
||||
# Check Tor status
|
||||
systemctl status tor
|
||||
cat /var/lib/tor/bflr/hostname
|
||||
|
||||
# Check Yggdrasil address
|
||||
yggdrasilctl getSelf
|
||||
|
||||
# Test reverse proxy
|
||||
curl -H "Host: collection.newedge.house" http://127.0.0.1:3000/login
|
||||
```
|
||||
Reference in New Issue
Block a user