From 40402a445426c974e4add55fb3671a379167a9bb Mon Sep 17 00:00:00 2001 From: TonyGarces Date: Fri, 21 Mar 2025 02:22:22 +0800 Subject: [PATCH] Fix certificate mismatch --- scripts/ssl-manager/ssl_manager.sh | 366 +++++++++++++++++++++-------- 1 file changed, 270 insertions(+), 96 deletions(-) diff --git a/scripts/ssl-manager/ssl_manager.sh b/scripts/ssl-manager/ssl_manager.sh index 338c739..432b937 100644 --- a/scripts/ssl-manager/ssl_manager.sh +++ b/scripts/ssl-manager/ssl_manager.sh @@ -122,43 +122,23 @@ update_litespeed_config() { cp "$config_file" "$backup_file" log "Created backup of LiteSpeed configuration at $backup_file" - # First, clean up any redundant listeners for this domain + # Clean up any redundant listeners for this domain cleanup_redundant_listeners "$config_file" "$DOMAIN" - # After cleaning up redundant listeners but before adding domain mappings, - # create a virtual host for the domain - create_domain_virtual_host "$DOMAIN" - - # Check if the required listeners exist and contain correct cert paths - local need_cert_update=false - local has_domain_listener=false - - # Check if domain exists in HTTPS and HTTPS-ipv6 listeners with correct cert paths - if grep -A30 "HTTPS" "$config_file" | grep -q "$DOMAIN" && \ - grep -A30 "HTTPS-ipv6" "$config_file" | grep -q "$DOMAIN"; then - # Domain exists in both listeners, check cert paths - if ! grep -A30 "HTTPS" "$config_file" | grep -q "$key_file" || \ - ! grep -A30 "HTTPS-ipv6" "$config_file" | grep -q "$key_file"; then - need_cert_update=true - log "Certificate paths need updating" - else - log "Domain mappings and certificate paths are already correct" - fi - else - log "Domain mappings need to be added" + # Create domain-specific virtual host + if ! create_domain_virtual_host "$DOMAIN"; then + log "ERROR: Failed to create virtual host for $DOMAIN. Aborting configuration update." + return 1 fi - # Update certificate paths if needed - if [ "$need_cert_update" = true ]; then - log "Updating certificate paths..." - # Update keyFile and certFile for all listeners that match our domain - sed -i "//,/<\/listener>/ s|.*letsencrypt/live/$DOMAIN/.*|$key_file|g" "$config_file" - sed -i "//,/<\/listener>/ s|.*letsencrypt/live/$DOMAIN/.*|$cert_file|g" "$config_file" + # Create domain-specific listener + if ! create_domain_listener "$DOMAIN"; then + log "ERROR: Failed to create listener for $DOMAIN. Aborting configuration update." + return 1 fi - # Add domain mapping to listeners (modified function will use the correct virtual host) - add_domain_mapping "HTTPS" - add_domain_mapping "HTTPS-ipv6" + # Remove domain from shared listeners - safer to avoid certificate mismatch errors + remove_domain_from_shared_listeners # Final validation of the complete file if ! validate_xml_config "$config_file" "$backup_file"; then @@ -167,7 +147,7 @@ update_litespeed_config() { return 1 fi - log "LiteSpeed configuration updated successfully with proper domain-to-virtualhost mapping." + log "LiteSpeed configuration updated successfully with dedicated domain configuration." return 0 } @@ -439,68 +419,53 @@ install_xml_tools() { return 0 } -# New function to create or update domain-specific virtual host -create_domain_virtual_host() { - local domain="$1" - local config_file="/var/www/conf/httpd_config.xml" - local vhost_name="${domain//./_}" # Replace dots with underscores for uniqueness - - log "Checking if virtual host for $domain needs to be created..." - - # Check if virtual host already exists - if grep -q "$vhost_name" "$config_file"; then - log "Virtual host '$vhost_name' already exists, skipping creation." - return 0 - fi - - log "Creating virtual host for $domain..." - local temp_file=$(mktemp) - - # Create the virtual host definition - local vhost_config="\n $vhost_name\n /var/www/webroot/\n \$SERVER_ROOT/conf/vhconf.xml\n 1\n 1\n 1\n 0\n 0\n" - - # Insert the virtual host before the end of virtualHostList tag - awk -v vhost="$vhost_config" ' - /<\/virtualHostList>/ { print " " vhost; } - { print } - ' "$config_file" > "$temp_file" - - # Check if file looks valid - if [ -s "$temp_file" ] && grep -q "" "$temp_file" && grep -q "" "$temp_file"; then - cp "$temp_file" "$config_file" - log "Virtual host for $domain created successfully." - else - log "Error: Generated configuration appears invalid. Virtual host not created." - rm -f "$temp_file" - return 1 - fi - - rm -f "$temp_file" - return 0 -} - -# Add a new function to create a domain-specific HTTPS listener +# Function to create or update a domain-specific HTTPS listener create_domain_listener() { local domain="$1" + local config_file="/var/www/conf/httpd_config.xml" + local vhost_name="${domain//[.]/_}" local key_file="/etc/letsencrypt/live/$domain/privkey.pem" local cert_file="/etc/letsencrypt/live/$domain/fullchain.pem" - local config_file="/var/www/conf/httpd_config.xml" - local vhost_name="${domain%%.*}" + local timestamp=$(date +%Y%m%d%H%M%S) + local backup_file="${config_file}.backup.${timestamp}" - log "Creating domain-specific HTTPS listener for $domain..." + log "Creating/updating domain-specific HTTPS listener for $domain..." + + # Create backup if not already done + if [ ! -f "$backup_file" ]; then + cp "$config_file" "$backup_file" + log "Created backup of LiteSpeed configuration at $backup_file" + fi # Check if listener already exists if grep -q "HTTPS-$domain" "$config_file"; then - log "HTTPS listener for $domain already exists, updating certificate paths..." - sed -i "/HTTPS-$domain<\/name>/,/<\/listener>/s|.*|$key_file|" "$config_file" - sed -i "/HTTPS-$domain<\/name>/,/<\/listener>/s|.*|$cert_file|" "$config_file" + log "HTTPS listener for $domain already exists, updating configuration..." + + # Update certificate paths in existing listener + sed -i "/HTTPS-$domain<\/name>/,/<\/listener>/ s|.*|$key_file|" "$config_file" + sed -i "/HTTPS-$domain<\/name>/,/<\/listener>/ s|.*|$cert_file|" "$config_file" + + # Verify updates were applied + if grep -A5 "HTTPS-$domain" "$config_file" | grep -q "$key_file"; then + log "Certificate paths updated successfully for $domain listener." + else + log "ERROR: Failed to update certificate paths for $domain listener." + return 1 + fi + return 0 fi - # Create a new listener with domain-specific settings - local temp_file=$(mktemp) + log "Creating new HTTPS listener for $domain..." - # Insert the new listener before the end of listenerList + # Create a temporary file for XML editing + local temp_file=$(mktemp) + if [ ! -f "$temp_file" ]; then + log "ERROR: Failed to create temporary file for configuration update." + return 1 + fi + + # Insert new listener into configuration before listenerList end tag awk -v domain="$domain" -v vhost="$vhost_name" -v key="$key_file" -v cert="$cert_file" ' /<\/listenerList>/ { print " " @@ -522,12 +487,233 @@ create_domain_listener() { print " 1" print " 15" print " " + print $0 + next } { print } ' "$config_file" > "$temp_file" - mv "$temp_file" "$config_file" - log "Domain-specific HTTPS listener created for $domain." + # Validate the temporary file + if [ ! -s "$temp_file" ]; then + log "ERROR: Generated configuration is empty. Keeping original configuration." + rm -f "$temp_file" + return 1 + fi + + # Check for basic XML validity + if ! grep -q "" "$temp_file" || ! grep -q "" "$temp_file"; then + log "ERROR: Generated configuration appears invalid. Keeping original configuration." + rm -f "$temp_file" + return 1 + fi + + # Apply changes + cp "$temp_file" "$config_file" + if [ $? -ne 0 ]; then + log "ERROR: Failed to update configuration file. Keeping original configuration." + rm -f "$temp_file" + return 1 + fi + + # Clean up temp file + rm -f "$temp_file" + + log "Domain-specific HTTPS listener for $domain created successfully." + return 0 +} + +# Function to create or update domain-specific virtual host +create_domain_virtual_host() { + local domain="$1" + local config_file="/var/www/conf/httpd_config.xml" + local vhost_name="${domain//[.]/_}" + + log "Checking if virtual host for $domain needs to be created..." + + # Check if virtual host already exists + if grep -q "$vhost_name" "$config_file"; then + log "Virtual host '$vhost_name' already exists, skipping creation." + return 0 + fi + + log "Creating virtual host for $domain..." + local temp_file=$(mktemp) + if [ ! -f "$temp_file" ]; then + log "ERROR: Failed to create temporary file for virtual host creation." + return 1 + fi + + # Insert new virtual host before virtualHostList end tag + awk -v vhost="$vhost_name" ' + /<\/virtualHostList>/ { + print " " + print " " vhost "" + print " /var/www/webroot/" + print " $SERVER_ROOT/conf/vhconf.xml" + print " 1" + print " 1" + print " 1" + print " 0" + print " 0" + print " " + print $0 + next + } + { print } + ' "$config_file" > "$temp_file" + + # Validate the temporary file + if [ ! -s "$temp_file" ]; then + log "ERROR: Generated virtual host configuration is empty. Keeping original configuration." + rm -f "$temp_file" + return 1 + fi + + # Apply changes + cp "$temp_file" "$config_file" + if [ $? -ne 0 ]; then + log "ERROR: Failed to update configuration with new virtual host. Keeping original configuration." + rm -f "$temp_file" + return 1 + fi + + # Clean up + rm -f "$temp_file" + + log "Virtual host for $domain created successfully." + return 0 +} + +# Function to remove domain from shared listeners to avoid certificate mismatch +remove_domain_from_shared_listeners() { + local config_file="/var/www/conf/httpd_config.xml" + local domain="$DOMAIN" + + log "Removing $domain from shared listeners to prevent certificate mismatch..." + + # Create temporary file + local temp_file=$(mktemp) + if [ ! -f "$temp_file" ]; then + log "ERROR: Failed to create temporary file for shared listener cleanup." + return 1 + fi + + # For HTTPS listener + awk -v domain="$domain" ' + /HTTPS<\/name>/,/<\/listener>/ { + if ($0 ~ //) { + in_vhostmap = 1 + vhostmap_buffer = $0 "\n" + next + } + if (in_vhostmap) { + vhostmap_buffer = vhostmap_buffer $0 "\n" + if ($0 ~ /<\/vhostMap>/) { + if (vhostmap_buffer !~ domain) { + printf "%s", vhostmap_buffer + } + in_vhostmap = 0 + vhostmap_buffer = "" + } + next + } + } + { print } + ' "$config_file" > "$temp_file" + + # Check if changes were made correctly + if [ ! -s "$temp_file" ]; then + log "ERROR: Generated configuration is empty after domain removal. Keeping original configuration." + rm -f "$temp_file" + return 1 + fi + + cp "$temp_file" "$config_file" + rm -f "$temp_file" + + # For HTTPS-ipv6 listener - repeat the process + temp_file=$(mktemp) + if [ ! -f "$temp_file" ]; then + log "ERROR: Failed to create temporary file for shared listener cleanup." + return 1 + fi + + awk -v domain="$domain" ' + /HTTPS-ipv6<\/name>/,/<\/listener>/ { + if ($0 ~ //) { + in_vhostmap = 1 + vhostmap_buffer = $0 "\n" + next + } + if (in_vhostmap) { + vhostmap_buffer = vhostmap_buffer $0 "\n" + if ($0 ~ /<\/vhostMap>/) { + if (vhostmap_buffer !~ domain) { + printf "%s", vhostmap_buffer + } + in_vhostmap = 0 + vhostmap_buffer = "" + } + next + } + } + { print } + ' "$config_file" > "$temp_file" + + if [ ! -s "$temp_file" ]; then + log "ERROR: Generated configuration is empty after domain removal. Keeping original configuration." + rm -f "$temp_file" + return 1 + fi + + cp "$temp_file" "$config_file" + rm -f "$temp_file" + + log "Domain successfully removed from shared listeners." + return 0 +} + +# Restart LiteSpeed with extra verification +restart_litespeed() { + log "Restarting LiteSpeed web server..." + + # Verify configuration before restart + if command -v /usr/local/lsws/bin/lshttpd > /dev/null; then + log "Verifying LiteSpeed configuration before restart..." + /usr/local/lsws/bin/lshttpd -t + if [ $? -ne 0 ]; then + log "ERROR: LiteSpeed configuration test failed. Not restarting server." + return 1 + fi + log "LiteSpeed configuration verified successfully." + fi + + # Now restart the service + if systemctl is-active --quiet lsws; then + systemctl restart lsws + if [ $? -ne 0 ]; then + log "ERROR: Failed to restart LiteSpeed. Please check logs." + return 1 + fi + + # Verify LiteSpeed is running after restart + sleep 2 + if ! systemctl is-active --quiet lsws; then + log "ERROR: LiteSpeed failed to start after restart. Please check logs." + return 1 + fi + + log "LiteSpeed successfully restarted." + else + systemctl start lsws + if [ $? -ne 0 ]; then + log "ERROR: Failed to start LiteSpeed. Please check logs." + return 1 + fi + log "LiteSpeed was not running. Started the service." + fi + + return 0 } # Parse input parameters @@ -609,18 +795,6 @@ fi CERTBOT_CMD="certbot certonly --webroot -w /var/www/webroot/ROOT -d $DOMAIN --agree-tos --non-interactive" [[ -n "${EMAIL:-}" ]] && CERTBOT_CMD+=" --email $EMAIL" -# Improved LiteSpeed service handling -restart_litespeed() { - log "Restarting LiteSpeed web server..." - if systemctl is-active --quiet lsws; then - systemctl reload lsws || systemctl restart lsws - log "LiteSpeed successfully restarted." - else - systemctl start lsws - log "LiteSpeed was not running. Started the service." - fi -} - # After Certbot installation and before existing certificate check install_xml_tools