#!/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