feat(caddy): integrate Bunny.net DNS and dynamic IP support
- Replace Bazarr with NZBGet in Caddyfile routes - Add global DNS provider configuration for ACME DNS-01 challenges - Implement dynamic DNS updater with Bunny.net provider - Add comprehensive security headers and authentication - Update documentation with new requirements and setup instructions - Add .env.example, Dockerfile, cron jobs, and scripts - Modify compose.yml to use local build and add environment variables BREAKING CHANGE: Requires Bunny.net API key and updated Caddyfile configuration
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
# .env.example - Example environment variables for Caddy Bunny
|
||||
# Copy this file to .env and fill in your actual values
|
||||
|
||||
# Bunny.net API Key (required for DNS management)
|
||||
BUNNY_API_KEY=your_bunny_api_key_here
|
||||
|
||||
# Email for Let's Encrypt account (required for TLS certificates)
|
||||
CADDY_EMAIL=your_email@example.com
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Custom Caddy Dockerfile with Bunny DNS and Dynamic DNS modules
|
||||
# Based on official Caddy Alpine image
|
||||
FROM caddy:2.7.6-alpine
|
||||
|
||||
# Install xcaddy for custom builds
|
||||
RUN apk add --no-cache xcaddy
|
||||
|
||||
# Build Caddy with required modules
|
||||
RUN xcaddy build \
|
||||
--output /usr/bin/caddy \
|
||||
--with github.com/caddy-dns/bunny \
|
||||
--with github.com/mholt/caddy-dynamicdns
|
||||
|
||||
# Verify modules are loaded
|
||||
RUN caddy list-modules | grep -E "dns.providers.bunny|dynamic_dns" && echo "Modules loaded successfully" || exit 1
|
||||
@@ -1,33 +1,27 @@
|
||||
# Caddy Reverse Proxy Konfiguration
|
||||
# Caddy Reverse Proxy med Bunny.net DNS och Dynamisk IP
|
||||
|
||||
Detta projekt innehåller konfigurationen för en **Docker-installation** av Caddy som fungerar som en reverse proxy för olika tjänster.
|
||||
Detta projekt innehåller konfigurationen för en **Docker-installation** av Caddy som fungerar som en reverse proxy för olika tjänster, med stöd för **Bunny.net DNS** och **automatisk IP-uppdatering**.
|
||||
|
||||
## Beskrivning
|
||||
|
||||
Caddy används för att dirigera trafik till olika tjänster som körs i Docker-containrar. Denna konfiguration inkluderar säkerhetsrubriker och autentisering för vissa tjänster. Dessutom har **Flutter** en egen Caddy-installation i sin Docker-container för att hantera specifika behov.
|
||||
UFW är uppe med följande regler:
|
||||
To Action From
|
||||
-- ------ ----
|
||||
[ 1] 22/tcp ALLOW IN Anywhere
|
||||
[ 2] 80/tcp ALLOW IN Anywhere
|
||||
[ 3] 443/tcp ALLOW IN Anywhere
|
||||
[ 4] 137,138/udp ALLOW IN 192.168.50.0/24 # Samba lokal UDP
|
||||
[ 5] 139,445/tcp ALLOW IN 192.168.50.0/24 # Samba lokal TCP
|
||||
[ 6] 22/tcp (v6) ALLOW IN Anywhere (v6)
|
||||
[ 7] 443/tcp (v6) ALLOW IN Anywhere (v6)
|
||||
Caddy används för att dirigera trafik till olika tjänster som körs i Docker-containrar. Denna konfiguration inkluderar:
|
||||
- Säkerhetsrubriker och autentisering för vissa tjänster.
|
||||
- **Bunny.net DNS-integration** för ACME DNS-01 challenges (Let's Encrypt-certifikat).
|
||||
- **Dynamisk DNS-uppdatering** när den publika IP:n ändras.
|
||||
- **Cron-script** för att övervaka IP-förändringar och säkerställa TLS-funktion.
|
||||
|
||||
## Tjänster
|
||||
|
||||
Följande domäner och tjänster hanteras av Caddy:
|
||||
|
||||
- `test.gynther.se` → `recipe-flutter:5000` (Flutter har en egen Caddy-installation i sin Docker-container)
|
||||
- `test.gynther.se` → `recipe-flutter:5000`
|
||||
- `bazarr.gynther.se` → `bazarr:6767`
|
||||
- `prowlarr.gynther.se` → `prowlarr:9696`
|
||||
- `radarr.gynther.se` → `radarr:7878`
|
||||
- `sonarr.gynther.se` → `sonarr:8989`
|
||||
- `jellyfin.gynther.se` → `jellyfin:8096`
|
||||
- `qbittorrent.gynther.se` → `192.168.50.4:8080`
|
||||
- `wetty.gynther.se` → `wetty:3001`
|
||||
- `wetty.gynther.se` → `wetty:3001` (med autentisering)
|
||||
- `portainer.gynther.se` → `portainer:9000`
|
||||
- `gitea.gynther.se` → `192.168.50.2:3002`
|
||||
- `import.gynther.se` → `recipe-import-service:3000`
|
||||
@@ -40,21 +34,104 @@ Följande domäner och tjänster hanteras av Caddy:
|
||||
|
||||
- **Autentisering**: Vissa tjänster, som `wetty.gynther.se`, kräver basautentisering.
|
||||
- **Säkerhetsrubriker**: Caddy lägger till säkerhetsrubriker för att skydda mot vanliga webbsårbarheter.
|
||||
- **TLS**: Automatisk certifikatshantering via Let's Encrypt med Bunny.net DNS-01 challenge.
|
||||
|
||||
## Installation och Körning
|
||||
|
||||
1. Se till att Docker och Docker Compose är installerade.
|
||||
2. Placera `Caddyfile` i `conf`-mappen.
|
||||
3. Kör `docker-compose up -d` för att starta tjänsterna.
|
||||
### Förutsättningar
|
||||
- Docker och Docker Compose installerade.
|
||||
- Bunny.net API-nyckel (för DNS-hantering).
|
||||
- Miljövariabler konfigurerade (se `.env.example`).
|
||||
|
||||
### Steg
|
||||
|
||||
1. **Klona repot och navigera till katalogen:**
|
||||
```bash
|
||||
git clone ssh://git@gitea.gynther.se:2222/nilsjohan/caddy-bunny.git
|
||||
cd caddy-bunny
|
||||
```
|
||||
|
||||
2. **Skapa en `.env`-fil med nödvändiga variabler:**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Redigera .env och lägg till din Bunny.net API-nyckel och e-post
|
||||
```
|
||||
|
||||
3. **Bygg och starta containrar:**
|
||||
```bash
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
4. **Verifiera att Caddy körs korrekt:**
|
||||
```bash
|
||||
docker-compose logs caddy
|
||||
docker-compose exec caddy caddy list-modules | grep -E "dns.providers.bunny|dynamic_dns"
|
||||
```
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### Caddyfile
|
||||
- **Globala inställningar**:
|
||||
- `acme_dns bunny {env.BUNNY_API_KEY}`: Aktiverar ACME DNS-01 challenge med Bunny.net.
|
||||
- `dynamic_dns`: Konfigurerar automatisk uppdatering av DNS-records när IP:n ändras.
|
||||
|
||||
- **Site-specifika regler**: Se `conf/Caddyfile` för detaljerad routing.
|
||||
|
||||
### Miljövariabler
|
||||
- `BUNNY_API_KEY`: Bunny.net API-nyckel (för DNS-hantering).
|
||||
- `CADDY_EMAIL`: E-postadress för Let's Encrypt-kontot.
|
||||
|
||||
## Dynamisk DNS och IP-övervakning
|
||||
|
||||
### Cron-script
|
||||
Ett script (`scripts/ddns-cert-sync.sh`) körs periodiskt (var 5:e minut) för att:
|
||||
1. Kontrollera den publika IP:n.
|
||||
2. Uppdatera DNS-records om IP:n har ändrats.
|
||||
3. Starta om Caddy för att säkerställa TLS-synkronisering.
|
||||
4. Verifiera HTTPS-anslutningar till kritiska domäner.
|
||||
|
||||
### Installation av Cron
|
||||
1. Kopiera cron-filen till servern:
|
||||
```bash
|
||||
scp cron/caddy-ddns-cert.cron user@server:/etc/cron.d/caddy-ddns-cert
|
||||
```
|
||||
|
||||
2. Säkerställa att scriptet är körbart:
|
||||
```bash
|
||||
chmod +x /app/scripts/ddns-cert-sync.sh
|
||||
```
|
||||
|
||||
## Felsökning
|
||||
|
||||
- **Loggar**:
|
||||
- Caddy-loggar: `docker-compose logs caddy`
|
||||
- Cron-loggar: `/var/log/cron-caddy-ddns.log`
|
||||
- Script-loggar: `/var/log/caddy-ddns-cert-sync.log`
|
||||
|
||||
- **Vanliga problem**:
|
||||
- **DNS-uppdateringar misslyckas**: Kontrollera att Bunny.net API-nyckeln är korrekt och att zonen och hostnames är korrekt konfigurerade.
|
||||
- **TLS-fel**: Verifiera att DNS-records har propagerats korrekt och att Caddy har startats om.
|
||||
- **Caddy startar inte**: Kontrollera att alla nödvändiga moduler är inkluderade i den anpassade builden (`docker-compose exec caddy caddy list-modules`).
|
||||
|
||||
## Uppdatering
|
||||
|
||||
För att uppdatera konfigurationen:
|
||||
1. Redigera `Caddyfile` efter behov.
|
||||
2. Starta om Caddy-containern med `docker-compose restart caddy`.
|
||||
2. Starta om Caddy-containern:
|
||||
```bash
|
||||
docker-compose restart caddy
|
||||
```
|
||||
|
||||
## Felsökning
|
||||
## Rollback
|
||||
|
||||
Om något inte fungerar som förväntat:
|
||||
- Kontrollera loggarna med `docker-compose logs caddy`.
|
||||
- Se till att alla tjänster som Caddy ska dirigera trafik till är igång och lyssnar på rätt portar.
|
||||
För att återgå till den tidigare versionen:
|
||||
1. Återgå till det gamla repot:
|
||||
```bash
|
||||
cd ../caddy
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Licens
|
||||
|
||||
MIT
|
||||
|
||||
+4
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
caddy:
|
||||
image: caddy:2.6.4
|
||||
build: .
|
||||
container_name: caddy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
@@ -11,6 +11,9 @@ services:
|
||||
- ./conf:/etc/caddy
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
environment:
|
||||
- BUNNY_API_KEY=${BUNNY_API_KEY}
|
||||
- CADDY_EMAIL=${CADDY_EMAIL}
|
||||
|
||||
volumes:
|
||||
caddy_data:
|
||||
|
||||
+141
@@ -19,6 +19,147 @@
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
# Global DNS provider for ACME DNS challenge
|
||||
acme_dns bunny {env.BUNNY_API_KEY}
|
||||
|
||||
# Dynamic DNS configuration
|
||||
dynamic_dns {
|
||||
provider bunny {env.BUNNY_API_KEY}
|
||||
domains {
|
||||
gynther.se @ www bazarr prowlarr radarr sonarr jellyfin qbittorrent wetty portainer gitea import recept test nzbget
|
||||
}
|
||||
check_interval 5m
|
||||
ttl 300s
|
||||
versions ipv4
|
||||
}
|
||||
}
|
||||
|
||||
test.gynther.se {
|
||||
import common
|
||||
reverse_proxy recipe-flutter:5000
|
||||
}
|
||||
|
||||
nzbget.gynther.se {
|
||||
import common
|
||||
reverse_proxy http://192.168.50.4:6789
|
||||
}
|
||||
|
||||
prowlarr.gynther.se {
|
||||
import common
|
||||
reverse_proxy http://prowlarr:9696
|
||||
}
|
||||
|
||||
radarr.gynther.se {
|
||||
import common
|
||||
reverse_proxy http://radarr:7878
|
||||
}
|
||||
|
||||
sonarr.gynther.se {
|
||||
import common
|
||||
reverse_proxy http://sonarr:8989
|
||||
}
|
||||
|
||||
jellyfin.gynther.se {
|
||||
reverse_proxy http://jellyfin:8096
|
||||
}
|
||||
|
||||
qbittorrent.gynther.se {
|
||||
import common
|
||||
reverse_proxy 192.168.50.4:8080
|
||||
}
|
||||
|
||||
wetty.gynther.se {
|
||||
import auth
|
||||
import common
|
||||
redir / /wetty
|
||||
reverse_proxy wetty:3001
|
||||
}
|
||||
|
||||
gitea.gynther.se {
|
||||
import common
|
||||
reverse_proxy 192.168.50.2:3002
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Import Service (Document Converter) - Standalone UI
|
||||
# ============================================
|
||||
import.gynther.se {
|
||||
import common
|
||||
reverse_proxy recipe-import-service:3000
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# RECIPE APP + IMPORT SERVICE
|
||||
# ============================================
|
||||
recept.gynther.se {
|
||||
import common
|
||||
|
||||
# === IMPORT SERVICE (Document Converter) ===
|
||||
# Dessa endpoints måste komma FÖRST innan backend reglerna!
|
||||
handle /api/recipes/import* {
|
||||
reverse_proxy recipe-import-service:3000
|
||||
}
|
||||
|
||||
# === RECIPE FRONTEND PROXY ENDPOINTS ===
|
||||
# Next.js API routes
|
||||
handle /api/inventory-history-proxy {
|
||||
reverse_proxy recipe-frontend:3000
|
||||
}
|
||||
|
||||
handle /api/admin/merge-preview-proxy {
|
||||
reverse_proxy recipe-frontend:3000
|
||||
}
|
||||
|
||||
handle /api/recipe-preview-proxy {
|
||||
reverse_proxy recipe-frontend:3000
|
||||
}
|
||||
|
||||
# === RECIPE BACKEND API ENDPOINTS ===
|
||||
# Backend körs på port 8080 (från docker-compose)
|
||||
handle /api/products* {
|
||||
reverse_proxy recipe-api:8080
|
||||
}
|
||||
|
||||
handle /api/inventory* {
|
||||
reverse_proxy recipe-api:8080
|
||||
}
|
||||
|
||||
handle /api/recipes* {
|
||||
reverse_proxy recipe-api:8080
|
||||
}
|
||||
|
||||
# === HEALTH CHECKS ===
|
||||
handle /health {
|
||||
reverse_proxy recipe-api:8080
|
||||
}
|
||||
|
||||
# === CATCH ALL ===
|
||||
# Övriga /api/* går till frontend
|
||||
handle /api/* {
|
||||
reverse_proxy recipe-frontend:3000
|
||||
}
|
||||
|
||||
# Frontend - catch all remaining routes (port 3000)
|
||||
reverse_proxy /* recipe-frontend:3000
|
||||
}
|
||||
}
|
||||
|
||||
(common) {
|
||||
encode gzip zstd
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()"
|
||||
Cross-Origin-Opener-Policy "same-origin"
|
||||
Cross-Origin-Resource-Policy "same-origin"
|
||||
Cross-Origin-Embedder-Policy "require-corp"
|
||||
}
|
||||
}
|
||||
|
||||
test.gynther.se {
|
||||
import auth
|
||||
import common
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Crontab entry for Caddy Dynamic DNS and Certificate Sync
|
||||
# Run every 5 minutes to monitor IP changes
|
||||
*/5 * * * * /bin/bash -c '/app/scripts/ddns-cert-sync.sh >> /var/log/cron-caddy-ddns.log 2>&1'
|
||||
@@ -0,0 +1,123 @@
|
||||
#!/bin/bash
|
||||
# Script: ddns-cert-sync.sh
|
||||
# Purpose: Monitor public IP changes and ensure Caddy DNS/TLS is synchronized
|
||||
# Usage: Run via cron (e.g., every 5 minutes)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
LOCK_FILE="/tmp/caddy-ddns-cert-sync.lock"
|
||||
STATE_FILE="/var/lib/caddy/last_ip.txt"
|
||||
LOG_FILE="/var/log/caddy-ddns-cert-sync.log"
|
||||
CADDY_BIN="/usr/bin/caddy"
|
||||
IP_CHECK_URL="https://api.ipify.org?format=txt"
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Check if script is already running
|
||||
if [ -f "$LOCK_FILE" ]; then
|
||||
log "Lock file exists. Another instance is running. Exiting."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create lock file
|
||||
trap "rm -f '$LOCK_FILE'; exit" INT TERM EXIT
|
||||
touch "$LOCK_FILE"
|
||||
|
||||
# Get current public IP
|
||||
get_current_ip() {
|
||||
curl -s --max-time 10 "$IP_CHECK_URL" || echo ""
|
||||
}
|
||||
|
||||
# Read last known IP from state file
|
||||
read_last_ip() {
|
||||
if [ -f "$STATE_FILE" ]; then
|
||||
cat "$STATE_FILE" 2>/dev/null || echo ""
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Write current IP to state file
|
||||
write_current_ip() {
|
||||
echo "$1" > "$STATE_FILE"
|
||||
}
|
||||
|
||||
# Test HTTPS connectivity
|
||||
test_https() {
|
||||
local domain="$1"
|
||||
if curl -s --max-time 10 -o /dev/null -w "%{http_code}" "https://$domain/" | grep -q "^200$"; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
log "Starting IP check and Caddy sync"
|
||||
|
||||
CURRENT_IP=$(get_current_ip)
|
||||
if [ -z "$CURRENT_IP" ]; then
|
||||
log "ERROR: Could not determine current public IP. Retrying in next cycle."
|
||||
rm -f "$LOCK_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Current public IP: $CURRENT_IP"
|
||||
|
||||
LAST_IP=$(read_last_ip)
|
||||
if [ -z "$LAST_IP" ]; then
|
||||
log "No previous IP found. Writing current IP to state file."
|
||||
write_current_ip "$CURRENT_IP"
|
||||
rm -f "$LOCK_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Last known IP: $LAST_IP"
|
||||
|
||||
if [ "$CURRENT_IP" = "$LAST_IP" ]; then
|
||||
log "IP unchanged. No action needed."
|
||||
rm -f "$LOCK_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# IP has changed
|
||||
log "IP changed from $LAST_IP to $CURRENT_IP. Triggering Caddy reload and TLS verification."
|
||||
|
||||
# Update state file
|
||||
write_current_ip "$CURRENT_IP"
|
||||
|
||||
# Reload Caddy to ensure DNS and TLS are synchronized
|
||||
if [ -x "$CADDY_BIN" ]; then
|
||||
log "Reloading Caddy..."
|
||||
if "$CADDY_BIN" reload --config /etc/caddy/Caddyfile; then
|
||||
log "Caddy reloaded successfully."
|
||||
else
|
||||
log "ERROR: Failed to reload Caddy. Attempting restart."
|
||||
"$CADDY_BIN" stop || true
|
||||
sleep 2
|
||||
"$CADDY_BIN" start --config /etc/caddy/Caddyfile --adapter caddyfile || log "ERROR: Failed to restart Caddy."
|
||||
fi
|
||||
else
|
||||
log "ERROR: Caddy binary not found at $CADDY_BIN"
|
||||
fi
|
||||
|
||||
# Verify HTTPS for critical domains
|
||||
log "Running HTTPS verification for critical domains..."
|
||||
CRITICAL_DOMAINS=("recept.gynther.se" "jellyfin.gynther.se" "wetty.gynther.se")
|
||||
for domain in "${CRITICAL_DOMAINS[@]}"; do
|
||||
if test_https "$domain"; then
|
||||
log "HTTPS test PASSED for $domain"
|
||||
else
|
||||
log "WARNING: HTTPS test FAILED for $domain (may be due to DNS propagation)"
|
||||
fi
|
||||
done
|
||||
|
||||
log "IP change handling completed."
|
||||
|
||||
# Cleanup
|
||||
rm -f "$LOCK_FILE"
|
||||
exit 0
|
||||
Reference in New Issue
Block a user