Simplify certificate renewal scripts and add certbot cleanup
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 59s
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 59s
Simplified all certificate renewal scripts to be more straightforward and reliable: - Scripts now just run certbot renew and copy cert+key files to HAProxy format - Removed overly complex retry logic and error handling - Both in-container and host-side scripts work with cron scheduling Added automatic certbot cleanup when domains are removed: - When a domain is deleted via API, certbot certificate is also removed - Prevents renewal errors for domains that no longer exist in HAProxy - Cleans up both HAProxy combined cert and Let's Encrypt certificate Script changes: - renew-certificates.sh: Simplified to 87 lines (from 215) - sync-certificates.sh: Simplified to 79 lines (from 200+) - host-renew-certificates.sh: Simplified to 36 lines (from 40) - All scripts use same pattern: query DB, copy certs, reload HAProxy Python changes: - remove_domain() now calls 'certbot delete' to remove certificates - Prevents orphaned certificates from causing renewal failures 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -682,15 +682,15 @@ def remove_domain():
|
|||||||
with sqlite3.connect(DB_FILE) as conn:
|
with sqlite3.connect(DB_FILE) as conn:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
# Get domain ID and check if it exists
|
# Get domain ID and SSL status
|
||||||
cursor.execute('SELECT id FROM domains WHERE domain = ?', (domain,))
|
cursor.execute('SELECT id, ssl_enabled, ssl_cert_path FROM domains WHERE domain = ?', (domain,))
|
||||||
domain_result = cursor.fetchone()
|
domain_result = cursor.fetchone()
|
||||||
|
|
||||||
if not domain_result:
|
if not domain_result:
|
||||||
log_operation('remove_domain', False, f'Domain {domain} not found')
|
log_operation('remove_domain', False, f'Domain {domain} not found')
|
||||||
return jsonify({'status': 'error', 'message': 'Domain not found'}), 404
|
return jsonify({'status': 'error', 'message': 'Domain not found'}), 404
|
||||||
|
|
||||||
domain_id = domain_result[0]
|
domain_id, ssl_enabled, ssl_cert_path = domain_result
|
||||||
|
|
||||||
# Get backend IDs associated with this domain
|
# Get backend IDs associated with this domain
|
||||||
cursor.execute('SELECT id FROM backends WHERE domain_id = ?', (domain_id,))
|
cursor.execute('SELECT id FROM backends WHERE domain_id = ?', (domain_id,))
|
||||||
@@ -706,14 +706,27 @@ def remove_domain():
|
|||||||
# Delete domain
|
# Delete domain
|
||||||
cursor.execute('DELETE FROM domains WHERE id = ?', (domain_id,))
|
cursor.execute('DELETE FROM domains WHERE id = ?', (domain_id,))
|
||||||
|
|
||||||
# Delete SSL certificate if it exists
|
# Delete SSL certificate from HAProxy certs directory
|
||||||
cursor.execute('SELECT ssl_cert_path FROM domains WHERE id = ? AND ssl_enabled = 1', (domain_id,))
|
if ssl_enabled and ssl_cert_path:
|
||||||
cert_result = cursor.fetchone()
|
|
||||||
if cert_result and cert_result[0]:
|
|
||||||
try:
|
try:
|
||||||
os.remove(cert_result[0])
|
os.remove(ssl_cert_path)
|
||||||
except OSError:
|
logger.info(f"Removed HAProxy certificate file: {ssl_cert_path}")
|
||||||
pass # Ignore errors if file doesn't exist
|
except OSError as e:
|
||||||
|
logger.warning(f"Failed to remove certificate file {ssl_cert_path}: {e}")
|
||||||
|
|
||||||
|
# Remove certificate from certbot
|
||||||
|
if ssl_enabled:
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['certbot', 'delete', '--cert-name', domain, '--non-interactive'],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
logger.info(f"Removed Let's Encrypt certificate for {domain}")
|
||||||
|
else:
|
||||||
|
logger.warning(f"Failed to remove Let's Encrypt certificate for {domain}: {result.stderr}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Error removing Let's Encrypt certificate for {domain}: {e}")
|
||||||
|
|
||||||
# Regenerate HAProxy config
|
# Regenerate HAProxy config
|
||||||
generate_config()
|
generate_config()
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Host-side Certificate Renewal Script
|
# Host-side Certificate Renewal Script
|
||||||
# This script can be run from the host machine via cron to trigger certificate renewal
|
# Run this from the host machine via cron to trigger certificate renewal inside the container
|
||||||
# inside the HAProxy Manager container using docker exec
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Configuration - Customize these values
|
# Configuration
|
||||||
CONTAINER_NAME="${CONTAINER_NAME:-haproxy-manager}"
|
CONTAINER_NAME="${CONTAINER_NAME:-haproxy-manager}"
|
||||||
LOG_FILE="${LOG_FILE:-/var/log/haproxy-manager-host-renewal.log}"
|
LOG_FILE="${LOG_FILE:-/var/log/haproxy-manager-host-renewal.log}"
|
||||||
|
|
||||||
# Logging functions
|
# Logging
|
||||||
log_info() {
|
log_info() {
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*" | tee -a "$LOG_FILE"
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*" | tee -a "$LOG_FILE"
|
||||||
}
|
}
|
||||||
@@ -19,8 +18,7 @@ log_error() {
|
|||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" | tee -a "$LOG_FILE"
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" | tee -a "$LOG_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Main execution
|
log_info "Starting certificate renewal"
|
||||||
log_info "Starting host-side certificate renewal process"
|
|
||||||
|
|
||||||
# Check if container is running
|
# Check if container is running
|
||||||
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||||
@@ -28,10 +26,9 @@ if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Execute renewal script inside container
|
# Run renewal script inside container
|
||||||
log_info "Executing renewal script in container '${CONTAINER_NAME}'"
|
|
||||||
if docker exec "$CONTAINER_NAME" /haproxy/scripts/renew-certificates.sh; then
|
if docker exec "$CONTAINER_NAME" /haproxy/scripts/renew-certificates.sh; then
|
||||||
log_info "Certificate renewal completed successfully"
|
log_info "Certificate renewal completed"
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
log_error "Certificate renewal failed"
|
log_error "Certificate renewal failed"
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Certificate Renewal Script for HAProxy Manager
|
# Certificate Renewal Script for HAProxy Manager
|
||||||
# This script handles Let's Encrypt certificate renewal with proper logging and error handling
|
# This script runs certbot renew and copies certificates to HAProxy format
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
LOG_FILE="${LOG_FILE:-/var/log/haproxy-manager.log}"
|
LOG_FILE="${LOG_FILE:-/var/log/haproxy-manager.log}"
|
||||||
ERROR_LOG_FILE="${ERROR_LOG_FILE:-/var/log/haproxy-manager-errors.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}"
|
DB_FILE="${DB_FILE:-/etc/haproxy/haproxy_config.db}"
|
||||||
SSL_CERTS_DIR="${SSL_CERTS_DIR:-/etc/haproxy/certs}"
|
SSL_CERTS_DIR="${SSL_CERTS_DIR:-/etc/haproxy/certs}"
|
||||||
MAX_RETRIES=3
|
|
||||||
RETRY_DELAY=5
|
|
||||||
|
|
||||||
# Logging functions
|
# Logging functions
|
||||||
log_info() {
|
log_info() {
|
||||||
@@ -23,192 +20,67 @@ log_error() {
|
|||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" | tee -a "$LOG_FILE" >> "$ERROR_LOG_FILE"
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" | tee -a "$LOG_FILE" >> "$ERROR_LOG_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
log_warning() {
|
log_info "Starting certificate renewal process"
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [WARNING] $*" | tee -a "$LOG_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if certbot is available
|
# Run certbot renewal
|
||||||
if ! command -v certbot &> /dev/null; then
|
if certbot renew --quiet --no-random-sleep-on-renew; then
|
||||||
log_error "certbot command not found"
|
log_info "Certbot renewal completed"
|
||||||
|
else
|
||||||
|
log_error "Certbot renewal failed with exit code $?"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if HAProxy socket exists and is accessible
|
# Copy all certificates to HAProxy format
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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
|
if [ ! -f "$DB_FILE" ]; then
|
||||||
log_error "Database file not found at $DB_FILE"
|
log_error "Database file not found at $DB_FILE"
|
||||||
return 1
|
exit 1
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if sqlite3 is available
|
|
||||||
if ! command -v sqlite3 &> /dev/null; then
|
|
||||||
log_error "sqlite3 command not found"
|
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure SSL certs directory exists
|
# Ensure SSL certs directory exists
|
||||||
mkdir -p "$SSL_CERTS_DIR"
|
mkdir -p "$SSL_CERTS_DIR"
|
||||||
|
|
||||||
# Get all domains with SSL enabled from database
|
# Get all SSL-enabled domains from database
|
||||||
local domains
|
DOMAINS=$(sqlite3 "$DB_FILE" "SELECT domain FROM domains WHERE ssl_enabled = 1;" 2>/dev/null)
|
||||||
domains=$(sqlite3 "$DB_FILE" "SELECT domain, ssl_cert_path FROM domains WHERE ssl_enabled = 1;" 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -z "$domains" ]; then
|
if [ -z "$DOMAINS" ]; then
|
||||||
log_info "No SSL-enabled domains found in database"
|
log_info "No SSL-enabled domains found"
|
||||||
return 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local updated_count=0
|
# Copy certificates for each domain
|
||||||
local error_count=0
|
UPDATED=0
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
# Process each domain
|
while read -r domain; do
|
||||||
while IFS='|' read -r domain cert_path; do
|
CERT_FILE="/etc/letsencrypt/live/${domain}/fullchain.pem"
|
||||||
if [ -z "$domain" ] || [ -z "$cert_path" ]; then
|
KEY_FILE="/etc/letsencrypt/live/${domain}/privkey.pem"
|
||||||
continue
|
COMBINED_FILE="${SSL_CERTS_DIR}/${domain}.pem"
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Processing certificate for domain: $domain"
|
if [ -f "$CERT_FILE" ] && [ -f "$KEY_FILE" ]; then
|
||||||
|
# Combine cert and key into single file for HAProxy
|
||||||
local letsencrypt_cert="/etc/letsencrypt/live/${domain}/fullchain.pem"
|
if cat "$CERT_FILE" "$KEY_FILE" > "$COMBINED_FILE"; then
|
||||||
local letsencrypt_key="/etc/letsencrypt/live/${domain}/privkey.pem"
|
log_info "Updated certificate for $domain"
|
||||||
|
UPDATED=$((UPDATED + 1))
|
||||||
# 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
|
|
||||||
# HAProxy requires fullchain.pem followed by privkey.pem in a single file
|
|
||||||
# Write to temp file first, then move to ensure atomic update
|
|
||||||
local temp_cert="${cert_path}.tmp"
|
|
||||||
if cat "$letsencrypt_cert" "$letsencrypt_key" > "$temp_cert"; then
|
|
||||||
# Verify the combined file is not empty and contains valid data
|
|
||||||
if [ -s "$temp_cert" ]; then
|
|
||||||
# Atomically move to final location
|
|
||||||
if mv "$temp_cert" "$cert_path"; then
|
|
||||||
log_info "Updated combined certificate for $domain at $cert_path"
|
|
||||||
updated_count=$((updated_count + 1))
|
|
||||||
else
|
else
|
||||||
log_error "Failed to move combined certificate for $domain to $cert_path"
|
log_error "Failed to combine certificate for $domain"
|
||||||
rm -f "$temp_cert"
|
FAILED=$((FAILED + 1))
|
||||||
error_count=$((error_count + 1))
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
log_error "Combined certificate file for $domain is empty"
|
log_error "Certificate files not found for $domain"
|
||||||
rm -f "$temp_cert"
|
FAILED=$((FAILED + 1))
|
||||||
error_count=$((error_count + 1))
|
|
||||||
fi
|
fi
|
||||||
|
done <<< "$DOMAINS"
|
||||||
|
|
||||||
|
log_info "Certificate update completed: $UPDATED updated, $FAILED failed"
|
||||||
|
|
||||||
|
# Reload HAProxy if any certificates were updated
|
||||||
|
if [ $UPDATED -gt 0 ]; then
|
||||||
|
if echo "reload" | socat stdio /tmp/haproxy-cli 2>/dev/null; then
|
||||||
|
log_info "HAProxy reloaded successfully"
|
||||||
else
|
else
|
||||||
log_error "Failed to combine certificate files for $domain"
|
log_error "Failed to reload HAProxy"
|
||||||
rm -f "$temp_cert"
|
|
||||||
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"
|
|
||||||
|
|
||||||
# Run certbot renewal
|
|
||||||
if certbot renew --quiet --no-random-sleep-on-renew 2>&1 | tee -a "$LOG_FILE"; then
|
|
||||||
RENEWAL_EXIT_CODE=${PIPESTATUS[0]}
|
|
||||||
|
|
||||||
if [ $RENEWAL_EXIT_CODE -eq 0 ]; then
|
|
||||||
log_info "Certificate renewal completed successfully"
|
|
||||||
|
|
||||||
# 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
|
exit 1
|
||||||
fi
|
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"
|
|
||||||
exit $RENEWAL_EXIT_CODE
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log_error "Certificate renewal command failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_info "Certificate renewal process completed"
|
log_info "Certificate renewal process completed"
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Certificate Sync Script for HAProxy Manager
|
# Certificate Sync Script for HAProxy Manager
|
||||||
# This script syncs all Let's Encrypt certificates to HAProxy format
|
# This script syncs all Let's Encrypt certificates to HAProxy format without running certbot renew
|
||||||
# Use this to update all certificates regardless of renewal status
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
LOG_FILE="${LOG_FILE:-/var/log/haproxy-manager.log}"
|
LOG_FILE="${LOG_FILE:-/var/log/haproxy-manager.log}"
|
||||||
ERROR_LOG_FILE="${ERROR_LOG_FILE:-/var/log/haproxy-manager-errors.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}"
|
DB_FILE="${DB_FILE:-/etc/haproxy/haproxy_config.db}"
|
||||||
SSL_CERTS_DIR="${SSL_CERTS_DIR:-/etc/haproxy/certs}"
|
SSL_CERTS_DIR="${SSL_CERTS_DIR:-/etc/haproxy/certs}"
|
||||||
MAX_RETRIES=3
|
|
||||||
RETRY_DELAY=5
|
|
||||||
|
|
||||||
# Logging functions
|
# Logging functions
|
||||||
log_info() {
|
log_info() {
|
||||||
@@ -24,178 +20,57 @@ log_error() {
|
|||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" | tee -a "$LOG_FILE" >> "$ERROR_LOG_FILE"
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" | tee -a "$LOG_FILE" >> "$ERROR_LOG_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
log_warning() {
|
log_info "Starting certificate sync process"
|
||||||
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
|
# Check if database exists
|
||||||
if [ ! -f "$DB_FILE" ]; then
|
if [ ! -f "$DB_FILE" ]; then
|
||||||
log_error "Database file not found at $DB_FILE"
|
log_error "Database file not found at $DB_FILE"
|
||||||
return 1
|
exit 1
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if sqlite3 is available
|
|
||||||
if ! command -v sqlite3 &> /dev/null; then
|
|
||||||
log_error "sqlite3 command not found"
|
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure SSL certs directory exists
|
# Ensure SSL certs directory exists
|
||||||
mkdir -p "$SSL_CERTS_DIR"
|
mkdir -p "$SSL_CERTS_DIR"
|
||||||
|
|
||||||
# Get all domains with SSL enabled from database
|
# Get all SSL-enabled domains from database
|
||||||
local domains
|
DOMAINS=$(sqlite3 "$DB_FILE" "SELECT domain FROM domains WHERE ssl_enabled = 1;" 2>/dev/null)
|
||||||
domains=$(sqlite3 "$DB_FILE" "SELECT domain, ssl_cert_path FROM domains WHERE ssl_enabled = 1;" 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -z "$domains" ]; then
|
if [ -z "$DOMAINS" ]; then
|
||||||
log_info "No SSL-enabled domains found in database"
|
log_info "No SSL-enabled domains found"
|
||||||
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
|
exit 0
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
SYNC_RESULT=$?
|
|
||||||
|
|
||||||
if [ $SYNC_RESULT -eq 2 ]; then
|
# Copy certificates for each domain
|
||||||
log_info "All certificates are already up to date, no reload needed"
|
UPDATED=0
|
||||||
exit 0
|
FAILED=0
|
||||||
|
|
||||||
|
while read -r domain; do
|
||||||
|
CERT_FILE="/etc/letsencrypt/live/${domain}/fullchain.pem"
|
||||||
|
KEY_FILE="/etc/letsencrypt/live/${domain}/privkey.pem"
|
||||||
|
COMBINED_FILE="${SSL_CERTS_DIR}/${domain}.pem"
|
||||||
|
|
||||||
|
if [ -f "$CERT_FILE" ] && [ -f "$KEY_FILE" ]; then
|
||||||
|
# Combine cert and key into single file for HAProxy
|
||||||
|
if cat "$CERT_FILE" "$KEY_FILE" > "$COMBINED_FILE"; then
|
||||||
|
log_info "Updated certificate for $domain"
|
||||||
|
UPDATED=$((UPDATED + 1))
|
||||||
else
|
else
|
||||||
log_error "Certificate sync failed"
|
log_error "Failed to combine certificate for $domain"
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_error "Certificate files not found for $domain"
|
||||||
|
FAILED=$((FAILED + 1))
|
||||||
|
fi
|
||||||
|
done <<< "$DOMAINS"
|
||||||
|
|
||||||
|
log_info "Certificate sync completed: $UPDATED updated, $FAILED failed"
|
||||||
|
|
||||||
|
# Reload HAProxy if any certificates were updated
|
||||||
|
if [ $UPDATED -gt 0 ]; then
|
||||||
|
if echo "reload" | socat stdio /tmp/haproxy-cli 2>/dev/null; then
|
||||||
|
log_info "HAProxy reloaded successfully"
|
||||||
|
else
|
||||||
|
log_error "Failed to reload HAProxy"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user