#!/bin/bash red='\033[0;31m' green='\033[0;32m' yellow='\033[0;33m' plain='\033[0m' #Add some basic function here function LOGD() { echo -e "${yellow}[DEG] $* ${plain}" } function LOGE() { echo -e "${red}[ERR] $* ${plain}" } function LOGI() { echo -e "${green}[INF] $* ${plain}" } # check root [[ $EUID -ne 0 ]] && LOGE "ERROR: You must be root to run this script! \n" && exit 1 # Check OS and set release variable if [[ -f /etc/os-release ]]; then source /etc/os-release release=$ID elif [[ -f /usr/lib/os-release ]]; then source /usr/lib/os-release release=$ID else echo "Failed to check the system OS, please contact the author!" >&2 exit 1 fi echo "The OS release is: $release" os_version="" os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.') if [[ "${release}" == "arch" ]]; then echo "Your OS is Arch Linux" elif [[ "${release}" == "parch" ]]; then echo "Your OS is Parch Linux" elif [[ "${release}" == "manjaro" ]]; then echo "Your OS is Manjaro" elif [[ "${release}" == "armbian" ]]; then echo "Your OS is Armbian" elif [[ "${release}" == "alpine" ]]; then echo "Your OS is Alpine Linux" elif [[ "${release}" == "opensuse-tumbleweed" ]]; then echo "Your OS is OpenSUSE Tumbleweed" elif [[ "${release}" == "openEuler" ]]; then if [[ ${os_version} -lt 2203 ]]; then echo -e "${red} Please use OpenEuler 22.03 or higher ${plain}\n" && exit 1 fi elif [[ "${release}" == "centos" ]]; then if [[ ${os_version} -lt 8 ]]; then echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1 fi elif [[ "${release}" == "ubuntu" ]]; then if [[ ${os_version} -lt 2004 ]]; then echo -e "${red} Please use Ubuntu 20 or higher version!${plain}\n" && exit 1 fi elif [[ "${release}" == "fedora" ]]; then if [[ ${os_version} -lt 36 ]]; then echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1 fi elif [[ "${release}" == "amzn" ]]; then if [[ ${os_version} != "2023" ]]; then echo -e "${red} Please use Amazon Linux 2023!${plain}\n" && exit 1 fi elif [[ "${release}" == "debian" ]]; then if [[ ${os_version} -lt 11 ]]; then echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1 fi elif [[ "${release}" == "almalinux" ]]; then if [[ ${os_version} -lt 80 ]]; then echo -e "${red} Please use AlmaLinux 8.0 or higher ${plain}\n" && exit 1 fi elif [[ "${release}" == "rocky" ]]; then if [[ ${os_version} -lt 8 ]]; then echo -e "${red} Please use Rocky Linux 8 or higher ${plain}\n" && exit 1 fi elif [[ "${release}" == "ol" ]]; then if [[ ${os_version} -lt 8 ]]; then echo -e "${red} Please use Oracle Linux 8 or higher ${plain}\n" && exit 1 fi else echo -e "${red}Your operating system is not supported by this script.${plain}\n" echo "Please ensure you are using one of the following supported operating systems:" echo "- Ubuntu 20.04+" echo "- Debian 11+" echo "- CentOS 8+" echo "- OpenEuler 22.03+" echo "- Fedora 36+" echo "- Arch Linux" echo "- Parch Linux" echo "- Manjaro" echo "- Armbian" echo "- AlmaLinux 8.0+" echo "- Rocky Linux 8+" echo "- Oracle Linux 8+" echo "- OpenSUSE Tumbleweed" echo "- Amazon Linux 2023" exit 1 fi # Declare Variables log_folder="${XUI_LOG_FOLDER:=/var/log}" iplimit_log_path="${log_folder}/3xipl.log" iplimit_banned_log_path="${log_folder}/3xipl-banned.log" confirm() { if [[ $# > 1 ]]; then echo && read -p "$1 [Default $2]: " temp if [[ "${temp}" == "" ]]; then temp=$2 fi else read -p "$1 [y/n]: " temp fi if [[ "${temp}" == "y" || "${temp}" == "Y" ]]; then return 0 else return 1 fi } confirm_restart() { confirm "Restart the panel, Attention: Restarting the panel will also restart xray" "y" if [[ $? == 0 ]]; then restart else show_menu fi } before_show_menu() { echo && echo -n -e "${yellow}Press enter to return to the main menu: ${plain}" && read temp show_menu } install() { bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh) if [[ $? == 0 ]]; then if [[ $# == 0 ]]; then start else start 0 fi fi } update() { confirm "This function will forcefully reinstall the latest version, and the data will not be lost. Do you want to continue?" "y" if [[ $? != 0 ]]; then LOGE "Cancelled" if [[ $# == 0 ]]; then before_show_menu fi return 0 fi bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh) if [[ $? == 0 ]]; then LOGI "Update is complete, Panel has automatically restarted " before_show_menu fi } update_menu() { echo -e "${yellow}Updating Menu${plain}" confirm "This function will update the menu to the latest changes." "y" if [[ $? != 0 ]]; then LOGE "Cancelled" if [[ $# == 0 ]]; then before_show_menu fi return 0 fi wget --no-check-certificate -O /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 /usr/bin/x-ui if [[ $? == 0 ]]; then echo -e "${green}Update successful. The panel has automatically restarted.${plain}" before_show_menu else echo -e "${red}Failed to update the menu.${plain}" return 1 fi } legacy_version() { echo "Enter the panel version (like 2.4.0):" read tag_version if [ -z "$tag_version" ]; then echo "Panel version cannot be empty. Exiting." exit 1 fi # Use the entered panel version in the download link install_command="bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/v$tag_version/install.sh") v$tag_version" echo "Downloading and installing panel version $tag_version..." eval $install_command } # Function to handle the deletion of the script file delete_script() { rm "$0" # Remove the script file itself exit 1 } uninstall() { confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" "n" if [[ $? != 0 ]]; then if [[ $# == 0 ]]; then show_menu fi return 0 fi systemctl stop x-ui systemctl disable x-ui rm /etc/systemd/system/x-ui.service -f systemctl daemon-reload systemctl reset-failed rm /etc/x-ui/ -rf rm /usr/local/x-ui/ -rf echo "" echo -e "Uninstalled Successfully.\n" echo "If you need to install this panel again, you can use below command:" echo -e "${green}bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)${plain}" echo "" # Trap the SIGTERM signal trap delete_script SIGTERM delete_script } reset_user() { confirm "Are you sure to reset the username and password of the panel?" "n" if [[ $? != 0 ]]; then if [[ $# == 0 ]]; then show_menu fi return 0 fi read -rp "Please set the login username [default is a random username]: " config_account [[ -z $config_account ]] && config_account=$(date +%s%N | md5sum | cut -c 1-8) read -rp "Please set the login password [default is a random password]: " config_password [[ -z $config_password ]] && config_password=$(date +%s%N | md5sum | cut -c 1-8) /usr/local/x-ui/x-ui setting -username ${config_account} -password ${config_password} >/dev/null 2>&1 /usr/local/x-ui/x-ui setting -remove_secret >/dev/null 2>&1 echo -e "Panel login username has been reset to: ${green} ${config_account} ${plain}" echo -e "Panel login password has been reset to: ${green} ${config_password} ${plain}" echo -e "${yellow} Panel login secret token disabled ${plain}" echo -e "${green} Please use the new login username and password to access the X-UI panel. Also remember them! ${plain}" confirm_restart } gen_random_string() { local length="$1" local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' /dev/null 2>&1 echo -e "Web base path has been reset to: ${green}${config_webBasePath}${plain}" echo -e "${green}Please use the new web base path to access the panel.${plain}" restart } reset_config() { confirm "Are you sure you want to reset all panel settings, Account data will not be lost, Username and password will not change" "n" if [[ $? != 0 ]]; then if [[ $# == 0 ]]; then show_menu fi return 0 fi /usr/local/x-ui/x-ui setting -reset echo -e "All panel settings have been reset to default." restart } check_config() { local info=$(/usr/local/x-ui/x-ui setting -show true) if [[ $? != 0 ]]; then LOGE "get current settings error, please check logs" show_menu return fi LOGI "${info}" 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 server_ip=$(curl -s https://api.ipify.org) if [[ -n "$existing_cert" ]]; then local domain=$(basename "$(dirname "$existing_cert")") if [[ "$domain" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then echo -e "${green}Access URL: https://${domain}:${existing_port}${existing_webBasePath}${plain}" else 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}" fi } set_port() { echo && echo -n -e "Enter port number[1-65535]: " && read port if [[ -z "${port}" ]]; then LOGD "Cancelled" before_show_menu else /usr/local/x-ui/x-ui setting -port ${port} echo -e "The port is set, Please restart the panel now, and use the new port ${green}${port}${plain} to access web panel" confirm_restart fi } start() { check_status if [[ $? == 0 ]]; then echo "" LOGI "Panel is running, No need to start again, If you need to restart, please select restart" else systemctl start x-ui sleep 2 check_status if [[ $? == 0 ]]; then LOGI "x-ui Started Successfully" else LOGE "panel Failed to start, Probably because it takes longer than two seconds to start, Please check the log information later" fi fi if [[ $# == 0 ]]; then before_show_menu fi } stop() { check_status if [[ $? == 1 ]]; then echo "" LOGI "Panel stopped, No need to stop again!" else systemctl stop x-ui sleep 2 check_status if [[ $? == 1 ]]; then LOGI "x-ui and xray stopped successfully" else LOGE "Panel stop failed, Probably because the stop time exceeds two seconds, Please check the log information later" fi fi if [[ $# == 0 ]]; then before_show_menu fi } restart() { systemctl restart x-ui sleep 2 check_status if [[ $? == 0 ]]; then LOGI "x-ui and xray Restarted successfully" else LOGE "Panel restart failed, Probably because it takes longer than two seconds to start, Please check the log information later" fi if [[ $# == 0 ]]; then before_show_menu fi } status() { systemctl status x-ui -l if [[ $# == 0 ]]; then before_show_menu fi } enable() { systemctl enable x-ui if [[ $? == 0 ]]; then LOGI "x-ui Set to boot automatically on startup successfully" else LOGE "x-ui Failed to set Autostart" fi if [[ $# == 0 ]]; then before_show_menu fi } disable() { systemctl disable x-ui if [[ $? == 0 ]]; then LOGI "x-ui Autostart Cancelled successfully" else LOGE "x-ui Failed to cancel autostart" fi if [[ $# == 0 ]]; then before_show_menu fi } show_log() { echo -e "${green}\t1.${plain} Debug Log" echo -e "${green}\t2.${plain} Clear All logs" echo -e "${green}\t0.${plain} Back to Main Menu" read -p "Choose an option: " choice case "$choice" in 0) show_menu ;; 1) journalctl -u x-ui -e --no-pager -f -p debug if [[ $# == 0 ]]; then before_show_menu fi ;; 2) sudo journalctl --rotate sudo journalctl --vacuum-time=1s echo "All Logs cleared." restart ;; *) echo -e "${red}Invalid option. Please select a valid number.${plain}\n" show_log ;; esac } show_banlog() { local system_log="/var/log/fail2ban.log" echo -e "${green}Checking ban logs...${plain}\n" if ! systemctl is-active --quiet fail2ban; then echo -e "${red}Fail2ban service is not running!${plain}\n" return 1 fi if [[ -f "$system_log" ]]; then echo -e "${green}Recent system ban activities from fail2ban.log:${plain}" grep "3x-ipl" "$system_log" | grep -E "Ban|Unban" | tail -n 10 || echo -e "${yellow}No recent system ban activities found${plain}" echo "" fi if [[ -f "${iplimit_banned_log_path}" ]]; then echo -e "${green}3X-IPL ban log entries:${plain}" if [[ -s "${iplimit_banned_log_path}" ]]; then grep -v "INIT" "${iplimit_banned_log_path}" | tail -n 10 || echo -e "${yellow}No ban entries found${plain}" else echo -e "${yellow}Ban log file is empty${plain}" fi else echo -e "${red}Ban log file not found at: ${iplimit_banned_log_path}${plain}" fi echo -e "\n${green}Current jail status:${plain}" fail2ban-client status 3x-ipl || echo -e "${yellow}Unable to get jail status${plain}" } bbr_menu() { echo -e "${green}\t1.${plain} Enable BBR" echo -e "${green}\t2.${plain} Disable BBR" echo -e "${green}\t0.${plain} Back to Main Menu" read -p "Choose an option: " choice case "$choice" in 0) show_menu ;; 1) enable_bbr bbr_menu ;; 2) disable_bbr bbr_menu ;; *) echo -e "${red}Invalid option. Please select a valid number.${plain}\n" bbr_menu ;; esac } disable_bbr() { if ! grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf || ! grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then echo -e "${yellow}BBR is not currently enabled.${plain}" before_show_menu fi # Replace BBR with CUBIC configurations sed -i 's/net.core.default_qdisc=fq/net.core.default_qdisc=pfifo_fast/' /etc/sysctl.conf sed -i 's/net.ipv4.tcp_congestion_control=bbr/net.ipv4.tcp_congestion_control=cubic/' /etc/sysctl.conf # Apply changes sysctl -p # Verify that BBR is replaced with CUBIC if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "cubic" ]]; then echo -e "${green}BBR has been replaced with CUBIC successfully.${plain}" else echo -e "${red}Failed to replace BBR with CUBIC. Please check your system configuration.${plain}" fi } enable_bbr() { if grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf && grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then echo -e "${green}BBR is already enabled!${plain}" before_show_menu fi # Check the OS and install necessary packages case "${release}" in ubuntu | debian | armbian) apt-get update && apt-get install -yqq --no-install-recommends ca-certificates ;; centos | almalinux | rocky | ol) yum -y update && yum -y install ca-certificates ;; fedora | amzn) dnf -y update && dnf -y install ca-certificates ;; arch | manjaro | parch) pacman -Sy --noconfirm ca-certificates ;; *) echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n" exit 1 ;; esac # Enable BBR echo "net.core.default_qdisc=fq" | tee -a /etc/sysctl.conf echo "net.ipv4.tcp_congestion_control=bbr" | tee -a /etc/sysctl.conf # Apply changes sysctl -p # Verify that BBR is enabled if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then echo -e "${green}BBR has been enabled successfully.${plain}" else echo -e "${red}Failed to enable BBR. Please check your system configuration.${plain}" fi } update_shell() { wget -O /usr/bin/x-ui -N --no-check-certificate https://github.com/MHSanaei/3x-ui/raw/main/x-ui.sh if [[ $? != 0 ]]; then echo "" LOGE "Failed to download script, Please check whether the machine can connect Github" before_show_menu else chmod +x /usr/bin/x-ui LOGI "Upgrade script succeeded, Please rerun the script" before_show_menu fi } # 0: running, 1: not running, 2: not installed check_status() { if [[ ! -f /etc/systemd/system/x-ui.service ]]; then return 2 fi temp=$(systemctl status x-ui | grep Active | awk '{print $3}' | cut -d "(" -f2 | cut -d ")" -f1) if [[ "${temp}" == "running" ]]; then return 0 else return 1 fi } check_enabled() { temp=$(systemctl is-enabled x-ui) if [[ "${temp}" == "enabled" ]]; then return 0 else return 1 fi } check_uninstall() { check_status if [[ $? != 2 ]]; then echo "" LOGE "Panel installed, Please do not reinstall" if [[ $# == 0 ]]; then before_show_menu fi return 1 else return 0 fi } check_install() { check_status if [[ $? == 2 ]]; then echo "" LOGE "Please install the panel first" if [[ $# == 0 ]]; then before_show_menu fi return 1 else return 0 fi } show_status() { check_status case $? in 0) echo -e "Panel state: ${green}Running${plain}" show_enable_status ;; 1) echo -e "Panel state: ${yellow}Not Running${plain}" show_enable_status ;; 2) echo -e "Panel state: ${red}Not Installed${plain}" ;; esac show_xray_status } show_enable_status() { check_enabled if [[ $? == 0 ]]; then echo -e "Start automatically: ${green}Yes${plain}" else echo -e "Start automatically: ${red}No${plain}" fi } check_xray_status() { count=$(ps -ef | grep "xray-linux" | grep -v "grep" | wc -l) if [[ count -ne 0 ]]; then return 0 else return 1 fi } show_xray_status() { check_xray_status if [[ $? == 0 ]]; then echo -e "xray state: ${green}Running${plain}" else echo -e "xray state: ${red}Not Running${plain}" fi } firewall_menu() { echo -e "${green}\t1.${plain} Install Firewall & open ports" echo -e "${green}\t2.${plain} Allowed List" echo -e "${green}\t3.${plain} Delete Ports from List" echo -e "${green}\t4.${plain} Disable Firewall" echo -e "${green}\t0.${plain} Back to Main Menu" read -p "Choose an option: " choice case "$choice" in 0) show_menu ;; 1) open_ports firewall_menu ;; 2) sudo ufw status firewall_menu ;; 3) delete_ports firewall_menu ;; 4) sudo ufw disable firewall_menu ;; *) echo -e "${red}Invalid option. Please select a valid number.${plain}\n" firewall_menu ;; esac } open_ports() { if ! command -v ufw &>/dev/null; then echo "ufw firewall is not installed. Installing now..." apt-get update apt-get install -y ufw else echo "ufw firewall is already installed" fi # Check if the firewall is inactive if ufw status | grep -q "Status: active"; then echo "Firewall is already active" else echo "Activating firewall..." # Open the necessary ports ufw allow ssh ufw allow http ufw allow https ufw allow 2053/tcp # Enable the firewall ufw --force enable fi # Prompt the user to enter a list of ports read -p "Enter the ports you want to open (e.g. 80,443,2053 or range 400-500): " ports # Check if the input is valid if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2 exit 1 fi # Open the specified ports using ufw IFS=',' read -ra PORT_LIST <<<"$ports" for port in "${PORT_LIST[@]}"; do if [[ $port == *-* ]]; then # Split the range into start and end ports start_port=$(echo $port | cut -d'-' -f1) end_port=$(echo $port | cut -d'-' -f2) ufw allow $start_port:$end_port/tcp ufw allow $start_port:$end_port/udp else ufw allow "$port" fi done # Confirm that the ports are open echo "The following ports are now open:" ufw status | grep "ALLOW" | grep -Eo "[0-9]+(/[a-z]+)?" echo "Firewall status:" ufw status verbose } delete_ports() { # Prompt the user to enter the ports they want to delete read -p "Enter the ports you want to delete (e.g. 80,443,2053 or range 400-500): " ports # Check if the input is valid if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2 exit 1 fi # Delete the specified ports using ufw IFS=',' read -ra PORT_LIST <<<"$ports" for port in "${PORT_LIST[@]}"; do if [[ $port == *-* ]]; then # Split the range into start and end ports start_port=$(echo $port | cut -d'-' -f1) end_port=$(echo $port | cut -d'-' -f2) # Delete the port range ufw delete allow $start_port:$end_port/tcp ufw delete allow $start_port:$end_port/udp else ufw delete allow "$port" fi done # Confirm that the ports are deleted echo "Deleted the specified ports:" for port in "${PORT_LIST[@]}"; do if [[ $port == *-* ]]; then start_port=$(echo $port | cut -d'-' -f1) end_port=$(echo $port | cut -d'-' -f2) # Check if the port range has been successfully deleted (ufw status | grep -q "$start_port:$end_port") || echo "$start_port-$end_port" else # Check if the individual port has been successfully deleted (ufw status | grep -q "$port") || echo "$port" fi done } update_geo() { echo -e "${green}\t1.${plain} Loyalsoldier (geoip.dat, geosite.dat)" echo -e "${green}\t2.${plain} chocolate4u (geoip_IR.dat, geosite_IR.dat)" echo -e "${green}\t3.${plain} vuong2023 (geoip_VN.dat, geosite_VN.dat)" echo -e "${green}\t0.${plain} Back to Main Menu" read -p "Choose an option: " choice systemctl stop x-ui cd /usr/local/x-ui/bin case "$choice" in 0) show_menu ;; 1) rm -f geoip.dat geosite.dat wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat echo -e "${green}Loyalsoldier datasets have been updated successfully!${plain}" restart ;; 2) rm -f geoip_IR.dat geosite_IR.dat wget -O geoip_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat wget -O geosite_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat echo -e "${green}chocolate4u datasets have been updated successfully!${plain}" restart ;; 3) rm -f geoip_VN.dat geosite_VN.dat wget -O geoip_VN.dat -N https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat wget -O geosite_VN.dat -N https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat echo -e "${green}vuong2023 datasets have been updated successfully!${plain}" restart ;; *) echo -e "${red}Invalid option. Please select a valid number.${plain}\n" update_geo ;; esac systemctl start x-ui before_show_menu } install_acme() { # Check if acme.sh is already installed if command -v ~/.acme.sh/acme.sh &>/dev/null; then LOGI "acme.sh is already installed." return 0 fi LOGI "Installing acme.sh..." cd ~ || return 1 # Ensure you can change to the home directory curl -s https://get.acme.sh | sh if [ $? -ne 0 ]; then LOGE "Installation of acme.sh failed." return 1 else LOGI "Installation of acme.sh succeeded." fi return 0 } ssl_cert_issue_main() { echo -e "${green}\t1.${plain} Get SSL" 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}\t0.${plain} Back to Main Menu" read -p "Choose an option: " choice case "$choice" in 0) show_menu ;; 1) ssl_cert_issue ssl_cert_issue_main ;; 2) local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) if [ -z "$domains" ]; then echo "No certificates found to revoke." else echo "Existing domains:" echo "$domains" read -p "Please enter a domain from the list to revoke the certificate: " domain if echo "$domains" | grep -qw "$domain"; then ~/.acme.sh/acme.sh --revoke -d ${domain} LOGI "Certificate revoked for domain: $domain" else echo "Invalid domain entered." fi fi ssl_cert_issue_main ;; 3) local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) if [ -z "$domains" ]; then echo "No certificates found to renew." else echo "Existing domains:" echo "$domains" read -p "Please enter a domain from the list to renew the SSL certificate: " domain if echo "$domains" | grep -qw "$domain"; then ~/.acme.sh/acme.sh --renew -d ${domain} --force LOGI "Certificate forcefully renewed for domain: $domain" else echo "Invalid domain entered." fi fi ssl_cert_issue_main ;; 4) local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) if [ -z "$domains" ]; then echo "No certificates found." else echo "Existing domains and their paths:" for domain in $domains; do local cert_path="/root/cert/${domain}/fullchain.pem" local key_path="/root/cert/${domain}/privkey.pem" if [[ -f "${cert_path}" && -f "${key_path}" ]]; then echo -e "Domain: ${domain}" echo -e "\tCertificate Path: ${cert_path}" echo -e "\tPrivate Key Path: ${key_path}" else echo -e "Domain: ${domain} - Certificate or Key missing." fi done fi ssl_cert_issue_main ;; 5) local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) if [ -z "$domains" ]; then echo "No certificates found." else echo "Available domains:" echo "$domains" read -p "Please choose a domain to set the panel paths: " domain if echo "$domains" | grep -qw "$domain"; then local webCertFile="/root/cert/${domain}/fullchain.pem" local webKeyFile="/root/cert/${domain}/privkey.pem" if [[ -f "${webCertFile}" && -f "${webKeyFile}" ]]; then /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" echo "Panel paths set for domain: $domain" echo " - Certificate File: $webCertFile" echo " - Private Key File: $webKeyFile" restart else echo "Certificate or private key not found for domain: $domain." fi else echo "Invalid domain entered." fi fi ssl_cert_issue_main ;; *) echo -e "${red}Invalid option. Please select a valid number.${plain}\n" ssl_cert_issue_main ;; esac } 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}') # check for acme.sh first if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then echo "acme.sh could not be found. we will install it" install_acme if [ $? -ne 0 ]; then LOGE "install acme failed, please check logs" exit 1 fi fi # install socat second case "${release}" in ubuntu | debian | armbian) apt update && apt install socat -y ;; centos | almalinux | rocky | ol) yum -y update && yum -y install socat ;; fedora | amzn) dnf -y update && dnf -y install socat ;; arch | manjaro | parch) pacman -Sy --noconfirm socat ;; *) echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n" exit 1 ;; esac if [ $? -ne 0 ]; then LOGE "install socat failed, please check logs" exit 1 else LOGI "install socat succeed..." fi # get the domain here, and we need to verify it local domain="" read -p "Please enter your domain name: " domain LOGD "Your domain is: ${domain}, checking it..." # 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) LOGE "System already has certificates for this domain. Cannot issue again. Current certificate details:" LOGI "$certInfo" exit 1 else LOGI "Your domain is ready for issuing certificates now..." 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 -p "Please choose which port to use (default is 80): " WebPort if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then LOGE "Your input ${WebPort} is invalid, will use default port 80." WebPort=80 fi LOGI "Will use port: ${WebPort} to issue certificates. Please make sure this port is open." # issue the certificate ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} if [ $? -ne 0 ]; then LOGE "Issuing certificate failed, please check logs." rm -rf ~/.acme.sh/${domain} exit 1 else LOGE "Issuing certificate succeeded, installing certificates..." 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 if [ $? -ne 0 ]; then LOGE "Installing certificate failed, exiting." rm -rf ~/.acme.sh/${domain} exit 1 else LOGI "Installing certificate succeeded, enabling auto renew..." fi # enable auto-renew ~/.acme.sh/acme.sh --upgrade --auto-upgrade if [ $? -ne 0 ]; then LOGE "Auto renew failed, certificate details:" ls -lah cert/* chmod 755 $certPath/* exit 1 else LOGI "Auto renew succeeded, certificate details:" ls -lah cert/* chmod 755 $certPath/* fi # Prompt user to set panel paths after successful certificate installation read -p "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 /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" LOGI "Panel paths set for domain: $domain" LOGI " - Certificate File: $webCertFile" LOGI " - Private Key File: $webKeyFile" echo -e "${green}Access URL: https://${domain}:${existing_port}${existing_webBasePath}${plain}" restart else LOGE "Error: Certificate or private key file not found for domain: $domain." fi else LOGI "Skipping panel path setting." fi } ssl_cert_issue_CF() { 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}') LOGI "****** Instructions for Use ******" LOGI "Follow the steps below to complete the process:" LOGI "1. Cloudflare Registered E-mail." LOGI "2. Cloudflare Global API Key." LOGI "3. The Domain Name." LOGI "4. Once the certificate is issued, you will be prompted to set the certificate for the panel (optional)." LOGI "5. The script also supports automatic renewal of the SSL certificate after installation." confirm "Do you confirm the information and wish to proceed? [y/n]" "y" if [ $? -eq 0 ]; then # Check for acme.sh first if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then echo "acme.sh could not be found. We will install it." install_acme if [ $? -ne 0 ]; then LOGE "Install acme failed, please check logs." exit 1 fi fi CF_Domain="" certPath="/root/cert-CF" if [ ! -d "$certPath" ]; then mkdir -p $certPath else rm -rf $certPath mkdir -p $certPath fi LOGD "Please set a domain name:" read -p "Input your domain here: " CF_Domain LOGD "Your domain name is set to: ${CF_Domain}" # Set up Cloudflare API details CF_GlobalKey="" CF_AccountEmail="" LOGD "Please set the API key:" read -p "Input your key here: " CF_GlobalKey LOGD "Your API key is: ${CF_GlobalKey}" LOGD "Please set up registered email:" read -p "Input your email here: " CF_AccountEmail LOGD "Your registered email address is: ${CF_AccountEmail}" # Set the default CA to Let's Encrypt ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt if [ $? -ne 0 ]; then LOGE "Default CA, Let'sEncrypt fail, script exiting..." exit 1 fi export CF_Key="${CF_GlobalKey}" export CF_Email="${CF_AccountEmail}" # Issue the certificate using Cloudflare DNS ~/.acme.sh/acme.sh --issue --dns dns_cf -d ${CF_Domain} -d *.${CF_Domain} --log if [ $? -ne 0 ]; then LOGE "Certificate issuance failed, script exiting..." exit 1 else LOGI "Certificate issued successfully, Installing..." fi # Install the certificate mkdir -p ${certPath}/${CF_Domain} if [ $? -ne 0 ]; then LOGE "Failed to create directory: ${certPath}/${CF_Domain}" exit 1 fi ~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} \ --fullchain-file ${certPath}/${CF_Domain}/fullchain.pem \ --key-file ${certPath}/${CF_Domain}/privkey.pem if [ $? -ne 0 ]; then LOGE "Certificate installation failed, script exiting..." exit 1 else LOGI "Certificate installed successfully, Turning on automatic updates..." fi # Enable auto-update ~/.acme.sh/acme.sh --upgrade --auto-upgrade if [ $? -ne 0 ]; then LOGE "Auto update setup failed, script exiting..." exit 1 else LOGI "The certificate is installed and auto-renewal is turned on. Specific information is as follows:" ls -lah ${certPath}/${CF_Domain} chmod 755 ${certPath}/${CF_Domain} fi # Prompt user to set panel paths after successful certificate installation read -p "Would you like to set this certificate for the panel? (y/n): " setPanel if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then local webCertFile="${certPath}/${CF_Domain}/fullchain.pem" local webKeyFile="${certPath}/${CF_Domain}/privkey.pem" if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" LOGI "Panel paths set for domain: $CF_Domain" LOGI " - Certificate File: $webCertFile" LOGI " - Private Key File: $webKeyFile" echo -e "${green}Access URL: https://${CF_Domain}:${existing_port}${existing_webBasePath}${plain}" restart else LOGE "Error: Certificate or private key file not found for domain: $CF_Domain." fi else LOGI "Skipping panel path setting." fi else show_menu fi } run_speedtest() { # Check if Speedtest is already installed if ! command -v speedtest &>/dev/null; then # If not installed, install it local pkg_manager="" local speedtest_install_script="" if command -v dnf &>/dev/null; then pkg_manager="dnf" speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh" elif command -v yum &>/dev/null; then pkg_manager="yum" speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh" elif command -v apt-get &>/dev/null; then pkg_manager="apt-get" speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh" elif command -v apt &>/dev/null; then pkg_manager="apt" speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh" fi if [[ -z $pkg_manager ]]; then echo "Error: Package manager not found. You may need to install Speedtest manually." return 1 else curl -s $speedtest_install_script | bash $pkg_manager install -y speedtest fi fi # Run Speedtest speedtest } create_iplimit_jails() { # Use default bantime if not passed => 15 minutes local bantime="${1:-15}" # Uncomment 'allowipv6 = auto' in fail2ban.conf sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf # On Debian 12+ fail2ban's default backend should be changed to systemd if [[ "${release}" == "debian" && ${os_version} -ge 12 ]]; then sed -i '0,/action =/s/backend = auto/backend = systemd/' /etc/fail2ban/jail.conf fi cat << EOF > /etc/fail2ban/jail.d/3x-ipl.conf [3x-ipl] enabled=true backend=auto filter=3x-ipl action = %(known/action)s[name=%(__name__)s, protocol="%(protocol)s", chain="%(chain)s"] logpath=${iplimit_log_path} maxretry=2 findtime=32 bantime=${bantime}m EOF cat << EOF > /etc/fail2ban/filter.d/3x-ipl.conf [Definition] datepattern = ^%%Y/%%m/%%d %%H:%%M:%%S failregex = \[LIMIT_IP\]\s*Email\s*=\s*.+\s*\|\|\s*SRC\s*=\s* ignoreregex = EOF cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf [INCLUDES] before = iptables-common.conf [Definition] actionstart = -N f2b- -A f2b- -j -I -p -j f2b- actionstop = -D -p -j f2b- -X f2b- actioncheck = -n -L | grep -q 'f2b-[ \t]' actionban = -I f2b- 1 -s -j echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") BAN [Email] = [IP] = banned for seconds." >> ${iplimit_banned_log_path} actionunban = -D f2b- -s -j echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = [IP] = unbanned." >> ${iplimit_banned_log_path} [Init] # Use default settings from iptables-common.conf # This will automatically handle both IPv4 and IPv6 name = default protocol = tcp chain = INPUT EOF echo -e "${green}Ip Limit jail files created with a bantime of ${bantime} minutes.${plain}" } iplimit_remove_conflicts() { local jail_files=( /etc/fail2ban/jail.conf /etc/fail2ban/jail.local ) for file in "${jail_files[@]}"; do # Check for [3x-ipl] config in jail file then remove it if test -f "${file}" && grep -qw '3x-ipl' ${file}; then sed -i "/\[3x-ipl\]/,/^$/d" ${file} echo -e "${yellow}Removing conflicts of [3x-ipl] in jail (${file})!${plain}\n" fi done } iplimit_main() { echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit" echo -e "${green}\t2.${plain} Change Ban Duration" echo -e "${green}\t3.${plain} Unban Everyone" echo -e "${green}\t4.${plain} Ban Logs" echo -e "${green}\t5.${plain} Real-Time Logs" echo -e "${green}\t6.${plain} Service Status" echo -e "${green}\t7.${plain} Service Restart" echo -e "${green}\t8.${plain} Uninstall Fail2ban and IP Limit" echo -e "${green}\t0.${plain} Back to Main Menu" read -p "Choose an option: " choice case "$choice" in 0) show_menu ;; 1) confirm "Proceed with installation of Fail2ban & IP Limit?" "y" if [[ $? == 0 ]]; then install_iplimit else iplimit_main fi ;; 2) read -rp "Please enter new Ban Duration in Minutes [default 30]: " NUM if [[ $NUM =~ ^[0-9]+$ ]]; then create_iplimit_jails ${NUM} systemctl restart fail2ban else echo -e "${red}${NUM} is not a number! Please, try again.${plain}" fi iplimit_main ;; 3) confirm "Proceed with Unbanning everyone from IP Limit jail?" "y" if [[ $? == 0 ]]; then fail2ban-client reload --restart --unban 3x-ipl truncate -s 0 "${iplimit_banned_log_path}" echo -e "${green}All users Unbanned successfully.${plain}" iplimit_main else echo -e "${yellow}Cancelled.${plain}" fi iplimit_main ;; 4) show_banlog iplimit_main ;; 5) tail -f /var/log/fail2ban.log iplimit_main ;; 6) service fail2ban status iplimit_main ;; 7) systemctl restart fail2ban iplimit_main ;; 8) remove_iplimit iplimit_main ;; *) echo -e "${red}Invalid option. Please select a valid number.${plain}\n" iplimit_main ;; esac } install_iplimit() { if ! command -v fail2ban-client &>/dev/null; then echo -e "${green}Fail2ban is not installed. Installing now...!${plain}\n" # Check the OS and install necessary packages case "${release}" in ubuntu) if [[ "${os_version}" -ge 24 ]]; then apt update && apt install python3-pip -y python3 -m pip install pyasynchat --break-system-packages fi apt update && apt install fail2ban -y ;; debian | armbian) apt update && apt install fail2ban -y ;; centos | almalinux | rocky | ol) yum update -y && yum install epel-release -y yum -y install fail2ban ;; fedora | amzn) dnf -y update && dnf -y install fail2ban ;; arch | manjaro | parch) pacman -Syu --noconfirm fail2ban ;; *) echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n" exit 1 ;; esac if ! command -v fail2ban-client &>/dev/null; then echo -e "${red}Fail2ban installation failed.${plain}\n" exit 1 fi echo -e "${green}Fail2ban installed successfully!${plain}\n" else echo -e "${yellow}Fail2ban is already installed.${plain}\n" fi echo -e "${green}Configuring IP Limit...${plain}\n" # make sure there's no conflict for jail files iplimit_remove_conflicts # Check if log file exists if ! test -f "${iplimit_banned_log_path}"; then touch ${iplimit_banned_log_path} fi # Check if service log file exists so fail2ban won't return error if ! test -f "${iplimit_log_path}"; then touch ${iplimit_log_path} fi # Create the iplimit jail files # we didn't pass the bantime here to use the default value create_iplimit_jails # Launching fail2ban if ! systemctl is-active --quiet fail2ban; then systemctl start fail2ban systemctl enable fail2ban else systemctl restart fail2ban fi systemctl enable fail2ban echo -e "${green}IP Limit installed and configured successfully!${plain}\n" before_show_menu } remove_iplimit() { echo -e "${green}\t1.${plain} Only remove IP Limit configurations" echo -e "${green}\t2.${plain} Uninstall Fail2ban and IP Limit" echo -e "${green}\t0.${plain} Back to Main Menu" read -p "Choose an option: " num case "$num" in 1) rm -f /etc/fail2ban/filter.d/3x-ipl.conf rm -f /etc/fail2ban/action.d/3x-ipl.conf rm -f /etc/fail2ban/jail.d/3x-ipl.conf systemctl restart fail2ban echo -e "${green}IP Limit removed successfully!${plain}\n" before_show_menu ;; 2) rm -rf /etc/fail2ban systemctl stop fail2ban case "${release}" in ubuntu | debian | armbian) apt-get remove -y fail2ban apt-get purge -y fail2ban -y apt-get autoremove -y ;; centos | almalinux | rocky | ol) yum remove fail2ban -y yum autoremove -y ;; fedora | amzn) dnf remove fail2ban -y dnf autoremove -y ;; arch | manjaro | parch) pacman -Rns --noconfirm fail2ban ;; *) echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n" exit 1 ;; esac echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n" before_show_menu ;; 0) show_menu ;; *) echo -e "${red}Invalid option. Please select a valid number.${plain}\n" remove_iplimit ;; esac } SSH_port_forwarding() { local server_ip=$(curl -s https://api.ipify.org) 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}') local existing_listenIP=$(/usr/local/x-ui/x-ui setting -getListen true | grep -Eo 'listenIP: .+' | awk '{print $2}') local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'cert: .+' | awk '{print $2}') local existing_key=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'key: .+' | awk '{print $2}') local config_listenIP="" local listen_choice="" if [[ -n "$existing_cert" && -n "$existing_key" ]]; then echo -e "${green}Panel is secure with SSL.${plain}" before_show_menu fi if [[ -z "$existing_cert" && -z "$existing_key" && (-z "$existing_listenIP" || "$existing_listenIP" == "0.0.0.0") ]]; then echo -e "\n${red}Warning: No Cert and Key found! The panel is not secure.${plain}" echo "Please obtain a certificate or set up SSH port forwarding." fi if [[ -n "$existing_listenIP" && "$existing_listenIP" != "0.0.0.0" && (-z "$existing_cert" && -z "$existing_key") ]]; then echo -e "\n${green}Current SSH Port Forwarding Configuration:${plain}" echo -e "Standard SSH command:" echo -e "${yellow}ssh -L 2222:${existing_listenIP}:${existing_port} root@${server_ip}${plain}" echo -e "\nIf using SSH key:" echo -e "${yellow}ssh -i -L 2222:${existing_listenIP}:${existing_port} root@${server_ip}${plain}" echo -e "\nAfter connecting, access the panel at:" echo -e "${yellow}http://localhost:2222${existing_webBasePath}${plain}" fi echo -e "\nChoose an option:" echo -e "${green}1.${plain} Set listen IP" echo -e "${green}2.${plain} Clear listen IP" echo -e "${green}0.${plain} Back to Main Menu" read -p "Choose an option: " num case "$num" in 1) if [[ -z "$existing_listenIP" || "$existing_listenIP" == "0.0.0.0" ]]; then echo -e "\nNo listenIP configured. Choose an option:" echo -e "1. Use default IP (127.0.0.1)" echo -e "2. Set a custom IP" read -p "Select an option (1 or 2): " listen_choice config_listenIP="127.0.0.1" [[ "$listen_choice" == "2" ]] && read -p "Enter custom IP to listen on: " config_listenIP /usr/local/x-ui/x-ui setting -listenIP "${config_listenIP}" >/dev/null 2>&1 echo -e "${green}listen IP has been set to ${config_listenIP}.${plain}" echo -e "\n${green}SSH Port Forwarding Configuration:${plain}" echo -e "Standard SSH command:" echo -e "${yellow}ssh -L 2222:${config_listenIP}:${existing_port} root@${server_ip}${plain}" echo -e "\nIf using SSH key:" echo -e "${yellow}ssh -i -L 2222:${config_listenIP}:${existing_port} root@${server_ip}${plain}" echo -e "\nAfter connecting, access the panel at:" echo -e "${yellow}http://localhost:2222${existing_webBasePath}${plain}" restart else config_listenIP="${existing_listenIP}" echo -e "${green}Current listen IP is already set to ${config_listenIP}.${plain}" fi ;; 2) /usr/local/x-ui/x-ui setting -listenIP 0.0.0.0 >/dev/null 2>&1 echo -e "${green}Listen IP has been cleared.${plain}" restart ;; 0) show_menu ;; *) echo -e "${red}Invalid option. Please select a valid number.${plain}\n" SSH_port_forwarding ;; esac } show_usage() { echo "x-ui control menu usages: " echo "------------------------------------------" echo -e "SUBCOMMANDS:" echo -e "x-ui - Admin Management Script" echo -e "x-ui start - Start" echo -e "x-ui stop - Stop" echo -e "x-ui restart - Restart" echo -e "x-ui status - Current Status" echo -e "x-ui settings - Current Settings" echo -e "x-ui enable - Enable Autostart on OS Startup" echo -e "x-ui disable - Disable Autostart on OS Startup" echo -e "x-ui log - Check logs" echo -e "x-ui banlog - Check Fail2ban ban logs" echo -e "x-ui update - Update" echo -e "x-ui custom - custom version" echo -e "x-ui install - Install" echo -e "x-ui uninstall - Uninstall" echo "------------------------------------------" } show_menu() { echo -e " ${green}3X-UI Panel Management Script${plain} ${green}0.${plain} Exit Script ———————————————— ${green}1.${plain} Install ${green}2.${plain} Update ${green}3.${plain} Update Menu ${green}4.${plain} Legacy Version ${green}5.${plain} Uninstall ———————————————— ${green}6.${plain} Reset Username & Password & Secret Token ${green}7.${plain} Reset Web Base Path ${green}8.${plain} Reset Settings ${green}9.${plain} Change Port ${green}10.${plain} View Current Settings ———————————————— ${green}11.${plain} Start ${green}12.${plain} Stop ${green}13.${plain} Restart ${green}14.${plain} Check Status ${green}15.${plain} Logs Management ———————————————— ${green}16.${plain} Enable Autostart ${green}17.${plain} Disable Autostart ———————————————— ${green}18.${plain} SSL Certificate Management ${green}19.${plain} Cloudflare SSL Certificate ${green}20.${plain} IP Limit Management ${green}21.${plain} Firewall Management ${green}22.${plain} SSH Port Forwarding Management ———————————————— ${green}23.${plain} Enable BBR ${green}24.${plain} Update Geo Files ${green}25.${plain} Speedtest by Ookla " show_status echo && read -p "Please enter your selection [0-25]: " num case "${num}" in 0) exit 0 ;; 1) check_uninstall && install ;; 2) check_install && update ;; 3) check_install && update_menu ;; 4) check_install && legacy_version ;; 5) check_install && uninstall ;; 6) check_install && reset_user ;; 7) check_install && reset_webbasepath ;; 8) check_install && reset_config ;; 9) check_install && set_port ;; 10) check_install && check_config ;; 11) check_install && start ;; 12) check_install && stop ;; 13) check_install && restart ;; 14) check_install && status ;; 15) check_install && show_log ;; 16) check_install && enable ;; 17) check_install && disable ;; 18) ssl_cert_issue_main ;; 19) ssl_cert_issue_CF ;; 20) iplimit_main ;; 21) firewall_menu ;; 22) SSH_port_forwarding ;; 23) bbr_menu ;; 24) update_geo ;; 25) run_speedtest ;; *) LOGE "Please enter the correct number [0-25]" ;; esac } if [[ $# > 0 ]]; then case $1 in "start") check_install 0 && start 0 ;; "stop") check_install 0 && stop 0 ;; "restart") check_install 0 && restart 0 ;; "status") check_install 0 && status 0 ;; "settings") check_install 0 && check_config 0 ;; "enable") check_install 0 && enable 0 ;; "disable") check_install 0 && disable 0 ;; "log") check_install 0 && show_log 0 ;; "banlog") check_install 0 && show_banlog 0 ;; "update") check_install 0 && update 0 ;; "legacy") check_install 0 && legacy_version 0 ;; "install") check_uninstall 0 && install 0 ;; "uninstall") check_install 0 && uninstall 0 ;; *) show_usage ;; esac else show_menu fi