mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-18 10:17:36 +07:00
- Issue acme.sh HTTP-01 over IPv4 unless the host has no global IPv4 address: the hardcoded --listen-v6 started a v6-only standalone listener, so validation of a domain whose A record points at this host always failed (#4994). - Add a custom cert/key path option to the "Set Cert paths" menu so certificates living outside /root/cert (e.g. certbot under /etc/letsencrypt) can be wired to the panel from the CLI (#5010). - Derive the displayed Access URL from the certificate's actual SAN list instead of the cert folder name, list the other covered names, and show the panel's custom-path certificate in "Show Existing Domains" (#5070). Also silence find when /root/cert doesn't exist.
This commit is contained in:
16
install.sh
16
install.sh
@ -56,6 +56,18 @@ is_domain() {
|
||||
[[ "$1" =~ ^([A-Za-z0-9](-*[A-Za-z0-9])*\.)+(xn--[a-z0-9]{2,}|[A-Za-z]{2,})$ ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
# acme.sh's standalone server binds IPv4 by default; --listen-v6 makes it
|
||||
# v6-only, which breaks HTTP-01 validation when the domain's A record points
|
||||
# at this host's IPv4 (#4994). Only force IPv6 when the host has no global
|
||||
# IPv4 address at all.
|
||||
acme_listen_flag() {
|
||||
if ip -4 addr show scope global 2> /dev/null | grep -q "inet "; then
|
||||
echo ""
|
||||
else
|
||||
echo "--listen-v6"
|
||||
fi
|
||||
}
|
||||
|
||||
# Port helpers
|
||||
is_port_in_use() {
|
||||
local port="$1"
|
||||
@ -292,7 +304,7 @@ setup_ssl_certificate() {
|
||||
echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
|
||||
|
||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force > /dev/null 2>&1
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} $(acme_listen_flag) --standalone --httpport 80 --force
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${yellow}Failed to issue certificate for ${domain}${plain}"
|
||||
@ -576,7 +588,7 @@ ssl_cert_issue() {
|
||||
if [[ ${cert_exists} -eq 0 ]]; then
|
||||
# issue the certificate
|
||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} $(acme_listen_flag) --standalone --httpport ${WebPort} --force
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${red}Issuing certificate failed, please check logs.${plain}"
|
||||
rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc
|
||||
|
||||
16
update.sh
16
update.sh
@ -81,6 +81,18 @@ is_domain() {
|
||||
[[ "$1" =~ ^([A-Za-z0-9](-*[A-Za-z0-9])*\.)+(xn--[a-z0-9]{2,}|[A-Za-z]{2,})$ ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
# acme.sh's standalone server binds IPv4 by default; --listen-v6 makes it
|
||||
# v6-only, which breaks HTTP-01 validation when the domain's A record points
|
||||
# at this host's IPv4 (#4994). Only force IPv6 when the host has no global
|
||||
# IPv4 address at all.
|
||||
acme_listen_flag() {
|
||||
if ip -4 addr show scope global 2> /dev/null | grep -q "inet "; then
|
||||
echo ""
|
||||
else
|
||||
echo "--listen-v6"
|
||||
fi
|
||||
}
|
||||
|
||||
# Port helpers
|
||||
is_port_in_use() {
|
||||
local port="$1"
|
||||
@ -200,7 +212,7 @@ setup_ssl_certificate() {
|
||||
echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
|
||||
|
||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force > /dev/null 2>&1
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} $(acme_listen_flag) --standalone --httpport 80 --force
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${yellow}Failed to issue certificate for ${domain}${plain}"
|
||||
@ -465,7 +477,7 @@ ssl_cert_issue() {
|
||||
if [[ ${cert_exists} -eq 0 ]]; then
|
||||
# issue the certificate
|
||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} $(acme_listen_flag) --standalone --httpport ${WebPort} --force
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${red}Issuing certificate failed, please check logs.${plain}"
|
||||
rm -rf ~/.acme.sh/${domain}
|
||||
|
||||
67
x-ui.sh
67
x-ui.sh
@ -50,6 +50,18 @@ is_domain() {
|
||||
[[ "$1" =~ ^([A-Za-z0-9](-*[A-Za-z0-9])*\.)+(xn--[a-z0-9]{2,}|[A-Za-z]{2,})$ ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
# acme.sh's standalone server binds IPv4 by default; --listen-v6 makes it
|
||||
# v6-only, which breaks HTTP-01 validation when the domain's A record points
|
||||
# at this host's IPv4 (#4994). Only force IPv6 when the host has no global
|
||||
# IPv4 address at all.
|
||||
acme_listen_flag() {
|
||||
if ip -4 addr show scope global 2> /dev/null | grep -q "inet "; then
|
||||
echo ""
|
||||
else
|
||||
echo "--listen-v6"
|
||||
fi
|
||||
}
|
||||
|
||||
# check root
|
||||
[[ $EUID -ne 0 ]] && LOGE "ERROR: You must be root to run this script! \n" && exit 1
|
||||
|
||||
@ -361,12 +373,26 @@ check_config() {
|
||||
|
||||
if [[ -n "$existing_cert" ]]; then
|
||||
local domain=$(basename "$(dirname "$existing_cert")")
|
||||
# The cert folder name is only the certificate's first domain. A
|
||||
# multidomain (SAN) certificate may be served under any name it covers,
|
||||
# so read the real names from the certificate itself (#5070).
|
||||
local cert_sans=""
|
||||
if [[ -f "$existing_cert" ]] && command -v openssl > /dev/null 2>&1; then
|
||||
cert_sans=$(openssl x509 -in "$existing_cert" -noout -ext subjectAltName 2> /dev/null \
|
||||
| grep -Eo 'DNS:[^,[:space:]]+' | cut -d: -f2)
|
||||
if [[ -n "$cert_sans" ]] && ! echo "$cert_sans" | grep -qx "$domain"; then
|
||||
domain=$(echo "$cert_sans" | head -n1)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$domain" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
|
||||
echo -e "${green}Access URL: https://${domain}:${existing_port}${existing_webBasePath}${plain}"
|
||||
else
|
||||
echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
|
||||
fi
|
||||
if [[ -n "$cert_sans" && $(echo "$cert_sans" | wc -l) -gt 1 ]]; then
|
||||
echo -e "${yellow}The certificate also covers:${plain} $(echo "$cert_sans" | grep -vx "$domain" | tr '\n' ' ')"
|
||||
fi
|
||||
else
|
||||
echo -e "${red}⚠ WARNING: No SSL certificate configured!${plain}"
|
||||
echo -e "${yellow}You can get a Let's Encrypt certificate for your IP address (valid ~6 days, auto-renews).${plain}"
|
||||
@ -1231,7 +1257,7 @@ ssl_cert_issue_main() {
|
||||
ssl_cert_issue_main
|
||||
;;
|
||||
2)
|
||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \; 2> /dev/null)
|
||||
if [ -z "$domains" ]; then
|
||||
echo "No certificates found to revoke."
|
||||
else
|
||||
@ -1272,7 +1298,7 @@ ssl_cert_issue_main() {
|
||||
ssl_cert_issue_main
|
||||
;;
|
||||
3)
|
||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \; 2> /dev/null)
|
||||
if [ -z "$domains" ]; then
|
||||
echo "No certificates found to renew."
|
||||
else
|
||||
@ -1289,9 +1315,9 @@ ssl_cert_issue_main() {
|
||||
ssl_cert_issue_main
|
||||
;;
|
||||
4)
|
||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \; 2> /dev/null)
|
||||
if [ -z "$domains" ]; then
|
||||
echo "No certificates found."
|
||||
echo "No certificates found under /root/cert."
|
||||
else
|
||||
echo "Existing domains and their paths:"
|
||||
for domain in $domains; do
|
||||
@ -1306,10 +1332,39 @@ ssl_cert_issue_main() {
|
||||
fi
|
||||
done
|
||||
fi
|
||||
# The panel's configured certificate may live outside /root/cert
|
||||
# (e.g. certbot under /etc/letsencrypt) — show it too (#5070).
|
||||
local panel_cert=$(${xui_folder}/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
|
||||
if [[ -n "${panel_cert}" && "${panel_cert}" != /root/cert/* ]]; then
|
||||
echo -e "Panel certificate (custom path): ${panel_cert}"
|
||||
if [[ -f "${panel_cert}" ]] && command -v openssl > /dev/null 2>&1; then
|
||||
local panel_sans=$(openssl x509 -in "${panel_cert}" -noout -ext subjectAltName 2> /dev/null \
|
||||
| grep -Eo 'DNS:[^,[:space:]]+' | cut -d: -f2 | tr '\n' ' ')
|
||||
[[ -n "${panel_sans}" ]] && echo -e "\tCovers: ${panel_sans}"
|
||||
fi
|
||||
fi
|
||||
ssl_cert_issue_main
|
||||
;;
|
||||
5)
|
||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||
echo -e "${green}\t1.${plain} Use a certificate from /root/cert"
|
||||
echo -e "${green}\t2.${plain} Enter custom certificate file paths (e.g. certbot, /etc/letsencrypt/...)"
|
||||
read -rp "Choose an option: " pathChoice
|
||||
if [[ "$pathChoice" == "2" ]]; then
|
||||
read -rp "Certificate file path (fullchain): " webCertFile
|
||||
read -rp "Private key file path: " webKeyFile
|
||||
if [[ -f "${webCertFile}" && -f "${webKeyFile}" ]]; then
|
||||
${xui_folder}/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile"
|
||||
echo "Panel certificate paths set:"
|
||||
echo " - Certificate File: $webCertFile"
|
||||
echo " - Private Key File: $webKeyFile"
|
||||
restart
|
||||
else
|
||||
echo "Certificate or private key file not found."
|
||||
fi
|
||||
ssl_cert_issue_main
|
||||
return
|
||||
fi
|
||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \; 2> /dev/null)
|
||||
if [ -z "$domains" ]; then
|
||||
echo "No certificates found."
|
||||
else
|
||||
@ -1691,7 +1746,7 @@ ssl_cert_issue() {
|
||||
if [[ ${cert_exists} -eq 0 ]]; then
|
||||
# issue the certificate
|
||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
||||
~/.acme.sh/acme.sh --issue -d ${domain} $(acme_listen_flag) --standalone --httpport ${WebPort} --force
|
||||
if [ $? -ne 0 ]; then
|
||||
LOGE "Issuing certificate failed, please check logs."
|
||||
rm -rf ~/.acme.sh/${domain} ~/.acme.sh/${domain}_ecc
|
||||
|
||||
Reference in New Issue
Block a user