Files
caddy-bunny/scripts/ddns-cert-sync.sh
T

151 lines
3.8 KiB
Bash

#!/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"
IP_CHECK_URL="https://api.ipify.org?format=txt"
CRITICAL_DOMAINS=("recept.gynther.se" "jellyfin.gynther.se" "wetty.gynther.se")
DOCKER_CONTAINER_NAME="caddy"
# Create directory for state file if it doesn't exist
if [ ! -d "/var/lib/caddy" ]; then
sudo mkdir -p /var/lib/caddy
sudo touch "$STATE_FILE"
sudo chown $USER:$USER "$STATE_FILE"
fi
# Create log file if it doesn't exist
if [ ! -f "$LOG_FILE" ]; then
sudo touch "$LOG_FILE"
sudo chown $USER:$USER "$LOG_FILE"
fi
# 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" || {
log "ERROR: Failed to fetch current IP from $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
}
# Reload Caddy in Docker container
reload_caddy_in_docker() {
if docker ps | grep -q "$DOCKER_CONTAINER_NAME"; then
log "Reloading Caddy in Docker container '$DOCKER_CONTAINER_NAME'..."
if docker exec "$DOCKER_CONTAINER_NAME" caddy reload --config /etc/caddy/Caddyfile; then
log "Caddy reloaded successfully in Docker."
return 0
else
log "ERROR: Failed to reload Caddy in Docker. Attempting container restart."
docker restart "$DOCKER_CONTAINER_NAME" || {
log "ERROR: Failed to restart Caddy container."
return 1
}
log "Caddy container restarted."
return 0
fi
else
log "ERROR: Caddy container '$DOCKER_CONTAINER_NAME' is not running."
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 in Docker
if ! reload_caddy_in_docker; then
log "ERROR: Failed to reload/restart Caddy in Docker."
fi
# Verify HTTPS for critical domains
log "Running HTTPS verification for critical domains..."
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