Sanaei 3 days ago
parent
commit
a9770e1da2
3 changed files with 377 additions and 257 deletions
  1. 144 73
      install.sh
  2. 140 70
      update.sh
  3. 93 114
      x-ui.sh

+ 144 - 73
install.sh

@@ -59,29 +59,29 @@ is_domain() {
 install_base() {
     case "${release}" in
         ubuntu | debian | armbian)
-            apt-get update && apt-get install -y -q curl tar tzdata openssl socat
+            apt-get update && apt-get install -y -q curl tar tzdata socat
         ;;
         fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
-            dnf -y update && dnf install -y -q curl tar tzdata openssl socat
+            dnf -y update && dnf install -y -q curl tar tzdata socat
         ;;
         centos)
             if [[ "${VERSION_ID}" =~ ^7 ]]; then
-                yum -y update && yum install -y curl tar tzdata openssl socat
+                yum -y update && yum install -y curl tar tzdata socat
             else
-                dnf -y update && dnf install -y -q curl tar tzdata openssl socat
+                dnf -y update && dnf install -y -q curl tar tzdata socat
             fi
         ;;
         arch | manjaro | parch)
-            pacman -Syu && pacman -Syu --noconfirm curl tar tzdata openssl socat
+            pacman -Syu && pacman -Syu --noconfirm curl tar tzdata socat
         ;;
         opensuse-tumbleweed | opensuse-leap)
-            zypper refresh && zypper -q install -y curl tar timezone openssl socat
+            zypper refresh && zypper -q install -y curl tar timezone socat
         ;;
         alpine)
-            apk update && apk add curl tar tzdata openssl socat
+            apk update && apk add curl tar tzdata socat
         ;;
         *)
-            apt-get update && apt-get install -y -q curl tar tzdata openssl socat
+            apt-get update && apt-get install -y -q curl tar tzdata socat
         ;;
     esac
 }
@@ -154,7 +154,9 @@ setup_ssl_certificate() {
     
     # Enable auto-renew
     ~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
-    chmod 755 $certPath/* 2>/dev/null
+    # Secure permissions: private key readable only by owner
+    chmod 600 $certPath/privkey.pem 2>/dev/null
+    chmod 644 $certPath/fullchain.pem 2>/dev/null
     
     # Set certificate for panel
     local webCertFile="/root/cert/${domain}/fullchain.pem"
@@ -170,56 +172,118 @@ setup_ssl_certificate() {
     fi
 }
 
-# Fallback: generate a self-signed certificate (not publicly trusted)
-setup_self_signed_certificate() {
-    local name="$1"   # domain or IP to place in SAN
-    local certDir="/root/cert/selfsigned"
+# Issue Let's Encrypt IP certificate with shortlived profile (~6 days validity)
+# Requires acme.sh and port 80 open for HTTP-01 challenge
+setup_ip_certificate() {
+    local ipv4="$1"
+    local ipv6="$2"  # optional
 
-    echo -e "${yellow}Generating a self-signed certificate (not publicly trusted)...${plain}"
+    echo -e "${green}Setting up Let's Encrypt IP certificate (shortlived profile)...${plain}"
+    echo -e "${yellow}Note: IP certificates are valid for ~6 days and will auto-renew.${plain}"
+    echo -e "${yellow}Port 80 must be open and accessible from the internet.${plain}"
 
+    # Check for acme.sh
+    if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
+        install_acme
+        if [ $? -ne 0 ]; then
+            echo -e "${red}Failed to install acme.sh${plain}"
+            return 1
+        fi
+    fi
+
+    # Validate IP address
+    if [[ -z "$ipv4" ]]; then
+        echo -e "${red}IPv4 address is required${plain}"
+        return 1
+    fi
+
+    if ! is_ipv4 "$ipv4"; then
+        echo -e "${red}Invalid IPv4 address: $ipv4${plain}"
+        return 1
+    fi
+
+    # Create certificate directory
+    local certDir="/root/cert/ip"
     mkdir -p "$certDir"
 
-    local sanExt=""
-    if is_ip "$name"; then
-        sanExt="IP:${name}"
-    else
-        sanExt="DNS:${name}"
+    # Build domain arguments
+    local domain_args="-d ${ipv4}"
+    if [[ -n "$ipv6" ]] && is_ipv6 "$ipv6"; then
+        domain_args="${domain_args} -d ${ipv6}"
+        echo -e "${green}Including IPv6 address: ${ipv6}${plain}"
     fi
 
-    # Use -addext if supported; fallback to config file if needed
-    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
-        -keyout "${certDir}/privkey.pem" \
-        -out "${certDir}/fullchain.pem" \
-        -subj "/CN=${name}" \
-        -addext "subjectAltName=${sanExt}" >/dev/null 2>&1
+    # Set reload command for auto-renewal (add || true so it doesn't fail during first install)
+    local reloadCmd="systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null || true"
 
-    if [[ $? -ne 0 ]]; then
-        # Fallback via temporary config file (for older OpenSSL versions)
-        local tmpCfg="${certDir}/openssl.cnf"
-        cat > "$tmpCfg" <<EOF
-[req]
-distinguished_name=req_distinguished_name
-req_extensions=v3_req
-[req_distinguished_name]
-[v3_req]
-subjectAltName=${sanExt}
-EOF
-        openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
-            -keyout "${certDir}/privkey.pem" \
-            -out "${certDir}/fullchain.pem" \
-            -subj "/CN=${name}" \
-            -config "$tmpCfg" -extensions v3_req >/dev/null 2>&1
-        rm -f "$tmpCfg"
+    # Issue certificate with shortlived profile
+    echo -e "${green}Issuing IP certificate for ${ipv4}...${plain}"
+    ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1
+    
+    ~/.acme.sh/acme.sh --issue \
+        ${domain_args} \
+        --standalone \
+        --server letsencrypt \
+        --certificate-profile shortlived \
+        --days 6 \
+        --httpport 80 \
+        --force
+
+    if [ $? -ne 0 ]; then
+        echo -e "${red}Failed to issue IP certificate${plain}"
+        echo -e "${yellow}Please ensure port 80 is open and accessible from the internet${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 ${certDir} 2>/dev/null
+        return 1
     fi
 
+    echo -e "${green}Certificate issued successfully, installing...${plain}"
+
+    # Install certificate
+    # Note: acme.sh may report "Reload error" and exit non-zero if reloadcmd fails,
+    # but the cert files are still installed. We check for files instead of exit code.
+    ~/.acme.sh/acme.sh --installcert -d ${ipv4} \
+        --key-file "${certDir}/privkey.pem" \
+        --fullchain-file "${certDir}/fullchain.pem" \
+        --reloadcmd "${reloadCmd}" 2>&1 || true
+
+    # Verify certificate files exist (don't rely on exit code - reloadcmd failure causes non-zero)
     if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
-        echo -e "${red}Failed to generate self-signed certificate${plain}"
+        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 ${certDir} 2>/dev/null
         return 1
     fi
+    
+    echo -e "${green}Certificate files installed successfully${plain}"
+
+    # Enable auto-upgrade for acme.sh (ensures cron job runs)
+    ~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
+
+    # Secure permissions: private key readable only by owner
+    chmod 600 ${certDir}/privkey.pem 2>/dev/null
+    chmod 644 ${certDir}/fullchain.pem 2>/dev/null
+
+    # Configure panel to use the certificate
+    echo -e "${green}Setting certificate paths for the panel...${plain}"
+    ${xui_folder}/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem"
+    
+    if [ $? -ne 0 ]; then
+        echo -e "${yellow}Warning: Could not set certificate paths automatically${plain}"
+        echo -e "${yellow}Certificate files are at:${plain}"
+        echo -e "  Cert: ${certDir}/fullchain.pem"
+        echo -e "  Key:  ${certDir}/privkey.pem"
+    else
+        echo -e "${green}Certificate paths configured successfully${plain}"
+    fi
 
-    chmod 755 ${certDir}/* 2>/dev/null
-    ${xui_folder}/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem" >/dev/null 2>&1
-    echo -e "${yellow}Self-signed certificate configured. Browsers will show a warning.${plain}"
+    echo -e "${green}IP certificate installed and configured successfully!${plain}"
+    echo -e "${green}Certificate valid for ~6 days, auto-renews via acme.sh cron job.${plain}"
+    echo -e "${yellow}acme.sh will automatically renew and reload x-ui before expiry.${plain}"
     return 0
 }
 
@@ -352,14 +416,18 @@ ssl_cert_issue() {
     if [ $? -ne 0 ]; then
         echo -e "${yellow}Auto renew setup had issues, certificate details:${plain}"
         ls -lah /root/cert/${domain}/
-        chmod 755 $certPath/*
+        # Secure permissions: private key readable only by owner
+        chmod 600 $certPath/privkey.pem 2>/dev/null
+        chmod 644 $certPath/fullchain.pem 2>/dev/null
     else
         echo -e "${green}Auto renew succeeded, certificate details:${plain}"
         ls -lah /root/cert/${domain}/
-        chmod 755 $certPath/*
+        # Secure permissions: private key readable only by owner
+        chmod 600 $certPath/privkey.pem 2>/dev/null
+        chmod 644 $certPath/fullchain.pem 2>/dev/null
     fi
 
-    # Restart panel
+    # start panel
     systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null
 
     # Prompt user to set panel paths after successful certificate installation
@@ -387,7 +455,7 @@ ssl_cert_issue() {
     return 0
 }
 
-# Reusable interactive SSL setup (domain or self-signed)
+# Reusable interactive SSL setup (domain or IP)
 # Sets global `SSL_HOST` to the chosen domain/IP for Access URL usage
 prompt_and_setup_ssl() {
     local panel_port="$1"
@@ -397,12 +465,13 @@ prompt_and_setup_ssl() {
     local ssl_choice=""
 
     echo -e "${yellow}Choose SSL certificate setup method:${plain}"
-    echo -e "${green}1.${plain} Let's Encrypt (domain required, recommended)"
-    echo -e "${green}2.${plain} Self-signed certificate (not publicly trusted)"
-    read -rp "Choose an option (default 2): " ssl_choice
+    echo -e "${green}1.${plain} Let's Encrypt for Domain (90-day validity, auto-renews)"
+    echo -e "${green}2.${plain} Let's Encrypt for IP Address (6-day validity, auto-renews)"
+    echo -e "${blue}Note:${plain} Both options require port 80 open. IP certs use shortlived profile."
+    read -rp "Choose an option (default 2 for IP): " ssl_choice
     ssl_choice="${ssl_choice// /}"  # Trim whitespace
     
-    # Default to 2 (self-signed) if not 1
+    # Default to 2 (IP cert) if not 1
     if [[ "$ssl_choice" != "1" ]]; then
         ssl_choice="2"
     fi
@@ -410,7 +479,7 @@ prompt_and_setup_ssl() {
     case "$ssl_choice" in
     1)
         # User chose Let's Encrypt domain option
-        echo -e "${green}Using ssl_cert_issue() for comprehensive domain setup...${plain}"
+        echo -e "${green}Using Let's Encrypt for domain certificate...${plain}"
         ssl_cert_issue
         # Extract the domain that was used from the certificate
         local cert_domain=$(~/.acme.sh/acme.sh --list 2>/dev/null | tail -1 | awk '{print $1}')
@@ -423,28 +492,30 @@ prompt_and_setup_ssl() {
         fi
         ;;
     2)
-        # User chose self-signed option
-        # Stop panel if running
+        # User chose Let's Encrypt IP certificate option
+        echo -e "${green}Using Let's Encrypt for IP certificate (shortlived profile)...${plain}"
+        
+        # Ask for optional IPv6
+        local ipv6_addr=""
+        read -rp "Do you have an IPv6 address to include? (leave empty to skip): " ipv6_addr
+        ipv6_addr="${ipv6_addr// /}"  # Trim whitespace
+        
+        # Stop panel if running (port 80 needed)
         if [[ $release == "alpine" ]]; then
             rc-service x-ui stop >/dev/null 2>&1
         else
             systemctl stop x-ui >/dev/null 2>&1
         fi
-        echo -e "${yellow}Using server IP for self-signed certificate: ${server_ip}${plain}"
-        setup_self_signed_certificate "${server_ip}"
+        
+        setup_ip_certificate "${server_ip}" "${ipv6_addr}"
         if [ $? -eq 0 ]; then
             SSL_HOST="${server_ip}"
-            echo -e "${green}✓ Self-signed SSL configured successfully${plain}"
+            echo -e "${green}✓ Let's Encrypt IP certificate configured successfully${plain}"
         else
-            echo -e "${red}✗ Self-signed SSL setup failed${plain}"
+            echo -e "${red}✗ IP certificate setup failed. Please check port 80 is open.${plain}"
             SSL_HOST="${server_ip}"
         fi
-        # Start panel after SSL is configured
-        if [[ $release == "alpine" ]]; then
-            rc-service x-ui start >/dev/null 2>&1
-        else
-            systemctl start x-ui >/dev/null 2>&1
-        fi
+        
         ;;
     *)
         echo -e "${red}Invalid option. Skipping SSL setup.${plain}"
@@ -497,7 +568,7 @@ config_after_install() {
             echo -e "${green}     SSL Certificate Setup (MANDATORY)     ${plain}"
             echo -e "${green}═══════════════════════════════════════════${plain}"
             echo -e "${yellow}For security, SSL certificate is required for all panels.${plain}"
-            echo -e "${yellow}Let's Encrypt requires a domain name (IP certificates are not issued).${plain}"
+            echo -e "${yellow}Let's Encrypt now supports both domains and IP addresses!${plain}"
             echo ""
 
             prompt_and_setup_ssl "${config_port}" "${config_webBasePath}" "${server_ip}"
@@ -527,7 +598,7 @@ config_after_install() {
                 echo -e "${green}═══════════════════════════════════════════${plain}"
                 echo -e "${green}     SSL Certificate Setup (RECOMMENDED)   ${plain}"
                 echo -e "${green}═══════════════════════════════════════════${plain}"
-                echo -e "${yellow}Let's Encrypt requires a domain name (IP certificates are not issued).${plain}"
+                echo -e "${yellow}Let's Encrypt now supports both domains and IP addresses!${plain}"
                 echo ""
                 prompt_and_setup_ssl "${existing_port}" "${config_webBasePath}" "${server_ip}"
                 echo -e "${green}Access URL:  https://${SSL_HOST}:${existing_port}/${config_webBasePath}${plain}"
@@ -552,7 +623,7 @@ config_after_install() {
             echo -e "${green}Username, Password, and WebBasePath are properly set.${plain}"
         fi
 
-        # Existing install: if no cert configured, prompt user to set domain or self-signed
+        # Existing install: if no cert configured, prompt user for SSL setup
         # Properly detect empty cert by checking if cert: line exists and has content after it
         existing_cert=$(${xui_folder}/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
         if [[ -z "$existing_cert" ]]; then
@@ -560,7 +631,7 @@ config_after_install() {
             echo -e "${green}═══════════════════════════════════════════${plain}"
             echo -e "${green}     SSL Certificate Setup (RECOMMENDED)   ${plain}"
             echo -e "${green}═══════════════════════════════════════════${plain}"
-            echo -e "${yellow}Let's Encrypt requires a domain name (IP certificates are not issued).${plain}"
+            echo -e "${yellow}Let's Encrypt now supports both domains and IP addresses!${plain}"
             echo ""
             prompt_and_setup_ssl "${existing_port}" "${existing_webBasePath}" "${server_ip}"
             echo -e "${green}Access URL:  https://${SSL_HOST}:${existing_port}/${existing_webBasePath}${plain}"
@@ -587,7 +658,7 @@ install_x-ui() {
             fi
         fi
         echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
-        curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz -z ${xui_folder}-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz
+        curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz
         if [[ $? -ne 0 ]]; then
             echo -e "${red}Downloading x-ui failed, please be sure that your server can access GitHub ${plain}"
             exit 1
@@ -604,7 +675,7 @@ install_x-ui() {
         
         url="https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz"
         echo -e "Beginning to install x-ui $1"
-        curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz -z ${xui_folder}-linux-$(arch).tar.gz ${url}
+        curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz ${url}
         if [[ $? -ne 0 ]]; then
             echo -e "${red}Download x-ui $1 failed, please check if the version exists ${plain}"
             exit 1

+ 140 - 70
update.sh

@@ -91,29 +91,29 @@ install_base() {
     echo -e "${green}Updating and install dependency packages...${plain}"
     case "${release}" in
         ubuntu | debian | armbian)
-            apt-get update >/dev/null 2>&1 && apt-get install -y -q curl tar tzdata openssl socat >/dev/null 2>&1
+            apt-get update >/dev/null 2>&1 && apt-get install -y -q curl tar tzdata socat >/dev/null 2>&1
         ;;
         fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
-            dnf -y update >/dev/null 2>&1 && dnf install -y -q curl tar tzdata openssl socat >/dev/null 2>&1
+            dnf -y update >/dev/null 2>&1 && dnf install -y -q curl tar tzdata socat >/dev/null 2>&1
         ;;
         centos)
             if [[ "${VERSION_ID}" =~ ^7 ]]; then
-                yum -y update >/dev/null 2>&1 && yum install -y -q curl tar tzdata openssl socat >/dev/null 2>&1
+                yum -y update >/dev/null 2>&1 && yum install -y -q curl tar tzdata socat >/dev/null 2>&1
             else
-                dnf -y update >/dev/null 2>&1 && dnf install -y -q curl tar tzdata openssl socat >/dev/null 2>&1
+                dnf -y update >/dev/null 2>&1 && dnf install -y -q curl tar tzdata socat >/dev/null 2>&1
             fi
         ;;
         arch | manjaro | parch)
-            pacman -Syu >/dev/null 2>&1 && pacman -Syu --noconfirm curl tar tzdata openssl socat >/dev/null 2>&1
+            pacman -Syu >/dev/null 2>&1 && pacman -Syu --noconfirm curl tar tzdata socat >/dev/null 2>&1
         ;;
         opensuse-tumbleweed | opensuse-leap)
-            zypper refresh >/dev/null 2>&1 && zypper -q install -y curl tar timezone openssl socat >/dev/null 2>&1
+            zypper refresh >/dev/null 2>&1 && zypper -q install -y curl tar timezone socat >/dev/null 2>&1
         ;;
         alpine)
-            apk update >/dev/null 2>&1 && apk add curl tar tzdata openssl socat >/dev/null 2>&1
+            apk update >/dev/null 2>&1 && apk add curl tar tzdata socat >/dev/null 2>&1
         ;;
         *)
-            apt-get update >/dev/null 2>&1 && apt install -y -q curl tar tzdata openssl socat >/dev/null 2>&1
+            apt-get update >/dev/null 2>&1 && apt install -y -q curl tar tzdata socat >/dev/null 2>&1
         ;;
     esac
 }
@@ -180,7 +180,8 @@ setup_ssl_certificate() {
     
     # Enable auto-renew
     ~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
-    chmod 755 $certPath/* 2>/dev/null
+    chmod 600 $certPath/privkey.pem 2>/dev/null
+    chmod 644 $certPath/fullchain.pem 2>/dev/null
     
     # Set certificate for panel
     local webCertFile="/root/cert/${domain}/fullchain.pem"
@@ -196,57 +197,119 @@ setup_ssl_certificate() {
     fi
 }
 
-# Fallback: generate a self-signed certificate (not publicly trusted)
-setup_self_signed_certificate() {
-    local name="$1"   # domain or IP to place in SAN
-    local certDir="/root/cert/selfsigned"
+# Issue Let's Encrypt IP certificate with shortlived profile (~6 days validity)
+# Requires acme.sh and port 80 open for HTTP-01 challenge
+setup_ip_certificate() {
+    local ipv4="$1"
+    local ipv6="$2"  # optional
 
-    echo -e "${yellow}Generating a self-signed certificate (not publicly trusted)...${plain}"
+    echo -e "${green}Setting up Let's Encrypt IP certificate (shortlived profile)...${plain}"
+    echo -e "${yellow}Note: IP certificates are valid for ~6 days and will auto-renew.${plain}"
+    echo -e "${yellow}Port 80 must be open and accessible from the internet.${plain}"
 
+    # Check for acme.sh
+    if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
+        install_acme
+        if [ $? -ne 0 ]; then
+            echo -e "${red}Failed to install acme.sh${plain}"
+            return 1
+        fi
+    fi
+
+    # Validate IP address
+    if [[ -z "$ipv4" ]]; then
+        echo -e "${red}IPv4 address is required${plain}"
+        return 1
+    fi
+
+    if ! is_ipv4 "$ipv4"; then
+        echo -e "${red}Invalid IPv4 address: $ipv4${plain}"
+        return 1
+    fi
+
+    # Create certificate directory
+    local certDir="/root/cert/ip"
     mkdir -p "$certDir"
 
-    local sanExt=""
-    if is_ip "$name"; then
-        sanExt="IP:${name}"
-    else
-        sanExt="DNS:${name}"
+    # Build domain arguments
+    local domain_args="-d ${ipv4}"
+    if [[ -n "$ipv6" ]] && is_ipv6 "$ipv6"; then
+        domain_args="${domain_args} -d ${ipv6}"
+        echo -e "${green}Including IPv6 address: ${ipv6}${plain}"
     fi
 
-    # Try -addext; fallback to config if not supported
-    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
-        -keyout "${certDir}/privkey.pem" \
-        -out "${certDir}/fullchain.pem" \
-        -subj "/CN=${name}" \
-        -addext "subjectAltName=${sanExt}" >/dev/null 2>&1
+    # Set reload command for auto-renewal (add || true so it doesn't fail if service stopped)
+    local reloadCmd="systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null || true"
 
-    if [[ $? -ne 0 ]]; then
-        local tmpCfg="${certDir}/openssl.cnf"
-        cat > "$tmpCfg" <<EOF
-[req]
-distinguished_name=req_distinguished_name
-req_extensions=v3_req
-[req_distinguished_name]
-[v3_req]
-subjectAltName=${sanExt}
-EOF
-        openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
-            -keyout "${certDir}/privkey.pem" \
-            -out "${certDir}/fullchain.pem" \
-            -subj "/CN=${name}" \
-            -config "$tmpCfg" -extensions v3_req >/dev/null 2>&1
-        rm -f "$tmpCfg"
+    # Issue certificate with shortlived profile
+    echo -e "${green}Issuing IP certificate for ${ipv4}...${plain}"
+    ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1
+    
+    ~/.acme.sh/acme.sh --issue \
+        ${domain_args} \
+        --standalone \
+        --server letsencrypt \
+        --certificate-profile shortlived \
+        --days 6 \
+        --httpport 80 \
+        --force
+
+    if [ $? -ne 0 ]; then
+        echo -e "${red}Failed to issue IP certificate${plain}"
+        echo -e "${yellow}Please ensure port 80 is open and accessible from the internet${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 ${certDir} 2>/dev/null
+        return 1
     fi
 
+    echo -e "${green}Certificate issued successfully, installing...${plain}"
+
+    # Install certificate
+    # Note: acme.sh may report "Reload error" and exit non-zero if reloadcmd fails,
+    # but the cert files are still installed. We check for files instead of exit code.
+    ~/.acme.sh/acme.sh --installcert -d ${ipv4} \
+        --key-file "${certDir}/privkey.pem" \
+        --fullchain-file "${certDir}/fullchain.pem" \
+        --reloadcmd "${reloadCmd}" 2>&1 || true
+
+    # Verify certificate files exist (don't rely on exit code - reloadcmd failure causes non-zero)
     if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
-        echo -e "${red}Failed to generate self-signed certificate${plain}"
+        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 ${certDir} 2>/dev/null
         return 1
     fi
+    
+    echo -e "${green}Certificate files installed successfully${plain}"
 
-    chmod 755 ${certDir}/* 2>/dev/null
-    ${xui_folder}/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem" >/dev/null 2>&1
-    echo -e "${yellow}Self-signed certificate configured. Browsers will show a warning.${plain}"
+    # Enable auto-upgrade for acme.sh (ensures cron job runs)
+    ~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
+
+    chmod 600 ${certDir}/privkey.pem 2>/dev/null
+    chmod 644 ${certDir}/fullchain.pem 2>/dev/null
+
+    # Configure panel to use the certificate
+    echo -e "${green}Setting certificate paths for the panel...${plain}"
+    ${xui_folder}/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem"
+    if [ $? -ne 0 ]; then
+        echo -e "${yellow}Warning: Could not set certificate paths automatically.${plain}"
+        echo -e "${yellow}You may need to set them manually in the panel settings.${plain}"
+        echo -e "${yellow}Cert path: ${certDir}/fullchain.pem${plain}"
+        echo -e "${yellow}Key path: ${certDir}/privkey.pem${plain}"
+    else
+        echo -e "${green}Certificate paths set successfully!${plain}"
+    fi
+
+    echo -e "${green}IP certificate installed and configured successfully!${plain}"
+    echo -e "${green}Certificate valid for ~6 days, auto-renews via acme.sh cron job.${plain}"
+    echo -e "${yellow}Panel will automatically restart after each renewal.${plain}"
     return 0
 }
+
 # Comprehensive manual SSL certificate issuance via acme.sh
 ssl_cert_issue() {
     local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep 'webBasePath:' | awk -F': ' '{print $2}' | tr -d '[:space:]' | sed 's#^/##')
@@ -376,11 +439,13 @@ ssl_cert_issue() {
     if [ $? -ne 0 ]; then
         echo -e "${yellow}Auto renew setup had issues, certificate details:${plain}"
         ls -lah /root/cert/${domain}/
-        chmod 755 $certPath/*
+        chmod 600 $certPath/privkey.pem
+        chmod 644 $certPath/fullchain.pem
     else
         echo -e "${green}Auto renew succeeded, certificate details:${plain}"
         ls -lah /root/cert/${domain}/
-        chmod 755 $certPath/*
+        chmod 600 $certPath/privkey.pem
+        chmod 644 $certPath/fullchain.pem
     fi
 
     # Restart panel
@@ -410,7 +475,7 @@ ssl_cert_issue() {
     
     return 0
 }
-# Unified interactive SSL setup (domain or self-signed)
+# Unified interactive SSL setup (domain or IP)
 # Sets global `SSL_HOST` to the chosen domain/IP
 prompt_and_setup_ssl() {
     local panel_port="$1"
@@ -420,12 +485,13 @@ prompt_and_setup_ssl() {
     local ssl_choice=""
 
     echo -e "${yellow}Choose SSL certificate setup method:${plain}"
-    echo -e "${green}1.${plain} Let's Encrypt (domain required, recommended)"
-    echo -e "${green}2.${plain} Self-signed certificate (for testing/local use)"
-    read -rp "Choose an option (default 2): " ssl_choice
+    echo -e "${green}1.${plain} Let's Encrypt for Domain (90-day validity, auto-renews)"
+    echo -e "${green}2.${plain} Let's Encrypt for IP Address (6-day validity, auto-renews)"
+    echo -e "${blue}Note:${plain} Both options require port 80 open. IP certs use shortlived profile."
+    read -rp "Choose an option (default 2 for IP): " ssl_choice
     ssl_choice="${ssl_choice// /}"  # Trim whitespace
     
-    # Default to 2 (self-signed) if not 1
+    # Default to 2 (IP cert) if not 1
     if [[ "$ssl_choice" != "1" ]]; then
         ssl_choice="2"
     fi
@@ -433,7 +499,7 @@ prompt_and_setup_ssl() {
     case "$ssl_choice" in
     1)
         # User chose Let's Encrypt domain option
-        echo -e "${green}Using ssl_cert_issue() for comprehensive domain setup...${plain}"
+        echo -e "${green}Using Let's Encrypt for domain certificate...${plain}"
         ssl_cert_issue
         # Extract the domain that was used from the certificate
         local cert_domain=$(~/.acme.sh/acme.sh --list 2>/dev/null | tail -1 | awk '{print $1}')
@@ -446,33 +512,37 @@ prompt_and_setup_ssl() {
         fi
         ;;
     2)
-        # User chose self-signed option
-        # Stop panel if running
+        # User chose Let's Encrypt IP certificate option
+        echo -e "${green}Using Let's Encrypt for IP certificate (shortlived profile)...${plain}"
+        
+        # Ask for optional IPv6
+        local ipv6_addr=""
+        read -rp "Do you have an IPv6 address to include? (leave empty to skip): " ipv6_addr
+        ipv6_addr="${ipv6_addr// /}"  # Trim whitespace
+        
+        # Stop panel if running (port 80 needed)
         if [[ $release == "alpine" ]]; then
             rc-service x-ui stop >/dev/null 2>&1
         else
             systemctl stop x-ui >/dev/null 2>&1
         fi
-        echo -e "${yellow}Using server IP for self-signed certificate: ${server_ip}${plain}"
-        setup_self_signed_certificate "${server_ip}"
+        
+        setup_ip_certificate "${server_ip}" "${ipv6_addr}"
         if [ $? -eq 0 ]; then
             SSL_HOST="${server_ip}"
-            echo -e "${green}✓ Self-signed SSL configured successfully${plain}"
+            echo -e "${green}✓ Let's Encrypt IP certificate configured successfully${plain}"
         else
-            echo -e "${red}✗ Self-signed SSL setup failed${plain}"
+            echo -e "${red}✗ IP certificate setup failed. Please check port 80 is open.${plain}"
             SSL_HOST="${server_ip}"
         fi
-        # Start panel after SSL is configured
+        
+        # Restart panel after SSL is configured (restart applies new cert settings)
         if [[ $release == "alpine" ]]; then
-            rc-service x-ui start >/dev/null 2>&1
+            rc-service x-ui restart >/dev/null 2>&1
         else
-            systemctl start x-ui >/dev/null 2>&1
+            systemctl restart x-ui >/dev/null 2>&1
         fi
         ;;
-    0)
-        echo -e "${yellow}Skipping SSL setup${plain}"
-        SSL_HOST="${server_ip}"
-        ;;
     *)
         echo -e "${red}Invalid option. Skipping SSL setup.${plain}"
         SSL_HOST="${server_ip}"
@@ -523,7 +593,7 @@ config_after_update() {
         echo -e "${red}      ⚠ NO SSL CERTIFICATE DETECTED ⚠     ${plain}"
         echo -e "${red}═══════════════════════════════════════════${plain}"
         echo -e "${yellow}For security, SSL certificate is MANDATORY for all panels.${plain}"
-        echo -e "${yellow}Let's Encrypt requires a domain name; IP certs are not issued. Use self-signed for IP.${plain}"
+        echo -e "${yellow}Let's Encrypt now supports both domains and IP addresses!${plain}"
         echo ""
         
         if [[ -z "${server_ip}" ]]; then
@@ -532,7 +602,7 @@ config_after_update() {
             return
         fi
         
-        # Prompt and setup SSL (domain or self-signed)
+        # Prompt and setup SSL (domain or IP)
         prompt_and_setup_ssl "${existing_port}" "${existing_webBasePath}" "${server_ip}"
         
         echo ""
@@ -576,10 +646,10 @@ update_x-ui() {
         fi
     fi
     echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
-    ${curl_bin} -fLRo ${xui_folder}-linux-$(arch).tar.gz -z ${xui_folder}-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz 2>/dev/null
+    ${curl_bin} -fLRo ${xui_folder}-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz 2>/dev/null
     if [[ $? -ne 0 ]]; then
         echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
-        ${curl_bin} -4fLRo ${xui_folder}-linux-$(arch).tar.gz -z ${xui_folder}-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz 2>/dev/null
+        ${curl_bin} -4fLRo ${xui_folder}-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz 2>/dev/null
         if [[ $? -ne 0 ]]; then
             _fail "ERROR: Failed to download x-ui, please be sure that your server can access GitHub"
         fi

+ 93 - 114
x-ui.sh

@@ -230,57 +230,6 @@ gen_random_string() {
     echo "$random_string"
 }
 
-# Generate and configure a self-signed SSL certificate
-setup_self_signed_certificate() {
-    local name="$1"   # domain or IP to place in SAN
-    local certDir="/root/cert/selfsigned"
-
-    LOGI "Generating a self-signed certificate (not publicly trusted)..."
-
-    mkdir -p "$certDir"
-
-    local sanExt=""
-    if [[ "$name" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ || "$name" =~ : ]]; then
-        sanExt="IP:${name}"
-    else
-        sanExt="DNS:${name}"
-    fi
-
-    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
-        -keyout "${certDir}/privkey.pem" \
-        -out "${certDir}/fullchain.pem" \
-        -subj "/CN=${name}" \
-        -addext "subjectAltName=${sanExt}" >/dev/null 2>&1
-
-    if [[ $? -ne 0 ]]; then
-        local tmpCfg="${certDir}/openssl.cnf"
-        cat > "$tmpCfg" <<EOF
-[req]
-distinguished_name=req_distinguished_name
-req_extensions=v3_req
-[req_distinguished_name]
-[v3_req]
-subjectAltName=${sanExt}
-EOF
-        openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
-            -keyout "${certDir}/privkey.pem" \
-            -out "${certDir}/fullchain.pem" \
-            -subj "/CN=${name}" \
-            -config "$tmpCfg" -extensions v3_req >/dev/null 2>&1
-        rm -f "$tmpCfg"
-    fi
-
-    if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
-        LOGE "Failed to generate self-signed certificate"
-        return 1
-    fi
-
-    chmod 755 ${certDir}/* >/dev/null 2>&1
-    ${xui_folder}/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem" >/dev/null 2>&1
-    LOGI "Self-signed certificate configured. Browsers will show a warning."
-    return 0
-}
-
 reset_webbasepath() {
     echo -e "${yellow}Resetting Web Base Path${plain}"
 
@@ -340,16 +289,19 @@ check_config() {
         fi
     else
         echo -e "${red}⚠ WARNING: No SSL certificate configured!${plain}"
-        read -rp "Generate a self-signed SSL certificate now? [y/N]: " gen_self
-        if [[ "$gen_self" == "y" || "$gen_self" == "Y" ]]; then
+        echo -e "${yellow}You can get a Let's Encrypt certificate for your IP address (valid ~6 days, auto-renews).${plain}"
+        read -rp "Generate SSL certificate for IP now? [y/N]: " gen_ssl
+        if [[ "$gen_ssl" == "y" || "$gen_ssl" == "Y" ]]; then
             stop >/dev/null 2>&1
-            setup_self_signed_certificate "${server_ip}"
+            ssl_cert_issue_for_ip
             if [[ $? -eq 0 ]]; then
-                restart >/dev/null 2>&1
                 echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
+                # ssl_cert_issue_for_ip already restarts the panel, but ensure it's running
+                start >/dev/null 2>&1
             else
-                LOGE "Self-signed SSL setup failed."
+                LOGE "IP certificate setup failed."
                 echo -e "${yellow}You can try again via option 18 (SSL Certificate Management).${plain}"
+                start >/dev/null 2>&1
             fi
         else
             echo -e "${yellow}Access URL: http://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
@@ -1036,12 +988,12 @@ install_acme() {
 }
 
 ssl_cert_issue_main() {
-    echo -e "${green}\t1.${plain} Get SSL"
+    echo -e "${green}\t1.${plain} Get SSL (Domain)"
     echo -e "${green}\t2.${plain} Revoke"
     echo -e "${green}\t3.${plain} Force Renew"
     echo -e "${green}\t4.${plain} Show Existing Domains"
     echo -e "${green}\t5.${plain} Set Cert paths for the panel"
-    echo -e "${green}\t6.${plain} Auto SSL for Server IP"
+    echo -e "${green}\t6.${plain} Get SSL for IP Address (6-day cert, auto-renews)"
     echo -e "${green}\t0.${plain} Back to Main Menu"
 
     read -rp "Choose an option: " choice
@@ -1136,9 +1088,10 @@ ssl_cert_issue_main() {
         ssl_cert_issue_main
         ;;
     6)
-        echo -e "${yellow}Automatic SSL Certificate for Server IP${plain}"
-        echo -e "This will automatically obtain and configure an SSL certificate for your server's IP address."
-        echo -e "${yellow}Note: Let's Encrypt supports IP certificates. Make sure port 80 is open.${plain}"
+        echo -e "${yellow}Let's Encrypt SSL Certificate for IP Address${plain}"
+        echo -e "This will obtain a certificate for your server's IP using the shortlived profile."
+        echo -e "${yellow}Certificate valid for ~6 days, auto-renews via acme.sh cron job.${plain}"
+        echo -e "${yellow}Port 80 must be open and accessible from the internet.${plain}"
         confirm "Do you want to proceed?" "y"
         if [[ $? == 0 ]]; then
             ssl_cert_issue_for_ip
@@ -1155,6 +1108,7 @@ ssl_cert_issue_main() {
 
 ssl_cert_issue_for_ip() {
     LOGI "Starting automatic SSL certificate generation for server IP..."
+    LOGI "Using Let's Encrypt shortlived profile (~6 days validity, auto-renews)"
     
     local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
     local existing_port=$(${xui_folder}/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
@@ -1172,6 +1126,11 @@ ssl_cert_issue_for_ip() {
     
     LOGI "Server IP detected: ${server_ip}"
     
+    # Ask for optional IPv6
+    local ipv6_addr=""
+    read -rp "Do you have an IPv6 address to include? (leave empty to skip): " ipv6_addr
+    ipv6_addr="${ipv6_addr// /}"  # Trim whitespace
+    
     # check for acme.sh first
     if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
         LOGI "acme.sh not found, installing..."
@@ -1211,66 +1170,83 @@ ssl_cert_issue_for_ip() {
         ;;
     esac
     
-    # check if certificate already exists for this IP
-    local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
-    if [ "${currentCert}" == "${server_ip}" ]; then
-        LOGI "Certificate already exists for IP: ${server_ip}"
-        certPath="/root/cert/${server_ip}"
+    # Create certificate directory
+    certPath="/root/cert/ip"
+    mkdir -p "$certPath"
+    
+    # Build domain arguments
+    local domain_args="-d ${server_ip}"
+    if [[ -n "$ipv6_addr" ]] && is_ipv6 "$ipv6_addr"; then
+        domain_args="${domain_args} -d ${ipv6_addr}"
+        LOGI "Including IPv6 address: ${ipv6_addr}"
+    fi
+    
+    # Use port 80 for certificate issuance
+    local WebPort=80
+    LOGI "Using port ${WebPort} to issue certificate for IP: ${server_ip}"
+    LOGI "Make sure port ${WebPort} is open and not in use..."
+    
+    # Reload command - restarts panel after renewal
+    local reloadCmd="systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null"
+    
+    # issue the certificate for IP with shortlived profile
+    ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
+    ~/.acme.sh/acme.sh --issue \
+        ${domain_args} \
+        --standalone \
+        --server letsencrypt \
+        --certificate-profile shortlived \
+        --days 6 \
+        --httpport ${WebPort} \
+        --force
+    
+    if [ $? -ne 0 ]; then
+        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 ${certPath} 2>/dev/null
+        return 1
     else
-        # create directory for certificate
-        certPath="/root/cert/${server_ip}"
-        if [ ! -d "$certPath" ]; then
-            mkdir -p "$certPath"
-        else
-            rm -rf "$certPath"
-            mkdir -p "$certPath"
-        fi
-        
-        # Use port 80 for certificate issuance
-        local WebPort=80
-        LOGI "Using port ${WebPort} to issue certificate for IP: ${server_ip}"
-        LOGI "Make sure port ${WebPort} is open and not in use..."
-        
-        # issue the certificate for IP
-        ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
-        ~/.acme.sh/acme.sh --issue -d ${server_ip} --listen-v6 --standalone --httpport ${WebPort} --force
-        if [ $? -ne 0 ]; then
-            LOGE "Failed to issue certificate for IP: ${server_ip}"
-            LOGE "Make sure port ${WebPort} is open and the server is accessible from the internet"
-            rm -rf ~/.acme.sh/${server_ip}
-            return 1
-        else
-            LOGI "Certificate issued successfully for IP: ${server_ip}"
-        fi
-        
-        # install the certificate
-        ~/.acme.sh/acme.sh --installcert -d ${server_ip} \
-            --key-file /root/cert/${server_ip}/privkey.pem \
-            --fullchain-file /root/cert/${server_ip}/fullchain.pem \
-            --reloadcmd "x-ui restart"
-        
-        if [ $? -ne 0 ]; then
-            LOGE "Failed to install certificate"
-            rm -rf ~/.acme.sh/${server_ip}
-            return 1
-        else
-            LOGI "Certificate installed successfully"
-        fi
-        
-        # enable auto-renew
-        ~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
-        chmod 755 $certPath/*
+        LOGI "Certificate issued successfully for IP: ${server_ip}"
+    fi
+    
+    # Install the certificate
+    # Note: acme.sh may report "Reload error" and exit non-zero if reloadcmd fails,
+    # but the cert files are still installed. We check for files instead of exit code.
+    ~/.acme.sh/acme.sh --installcert -d ${server_ip} \
+        --key-file "${certPath}/privkey.pem" \
+        --fullchain-file "${certPath}/fullchain.pem" \
+        --reloadcmd "${reloadCmd}" 2>&1 || true
+    
+    # Verify certificate files exist (don't rely on exit code - reloadcmd failure causes non-zero)
+    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 ${certPath} 2>/dev/null
+        return 1
     fi
     
+    LOGI "Certificate files installed successfully"
+    
+    # enable auto-renew
+    ~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1
+    chmod 600 $certPath/privkey.pem 2>/dev/null
+    chmod 644 $certPath/fullchain.pem 2>/dev/null
+    
     # Set certificate paths for the panel
-    local webCertFile="/root/cert/${server_ip}/fullchain.pem"
-    local webKeyFile="/root/cert/${server_ip}/privkey.pem"
+    local webCertFile="${certPath}/fullchain.pem"
+    local webKeyFile="${certPath}/privkey.pem"
     
     if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then
         ${xui_folder}/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile"
         LOGI "Certificate configured for panel"
         LOGI "  - Certificate File: $webCertFile"
         LOGI "  - Private Key File: $webKeyFile"
+        LOGI "  - Validity: ~6 days (auto-renews via acme.sh cron)"
         echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
         LOGI "Panel will restart to apply SSL certificate..."
         restart
@@ -1433,12 +1409,14 @@ ssl_cert_issue() {
     if [ $? -ne 0 ]; then
         LOGE "Auto renew failed, certificate details:"
         ls -lah cert/*
-        chmod 755 $certPath/*
+        chmod 600 $certPath/privkey.pem
+        chmod 644 $certPath/fullchain.pem
         exit 1
     else
         LOGI "Auto renew succeeded, certificate details:"
         ls -lah cert/*
-        chmod 755 $certPath/*
+        chmod 600 $certPath/privkey.pem
+        chmod 644 $certPath/fullchain.pem
     fi
 
     # Prompt user to set panel paths after successful certificate installation
@@ -1578,7 +1556,8 @@ ssl_cert_issue_CF() {
         else
             LOGI "The certificate is installed and auto-renewal is turned on. Specific information is as follows:"
             ls -lah ${certPath}/*
-            chmod 755 ${certPath}/*
+            chmod 600 ${certPath}/privkey.pem
+            chmod 644 ${certPath}/fullchain.pem
         fi
 
         # Prompt user to set panel paths after successful certificate installation