Optimized SSL Remover Shell SCript

main
Anthony 2025-03-27 22:42:08 +08:00
parent 5653d318ba
commit a0feb8d70d
1 changed files with 127 additions and 77 deletions

View File

@ -1,25 +1,42 @@
#!/bin/bash #!/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 set -euo pipefail
# Log file setup # === Configuration ===
CONF_FILE="/var/www/conf/httpd_config.xml"
BACKUP_DIR="/var/www/conf/backups"
LOG_DIR="/var/log/mb-ssl" LOG_DIR="/var/log/mb-ssl"
LOG_FILE="$LOG_DIR/ssl-remover.log" CERT_DIR="/etc/letsencrypt/live"
mkdir -p "$LOG_DIR" SCRIPT_LOG="${LOG_DIR}/ssl-remover.log"
chmod 0755 "$LOG_DIR" VERBOSE=0
exec > >(tee -a "$LOG_FILE") 2>&1
# Function to log messages # === Functions ===
log() { log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$SCRIPT_LOG"
}
log_verbose() {
[[ "$VERBOSE" -eq 1 ]] && log "[VERBOSE] $1"
} }
# Email function (same as in ssl_manager.sh)
send_email() { send_email() {
local subject="$1" local subject="$1"
local body="$2" local body="$2"
local recipient="${EMAIL:-}" local recipient="${EMAIL:-}"
[[ -n "$recipient" ]] && { if [[ -n "$recipient" ]]; then
log "Sending email notification to $recipient..." log "Sending email notification to $recipient..."
curl -s "https://api.postmarkapp.com/email" \ curl -s "https://api.postmarkapp.com/email" \
-X POST \ -X POST \
@ -33,121 +50,153 @@ send_email() {
\"HtmlBody\": \"$body\", \"HtmlBody\": \"$body\",
\"MessageStream\": \"outbound\" \"MessageStream\": \"outbound\"
}" > /dev/null && log "Email sent." || log "Email failed." }" > /dev/null && log "Email sent." || log "Email failed."
}
}
# Backup configuration with timestamp
backup_config() {
local config_file="/var/www/conf/httpd_config.xml"
local backup_dir="/var/www/conf/backups"
local timestamp=$(date +%Y%m%d%H%M%S)
mkdir -p "$backup_dir"
cp "$config_file" "$backup_dir/httpd_config.pre-removal-$timestamp.xml"
log "Config backup saved to $backup_dir/httpd_config.pre-removal-$timestamp.xml"
}
# Remove certificate using Certbot
remove_certificate() {
local domain="$1"
if certbot certificates | grep -q "Domains: $domain"; then
log "Removing certificate for $domain..."
certbot delete --cert-name "$domain" --non-interactive
rm -rf "/etc/letsencrypt/live/$domain"*
log "Certificate removed for $domain"
else
log "No certificate found for $domain"
fi fi
} }
# Remove listeners and associated configurations 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() { cleanup_listeners() {
local domain="$1" local domain="$1"
local config_file="/var/www/conf/httpd_config.xml" log "Cleaning up listeners and configurations for '$domain'..."
local temp_file
log "Cleaning up listeners for $domain..." # Remove listener for the domain
sudo xmlstarlet ed -L \
# Remove listeners -d "//listener[name='HTTPS-$domain']" \
sed -i "/<name>HTTPS-$domain<\/name>/,/<\/listener>/d" "$config_file" "$CONF_FILE"
# Remove vhostMap entries # Remove vhostMap entries
sed -i "/<domain>$domain<\/domain>/,/<\/vhostMap>/d" "$config_file" sudo xmlstarlet ed -L \
-d "//vhostMap[domain='$domain']" \
"$CONF_FILE"
# Remove related virtual host # Remove related virtual host
local vhost_name="${domain//./_}" local vhost_name="${domain//./_}"
sed -i "/<name>$vhost_name<\/name>/,/<\/virtualHost>/d" "$config_file" sudo xmlstarlet ed -L \
-d "//virtualHost[name='$vhost_name']" \
"$CONF_FILE"
# Cleanup empty listenerList tags # Cleanup empty listenerList tags
temp_file=$(mktemp) sudo xmlstarlet ed -L \
awk '/<listenerList>/ {flag=1; print; next} /<\/listenerList>/ {flag=0; print; next} flag && /^[[:space:]]*$/ {next} {print}' "$config_file" > "$temp_file" -d "//listenerList[count(*)=0]" \
mv "$temp_file" "$config_file" "$CONF_FILE"
} }
# Validate XML configuration
validate_xml() { validate_xml() {
local config_file="/var/www/conf/httpd_config.xml" log "Validating XML configuration..."
if ! sudo xmllint --noout "$CONF_FILE" 2>/dev/null; then
if command -v xmllint >/dev/null; then log "❌ ERROR: Invalid XML configuration after cleanup. Check backups."
log "Validating XML configuration..." return 1
if ! xmllint --noout "$config_file"; then
log "ERROR: Invalid XML configuration after cleanup. Check backups."
return 1
fi
fi fi
log "✔ XML configuration is valid."
return 0 return 0
} }
# Restart LiteSpeed if needed
restart_litespeed() { restart_litespeed() {
log "Restarting LiteSpeed..." log "Restarting LiteSpeed server..."
systemctl restart lsws && log "LiteSpeed restarted successfully." || log "LiteSpeed restart failed." if sudo systemctl restart lsws; then
log "✔ LiteSpeed server restarted successfully."
else
log "❌ ERROR: Failed to restart LiteSpeed server."
return 3
fi
} }
# Main execution # === Main Script Logic ===
main() { main() {
declare -a DOMAINS declare -a DOMAINS
EMAIL=""
# Parse parameters # Parse parameters
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--domains=*) --domains=*)
IFS=',' read -ra DOMAINS <<< "${1#*=}" IFS=',' read -ra DOMAINS <<< "${1#*=}"
shift
;; ;;
--email=*) --email=*)
EMAIL="${1#*=}" EMAIL="${1#*=}"
shift ;;
--verbose)
VERBOSE=1
log "Verbose mode enabled."
;; ;;
*) *)
echo "Invalid parameter: $1" log "Invalid parameter: $1"
exit 1 exit 1
;; ;;
esac esac
shift
done done
# Validate input # Validate input
if [[ ${#DOMAINS[@]} -eq 0 ]]; then if [[ ${#DOMAINS[@]} -eq 0 ]]; then
echo "Error: --domains parameter is required" log "❌ ERROR: --domains parameter is required."
exit 1 exit 1
fi 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 backup_config
# Process each domain
for domain in "${DOMAINS[@]}"; do for domain in "${DOMAINS[@]}"; do
log "Processing domain: $domain" log "Processing domain: $domain"
# Validate domain format # Validate domain format
[[ "$domain" =~ ^([a-zA-Z0-9](-*[a-zA-Z0-9])*\.)+[a-zA-Z]{2,}$ ]] || { if ! validate_domain "$domain"; then
log "Invalid domain: $domain" log "❌ ERROR: Invalid domain '$domain'. Skipping."
continue continue
} fi
# Remove certificate
remove_certificate "$domain" remove_certificate "$domain"
# Clean up listeners and configurations
cleanup_listeners "$domain" cleanup_listeners "$domain"
done done
# Validate XML configuration
if validate_xml; then if validate_xml; then
restart_litespeed restart_litespeed
send_email "SSL Removal Complete" "Successfully removed SSL for domains: ${DOMAINS[*]}" send_email "SSL Removal Complete" "Successfully removed SSL for domains: ${DOMAINS[*]}"
@ -157,4 +206,5 @@ main() {
fi fi
} }
# === Entry Point ===
main "$@" main "$@"