Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSanaei <ho3ein.sanaei@gmail.com>2025-12-28 02:03:33 +0300
committerGitHub <noreply@github.com>2025-12-28 02:03:33 +0300
commit69ccdba734226ad6c1b8853d545bf8c8b462bdd6 (patch)
tree80bb9a21c36df928226d643bc8bd23b9de94fccc /x-ui.sh
parent4c797dc154b2b50f7d2d2bdfd28b7c5e30a78cb4 (diff)
Self-signed SSL (#3611)
Diffstat (limited to 'x-ui.sh')
-rw-r--r--x-ui.sh253
1 files changed, 236 insertions, 17 deletions
diff --git a/x-ui.sh b/x-ui.sh
index f8ead9d6..8e62b72a 100644
--- a/x-ui.sh
+++ b/x-ui.sh
@@ -213,6 +213,57 @@ 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
+ /usr/local/x-ui/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}"
@@ -256,7 +307,7 @@ check_config() {
local existing_webBasePath=$(echo "$info" | grep -Eo 'webBasePath: .+' | awk '{print $2}')
local existing_port=$(echo "$info" | grep -Eo 'port: .+' | awk '{print $2}')
- local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'cert: .+' | awk '{print $2}')
+ local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
local server_ip=$(curl -s --max-time 3 https://api.ipify.org)
if [ -z "$server_ip" ]; then
server_ip=$(curl -s --max-time 3 https://4.ident.me)
@@ -271,7 +322,22 @@ check_config() {
echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
fi
else
- echo -e "${green}Access URL: http://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
+ 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
+ stop >/dev/null 2>&1
+ setup_self_signed_certificate "${server_ip}"
+ if [[ $? -eq 0 ]]; then
+ restart >/dev/null 2>&1
+ echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
+ else
+ LOGE "Self-signed SSL setup failed."
+ echo -e "${yellow}You can try again via option 18 (SSL Certificate Management).${plain}"
+ fi
+ else
+ echo -e "${yellow}Access URL: http://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
+ echo -e "${yellow}For security, please configure SSL certificate using option 18 (SSL Certificate Management)${plain}"
+ fi
fi
}
@@ -958,6 +1024,7 @@ ssl_cert_issue_main() {
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}\t0.${plain} Back to Main Menu"
read -rp "Choose an option: " choice
@@ -1051,6 +1118,16 @@ ssl_cert_issue_main() {
fi
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}"
+ confirm "Do you want to proceed?" "y"
+ if [[ $? == 0 ]]; then
+ ssl_cert_issue_for_ip
+ fi
+ ssl_cert_issue_main
+ ;;
*)
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
@@ -1059,6 +1136,134 @@ ssl_cert_issue_main() {
esac
}
+ssl_cert_issue_for_ip() {
+ LOGI "Starting automatic SSL certificate generation for server IP..."
+
+ local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
+ local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
+
+ # Get server IP
+ local server_ip=$(curl -s --max-time 3 https://api.ipify.org)
+ if [ -z "$server_ip" ]; then
+ server_ip=$(curl -s --max-time 3 https://4.ident.me)
+ fi
+
+ if [ -z "$server_ip" ]; then
+ LOGE "Failed to get server IP address"
+ return 1
+ fi
+
+ LOGI "Server IP detected: ${server_ip}"
+
+ # check for acme.sh first
+ if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
+ LOGI "acme.sh not found, installing..."
+ install_acme
+ if [ $? -ne 0 ]; then
+ LOGE "Failed to install acme.sh"
+ return 1
+ fi
+ fi
+
+ # install socat
+ case "${release}" in
+ ubuntu | debian | armbian)
+ apt-get update >/dev/null 2>&1 && apt-get install socat -y >/dev/null 2>&1
+ ;;
+ fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
+ dnf -y update >/dev/null 2>&1 && dnf -y install socat >/dev/null 2>&1
+ ;;
+ centos)
+ if [[ "${VERSION_ID}" =~ ^7 ]]; then
+ yum -y update >/dev/null 2>&1 && yum -y install socat >/dev/null 2>&1
+ else
+ dnf -y update >/dev/null 2>&1 && dnf -y install socat >/dev/null 2>&1
+ fi
+ ;;
+ arch | manjaro | parch)
+ pacman -Sy --noconfirm socat >/dev/null 2>&1
+ ;;
+ opensuse-tumbleweed | opensuse-leap)
+ zypper refresh >/dev/null 2>&1 && zypper -q install -y socat >/dev/null 2>&1
+ ;;
+ alpine)
+ apk add socat curl openssl >/dev/null 2>&1
+ ;;
+ *)
+ LOGW "Unsupported OS for automatic socat installation"
+ ;;
+ 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}"
+ 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/*
+ fi
+
+ # Set certificate paths for the panel
+ local webCertFile="/root/cert/${server_ip}/fullchain.pem"
+ local webKeyFile="/root/cert/${server_ip}/privkey.pem"
+
+ if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then
+ /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile"
+ LOGI "Certificate configured for panel"
+ LOGI " - Certificate File: $webCertFile"
+ LOGI " - Private Key File: $webKeyFile"
+ echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
+ LOGI "Panel will restart to apply SSL certificate..."
+ restart
+ return 0
+ else
+ LOGE "Certificate files not found after installation"
+ return 1
+ fi
+}
+
ssl_cert_issue() {
local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
@@ -1072,33 +1277,32 @@ ssl_cert_issue() {
fi
fi
- # install socat second
+ # install socat
case "${release}" in
ubuntu | debian | armbian)
- apt-get update && apt-get install socat -y
+ apt-get update >/dev/null 2>&1 && apt-get install socat -y >/dev/null 2>&1
;;
fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol)
- dnf -y update && dnf -y install socat
+ dnf -y update >/dev/null 2>&1 && dnf -y install socat >/dev/null 2>&1
;;
centos)
- if [[ "${VERSION_ID}" =~ ^7 ]]; then
- yum -y update && yum -y install socat
- else
- dnf -y update && dnf -y install socat
- fi
+ if [[ "${VERSION_ID}" =~ ^7 ]]; then
+ yum -y update >/dev/null 2>&1 && yum -y install socat >/dev/null 2>&1
+ else
+ dnf -y update >/dev/null 2>&1 && dnf -y install socat >/dev/null 2>&1
+ fi
;;
arch | manjaro | parch)
- pacman -Sy --noconfirm socat
+ pacman -Sy --noconfirm socat >/dev/null 2>&1
;;
- opensuse-tumbleweed | opensuse-leap)
- zypper refresh && zypper -q install -y socat
+ opensuse-tumbleweed | opensuse-leap)
+ zypper refresh >/dev/null 2>&1 && zypper -q install -y socat >/dev/null 2>&1
;;
alpine)
- apk add socat curl openssl
+ apk add socat curl openssl >/dev/null 2>&1
;;
*)
- echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
- exit 1
+ LOGW "Unsupported OS for automatic socat installation"
;;
esac
if [ $? -ne 0 ]; then
@@ -1110,7 +1314,22 @@ ssl_cert_issue() {
# get the domain here, and we need to verify it
local domain=""
- read -rp "Please enter your domain name: " domain
+ while true; do
+ read -rp "Please enter your domain name: " domain
+ domain="${domain// /}" # Trim whitespace
+
+ if [[ -z "$domain" ]]; then
+ LOGE "Domain name cannot be empty. Please try again."
+ continue
+ fi
+
+ if ! is_domain "$domain"; then
+ LOGE "Invalid domain format: ${domain}. Please enter a valid domain name."
+ continue
+ fi
+
+ break
+ done
LOGD "Your domain is: ${domain}, checking it..."
# check if there already exists a certificate