| README.md | ||
Edge-Proxy: WireGuard + Caddy in einem Podman-Pod
Öffentlich erreichbarer Reverse-Proxy für Dienste hinter NAT. Ein VPS mit öffentlicher IP nimmt den Traffic an und schiebt ihn durch einen WireGuard-Tunnel zu diesem Pod. Caddy terminiert TLS (Let's Encrypt) und proxyt an die Backends im LAN.
Funktionsprinzip
Internet-Nutzer
│ :80 / :443
▼
VPS (öffentliche IP, WireGuard-Server, DNAT + MASQUERADE)
│ durch den WG-Tunnel
▼
┌─────────────── Podman-Pod (ein Netzwerk-Namespace) ───────────────┐
│ │
│ wireguard-Container ──baut wg0──► 10.0.100.2/24 │
│ ▲ │
│ caddy-Container ──lauscht :80/:443────┘ (sieht wg0 direkt) │
│ │ │
└─────────┼─────────────────────────────────────────────────────────┘
▼
Backends im LAN (selbes Subnetz)
Kernidee: Beide Container teilen sich denselben Netzwerk-Namespace (ein Pod).
Caddy lauscht dadurch direkt auf wg0. Der Rückweg der Antworten ist damit
automatisch symmetrisch — auf diesem Host sind keine Forwarding-, DNAT- oder
Routing-Regeln nötig. Genau das macht das Setup portabel: drei Dateien kopieren,
starten, fertig.
Öffentlicher Traffic kommt nur durch den Tunnel — der Pod hat bewusst
kein Host-Portmapping (-p 80:80).
Dateien
| Datei | Rolle |
|---|---|
edge-proxy.yaml |
Was läuft: Pod-Definition (2 Container, Volumes, Caps). |
edge-proxy.kube |
Wie systemd startet: Quadlet-Unit → edge-proxy.service. |
wg0.conf |
WireGuard-Client-Config (PrivateKey! → chmod 600). |
Caddyfile |
Reverse-Proxy-Regeln + Domains für automatisches HTTPS. |
Die .kube ist der moderne Standard-Weg, podman kube play unter systemd zu
fahren (ersetzt das deprecatete podman generate systemd). Der Service ruft
intern beim Start kube play <yaml> und beim Stop kube down auf — der Pod
wird also aus der YAML neu erzeugt, nicht nur ge-startet.
Verzeichnis-Layout auf dem Host (rootful)
/etc/containers/systemd/
├── edge-proxy.kube # Quadlet-Unit
└── edge-proxy.yaml # Pod-Definition
/opt/edge-proxy/
├── wg_confs/
│ └── wg0.conf # chmod 600, root:root
├── Caddyfile
├── caddy-data/ # PERSISTENT: Certs + ACME-Account ← nicht löschen
└── caddy-config/ # Caddy autosave
Warum
/datapersistent sein muss: Ohne persistentescaddy-data/holt Caddy nach jedem Neustart neue Zertifikate und läuft in die Let's-Encrypt- Rate-Limits (5 identische Certs/Woche). Das Volume ist Pflicht.
Erstinstallation
# 1) Host: WireGuard-Kernelmodul dauerhaft laden
sudo modprobe wireguard
echo wireguard | sudo tee /etc/modules-load.d/wireguard.conf
# -> danach kann SYS_MODULE in der YAML entfallen (ein Privileg weniger)
# 2) Verzeichnisse + Dateien
sudo mkdir -p /etc/containers/systemd /opt/edge-proxy/wg_confs
sudo cp edge-proxy.yaml edge-proxy.kube /etc/containers/systemd/
sudo cp wg0.conf /opt/edge-proxy/wg_confs/
sudo cp Caddyfile /opt/edge-proxy/Caddyfile
# 3) Rechte auf den privaten Schlüssel (WICHTIG)
sudo chmod 600 /opt/edge-proxy/wg_confs/wg0.conf
sudo chown root:root /opt/edge-proxy/wg_confs/wg0.conf
sudo chmod 700 /opt/edge-proxy/wg_confs
# 4) (Optional) Trockenlauf mit sichtbarer Ausgabe, bevor systemd übernimmt
sudo podman kube play /etc/containers/systemd/edge-proxy.yaml
sudo podman exec edge-proxy-wireguard wg show # Handshake da?
sudo podman logs edge-proxy-caddy # Certs / Fehler?
sudo podman kube down /etc/containers/systemd/edge-proxy.yaml # wieder abräumen!
# 5) systemd übernimmt
sudo systemctl daemon-reload
sudo systemctl start edge-proxy.service
sudo systemctl status edge-proxy.service
Kein systemctl enable nötig — Quadlet-Units sind "generated". Der
Boot-Start kommt aus der [Install]-Sektion (WantedBy=multi-user.target) und
wird beim daemon-reload verdrahtet. systemctl enable würde sogar fehlschlagen.
Betrieb / Alltag
Rootful → System-Scope, immer mit sudo systemctl (nicht --user).
sudo systemctl start edge-proxy.service
sudo systemctl stop edge-proxy.service
sudo systemctl restart edge-proxy.service
sudo systemctl status edge-proxy.service
journalctl -u edge-proxy.service -f # Live-Log
sudo systemctl is-enabled edge-proxy.service # "generated"/"enabled" = Boot-Start ok
Nach Änderung an edge-proxy.yaml, edge-proxy.kube, Caddyfile oder
wg0.conf:
sudo systemctl daemon-reload # nur bei .kube/.yaml-Struktur-Änderungen nötig
sudo systemctl restart edge-proxy.service
Container-Namen folgen dem Muster <pod>-<container>:
edge-proxy-wireguard, edge-proxy-caddy.
Health-Checks
# WireGuard: läuft der Tunnel? -> "latest handshake" + "transfer: … received"
sudo podman exec edge-proxy-wireguard wg show
# Caddy: liegen echte Production-Certs im persistenten Volume?
sudo podman exec edge-proxy-caddy ls -R /data/caddy/certificates
# Pfad "acme-v02.api.letsencrypt.org-directory/<domain>/" mit .crt/.key = ok
# End-to-End durch den kompletten Weg (DNS -> VPS -> Tunnel -> Caddy)
curl -vI https://<deine-domain>
# erwartet: issuer "Let's Encrypt" + HTTP-Statuscode vom Backend
Ein ruhiger Caddy-Log ist der beste Log. Fehlen obtaining certificate-
Zeilen, heißt das meist: Certs sind schon im /data und werden nur geladen.
Troubleshooting
| Symptom | Wahrscheinliche Ursache / Fix |
|---|---|
wg show zeigt keinen Handshake |
AllowedIPs oder PersistentKeepalive = 25 in wg0.conf prüfen (Peer-Block). |
| Tunnel steht, aber HTTPS bricht ab | Rückweg-Routing auf dem VPS (MASQUERADE/SNAT). Muss dort stimmen — hier nichts zu tun. |
| Certs kommen nicht | Öffentliches DNS muss auf die VPS-IP zeigen; VPS muss 80 UND 443 forwarden. |
| Cert-Fehler / Rate-Limit beim Testen | Erst mit LE-Staging testen (acme_ca https://acme-staging-v02.api.letsencrypt.org/directory als globale Caddyfile-Option), dann auf Prod. |
world accessible-Warnung (WG) |
sudo chmod 600 /opt/edge-proxy/wg_confs/wg0.conf. |
| Service startet nicht via systemd | sudo /usr/libexec/podman/quadlet -dryrun zeigt, was Quadlet aus der .kube generiert. |
| Name-Kollision beim Start | Alten Pod abräumen: sudo podman kube down …/edge-proxy.yaml. |
Nur kosmetisch (ignorierbar)
Caddyfile input is not formatted→caddy fmt --overwrite.failed to sufficiently increase receive buffer size→ HTTP/3-UDP-Puffer; optionalnet.core.rmem_max/wmem_maxper sysctl erhöhen.
Wichtige Voraussetzungen (Merkzettel)
- DNS der Domains → öffentliche VPS-IP.
- VPS forwardet
:80und:443in den Tunnel und macht den Rückweg (MASQUERADE/SNAT). Dieser Teil lebt auf dem VPS, nicht hier. wg0.conf:PersistentKeepalive = 25im[Peer](hält das NAT-Mapping offen).- Rootful — WireGuard braucht
NET_ADMIN+ Kernelmodul. caddy-data/niemals löschen (enthält die Zertifikate).
Umzug auf einen anderen Host
Portabilität war das Designziel:
# auf dem neuen Host:
sudo modprobe wireguard && echo wireguard | sudo tee /etc/modules-load.d/wireguard.conf
# edge-proxy.yaml + edge-proxy.kube -> /etc/containers/systemd/
# wg0.conf + Caddyfile -> /opt/edge-proxy/... (chmod 600 wg0.conf!)
sudo systemctl daemon-reload && sudo systemctl start edge-proxy.service
Die Zertifikate holt Caddy auf dem neuen Host automatisch neu — sofern DNS/VPS
weiter passen. Optional caddy-data/ mitkopieren, dann sind die alten Certs
sofort da.