diff --git a/DockerInit.sh b/DockerInit.sh index 2c79bd22..debfbbb8 100755 --- a/DockerInit.sh +++ b/DockerInit.sh @@ -27,7 +27,7 @@ case $1 in esac mkdir -p build/bin cd build/bin -curl -sfLRO "https://github.com/XTLS/Xray-core/releases/download/v25.12.2/Xray-linux-${ARCH}.zip" +curl -sfLRO "https://github.com/XTLS/Xray-core/releases/download/v25.12.8/Xray-linux-${ARCH}.zip" unzip "Xray-linux-${ARCH}.zip" rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat mv xray "xray-linux-${FNAME}" diff --git a/install.sh b/install.sh index 7d84259a..eabbad34 100644 --- a/install.sh +++ b/install.sh @@ -584,7 +584,7 @@ install_x-ui() { fi fi echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..." - curl -4fLRo /usr/local/x-ui-linux-$(arch).tar.gz -z /usr/local/x-ui-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 -z ${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 @@ -601,7 +601,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 /usr/local/x-ui-linux-$(arch).tar.gz -z /usr/local/x-ui-linux-$(arch).tar.gz ${url} + curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz -z ${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 diff --git a/update.sh b/update.sh index 0f1c533b..214463b3 100755 --- a/update.sh +++ b/update.sh @@ -115,6 +115,368 @@ install_base() { esac } +install_acme() { + echo -e "${green}Installing acme.sh for SSL certificate management...${plain}" + cd ~ || return 1 + curl -s https://get.acme.sh | sh >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo -e "${red}Failed to install acme.sh${plain}" + return 1 + else + echo -e "${green}acme.sh installed successfully${plain}" + fi + return 0 +} + +setup_ssl_certificate() { + local domain="$1" + local server_ip="$2" + local existing_port="$3" + local existing_webBasePath="$4" + + echo -e "${green}Setting up SSL certificate...${plain}" + + # Check if acme.sh is installed + if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then + install_acme + if [ $? -ne 0 ]; then + echo -e "${yellow}Failed to install acme.sh, skipping SSL setup${plain}" + return 1 + fi + fi + + # Create certificate directory + local certPath="/root/cert/${domain}" + mkdir -p "$certPath" + + # Issue certificate + echo -e "${green}Issuing SSL certificate for ${domain}...${plain}" + echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}" + + ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1 + ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force + + 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 "$certPath" 2>/dev/null + return 1 + fi + + # Install certificate + ~/.acme.sh/acme.sh --installcert -d ${domain} \ + --key-file /root/cert/${domain}/privkey.pem \ + --fullchain-file /root/cert/${domain}/fullchain.pem \ + --reloadcmd "systemctl restart x-ui" >/dev/null 2>&1 + + if [ $? -ne 0 ]; then + echo -e "${yellow}Failed to install certificate${plain}" + return 1 + fi + + # Enable auto-renew + ~/.acme.sh/acme.sh --upgrade --auto-upgrade >/dev/null 2>&1 + chmod 755 $certPath/* 2>/dev/null + + # Set certificate for panel + local webCertFile="/root/cert/${domain}/fullchain.pem" + local webKeyFile="/root/cert/${domain}/privkey.pem" + + if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then + ${xui_folder}/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" >/dev/null 2>&1 + echo -e "${green}SSL certificate installed and configured successfully!${plain}" + return 0 + else + echo -e "${yellow}Certificate files not found${plain}" + return 1 + 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" + + echo -e "${yellow}Generating a self-signed certificate (not publicly trusted)...${plain}" + + mkdir -p "$certDir" + + local sanExt="" + if is_ip "$name"; then + sanExt="IP:${name}" + else + sanExt="DNS:${name}" + 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 + + if [[ $? -ne 0 ]]; then + local tmpCfg="${certDir}/openssl.cnf" + cat > "$tmpCfg" </dev/null 2>&1 + rm -f "$tmpCfg" + fi + + if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then + echo -e "${red}Failed to generate self-signed certificate${plain}" + return 1 + 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}" + 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#^/##') + local existing_port=$(${xui_folder}/x-ui setting -show true | grep 'port:' | awk -F': ' '{print $2}' | tr -d '[:space:]') + + # check for acme.sh first + if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then + echo "acme.sh could not be found. Installing now..." + cd ~ || return 1 + curl -s https://get.acme.sh | sh + if [ $? -ne 0 ]; then + echo -e "${red}Failed to install acme.sh${plain}" + return 1 + else + echo -e "${green}acme.sh installed successfully${plain}" + fi + fi + + # get the domain here, and we need to verify it + local domain="" + while true; do + read -rp "Please enter your domain name: " domain + domain="${domain// /}" # Trim whitespace + + if [[ -z "$domain" ]]; then + echo -e "${red}Domain name cannot be empty. Please try again.${plain}" + continue + fi + + if ! is_domain "$domain"; then + echo -e "${red}Invalid domain format: ${domain}. Please enter a valid domain name.${plain}" + continue + fi + + break + done + echo -e "${green}Your domain is: ${domain}, checking it...${plain}" + + # check if there already exists a certificate + local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}') + if [ "${currentCert}" == "${domain}" ]; then + local certInfo=$(~/.acme.sh/acme.sh --list) + echo -e "${red}System already has certificates for this domain. Cannot issue again.${plain}" + echo -e "${yellow}Current certificate details:${plain}" + echo "$certInfo" + return 1 + else + echo -e "${green}Your domain is ready for issuing certificates now...${plain}" + fi + + # create a directory for the certificate + certPath="/root/cert/${domain}" + if [ ! -d "$certPath" ]; then + mkdir -p "$certPath" + else + rm -rf "$certPath" + mkdir -p "$certPath" + fi + + # get the port number for the standalone server + local WebPort=80 + read -rp "Please choose which port to use (default is 80): " WebPort + if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then + echo -e "${yellow}Your input ${WebPort} is invalid, will use default port 80.${plain}" + WebPort=80 + fi + echo -e "${green}Will use port: ${WebPort} to issue certificates. Please make sure this port is open.${plain}" + + # Stop panel temporarily + echo -e "${yellow}Stopping panel temporarily...${plain}" + systemctl stop x-ui 2>/dev/null || rc-service x-ui stop 2>/dev/null + + # issue the certificate + ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt + ~/.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} + systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null + return 1 + else + echo -e "${green}Issuing certificate succeeded, installing certificates...${plain}" + fi + + # Setup reload command + reloadCmd="systemctl restart x-ui || rc-service x-ui restart" + echo -e "${green}Default --reloadcmd for ACME is: ${yellow}systemctl restart x-ui || rc-service x-ui restart${plain}" + echo -e "${green}This command will run on every certificate issue and renew.${plain}" + read -rp "Would you like to modify --reloadcmd for ACME? (y/n): " setReloadcmd + if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then + echo -e "\n${green}\t1.${plain} Preset: systemctl reload nginx ; systemctl restart x-ui" + echo -e "${green}\t2.${plain} Input your own command" + echo -e "${green}\t0.${plain} Keep default reloadcmd" + read -rp "Choose an option: " choice + case "$choice" in + 1) + echo -e "${green}Reloadcmd is: systemctl reload nginx ; systemctl restart x-ui${plain}" + reloadCmd="systemctl reload nginx ; systemctl restart x-ui" + ;; + 2) + echo -e "${yellow}It's recommended to put x-ui restart at the end${plain}" + read -rp "Please enter your custom reloadcmd: " reloadCmd + echo -e "${green}Reloadcmd is: ${reloadCmd}${plain}" + ;; + *) + echo -e "${green}Keeping default reloadcmd${plain}" + ;; + esac + fi + + # install the certificate + ~/.acme.sh/acme.sh --installcert -d ${domain} \ + --key-file /root/cert/${domain}/privkey.pem \ + --fullchain-file /root/cert/${domain}/fullchain.pem --reloadcmd "${reloadCmd}" + + if [ $? -ne 0 ]; then + echo -e "${red}Installing certificate failed, exiting.${plain}" + rm -rf ~/.acme.sh/${domain} + systemctl start x-ui 2>/dev/null || rc-service x-ui start 2>/dev/null + return 1 + else + echo -e "${green}Installing certificate succeeded, enabling auto renew...${plain}" + fi + + # enable auto-renew + ~/.acme.sh/acme.sh --upgrade --auto-upgrade + if [ $? -ne 0 ]; then + echo -e "${yellow}Auto renew setup had issues, certificate details:${plain}" + ls -lah /root/cert/${domain}/ + chmod 755 $certPath/* + else + echo -e "${green}Auto renew succeeded, certificate details:${plain}" + ls -lah /root/cert/${domain}/ + chmod 755 $certPath/* + fi + + # Restart 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 + read -rp "Would you like to set this certificate for the panel? (y/n): " setPanel + if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then + local webCertFile="/root/cert/${domain}/fullchain.pem" + local webKeyFile="/root/cert/${domain}/privkey.pem" + + if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then + ${xui_folder}/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" + echo -e "${green}Certificate paths set for the panel${plain}" + echo -e "${green}Certificate File: $webCertFile${plain}" + echo -e "${green}Private Key File: $webKeyFile${plain}" + echo "" + echo -e "${green}Access URL: https://${domain}:${existing_port}/${existing_webBasePath}${plain}" + echo -e "${yellow}Panel will restart to apply SSL certificate...${plain}" + systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null + else + echo -e "${red}Error: Certificate or private key file not found for domain: $domain.${plain}" + fi + else + echo -e "${yellow}Skipping panel path setting.${plain}" + fi + + return 0 +} +# Unified interactive SSL setup (domain or self-signed) +# Sets global `SSL_HOST` to the chosen domain/IP +prompt_and_setup_ssl() { + local panel_port="$1" + local web_base_path="$2" # expected without leading slash + local server_ip="$3" + + 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 + ssl_choice="${ssl_choice// /}" # Trim whitespace + + # Default to 2 (self-signed) if not 1 + if [[ "$ssl_choice" != "1" ]]; then + ssl_choice="2" + fi + + case "$ssl_choice" in + 1) + # User chose Let's Encrypt domain option + echo -e "${green}Using ssl_cert_issue() for comprehensive domain setup...${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}') + if [[ -n "${cert_domain}" ]]; then + SSL_HOST="${cert_domain}" + echo -e "${green}✓ SSL certificate configured successfully with domain: ${cert_domain}${plain}" + else + echo -e "${yellow}SSL setup may have completed, but domain extraction failed${plain}" + SSL_HOST="${server_ip}" + fi + ;; + 2) + # User chose self-signed option + # Stop panel if running + 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}" + if [ $? -eq 0 ]; then + SSL_HOST="${server_ip}" + echo -e "${green}✓ Self-signed SSL configured successfully${plain}" + else + echo -e "${red}✗ Self-signed SSL setup failed${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 + ;; + 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}" + ;; + esac +} + config_after_update() { echo -e "${yellow}x-ui settings:${plain}" /usr/local/x-ui/x-ui setting -show true @@ -211,10 +573,10 @@ update_x-ui() { fi fi echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..." - ${curl_bin} -fLRo /usr/local/x-ui-linux-$(arch).tar.gz -z /usr/local/x-ui-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 -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 if [[ $? -ne 0 ]]; then echo -e "${yellow}Trying to fetch version with IPv4...${plain}" - ${curl_bin} -4fLRo /usr/local/x-ui-linux-$(arch).tar.gz -z /usr/local/x-ui-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 -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 if [[ $? -ne 0 ]]; then _fail "ERROR: Failed to download x-ui, please be sure that your server can access GitHub" fi diff --git a/x-ui.sh b/x-ui.sh index c616ff87..a7a906c2 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -127,7 +127,7 @@ update_menu() { fi curl -fLRo /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh - chmod +x /usr/local/x-ui/x-ui.sh + chmod +x ${xui_folder}/x-ui.sh chmod +x /usr/bin/x-ui if [[ $? == 0 ]]; then