diff --git a/scripts/renew-certificates.sh b/scripts/renew-certificates.sh index d1dfa9f..6331233 100644 --- a/scripts/renew-certificates.sh +++ b/scripts/renew-certificates.sh @@ -9,6 +9,8 @@ set -e LOG_FILE="${LOG_FILE:-/var/log/haproxy-manager.log}" ERROR_LOG_FILE="${ERROR_LOG_FILE:-/var/log/haproxy-manager-errors.log}" HAPROXY_SOCKET="${HAPROXY_SOCKET:-/tmp/haproxy-cli}" +DB_FILE="${DB_FILE:-/etc/haproxy/haproxy_config.db}" +SSL_CERTS_DIR="${SSL_CERTS_DIR:-/etc/haproxy/certs}" MAX_RETRIES=3 RETRY_DELAY=5 @@ -74,6 +76,80 @@ reload_haproxy() { return 1 } +# Update combined certificate files for HAProxy +update_combined_certificates() { + log_info "Updating combined certificate files for HAProxy" + + # Check if database exists + if [ ! -f "$DB_FILE" ]; then + log_error "Database file not found at $DB_FILE" + return 1 + fi + + # Check if sqlite3 is available + if ! command -v sqlite3 &> /dev/null; then + log_error "sqlite3 command not found" + return 1 + fi + + # Ensure SSL certs directory exists + mkdir -p "$SSL_CERTS_DIR" + + # Get all domains with SSL enabled from database + local domains + domains=$(sqlite3 "$DB_FILE" "SELECT domain, ssl_cert_path FROM domains WHERE ssl_enabled = 1;" 2>/dev/null) + + if [ -z "$domains" ]; then + log_info "No SSL-enabled domains found in database" + return 0 + fi + + local updated_count=0 + local error_count=0 + + # Process each domain + while IFS='|' read -r domain cert_path; do + if [ -z "$domain" ] || [ -z "$cert_path" ]; then + continue + fi + + log_info "Processing certificate for domain: $domain" + + local letsencrypt_cert="/etc/letsencrypt/live/${domain}/fullchain.pem" + local letsencrypt_key="/etc/letsencrypt/live/${domain}/privkey.pem" + + # Check if Let's Encrypt certificate files exist + if [ ! -f "$letsencrypt_cert" ]; then + log_warning "Certificate not found for $domain at $letsencrypt_cert" + error_count=$((error_count + 1)) + continue + fi + + if [ ! -f "$letsencrypt_key" ]; then + log_warning "Private key not found for $domain at $letsencrypt_key" + error_count=$((error_count + 1)) + continue + fi + + # Combine certificate and key into single file for HAProxy + if cat "$letsencrypt_cert" "$letsencrypt_key" > "$cert_path"; then + log_info "Updated combined certificate for $domain at $cert_path" + updated_count=$((updated_count + 1)) + else + log_error "Failed to combine certificate files for $domain" + error_count=$((error_count + 1)) + fi + done <<< "$domains" + + log_info "Certificate update completed: $updated_count updated, $error_count errors" + + if [ $error_count -gt 0 ]; then + return 1 + fi + + return 0 +} + # Main renewal process log_info "Starting certificate renewal process" @@ -84,17 +160,29 @@ if certbot renew --quiet --no-random-sleep-on-renew 2>&1 | tee -a "$LOG_FILE"; t if [ $RENEWAL_EXIT_CODE -eq 0 ]; then log_info "Certificate renewal completed successfully" - # Check if any certificates were actually renewed - if grep -q "Cert not yet due for renewal" "$LOG_FILE" 2>/dev/null; then - log_info "No certificates needed renewal at this time" - else - log_info "Certificates were renewed, reloading HAProxy" + # Always update combined certificate files after renewal + # (certbot may have renewed some certificates even if the message says otherwise) + log_info "Updating combined certificate files for HAProxy" + if update_combined_certificates; then + log_info "Combined certificates updated successfully" + + # Reload HAProxy to pick up the updated certificates + log_info "Reloading HAProxy" if reload_haproxy; then log_info "Certificate renewal and HAProxy reload completed successfully" else log_error "Certificate renewal succeeded but HAProxy reload failed" exit 1 fi + else + log_warning "Certificate update completed with some errors, but attempting HAProxy reload" + # Still try to reload HAProxy even if some certificates failed + if reload_haproxy; then + log_warning "HAProxy reloaded successfully despite certificate update errors" + else + log_error "Certificate update had errors and HAProxy reload failed" + exit 1 + fi fi else log_error "Certificate renewal failed with exit code $RENEWAL_EXIT_CODE" diff --git a/scripts/sync-certificates.sh b/scripts/sync-certificates.sh new file mode 100755 index 0000000..bda73e4 --- /dev/null +++ b/scripts/sync-certificates.sh @@ -0,0 +1,204 @@ +#!/usr/bin/env bash + +# Certificate Sync Script for HAProxy Manager +# This script syncs all Let's Encrypt certificates to HAProxy format +# Use this to update all certificates regardless of renewal status + +set -e + +# Configuration +LOG_FILE="${LOG_FILE:-/var/log/haproxy-manager.log}" +ERROR_LOG_FILE="${ERROR_LOG_FILE:-/var/log/haproxy-manager-errors.log}" +HAPROXY_SOCKET="${HAPROXY_SOCKET:-/tmp/haproxy-cli}" +DB_FILE="${DB_FILE:-/etc/haproxy/haproxy_config.db}" +SSL_CERTS_DIR="${SSL_CERTS_DIR:-/etc/haproxy/certs}" +MAX_RETRIES=3 +RETRY_DELAY=5 + +# Logging functions +log_info() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*" | tee -a "$LOG_FILE" +} + +log_error() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" | tee -a "$LOG_FILE" >> "$ERROR_LOG_FILE" +} + +log_warning() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] [WARNING] $*" | tee -a "$LOG_FILE" +} + +# Check if HAProxy socket exists and is accessible +check_haproxy_socket() { + if [ ! -S "$HAPROXY_SOCKET" ]; then + log_warning "HAProxy socket not found at $HAPROXY_SOCKET" + return 1 + fi + + # Test socket connectivity + if ! echo "show info" | socat stdio "$HAPROXY_SOCKET" &> /dev/null; then + log_warning "HAProxy socket exists but is not responding" + return 1 + fi + + return 0 +} + +# Reload HAProxy configuration +reload_haproxy() { + local retry_count=0 + + while [ $retry_count -lt $MAX_RETRIES ]; do + if check_haproxy_socket; then + log_info "Reloading HAProxy via socket" + if echo "reload" | socat stdio "$HAPROXY_SOCKET"; then + log_info "HAProxy reloaded successfully" + return 0 + else + log_warning "HAProxy reload command failed (attempt $((retry_count + 1))/$MAX_RETRIES)" + fi + else + log_warning "HAProxy socket check failed (attempt $((retry_count + 1))/$MAX_RETRIES)" + fi + + retry_count=$((retry_count + 1)) + if [ $retry_count -lt $MAX_RETRIES ]; then + sleep $RETRY_DELAY + fi + done + + log_error "Failed to reload HAProxy after $MAX_RETRIES attempts" + return 1 +} + +# Sync all certificate files for HAProxy +sync_all_certificates() { + log_info "Syncing all certificate files to HAProxy format" + + # Check if database exists + if [ ! -f "$DB_FILE" ]; then + log_error "Database file not found at $DB_FILE" + return 1 + fi + + # Check if sqlite3 is available + if ! command -v sqlite3 &> /dev/null; then + log_error "sqlite3 command not found" + return 1 + fi + + # Ensure SSL certs directory exists + mkdir -p "$SSL_CERTS_DIR" + + # Get all domains with SSL enabled from database + local domains + domains=$(sqlite3 "$DB_FILE" "SELECT domain, ssl_cert_path FROM domains WHERE ssl_enabled = 1;" 2>/dev/null) + + if [ -z "$domains" ]; then + log_info "No SSL-enabled domains found in database" + return 0 + fi + + local updated_count=0 + local error_count=0 + local skipped_count=0 + + # Process each domain + while IFS='|' read -r domain cert_path; do + if [ -z "$domain" ] || [ -z "$cert_path" ]; then + continue + fi + + log_info "Processing certificate for domain: $domain" + + local letsencrypt_cert="/etc/letsencrypt/live/${domain}/fullchain.pem" + local letsencrypt_key="/etc/letsencrypt/live/${domain}/privkey.pem" + + # Check if Let's Encrypt certificate files exist + if [ ! -f "$letsencrypt_cert" ]; then + log_warning "Certificate not found for $domain at $letsencrypt_cert" + skipped_count=$((skipped_count + 1)) + continue + fi + + if [ ! -f "$letsencrypt_key" ]; then + log_warning "Private key not found for $domain at $letsencrypt_key" + skipped_count=$((skipped_count + 1)) + continue + fi + + # Get modification times to check if update is needed + local needs_update=false + if [ ! -f "$cert_path" ]; then + log_info "Combined certificate does not exist for $domain, creating it" + needs_update=true + else + # Check if source files are newer than the combined file + if [ "$letsencrypt_cert" -nt "$cert_path" ] || [ "$letsencrypt_key" -nt "$cert_path" ]; then + log_info "Let's Encrypt certificate is newer than combined file for $domain" + needs_update=true + else + log_info "Certificate for $domain is already up to date" + fi + fi + + # Combine certificate and key into single file for HAProxy + if [ "$needs_update" = true ]; then + if cat "$letsencrypt_cert" "$letsencrypt_key" > "$cert_path"; then + log_info "Updated combined certificate for $domain at $cert_path" + updated_count=$((updated_count + 1)) + else + log_error "Failed to combine certificate files for $domain" + error_count=$((error_count + 1)) + fi + fi + done <<< "$domains" + + log_info "Certificate sync completed: $updated_count updated, $skipped_count skipped, $error_count errors" + + if [ $error_count -gt 0 ]; then + return 1 + fi + + # Return success if we updated any certificates + if [ $updated_count -gt 0 ]; then + return 0 + fi + + # Return special code (2) if nothing needed updating + return 2 +} + +# Main sync process +log_info "Starting certificate sync process" + +if sync_all_certificates; then + SYNC_RESULT=$? + + if [ $SYNC_RESULT -eq 0 ]; then + log_info "Certificates were updated, reloading HAProxy" + if reload_haproxy; then + log_info "Certificate sync and HAProxy reload completed successfully" + exit 0 + else + log_error "Certificate sync succeeded but HAProxy reload failed" + exit 1 + fi + elif [ $SYNC_RESULT -eq 2 ]; then + log_info "All certificates are already up to date, no reload needed" + exit 0 + fi +else + SYNC_RESULT=$? + + if [ $SYNC_RESULT -eq 2 ]; then + log_info "All certificates are already up to date, no reload needed" + exit 0 + else + log_error "Certificate sync failed" + exit 1 + fi +fi + +log_info "Certificate sync process completed" +exit 0