mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-08 14:36:13 +00:00
ip cert (#3631)
This commit is contained in:
209
x-ui.sh
209
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}"
|
||||
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/*
|
||||
# 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
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user