From 87f4b17c5f264d90c0617f11b76c36812c15c8d0 Mon Sep 17 00:00:00 2001 From: solodecode Date: Mon, 4 Nov 2024 05:44:11 +0300 Subject: [PATCH] add building from mac to linux --- Dockerfile | 79 +- Makefile | 21 + build_files/x-ui.service | 15 + build_files/x-ui.sh | 1710 ++++++++++++++++++++++++++ web/service/tgbot.go | 2 +- web/translation/translate.ru_RU.toml | 3 +- 6 files changed, 1801 insertions(+), 29 deletions(-) create mode 100644 Makefile create mode 100644 build_files/x-ui.service create mode 100644 build_files/x-ui.sh diff --git a/Dockerfile b/Dockerfile index ac09b531..4d999f1d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,53 +1,78 @@ # ======================================================== # Stage: Builder # ======================================================== -FROM golang:1.23-alpine AS builder +FROM --platform=linux/amd64 golang:1.23.2-bullseye AS builder WORKDIR /app -ARG TARGETARCH -RUN apk --no-cache --update add \ - build-base \ - gcc \ - wget \ - unzip +# Устанавливаем необходимые пакеты +RUN apt-get update && apt-get install -y \ + build-essential \ + gcc \ + wget \ + unzip \ + tar \ + && rm -rf /var/lib/apt/lists/* COPY . . +# Устанавливаем переменные окружения для компиляции ENV CGO_ENABLED=1 ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE" -RUN go build -o build/x-ui main.go -RUN ./DockerInit.sh "$TARGETARCH" +ENV GOOS=linux +ENV GOARCH=amd64 + +# Сборка бинарного файла +RUN go build -o x-ui main.go +RUN ./DockerInit.sh "amd64" + +# Копирование файлов из build_files +COPY build_files /app/build_files + +# Создание директории build и каталога x-ui внутри неё +RUN mkdir -p build/x-ui + +# Копирование файлов в каталог x-ui внутри build +RUN cp x-ui build/x-ui/ +RUN cp -r build_files/* build/x-ui/ + +# Создание архива tar.gz, включая каталог x-ui +RUN tar -czvf x-ui-linux-amd64.tar.gz -C build x-ui # ======================================================== -# Stage: Final Image of 3x-ui +# Stage: Final Image of x-ui # ======================================================== -FROM alpine +FROM debian:bullseye-slim AS final ENV TZ=Asia/Tehran WORKDIR /app -RUN apk add --no-cache --update \ - ca-certificates \ - tzdata \ - fail2ban \ - bash +RUN apt-get update && apt-get install -y \ + ca-certificates \ + tzdata \ + fail2ban \ + bash \ + && rm -rf /var/lib/apt/lists/* -COPY --from=builder /app/build/ /app/ +COPY --from=builder /app/build/x-ui/ /app/ COPY --from=builder /app/DockerEntrypoint.sh /app/ COPY --from=builder /app/x-ui.sh /usr/bin/x-ui - -# Configure fail2ban -RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \ - && cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local \ - && sed -i "s/^\[ssh\]$/&\nenabled = false/" /etc/fail2ban/jail.local \ - && sed -i "s/^\[sshd\]$/&\nenabled = false/" /etc/fail2ban/jail.local \ - && sed -i "s/#allowipv6 = auto/allowipv6 = auto/g" /etc/fail2ban/fail2ban.conf +# Настройка fail2ban +RUN rm -f /etc/fail2ban/jail.d/defaults-debian.conf \ + && cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local \ + && sed -i "s/^\[ssh\]$/&\nenabled = false/" /etc/fail2ban/jail.local \ + && sed -i "s/^\[sshd\]$/&\nenabled = false/" /etc/fail2ban/jail.local RUN chmod +x \ - /app/DockerEntrypoint.sh \ - /app/x-ui \ - /usr/bin/x-ui + /app/DockerEntrypoint.sh \ + /app/x-ui \ + /usr/bin/x-ui VOLUME [ "/etc/x-ui" ] CMD [ "./x-ui" ] ENTRYPOINT [ "/app/DockerEntrypoint.sh" ] + +# ======================================================== +# Stage: Export Archive +# ======================================================== +FROM scratch AS export-stage +COPY --from=builder /app/x-ui-linux-amd64.tar.gz /x-ui-linux-amd64.tar.gz diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..a472ff0d --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +APP_NAME = x-ui +DOCKER_IMAGE = my-go-app +BUILD_DIR = build + +TARGETARCH ?= amd64 +TARGETOS ?= linux +ARCHIVE_NAME = x-ui-$(TARGETOS)-$(TARGETARCH).tar.gz + +.PHONY: build clean + +build: + mkdir -p $(BUILD_DIR) + docker build \ + --build-arg TARGETOS=$(TARGETOS) \ + --build-arg TARGETARCH=$(TARGETARCH) \ + --target export-stage \ + -o $(BUILD_DIR) . + +clean: + rm -rf $(BUILD_DIR) + docker rmi $(DOCKER_IMAGE) \ No newline at end of file diff --git a/build_files/x-ui.service b/build_files/x-ui.service new file mode 100644 index 00000000..29d2a63a --- /dev/null +++ b/build_files/x-ui.service @@ -0,0 +1,15 @@ +[Unit] +Description=x-ui Service +After=network.target +Wants=network.target + +[Service] +Environment="XRAY_VMESS_AEAD_FORCED=false" +Type=simple +WorkingDirectory=/usr/local/x-ui/ +ExecStart=/usr/local/x-ui/x-ui +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/build_files/x-ui.sh b/build_files/x-ui.sh new file mode 100644 index 00000000..e6d262f4 --- /dev/null +++ b/build_files/x-ui.sh @@ -0,0 +1,1710 @@ +#!/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 " + exit 0 + 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}" + exit 0 + 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, Please restart the panel now, and use the default ${green}2053${plain} Port to Access the web Panel" + confirm_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 server_ip=$(curl -s https://api.ipify.org) + + echo -e "${green}Access URL: http://${server_ip}:${existing_port}${existing_webBasePath}${plain}" +} + +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) + return + ;; + 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 "Invalid choice" + ;; + esac +} + +show_banlog() { + if test -f "${iplimit_banned_log_path}"; then + if [[ -s "${iplimit_banned_log_path}" ]]; then + cat ${iplimit_banned_log_path} + else + echo -e "${red}Log file is empty.${plain}\n" + fi + else + echo -e "${red}Log file not found. Please Install Fail2ban and IP Limit first.${plain}\n" + fi +} + +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 + ;; + 2) + disable_bbr + ;; + *) echo "Invalid choice" ;; + 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}" + exit 0 + 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}" + exit 0 + 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" && exit 0 + 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 + ;; + 2) + sudo ufw status + ;; + 3) + delete_ports + ;; + 4) + sudo ufw disable + ;; + *) echo "Invalid choice" ;; + 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}" + ;; + 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}" + ;; + 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}" + ;; + *) + echo "Invalid option selected! No updates made." + ;; + 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 + ;; + 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 + ;; + 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 + ;; + 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 + ;; + 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 + ;; + + *) + echo "Invalid choice" + ;; + esac +} + +ssl_cert_issue() { + # 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" + 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() { + echo -E "" + LOGD "******Instructions for use******" + LOGI "This Acme script requires the following data:" + LOGI "1.Cloudflare Registered e-mail" + LOGI "2.Cloudflare Global API Key" + LOGI "3.The domain name that has been resolved dns to the current server by Cloudflare" + LOGI "4.The script applies for a certificate. The default installation path is /root/cert " + confirm "Confirmed?[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="" + CF_GlobalKey="" + CF_AccountEmail="" + certPath=/root/cert + if [ ! -d "$certPath" ]; then + mkdir $certPath + else + rm -rf $certPath + mkdir $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}" + 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}" + ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt + if [ $? -ne 0 ]; then + LOGE "Default CA, Lets'Encrypt fail, script exiting..." + exit 1 + fi + export CF_Key="${CF_GlobalKey}" + export CF_Email=${CF_AccountEmail} + ~/.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 + ~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} --ca-file /root/cert/ca.cer \ + --cert-file /root/cert/${CF_Domain}.cer --key-file /root/cert/${CF_Domain}.key \ + --fullchain-file /root/cert/fullchain.cer + if [ $? -ne 0 ]; then + LOGE "Certificate installation failed, script exiting..." + exit 1 + else + LOGI "Certificate installed Successfully,Turning on automatic updates..." + fi + ~/.acme.sh/acme.sh --upgrade --auto-upgrade + if [ $? -ne 0 ]; then + LOGE "Auto update setup Failed, script exiting..." + ls -lah cert + chmod 755 $certPath + exit 1 + else + LOGI "The certificate is installed and auto-renewal is turned on, Specific information is as follows" + ls -lah cert + chmod 755 $certPath + 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 + ;; + 5) + tail -f /var/log/fail2ban.log + ;; + 6) + service fail2ban status + ;; + 7) + systemctl restart fail2ban + ;; + 8) + remove_iplimit + ;; + *) echo "Invalid choice" ;; + 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} Abort" + 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) + echo -e "${yellow}Cancelled.${plain}\n" + iplimit_main + ;; + *) + 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}" + return 0 + fi + if [[ -z "$existing_cert" && -z "$existing_key" && -z "$existing_listenIP" ]]; 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" && (-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} Abort" + read -p "Choose an option: " num + + case "$num" in + 1) + if [[ -z "$existing_listenIP" ]]; 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}" + restart + else + config_listenIP="${existing_listenIP}" + echo -e "${green}Current listen IP is already set to ${config_listenIP}.${plain}" + fi + + if [[ -n "${config_listenIP}" ]]; then + 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}" + fi + ;; + 2) + /usr/local/x-ui/x-ui setting -listenIP ' ' >/dev/null 2>&1 + echo -e "${green}Listen IP has been cleared.${plain}" + restart + ;; + 0) + echo "Operation aborted." + ;; + *) + echo "Invalid option. Exiting." + ;; + 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 diff --git a/web/service/tgbot.go b/web/service/tgbot.go index 5858ec36..992b326e 100644 --- a/web/service/tgbot.go +++ b/web/service/tgbot.go @@ -1246,7 +1246,7 @@ func (t *Tgbot) clientInfoMsg( ) string { now := time.Now().Unix() expiryTime := "" - flag := false + flag := true diff := traffic.ExpiryTime/1000 - now if traffic.ExpiryTime == 0 { expiryTime = t.I18nBot("tgbot.unlimited") diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index 63f8f9d0..81e48af4 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -552,7 +552,7 @@ "depleteSoon" = "Скоро исчерпание" "clientUsage" = "Получить использование" "onlines" = "Онлайн-клиенты" -"commands" = "Команды" +"adminContact" = "Нужна помощь" "refresh" = "🔄 Обновить" "clearIPs" = "❌ Очистить IP" "removeTGUser" = "❌ Удалить пользователя Telegram" @@ -593,3 +593,4 @@ "askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: {{ .TgUserID }}" "chooseClient" = "Выберите пользователя для подключения {{ .Inbound }}" "chooseInbound" = "Выберите подключение" +"adminContact" = "Если вам нужна помощь, свяжитесь с администратором: @sainthrill" \ No newline at end of file