diff options
| author | Sanaei <ho3ein.sanaei@gmail.com> | 2026-01-05 07:28:02 +0300 |
|---|---|---|
| committer | MHSanaei <ho3ein.sanaei@gmail.com> | 2026-01-05 07:47:15 +0300 |
| commit | a9770e1da2453269a6337f0e8ab469c44ef08af5 (patch) | |
| tree | 63157d68fe6bc9655d6a5283e137580fe009913b /x-ui.sh | |
| parent | 3f15d21f1321932324dffc8c1cacee7dce5b9dc4 (diff) | |
ip cert (#3631)
Diffstat (limited to 'x-ui.sh')
| -rw-r--r-- | x-ui.sh | 207 |
1 files changed, 93 insertions, 114 deletions
@@ -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 |
