Caddy
Caddy ist ein Reverseproxy, der besonders wegen seiner Einfachheit beliebt ist. Die Konfiguration erfolgt vollständig über eine Datei, das Caddyfile. Man kann Caddy per Docker oder nativ als Binary auf dem Host installieren. Das Erstere erleichtert die Einbindung von Plugins. Da ich es außerdem auf einem Server nutzen möchte, auf dem bereits Docker läuft, betreibe ich es unter Docker. Ich nutze Caddy, um die Weblösungen, die ich in meinem lokalen Homelab betreibe, mit Domains und Let's Encrypt Zertifikaten zu verknüpfen. Ich nutze Domains, die bei den Providern Netcup und All-inkl gehostet werden. Hier dokumentiere ich, wie ich Domains bei diesen beiden Providern für mein lokales Netz nutze. Ich brauche dazu Caddy und einen DNS-Server (in meinem Fall dnsmasq).
Installation
Ich bin auf meinem Server per SSH angemeldet und besitze auch die Sudo-Rechte (siehe hierzu: server-absichern.html). Zunächst ist eine Dockerumgebung zu installieren. Dazu gibt es verschiedene Methoden. Meine habe ich hier: Docker beschrieben. Dann erstelle ich ein Verzeichnis namens "caddy" und wechsele in selbiges. Hier erstelle ich zunächst mit meinem Lieblings-Editor nano eine Datei namens docker-compose.yml mit folgendem Inhalt:
services:
caddy:
build:
context: .
dockerfile_inline: |
FROM caddy:builder AS builder
# Caddy mit dem All-Inkl Plugin
RUN xcaddy build \
--with github.com/caddy-dns/netcup \
--with github.com/caddy-dns/all-inkl
FROM caddy:latest
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # Wichtig für HTTP/3 Support
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./data:/data
- ./config:/config
environment:
- NETCUP_CUSTOMER_NUMBER="******" # Netcup-Kundennummer
- NETCUP_API_KEY="Zn************************ND"
- NETCUP_API_PASSWORD="Zk5*********************************dlRK"
- KAS_USERNAME=********
- KAS_PASSWORD=********
In der Environment-Sektion definiere ich natürlich die Credentials für meine Provider. Bei Netcup ist das die Kundennummer, der API-Key und das API-Passwort. Letztere kann ich im Customer Control Panel von Netcup generieren. Für All-Inkl. brauche ich die Zugangsdaten für das KAS (keine Ahnung, wofür die Abkürzung steht).
Wenn ich nun den Container mit
docker compose up -d --build
Definition der Proxy Hosts
Die Proxy Hosts werden in Caddy im Caddyfile definiert. Wenn ich Caddy direkt auf dem Host installiert hätte, dann läge das Caddyfile unter /etc/caddy. In meiner Docker-Umgebung liegt es im gleichen Verzeichnis, wie die docker-compose.yml. Das Caddyfile heißt schlicht "Caddyfile" und hat folgenden exemplarischen Inhalt:
{
# Globale Einstellungen
email mail@sechzig-veedel.de
}
# --- ALL-INKL DOMAIN ---
*.korte-kernke.koeln, korte-kernke.koeln {
tls {
issuer acme {
dns allinkl {
kas_username {$KAS_USERNAME}
kas_password {$KAS_PASSWORD}
}
propagation_delay 120s
propagation_timeout -1
}
}
# Proxmox VE
@pve00 host pve00.korte-kernke.koeln
handle @pve00 {
reverse_proxy https://192.168.1.140:8006 {
transport http {
tls_insecure_skip_verify
}
}
}
# Interner Webserver
@intranet host intranet.korte-kernke.koeln
handle @intranet {
reverse_proxy 192.168.1.40:80
}
# Fallback innerhalb dieses Blocks: Alles andere abweisen
handle {
abort
}
}
# --- NETCUP DOMAIN ---
*.korte-koeln.de, korte-koeln.de {
tls {
issuer acme {
dns netcup {
customer_number {$NETCUP_CUSTOMER_NUMBER}
api_key {$NETCUP_API_KEY}
api_password {$NETCUP_API_PASSWORD}
}
propagation_delay 30s
resolvers 1.1.1.1
}
}
# Loki Logging
@loki host loki.korte-koeln.de
handle @loki {
reverse_proxy 192.168.1.60:3002 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up Connection {>Connection}
header_up Upgrade {>Upgrade}
}
}
# Fallback innerhalb dieses Blocks
handle {
abort
}
}
# Interner Webserver
@intranet host intranet.korte-kernke.koeln
handle @intranet {
reverse_proxy 192.168.1.40:80
}
# Proxmox VE
@pve00 host pve00.korte-kernke.koeln
handle @pve00 {
reverse_proxy https://192.168.1.140:8006 {
transport http {
tls_insecure_skip_verify
}
}
}
# Loki Logging
@loki host loki.korte-koeln.de
handle @loki {
reverse_proxy 192.168.1.60:3002 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up Connection {>Connection}
header_up Upgrade {>Upgrade}
}
}
docker exec -w /etc/caddy caddy caddy fmt --overwrite
docker exec -w /etc/caddy caddy caddy reload
Mein lokaler DNS löst Subdomains, die ich dort eintrage mit IP-Adressen aus meinem lokalen Netzwerk auf. Für die Domains habe ich bei den Providern einen sogenannten Wildcard A-Record eingerichtet. Wenn ein System aus dem Internet bei den öffentlich verfügbaren DNS-Servern die Adresse einer Subdomain dieser Domains nachfragt, wird die bei diesen Providern hinterlegte IP-Adresse ausgegeben. Wenn ein Client aus meinem lokalen Netz irgendeine Domain oder Subdomain aufruft, dann wird zunächst der lokale DNS-Server befragt. Wenn er einen dazu passenden Eintrag hat, dann liefert er den aus, ansonsten routet er an öffentlich verfügbare DNS-Server weiter. Wenn ich also eine Subdomain unter einer der og. Domains aufrufe, dann wird zunächst geprüft, ob sie lokal definiert ist bevor sie im WWW nachgeschlagen wird. So kann ich unter diesen Domains lokale Dienste hosten und mit einem sicheren Zertifikat verbinden, ohne sie ins Internet exponieren zu müssen. Außerdem kann ich bei Bedarf auch Dienste mit Subdomains unter diesen Domains im Internet veröffentlichen. So will ich das haben.