#!/bin/bash # ============================================================================== # Script Name: ssl_remover.sh # Description: Removes SSL certificates and cleans up LiteSpeed configurations. # Ensures safe removal of listeners, virtual hosts, and certificates. # Version: 2.0.0 (Professional-Grade Optimized) # Author: Gemini (Based on user request and feedback) # Date: 2025-03-26 # Exit Codes: # 0: Success (SSL removed and configuration cleaned up) # 1: General Error (e.g., invalid parameters, XML validation failed) # 2: Backup/Restore Error (e.g., failed to create backup) # 3: Restart Error (e.g., LiteSpeed service failed to restart) # ============================================================================== set -euo pipefail # === Configuration === CONF_FILE="/var/www/conf/httpd_config.xml" BACKUP_DIR="/var/www/conf/backups" LOG_DIR="/var/log/mb-ssl" CERT_DIR="/etc/letsencrypt/live" SCRIPT_LOG="${LOG_DIR}/ssl-remover.log" VERBOSE=0 # === Functions === log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$SCRIPT_LOG" } log_verbose() { [[ "$VERBOSE" -eq 1 ]] && log "[VERBOSE] $1" } send_email() { local subject="$1" local body="$2" local recipient="${EMAIL:-}" if [[ -n "$recipient" ]]; then log "Sending email notification to $recipient..." curl -s "https://api.postmarkapp.com/email" \ -X POST \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "X-Postmark-Server-Token: d88b25c4-2fdb-43d3-9097-f6c655a9742b" \ -d "{ \"From\": \"admin@mightybox.io\", \"To\": \"$recipient\", \"Subject\": \"$subject\", \"HtmlBody\": \"$body\", \"MessageStream\": \"outbound\" }" > /dev/null && log "Email sent." || log "Email failed." fi } validate_domain() { local domain="$1" if [[ "$domain" =~ ^([a-zA-Z0-9](-*[a-zA-Z0-9])*\.)+[a-zA-Z]{2,}$ ]]; then return 0 else return 1 fi } backup_config() { local timestamp=$(date +%Y%m%d%H%M%S) local backup_file="${BACKUP_DIR}/httpd_config.pre-removal-${timestamp}.xml" mkdir -p "$BACKUP_DIR" if cp "$CONF_FILE" "$backup_file"; then log "Config backup saved to $backup_file" else log "❌ ERROR: Failed to create backup '$backup_file'. Exiting." exit 2 fi } remove_certificate() { local domain="$1" log "Checking for certificate for domain '$domain'..." if certbot certificates | grep -q "Domains: $domain"; then log "Removing certificate for '$domain'..." if certbot delete --cert-name "$domain" --non-interactive; then rm -rf "/etc/letsencrypt/live/$domain"* log "Certificate successfully removed for '$domain'." else log "❌ ERROR: Failed to remove certificate for '$domain'." return 1 fi else log "No certificate found for '$domain'. Skipping removal." fi } cleanup_listeners() { local domain="$1" log "Cleaning up listeners and configurations for '$domain'..." # Remove listener for the domain sudo xmlstarlet ed -L \ -d "//listener[name='HTTPS-$domain']" \ "$CONF_FILE" # Remove vhostMap entries sudo xmlstarlet ed -L \ -d "//vhostMap[domain='$domain']" \ "$CONF_FILE" # Remove related virtual host local vhost_name="${domain//./_}" sudo xmlstarlet ed -L \ -d "//virtualHost[name='$vhost_name']" \ "$CONF_FILE" # Cleanup empty listenerList tags sudo xmlstarlet ed -L \ -d "//listenerList[count(*)=0]" \ "$CONF_FILE" } validate_xml() { log "Validating XML configuration..." if ! sudo xmllint --noout "$CONF_FILE" 2>/dev/null; then log "❌ ERROR: Invalid XML configuration after cleanup. Check backups." return 1 fi log "✔ XML configuration is valid." return 0 } restart_litespeed() { log "Restarting LiteSpeed server..." if sudo systemctl restart lsws; then log "✔ LiteSpeed server restarted successfully." else log "❌ ERROR: Failed to restart LiteSpeed server." return 3 fi } # === Main Script Logic === main() { declare -a DOMAINS EMAIL="" # Parse parameters while [[ $# -gt 0 ]]; do case "$1" in --domains=*) IFS=',' read -ra DOMAINS <<< "${1#*=}" ;; --email=*) EMAIL="${1#*=}" ;; --verbose) VERBOSE=1 log "Verbose mode enabled." ;; *) log "Invalid parameter: $1" exit 1 ;; esac shift done # Validate input if [[ ${#DOMAINS[@]} -eq 0 ]]; then log "❌ ERROR: --domains parameter is required." exit 1 fi # Ensure log directory exists mkdir -p "$LOG_DIR" || { log "❌ ERROR: Cannot create log directory '$LOG_DIR'. Check permissions."; exit 1; } touch "$SCRIPT_LOG" chmod 0644 "$SCRIPT_LOG" # Backup configuration backup_config # Process each domain for domain in "${DOMAINS[@]}"; do log "Processing domain: $domain" # Validate domain format if ! validate_domain "$domain"; then log "❌ ERROR: Invalid domain '$domain'. Skipping." continue fi # Remove certificate remove_certificate "$domain" # Clean up listeners and configurations cleanup_listeners "$domain" done # Validate XML configuration if validate_xml; then restart_litespeed send_email "SSL Removal Complete" "Successfully removed SSL for domains: ${DOMAINS[*]}" else send_email "SSL Removal Warning" "SSL removed but configuration validation failed for domains: ${DOMAINS[*]}" exit 1 fi } # === Entry Point === main "$@"