292 lines
9.2 KiB
Bash
292 lines
9.2 KiB
Bash
#!/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"
|
|
|
|
# Auto-configure nginx with SSL if setup script exists
|
|
local ssl_setup_script="$(dirname "$0")/nginx-ssl-setup.sh"
|
|
if [[ -f "$ssl_setup_script" ]] && [[ ! -f "/etc/nginx/sites-available/$DOMAIN" ]]; then
|
|
log "SSL configuration not found - setting up nginx SSL configuration..."
|
|
if bash "$ssl_setup_script" setup; then
|
|
log "✅ Nginx SSL configuration completed automatically"
|
|
else
|
|
log "⚠️ Nginx SSL auto-configuration failed - please run manually:"
|
|
log " sudo $ssl_setup_script"
|
|
fi
|
|
fi
|
|
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 "$@"
|