mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
fix(ssl): clean ECC state, guard cert reuse, register renew hook (#4875)
- Cleanup on issuance/install failure now also removes the acme.sh
${domain}_ecc (and ${ip}_ecc) directory, not just ${domain}, so a
failed run no longer leaves partial state behind.
- The 'existing certificate' check only reuses a cert when its
fullchain.cer and key files are actually present and non-empty;
otherwise the broken state is removed and issuance proceeds. This
fixes the 0-byte fullchain.pem produced by reusing failed state.
- Menu option 5 (set cert paths) now registers the acme.sh --installcert
hook with --reloadcmd 'x-ui restart' when acme.sh knows the domain, so
auto-renewal copies the renewed cert and reloads the panel.
This commit is contained in:
parent
b1d079fc24
commit
44291de989
2 changed files with 67 additions and 25 deletions
42
install.sh
42
install.sh
|
|
@ -297,7 +297,7 @@ setup_ssl_certificate() {
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo -e "${yellow}Failed to issue certificate for ${domain}${plain}"
|
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}"
|
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
|
rm -rf "$certPath" 2> /dev/null
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -431,8 +431,8 @@ setup_ip_certificate() {
|
||||||
echo -e "${red}Failed to issue IP certificate${plain}"
|
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}"
|
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
|
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
|
||||||
rm -rf ~/.acme.sh/${ipv4} 2> /dev/null
|
rm -rf ~/.acme.sh/${ipv4} ~/.acme.sh/${ipv4}_ecc 2> /dev/null
|
||||||
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2> /dev/null
|
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} ~/.acme.sh/${ipv6}_ecc 2> /dev/null
|
||||||
rm -rf ${certDir} 2> /dev/null
|
rm -rf ${certDir} 2> /dev/null
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -451,8 +451,8 @@ setup_ip_certificate() {
|
||||||
if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
|
if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
|
||||||
echo -e "${red}Certificate files not found after installation${plain}"
|
echo -e "${red}Certificate files not found after installation${plain}"
|
||||||
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
|
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
|
||||||
rm -rf ~/.acme.sh/${ipv4} 2> /dev/null
|
rm -rf ~/.acme.sh/${ipv4} ~/.acme.sh/${ipv4}_ecc 2> /dev/null
|
||||||
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2> /dev/null
|
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} ~/.acme.sh/${ipv6}_ecc 2> /dev/null
|
||||||
rm -rf ${certDir} 2> /dev/null
|
rm -rf ${certDir} 2> /dev/null
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -524,14 +524,30 @@ ssl_cert_issue() {
|
||||||
echo -e "${green}Your domain is: ${domain}, checking it...${plain}"
|
echo -e "${green}Your domain is: ${domain}, checking it...${plain}"
|
||||||
SSL_ISSUED_DOMAIN="${domain}"
|
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
|
local cert_exists=0
|
||||||
if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then
|
if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then
|
||||||
cert_exists=1
|
local acmeCertDir=""
|
||||||
local certInfo=$(~/.acme.sh/acme.sh --list 2> /dev/null | grep -F "${domain}")
|
if [[ -s ~/.acme.sh/${domain}_ecc/fullchain.cer && -s ~/.acme.sh/${domain}_ecc/${domain}.key ]]; then
|
||||||
echo -e "${yellow}Existing certificate found for ${domain}, will reuse it.${plain}"
|
acmeCertDir=~/.acme.sh/${domain}_ecc
|
||||||
[[ -n "${certInfo}" ]] && echo "$certInfo"
|
elif [[ -s ~/.acme.sh/${domain}/fullchain.cer && -s ~/.acme.sh/${domain}/${domain}.key ]]; then
|
||||||
else
|
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}"
|
echo -e "${green}Your domain is ready for issuing certificates now...${plain}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -563,7 +579,7 @@ ssl_cert_issue() {
|
||||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo -e "${red}Issuing certificate failed, please check logs.${plain}"
|
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
|
systemctl start x-ui 2> /dev/null || rc-service x-ui start 2> /dev/null
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
|
|
@ -617,7 +633,7 @@ ssl_cert_issue() {
|
||||||
else
|
else
|
||||||
echo -e "${red}Installing certificate failed, exiting.${plain}"
|
echo -e "${red}Installing certificate failed, exiting.${plain}"
|
||||||
if [[ ${cert_exists} -eq 0 ]]; then
|
if [[ ${cert_exists} -eq 0 ]]; then
|
||||||
rm -rf ~/.acme.sh/${domain}
|
rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc
|
||||||
fi
|
fi
|
||||||
systemctl start x-ui 2> /dev/null || rc-service x-ui start 2> /dev/null
|
systemctl start x-ui 2> /dev/null || rc-service x-ui start 2> /dev/null
|
||||||
return 1
|
return 1
|
||||||
|
|
|
||||||
50
x-ui.sh
50
x-ui.sh
|
|
@ -1269,6 +1269,16 @@ ssl_cert_issue_main() {
|
||||||
echo "Panel paths set for domain: $domain"
|
echo "Panel paths set for domain: $domain"
|
||||||
echo " - Certificate File: $webCertFile"
|
echo " - Certificate File: $webCertFile"
|
||||||
echo " - Private Key File: $webKeyFile"
|
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
|
restart
|
||||||
else
|
else
|
||||||
echo "Certificate or private key not found for domain: $domain."
|
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 "Failed to issue certificate for IP: ${server_ip}"
|
||||||
LOGE "Make sure port ${WebPort} is open and the server is accessible from the internet"
|
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
|
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
|
||||||
rm -rf ~/.acme.sh/${server_ip} 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} 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
|
rm -rf ${certPath} 2> /dev/null
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
|
|
@ -1468,8 +1478,8 @@ ssl_cert_issue_for_ip() {
|
||||||
if [[ ! -f "${certPath}/fullchain.pem" || ! -f "${certPath}/privkey.pem" ]]; then
|
if [[ ! -f "${certPath}/fullchain.pem" || ! -f "${certPath}/privkey.pem" ]]; then
|
||||||
LOGE "Certificate files not found after installation"
|
LOGE "Certificate files not found after installation"
|
||||||
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
|
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
|
||||||
rm -rf ~/.acme.sh/${server_ip} 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} 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
|
rm -rf ${certPath} 2> /dev/null
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -1576,14 +1586,30 @@ ssl_cert_issue() {
|
||||||
LOGD "Your domain is: ${domain}, checking it..."
|
LOGD "Your domain is: ${domain}, checking it..."
|
||||||
SSL_ISSUED_DOMAIN="${domain}"
|
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
|
local cert_exists=0
|
||||||
if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then
|
if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then
|
||||||
cert_exists=1
|
local acmeCertDir=""
|
||||||
local certInfo=$(~/.acme.sh/acme.sh --list 2> /dev/null | grep -F "${domain}")
|
if [[ -s ~/.acme.sh/${domain}_ecc/fullchain.cer && -s ~/.acme.sh/${domain}_ecc/${domain}.key ]]; then
|
||||||
LOGI "Existing certificate found for ${domain}, will reuse it."
|
acmeCertDir=~/.acme.sh/${domain}_ecc
|
||||||
[[ -n "${certInfo}" ]] && LOGI "${certInfo}"
|
elif [[ -s ~/.acme.sh/${domain}/fullchain.cer && -s ~/.acme.sh/${domain}/${domain}.key ]]; then
|
||||||
else
|
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..."
|
LOGI "Your domain is ready for issuing certificates now..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -1611,7 +1637,7 @@ ssl_cert_issue() {
|
||||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Issuing certificate failed, please check logs."
|
LOGE "Issuing certificate failed, please check logs."
|
||||||
rm -rf ~/.acme.sh/${domain}
|
rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
LOGE "Issuing certificate succeeded, installing certificates..."
|
LOGE "Issuing certificate succeeded, installing certificates..."
|
||||||
|
|
@ -1664,7 +1690,7 @@ ssl_cert_issue() {
|
||||||
else
|
else
|
||||||
LOGE "Installing certificate failed, exiting."
|
LOGE "Installing certificate failed, exiting."
|
||||||
if [[ ${cert_exists} -eq 0 ]]; then
|
if [[ ${cert_exists} -eq 0 ]]; then
|
||||||
rm -rf ~/.acme.sh/${domain}
|
rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc
|
||||||
fi
|
fi
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue