#!/bin/bash # SSL Certificate Management Script for dev.uggla.uamils.com # This script manages Let's Encrypt certificates for use with external nginx set -e # Configuration DOMAIN="${DOMAIN:-dev.uggla.uamils.com}" EMAIL="${EMAIL:-admin@uggla.uamils.com}" RENEWAL_DAYS="${RENEWAL_DAYS:-10}" CERT_DIR="/etc/letsencrypt" WEBROOT_PATH="/var/www/html" LOG_FILE="/var/log/letsencrypt/renewal.log" # Loopia API credentials (optional, for DNS challenge) LOOPIA_USER="${LOOPIA_USER:-}" LOOPIA_PASSWORD="${LOOPIA_PASSWORD:-}" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } check_dependencies() { local missing_deps=() if ! command -v certbot >/dev/null 2>&1; then missing_deps+=("certbot") fi if ! command -v nginx >/dev/null 2>&1; then missing_deps+=("nginx") fi if ! command -v openssl >/dev/null 2>&1; then missing_deps+=("openssl") fi if [[ ${#missing_deps[@]} -gt 0 ]]; then log "ERROR: Missing dependencies: ${missing_deps[*]}" log "Please install missing packages and try again" exit 1 fi } check_certificate_expiry() { local cert_path="$CERT_DIR/live/$DOMAIN/cert.pem" if [[ ! -f "$cert_path" ]]; then log "Certificate not found at $cert_path - will attempt to obtain new certificate" return 1 fi # Get certificate expiry date local expiry_date=$(openssl x509 -in "$cert_path" -noout -enddate | cut -d= -f2) local expiry_epoch=$(date -d "$expiry_date" +%s) local current_epoch=$(date +%s) local days_until_expiry=$(( (expiry_epoch - current_epoch) / 86400 )) log "Certificate expires in $days_until_expiry days" if [[ $days_until_expiry -le $RENEWAL_DAYS ]]; then log "Certificate expires in $days_until_expiry days - renewal needed" return 1 else log "Certificate is valid for $days_until_expiry more days - no renewal needed" return 0 fi } attempt_dns_challenge() { log "Attempting DNS challenge..." if [[ -z "$LOOPIA_USER" || -z "$LOOPIA_PASSWORD" ]]; then log "LOOPIA_USER or LOOPIA_PASSWORD not set - skipping DNS challenge" return 1 fi # Check if dns-lexicon is available if command -v lexicon >/dev/null 2>&1; then log "Using dns-lexicon for DNS challenge..." certbot certonly \ --dns-lexicon \ --dns-lexicon-provider loopia \ --dns-lexicon-loopia-username="$LOOPIA_USER" \ --dns-lexicon-loopia-password="$LOOPIA_PASSWORD" \ --email "$EMAIL" \ --agree-tos \ --non-interactive \ --expand \ -d "$DOMAIN" \ -d "*.$DOMAIN" return $? fi # Fallback: Manual DNS with custom hook if [[ -f "$(dirname "$0")/loopia-hook.sh" ]]; then log "Using custom Loopia hook for DNS challenge..." LOOPIA_USER="$LOOPIA_USER" LOOPIA_PASSWORD="$LOOPIA_PASSWORD" \ certbot certonly \ --manual \ --preferred-challenges dns \ --manual-auth-hook "$(dirname "$0")/loopia-hook.sh auth" \ --manual-cleanup-hook "$(dirname "$0")/loopia-hook.sh cleanup" \ --email "$EMAIL" \ --agree-tos \ --non-interactive \ --expand \ -d "$DOMAIN" \ -d "*.$DOMAIN" return $? fi log "No DNS challenge method available" return 1 } attempt_http_challenge() { log "Attempting HTTP challenge..." # Ensure webroot directory exists mkdir -p "$WEBROOT_PATH" # Check if nginx is configured for ACME challenges if ! nginx -t 2>/dev/null; then log "WARNING: nginx configuration test failed" fi certbot certonly \ --webroot \ --webroot-path="$WEBROOT_PATH" \ --email "$EMAIL" \ --agree-tos \ --non-interactive \ --expand \ -d "$DOMAIN" return $? } reload_nginx() { log "Reloading nginx configuration..." # Test nginx configuration first if nginx -t; then systemctl reload nginx || service nginx reload || nginx -s reload log "Nginx reloaded successfully" else log "ERROR: nginx configuration test failed - not reloading" return 1 fi } show_certificate_info() { local cert_path="$CERT_DIR/live/$DOMAIN/cert.pem" if [[ ! -f "$cert_path" ]]; then log "No certificate found for $DOMAIN" return 1 fi log "Certificate Information:" log "========================" # Get certificate details local subject=$(openssl x509 -in "$cert_path" -noout -subject | sed 's/subject=//') local issuer=$(openssl x509 -in "$cert_path" -noout -issuer | sed 's/issuer=//') local expiry_date=$(openssl x509 -in "$cert_path" -noout -enddate | cut -d= -f2) local san=$(openssl x509 -in "$cert_path" -noout -text | grep -A1 "Subject Alternative Name" | tail -1 | sed 's/^[[:space:]]*//') # Calculate days until expiry local expiry_epoch=$(date -d "$expiry_date" +%s) local current_epoch=$(date +%s) local days_until_expiry=$(( (expiry_epoch - current_epoch) / 86400 )) log " Domain: $DOMAIN" log " Subject: $subject" log " Issuer: $issuer" log " SAN: $san" log " Expires: $expiry_date" log " Days until expiry: $days_until_expiry" if [[ $days_until_expiry -le $RENEWAL_DAYS ]]; then log " STATUS: ⚠️ Certificate expires soon - renewal recommended" elif [[ $days_until_expiry -le 30 ]]; then log " STATUS: ⚡ Certificate expires in less than 30 days" else log " STATUS: ✅ Certificate is valid" fi } main() { # Create log directory mkdir -p "$(dirname "$LOG_FILE")" log "Starting SSL certificate management for $DOMAIN" # Check dependencies check_dependencies case "${1:-check}" in "check") log "Checking certificate status..." if check_certificate_expiry; then log "Certificate is valid - no action needed" show_certificate_info exit 0 else log "Certificate renewal required" exit 1 fi ;; "renew") log "Forcing certificate renewal..." # Try DNS challenge first (for wildcard support) if attempt_dns_challenge; then log "DNS challenge successful!" reload_nginx show_certificate_info log "Certificate renewal completed successfully" exit 0 fi # Fallback to HTTP challenge log "DNS challenge failed - falling back to HTTP challenge" if attempt_http_challenge; then log "HTTP challenge successful!" reload_nginx show_certificate_info log "Certificate renewal completed successfully" exit 0 fi log "ERROR: All certificate renewal attempts failed" exit 1 ;; "status"|"info") show_certificate_info ;; "auto") log "Auto-renewal check (runs only if needed)..." if check_certificate_expiry; then log "Certificate is valid - no renewal needed" exit 0 else log "Certificate needs renewal - attempting automatic renewal" exec "$0" renew fi ;; *) echo "SSL Certificate Management for $DOMAIN" echo "======================================" echo "" echo "Usage: $0 [command]" echo "" echo "Commands:" echo " check Check if certificate needs renewal" echo " renew Force certificate renewal" echo " status Show certificate information" echo " auto Auto-renew only if needed (for cron)" echo "" echo "Environment variables:" echo " DOMAIN Domain name (default: $DOMAIN)" echo " EMAIL Email for Let's Encrypt (default: $EMAIL)" echo " RENEWAL_DAYS Days before expiry to renew (default: $RENEWAL_DAYS)" echo " LOOPIA_USER Loopia username for DNS challenge" echo " LOOPIA_PASSWORD Loopia password for DNS challenge" exit 0 ;; esac } # Run main function main "$@"