diff --git a/install.sh b/install.sh index 1b2b114d..6d791bb0 100644 --- a/install.sh +++ b/install.sh @@ -297,7 +297,7 @@ setup_ssl_certificate() { if [ $? -ne 0 ]; then echo -e "${yellow}Failed to issue certificate for ${domain}${plain}" echo -e "${yellow}Please ensure port 80 is open and try again later with: x-ui${plain}" - rm -rf ~/.acme.sh/${domain} 2> /dev/null + rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc 2> /dev/null rm -rf "$certPath" 2> /dev/null return 1 fi @@ -431,8 +431,8 @@ setup_ip_certificate() { echo -e "${red}Failed to issue IP certificate${plain}" echo -e "${yellow}Please ensure port ${WebPort} is reachable (or forwarded from external port 80)${plain}" # Cleanup acme.sh data for both IPv4 and IPv6 if specified - rm -rf ~/.acme.sh/${ipv4} 2> /dev/null - [[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2> /dev/null + rm -rf ~/.acme.sh/${ipv4} ~/.acme.sh/${ipv4}_ecc 2> /dev/null + [[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} ~/.acme.sh/${ipv6}_ecc 2> /dev/null rm -rf ${certDir} 2> /dev/null return 1 fi @@ -451,8 +451,8 @@ setup_ip_certificate() { if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then echo -e "${red}Certificate files not found after installation${plain}" # Cleanup acme.sh data for both IPv4 and IPv6 if specified - rm -rf ~/.acme.sh/${ipv4} 2> /dev/null - [[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2> /dev/null + rm -rf ~/.acme.sh/${ipv4} ~/.acme.sh/${ipv4}_ecc 2> /dev/null + [[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} ~/.acme.sh/${ipv6}_ecc 2> /dev/null rm -rf ${certDir} 2> /dev/null return 1 fi @@ -524,14 +524,30 @@ ssl_cert_issue() { echo -e "${green}Your domain is: ${domain}, checking it...${plain}" SSL_ISSUED_DOMAIN="${domain}" - # detect existing certificate and reuse it if present + # detect existing certificate and reuse it only if its files are actually + # present and non-empty. acme.sh stores ECC certs under ${domain}_ecc and RSA + # certs under ${domain}; a failed issuance can leave a domain entry in --list + # with no usable cert files, which must not be reused (it produces a 0-byte + # fullchain.pem). Broken partial state is cleaned up so issuance can proceed. local cert_exists=0 if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then - cert_exists=1 - local certInfo=$(~/.acme.sh/acme.sh --list 2> /dev/null | grep -F "${domain}") - echo -e "${yellow}Existing certificate found for ${domain}, will reuse it.${plain}" - [[ -n "${certInfo}" ]] && echo "$certInfo" - else + local acmeCertDir="" + if [[ -s ~/.acme.sh/${domain}_ecc/fullchain.cer && -s ~/.acme.sh/${domain}_ecc/${domain}.key ]]; then + acmeCertDir=~/.acme.sh/${domain}_ecc + elif [[ -s ~/.acme.sh/${domain}/fullchain.cer && -s ~/.acme.sh/${domain}/${domain}.key ]]; then + acmeCertDir=~/.acme.sh/${domain} + fi + if [[ -n "${acmeCertDir}" ]]; then + cert_exists=1 + local certInfo=$(~/.acme.sh/acme.sh --list 2> /dev/null | grep -F "${domain}") + echo -e "${yellow}Existing certificate found for ${domain}, will reuse it.${plain}" + [[ -n "${certInfo}" ]] && echo "$certInfo" + else + echo -e "${yellow}Found incomplete acme.sh state for ${domain} (no valid certificate files); cleaning it up and re-issuing.${plain}" + rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc + fi + fi + if [[ ${cert_exists} -eq 0 ]]; then echo -e "${green}Your domain is ready for issuing certificates now...${plain}" fi @@ -563,7 +579,7 @@ ssl_cert_issue() { ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force if [ $? -ne 0 ]; then echo -e "${red}Issuing certificate failed, please check logs.${plain}" - rm -rf ~/.acme.sh/${domain} + rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc systemctl start x-ui 2> /dev/null || rc-service x-ui start 2> /dev/null return 1 else @@ -617,7 +633,7 @@ ssl_cert_issue() { else echo -e "${red}Installing certificate failed, exiting.${plain}" if [[ ${cert_exists} -eq 0 ]]; then - rm -rf ~/.acme.sh/${domain} + rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc fi systemctl start x-ui 2> /dev/null || rc-service x-ui start 2> /dev/null return 1 diff --git a/x-ui.sh b/x-ui.sh index 9b79d933..ee0f3763 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -1269,6 +1269,16 @@ ssl_cert_issue_main() { echo "Panel paths set for domain: $domain" echo " - Certificate File: $webCertFile" echo " - Private Key File: $webKeyFile" + # Register the acme.sh install-cert hook so auto-renewal copies the + # renewed cert to these paths and reloads the panel. Without it acme.sh + # renews but never updates /root/cert, silently serving a stale cert. + if command -v ~/.acme.sh/acme.sh &> /dev/null && ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then + ~/.acme.sh/acme.sh --installcert -d "${domain}" \ + --key-file "${webKeyFile}" \ + --fullchain-file "${webCertFile}" \ + --reloadcmd "x-ui restart" 2>&1 || true + echo "Registered acme.sh auto-renewal hook for ${domain}." + fi restart else echo "Certificate or private key not found for domain: $domain." @@ -1448,8 +1458,8 @@ ssl_cert_issue_for_ip() { LOGE "Failed to issue certificate for IP: ${server_ip}" LOGE "Make sure port ${WebPort} is open and the server is accessible from the internet" # Cleanup acme.sh data for both IPv4 and IPv6 if specified - rm -rf ~/.acme.sh/${server_ip} 2> /dev/null - [[ -n "$ipv6_addr" ]] && rm -rf ~/.acme.sh/${ipv6_addr} 2> /dev/null + rm -rf ~/.acme.sh/${server_ip} ~/.acme.sh/${server_ip}_ecc 2> /dev/null + [[ -n "$ipv6_addr" ]] && rm -rf ~/.acme.sh/${ipv6_addr} ~/.acme.sh/${ipv6_addr}_ecc 2> /dev/null rm -rf ${certPath} 2> /dev/null return 1 else @@ -1468,8 +1478,8 @@ ssl_cert_issue_for_ip() { if [[ ! -f "${certPath}/fullchain.pem" || ! -f "${certPath}/privkey.pem" ]]; then LOGE "Certificate files not found after installation" # Cleanup acme.sh data for both IPv4 and IPv6 if specified - rm -rf ~/.acme.sh/${server_ip} 2> /dev/null - [[ -n "$ipv6_addr" ]] && rm -rf ~/.acme.sh/${ipv6_addr} 2> /dev/null + rm -rf ~/.acme.sh/${server_ip} ~/.acme.sh/${server_ip}_ecc 2> /dev/null + [[ -n "$ipv6_addr" ]] && rm -rf ~/.acme.sh/${ipv6_addr} ~/.acme.sh/${ipv6_addr}_ecc 2> /dev/null rm -rf ${certPath} 2> /dev/null return 1 fi @@ -1576,14 +1586,30 @@ ssl_cert_issue() { LOGD "Your domain is: ${domain}, checking it..." SSL_ISSUED_DOMAIN="${domain}" - # detect existing certificate and reuse it if present + # detect existing certificate and reuse it only if its files are actually + # present and non-empty. acme.sh stores ECC certs under ${domain}_ecc and RSA + # certs under ${domain}; a failed issuance can leave a domain entry in --list + # with no usable cert files, which must not be reused (it produces a 0-byte + # fullchain.pem). Broken partial state is cleaned up so issuance can proceed. local cert_exists=0 if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then - cert_exists=1 - local certInfo=$(~/.acme.sh/acme.sh --list 2> /dev/null | grep -F "${domain}") - LOGI "Existing certificate found for ${domain}, will reuse it." - [[ -n "${certInfo}" ]] && LOGI "${certInfo}" - else + local acmeCertDir="" + if [[ -s ~/.acme.sh/${domain}_ecc/fullchain.cer && -s ~/.acme.sh/${domain}_ecc/${domain}.key ]]; then + acmeCertDir=~/.acme.sh/${domain}_ecc + elif [[ -s ~/.acme.sh/${domain}/fullchain.cer && -s ~/.acme.sh/${domain}/${domain}.key ]]; then + acmeCertDir=~/.acme.sh/${domain} + fi + if [[ -n "${acmeCertDir}" ]]; then + cert_exists=1 + local certInfo=$(~/.acme.sh/acme.sh --list 2> /dev/null | grep -F "${domain}") + LOGI "Existing certificate found for ${domain}, will reuse it." + [[ -n "${certInfo}" ]] && LOGI "${certInfo}" + else + LOGW "Found incomplete acme.sh state for ${domain} (no valid certificate files); cleaning it up and re-issuing." + rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc + fi + fi + if [[ ${cert_exists} -eq 0 ]]; then LOGI "Your domain is ready for issuing certificates now..." fi @@ -1611,7 +1637,7 @@ ssl_cert_issue() { ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force if [ $? -ne 0 ]; then LOGE "Issuing certificate failed, please check logs." - rm -rf ~/.acme.sh/${domain} + rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc exit 1 else LOGE "Issuing certificate succeeded, installing certificates..." @@ -1664,7 +1690,7 @@ ssl_cert_issue() { else LOGE "Installing certificate failed, exiting." if [[ ${cert_exists} -eq 0 ]]; then - rm -rf ~/.acme.sh/${domain} + rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc fi exit 1 fi