2025-01-29 13:03:24 +00:00
|
|
|
#!/bin/bash
|
2025-03-25 18:45:01 +00:00
|
|
|
# ==============================================================================
|
|
|
|
# Script Name: ssl_manager.sh
|
|
|
|
# Description: Automates SSL certificate issuance and updates LiteSpeed's httpd_config.xml.
|
|
|
|
# Ensures robust backups, validation, and error-free updates.
|
2025-03-27 16:26:05 +00:00
|
|
|
# Version: 2.0.4 (Idempotent, Clean Exit Messages)
|
2025-03-27 16:36:52 +00:00
|
|
|
# Author: Anthony Garces (tony@mightybox.io)
|
2025-03-25 18:45:01 +00:00
|
|
|
# Date: 2025-03-26
|
|
|
|
# ==============================================================================
|
2025-01-29 13:03:24 +00:00
|
|
|
set -euo pipefail
|
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
CONF_FILE="/var/www/conf/httpd_config.xml"
|
|
|
|
DEFAULT_CONF="/var/www/conf/httpd_config.default.xml"
|
|
|
|
SERVER_ROOT="/var/www"
|
2025-01-29 13:03:24 +00:00
|
|
|
LOG_DIR="/var/log/mb-ssl"
|
2025-03-25 18:45:01 +00:00
|
|
|
CERT_DIR="/etc/letsencrypt/live"
|
2025-03-27 16:26:05 +00:00
|
|
|
PUBLIC_IP=""
|
|
|
|
DOMAINS=()
|
|
|
|
EMAIL=""
|
2025-03-25 18:45:01 +00:00
|
|
|
VERBOSE=0
|
2025-01-29 13:03:24 +00:00
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
SCRIPT_LOG="${LOG_DIR}/ssl_manager.log"
|
2025-03-27 16:26:05 +00:00
|
|
|
ERROR_LOG="${LOG_DIR}/ssl_manager-error.log"
|
|
|
|
DEBUG_LOG="${LOG_DIR}/ssl_manager-debug.log"
|
|
|
|
BACKUP_FILE="${LOG_DIR}/httpd_config_backup_$(date +%Y%m%d%H%M%S).xml"
|
|
|
|
SCRIPT_EXIT_STATUS=0
|
|
|
|
|
|
|
|
setup_logging() {
|
|
|
|
# Create log directory if it doesn't exist
|
|
|
|
sudo mkdir -p "$LOG_DIR" || { echo "❌ ERROR: Cannot create log directory '$LOG_DIR'. Check permissions."; exit 1; }
|
|
|
|
|
|
|
|
# Set proper permissions
|
|
|
|
sudo chown -R "$(whoami)":"$(id -gn)" "$LOG_DIR"
|
|
|
|
sudo chmod 755 "$LOG_DIR"
|
|
|
|
|
|
|
|
# Create log files with proper permissions
|
|
|
|
touch "$SCRIPT_LOG" "$ERROR_LOG" "$DEBUG_LOG"
|
|
|
|
chmod 644 "$SCRIPT_LOG" "$ERROR_LOG" "$DEBUG_LOG"
|
|
|
|
|
|
|
|
# Add log rotation if log files are too large (>10MB)
|
|
|
|
for log_file in "$SCRIPT_LOG" "$ERROR_LOG" "$DEBUG_LOG"; do
|
|
|
|
if [ -f "$log_file" ] && [ "$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file")" -gt 10485760 ]; then
|
|
|
|
mv "$log_file" "${log_file}.$(date +%Y%m%d)"
|
|
|
|
touch "$log_file"
|
|
|
|
chmod 644 "$log_file"
|
|
|
|
gzip "${log_file}.$(date +%Y%m%d)"
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
}
|
2025-01-29 13:03:24 +00:00
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
log() {
|
2025-03-27 16:26:05 +00:00
|
|
|
local level="INFO"
|
|
|
|
local message="$1"
|
|
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
|
|
|
|
|
|
# Log to main log file
|
|
|
|
echo "[$timestamp] [$level] $message" | sudo tee -a "$SCRIPT_LOG"
|
|
|
|
|
|
|
|
# Log errors to error log file
|
|
|
|
if [[ "$message" == *"ERROR"* ]] || [[ "$message" == *"❌"* ]]; then
|
|
|
|
echo "[$timestamp] [$level] $message" | sudo tee -a "$ERROR_LOG"
|
|
|
|
fi
|
2025-01-29 13:03:24 +00:00
|
|
|
}
|
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
log_verbose() {
|
2025-03-27 16:26:05 +00:00
|
|
|
if [[ "$VERBOSE" -eq 1 ]]; then
|
|
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
|
|
echo "[$timestamp] [DEBUG] $1" | sudo tee -a "$DEBUG_LOG"
|
|
|
|
fi
|
2025-01-29 13:03:24 +00:00
|
|
|
}
|
|
|
|
|
2025-03-27 16:26:05 +00:00
|
|
|
log_error() {
|
|
|
|
local message="$1"
|
|
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
|
|
echo "[$timestamp] [ERROR] $message" | sudo tee -a "$ERROR_LOG" "$SCRIPT_LOG"
|
2025-01-29 13:03:24 +00:00
|
|
|
}
|
|
|
|
|
2025-03-27 16:26:05 +00:00
|
|
|
log_success() {
|
|
|
|
local message="$1"
|
|
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
|
|
echo "[$timestamp] [SUCCESS] $message" | sudo tee -a "$SCRIPT_LOG"
|
2025-01-29 13:03:24 +00:00
|
|
|
}
|
|
|
|
|
2025-03-27 16:26:05 +00:00
|
|
|
on_exit() {
|
|
|
|
if [[ $SCRIPT_EXIT_STATUS -ne 0 ]]; then
|
|
|
|
log_error "Script ended with error. Exit Code: $SCRIPT_EXIT_STATUS"
|
|
|
|
log_error "Backup file retained: $BACKUP_FILE"
|
2025-01-29 13:03:24 +00:00
|
|
|
else
|
2025-03-27 16:26:05 +00:00
|
|
|
sudo rm -f "$BACKUP_FILE"
|
|
|
|
log_success "Script completed successfully. Backup cleaned."
|
2025-03-20 18:07:43 +00:00
|
|
|
fi
|
2025-03-27 16:26:05 +00:00
|
|
|
exit $SCRIPT_EXIT_STATUS
|
2025-01-29 13:03:24 +00:00
|
|
|
}
|
2025-03-27 16:26:05 +00:00
|
|
|
trap 'on_exit' EXIT
|
2025-01-29 13:03:24 +00:00
|
|
|
|
2025-03-27 16:26:05 +00:00
|
|
|
check_command() {
|
|
|
|
local cmd="$1"
|
|
|
|
local apt_pkg="$2"
|
|
|
|
if ! command -v "$cmd" &>/dev/null; then
|
|
|
|
log "Required command '$cmd' not found. Attempting to install package '$apt_pkg'..."
|
|
|
|
if sudo yum install -y "$apt_pkg"; then
|
|
|
|
log_success "Successfully installed '$apt_pkg'"
|
|
|
|
else
|
|
|
|
log_error "Failed to install '$apt_pkg'"
|
|
|
|
SCRIPT_EXIT_STATUS=1; return 1
|
|
|
|
fi
|
2025-03-20 18:07:43 +00:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2025-03-27 16:26:05 +00:00
|
|
|
validate_domain() { [[ "$1" =~ ^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$ ]]; }
|
|
|
|
validate_ip() { [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; }
|
|
|
|
validate_email() { [[ "$1" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; }
|
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
create_default_backup() {
|
|
|
|
if [[ ! -f "$DEFAULT_CONF" ]]; then
|
|
|
|
log "Creating initial backup of '$CONF_FILE' as '$DEFAULT_CONF'..."
|
2025-03-27 16:26:05 +00:00
|
|
|
sudo cp -a "$CONF_FILE" "$DEFAULT_CONF" || {
|
|
|
|
log_error "FATAL: Failed to create initial backup '$DEFAULT_CONF'"
|
|
|
|
SCRIPT_EXIT_STATUS=2; return 1
|
|
|
|
}
|
|
|
|
log_success "Created initial backup successfully"
|
2025-03-20 18:07:43 +00:00
|
|
|
else
|
2025-03-27 16:26:05 +00:00
|
|
|
log "Default backup '$DEFAULT_CONF' already exists. Skipping creation."
|
2025-03-20 18:07:43 +00:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
validate_xml() {
|
2025-03-27 16:26:05 +00:00
|
|
|
log_verbose "Validating XML structure of '$1'..."
|
|
|
|
sudo xmllint --noout "$1" 2>/dev/null || {
|
|
|
|
log_error "Invalid XML structure in '$1'"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
2025-03-20 18:07:43 +00:00
|
|
|
return 1
|
2025-03-27 16:26:05 +00:00
|
|
|
}
|
|
|
|
log_verbose "XML validation passed for '$1'"
|
2025-03-20 18:07:43 +00:00
|
|
|
}
|
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
validate_dns() {
|
|
|
|
local resolved_ip
|
2025-03-27 16:26:05 +00:00
|
|
|
resolved_ip=$(dig +short "$1" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1)
|
|
|
|
[[ "$resolved_ip" == "$2" ]]
|
2025-03-20 18:07:43 +00:00
|
|
|
}
|
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
validate_http_access() {
|
|
|
|
local token
|
|
|
|
token=$(openssl rand -hex 16)
|
|
|
|
echo "$token" > "/var/www/webroot/ROOT/.well-known/acme-challenge/test-token"
|
2025-03-27 16:26:05 +00:00
|
|
|
[[ "$(curl -s "http://$1/.well-known/acme-challenge/test-token")" == "$token" ]]
|
2025-03-20 18:22:22 +00:00
|
|
|
}
|
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
issue_certificate() {
|
2025-03-27 16:26:05 +00:00
|
|
|
if [[ -f "$CERT_DIR/$1/fullchain.pem" ]]; then
|
|
|
|
log_success "Certificate already exists for '$1'. Skipping issuance."
|
|
|
|
return
|
2025-03-20 18:22:22 +00:00
|
|
|
fi
|
2025-03-27 16:26:05 +00:00
|
|
|
log "Issuing SSL certificate for domain '$1' with email '$2'..."
|
|
|
|
sudo certbot certonly --standalone --preferred-challenges http -d "$1" --non-interactive --agree-tos --email "$2" || {
|
|
|
|
log_error "Failed to issue certificate for '$1'"
|
|
|
|
SCRIPT_EXIT_STATUS=1; return 1
|
|
|
|
}
|
|
|
|
log_success "Certificate successfully issued for '$1'"
|
2025-03-20 18:22:22 +00:00
|
|
|
}
|
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
update_httpd_config() {
|
|
|
|
local domain="$1"
|
|
|
|
local ip="$2"
|
2025-03-27 16:26:05 +00:00
|
|
|
local listener_name="HTTPS-$domain"
|
|
|
|
local vhost_name="Jelastic"
|
|
|
|
|
|
|
|
log "Checking if listener exists for '$listener_name'..."
|
|
|
|
local existing
|
|
|
|
existing=$(xmlstarlet sel -t -v "/httpServerConfig/listenerList/listener[name='$listener_name']/name" "$CONF_FILE" 2>/dev/null || true)
|
|
|
|
|
|
|
|
if [[ -n "$existing" ]]; then
|
|
|
|
log "Listener '$listener_name' already exists. Checking for vhost mapping..."
|
|
|
|
local vhost_map_exists
|
|
|
|
vhost_map_exists=$(xmlstarlet sel -t -v "/httpServerConfig/listenerList/listener[name='$listener_name']/vhostMapList/vhostMap/domain" "$CONF_FILE" 2>/dev/null || true)
|
|
|
|
if [[ -z "$vhost_map_exists" ]]; then
|
|
|
|
log "Adding vhostMapList to existing listener for '$listener_name'..."
|
|
|
|
sudo cp -a "$CONF_FILE" "$BACKUP_FILE"
|
|
|
|
|
|
|
|
# Create a temporary XML file with the new vhostMapList
|
|
|
|
local temp_xml=$(mktemp)
|
|
|
|
cat > "$temp_xml" << EOF
|
|
|
|
<?xml version="1.0"?>
|
|
|
|
<vhostMapList>
|
|
|
|
<vhostMap>
|
|
|
|
<vhost>$vhost_name</vhost>
|
|
|
|
<domain>$domain</domain>
|
|
|
|
</vhostMap>
|
|
|
|
</vhostMapList>
|
|
|
|
EOF
|
|
|
|
|
|
|
|
# First validate the temporary XML
|
|
|
|
if ! xmllint --noout "$temp_xml" 2>/dev/null; then
|
|
|
|
log_error "Invalid temporary XML structure"
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Insert the vhostMapList into the existing listener
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-i "/httpServerConfig/listenerList/listener[name='$listener_name']" \
|
|
|
|
-t elem -n "vhostMapList" \
|
|
|
|
-v "$(xmlstarlet sel -t -c "//vhostMapList/*" "$temp_xml")" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to insert vhostMapList"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
|
|
|
|
# Validate the modified config
|
|
|
|
if ! xmllint --noout "$CONF_FILE" 2>/dev/null; then
|
|
|
|
log_error "Invalid XML structure after modification. Restoring backup..."
|
|
|
|
sudo cp -a "$BACKUP_FILE" "$CONF_FILE"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
log_success "Successfully added vhostMapList to existing listener"
|
|
|
|
else
|
|
|
|
log_success "vhostMap already present. No update needed."
|
|
|
|
fi
|
|
|
|
return
|
|
|
|
fi
|
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
log "Updating httpd_config.xml for domain '$domain' with IP '$ip'..."
|
2025-03-27 16:26:05 +00:00
|
|
|
sudo cp -a "$CONF_FILE" "$BACKUP_FILE"
|
|
|
|
|
|
|
|
# Create a temporary XML file with the new listener configuration
|
|
|
|
local temp_xml=$(mktemp)
|
|
|
|
cat > "$temp_xml" << EOF
|
|
|
|
<?xml version="1.0"?>
|
|
|
|
<listener>
|
|
|
|
<name>$listener_name</name>
|
|
|
|
<address>*:443</address>
|
|
|
|
<secure>1</secure>
|
|
|
|
<keyFile>/etc/letsencrypt/live/$domain/privkey.pem</keyFile>
|
|
|
|
<certFile>/etc/letsencrypt/live/$domain/fullchain.pem</certFile>
|
|
|
|
<vhostMapList>
|
|
|
|
<vhostMap>
|
|
|
|
<vhost>$vhost_name</vhost>
|
|
|
|
<domain>$domain</domain>
|
|
|
|
</vhostMap>
|
|
|
|
</vhostMapList>
|
|
|
|
</listener>
|
|
|
|
EOF
|
|
|
|
|
|
|
|
# First validate the temporary XML
|
|
|
|
if ! xmllint --noout "$temp_xml" 2>/dev/null; then
|
|
|
|
log_error "Invalid temporary XML structure"
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
fi
|
2025-03-25 18:45:01 +00:00
|
|
|
|
2025-03-27 16:26:05 +00:00
|
|
|
# Insert the new listener into the configuration using a different approach
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-s "//listenerList" \
|
|
|
|
-t elem -n "listener" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to create new listener element"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
# Now add each element individually
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-s "//listenerList/listener[last()]" \
|
|
|
|
-t elem -n "name" \
|
|
|
|
-v "$listener_name" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to add name element"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-s "//listenerList/listener[last()]" \
|
|
|
|
-t elem -n "address" \
|
|
|
|
-v "*:443" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to add address element"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-s "//listenerList/listener[last()]" \
|
|
|
|
-t elem -n "secure" \
|
|
|
|
-v "1" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to add secure element"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-s "//listenerList/listener[last()]" \
|
|
|
|
-t elem -n "keyFile" \
|
|
|
|
-v "/etc/letsencrypt/live/$domain/privkey.pem" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to add keyFile element"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-s "//listenerList/listener[last()]" \
|
|
|
|
-t elem -n "certFile" \
|
|
|
|
-v "/etc/letsencrypt/live/$domain/fullchain.pem" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to add certFile element"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
# Add vhostMapList and its children
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-s "//listenerList/listener[last()]" \
|
|
|
|
-t elem -n "vhostMapList" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to add vhostMapList element"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-s "//listenerList/listener[last()]/vhostMapList" \
|
|
|
|
-t elem -n "vhostMap" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to add vhostMap element"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-s "//listenerList/listener[last()]/vhostMapList/vhostMap" \
|
|
|
|
-t elem -n "vhost" \
|
|
|
|
-v "$vhost_name" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to add vhost element"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
sudo xmlstarlet ed -L \
|
|
|
|
-s "//listenerList/listener[last()]/vhostMapList/vhostMap" \
|
|
|
|
-t elem -n "domain" \
|
|
|
|
-v "$domain" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
log_error "Failed to add domain element"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
rm -f "$temp_xml"
|
|
|
|
|
|
|
|
# Validate the modified config
|
|
|
|
if ! xmllint --noout "$CONF_FILE" 2>/dev/null; then
|
|
|
|
log_error "Invalid XML structure after modification. Restoring backup..."
|
|
|
|
sudo cp -a "$BACKUP_FILE" "$CONF_FILE"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
2025-03-25 18:45:01 +00:00
|
|
|
fi
|
|
|
|
|
2025-03-27 16:26:05 +00:00
|
|
|
log_success "Listener + vhostMap added for '$listener_name'"
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup_xml() {
|
|
|
|
local domain="$1"
|
|
|
|
log "Cleaning up XML structure..."
|
|
|
|
sudo cp -a "$CONF_FILE" "$BACKUP_FILE"
|
|
|
|
|
|
|
|
# Remove any incorrectly placed vhostMapList elements
|
2025-03-25 18:45:01 +00:00
|
|
|
sudo xmlstarlet ed -L \
|
2025-03-27 16:26:05 +00:00
|
|
|
-d "//listenerList/vhostMapList[not(parent::listener)]" \
|
|
|
|
"$CONF_FILE" || {
|
|
|
|
log_error "Failed to clean up XML structure"
|
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
# Validate the cleaned config
|
|
|
|
if ! xmllint --noout "$CONF_FILE" 2>/dev/null; then
|
|
|
|
log_error "Invalid XML structure after cleanup. Restoring backup..."
|
2025-03-25 18:45:01 +00:00
|
|
|
sudo cp -a "$BACKUP_FILE" "$CONF_FILE"
|
2025-03-27 16:26:05 +00:00
|
|
|
SCRIPT_EXIT_STATUS=1
|
|
|
|
return 1
|
2025-03-20 18:22:22 +00:00
|
|
|
fi
|
2025-03-27 16:26:05 +00:00
|
|
|
log_success "XML structure cleaned up successfully"
|
2025-03-20 18:22:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
restart_litespeed() {
|
2025-03-25 18:45:01 +00:00
|
|
|
log "Restarting LiteSpeed server..."
|
2025-03-27 16:26:05 +00:00
|
|
|
sudo systemctl restart lsws || {
|
|
|
|
log_error "Failed to restart LiteSpeed. Restoring backup..."
|
2025-03-25 18:45:01 +00:00
|
|
|
sudo cp -a "$BACKUP_FILE" "$CONF_FILE"
|
2025-03-27 16:26:05 +00:00
|
|
|
SCRIPT_EXIT_STATUS=3
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
log_success "LiteSpeed server restarted successfully"
|
2025-03-20 18:07:43 +00:00
|
|
|
}
|
|
|
|
|
2025-03-25 18:45:01 +00:00
|
|
|
# === Main Script Logic ===
|
2025-03-27 16:26:05 +00:00
|
|
|
main() {
|
|
|
|
# Setup logging first
|
|
|
|
setup_logging
|
|
|
|
log "Starting SSL Manager V2.0.4"
|
|
|
|
|
|
|
|
# Parse parameters
|
|
|
|
for arg in "$@"; do
|
|
|
|
case $arg in
|
|
|
|
--public-ip=*) PUBLIC_IP="${arg#*=}"; log_verbose "Set public IP: $PUBLIC_IP";;
|
|
|
|
--domain=*) PRIMARY_DOMAIN="${arg#*=}"; DOMAINS=("$PRIMARY_DOMAIN"); log_verbose "Set primary domain: $PRIMARY_DOMAIN";;
|
|
|
|
--domains=*) IFS=',' read -ra DOMAINS <<< "${arg#*=}"; PRIMARY_DOMAIN="${DOMAINS[0]}"; log_verbose "Set domains: ${DOMAINS[*]}";;
|
|
|
|
--email=*) EMAIL="${arg#*=}"; log_verbose "Set email: $EMAIL";;
|
|
|
|
--verbose) VERBOSE=1; log "Verbose mode enabled";;
|
|
|
|
*) log_error "Invalid argument: $arg"; SCRIPT_EXIT_STATUS=1; exit 1;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
|
|
|
|
[[ -z "$PRIMARY_DOMAIN" || -z "$PUBLIC_IP" || -z "$EMAIL" ]] && {
|
|
|
|
log_error "Missing required parameters. Provide --domains, --public-ip, and --email."
|
|
|
|
SCRIPT_EXIT_STATUS=1; exit 1
|
|
|
|
}
|
|
|
|
|
|
|
|
validate_domain "$PRIMARY_DOMAIN" || { log_error "Invalid domain '$PRIMARY_DOMAIN'"; SCRIPT_EXIT_STATUS=1; exit 1; }
|
|
|
|
validate_ip "$PUBLIC_IP" || { log_error "Invalid IP '$PUBLIC_IP'"; SCRIPT_EXIT_STATUS=1; exit 1; }
|
|
|
|
validate_email "$EMAIL" || { log_error "Invalid email '$EMAIL'"; SCRIPT_EXIT_STATUS=1; exit 1; }
|
|
|
|
|
|
|
|
validate_dns "$PRIMARY_DOMAIN" "$PUBLIC_IP" || { log_error "DNS validation failed"; SCRIPT_EXIT_STATUS=1; exit 1; }
|
|
|
|
validate_http_access "$PRIMARY_DOMAIN" || { log_error "HTTP access validation failed"; SCRIPT_EXIT_STATUS=1; exit 1; }
|
|
|
|
|
|
|
|
log_verbose "Checking dependencies..."
|
|
|
|
check_command xmllint libxml2-utils
|
|
|
|
check_command xmlstarlet xmlstarlet
|
|
|
|
check_command certbot certbot
|
|
|
|
|
|
|
|
create_default_backup
|
|
|
|
issue_certificate "$PRIMARY_DOMAIN" "$EMAIL"
|
|
|
|
|
|
|
|
for domain in "${DOMAINS[@]}"; do
|
|
|
|
update_httpd_config "$domain" "$PUBLIC_IP"
|
|
|
|
cleanup_xml "$domain"
|
|
|
|
done
|
|
|
|
|
|
|
|
restart_litespeed
|
|
|
|
|
|
|
|
log_success "SSL Manager completed successfully"
|
|
|
|
SCRIPT_EXIT_STATUS=0
|
|
|
|
}
|
|
|
|
|
|
|
|
# === Entry Point ===
|
|
|
|
main "$@"
|