From 6e22aa59e72a195c3436991ec830d45a220ce2c4 Mon Sep 17 00:00:00 2001 From: somebodywashere <68244480+somebodywashere@users.noreply.github.com> Date: Sat, 24 Jun 2023 23:36:18 +0300 Subject: [PATCH] Added IP Limit Management to x-ui menu, Tweaked IP Limit to check every 20s (#615) Co-authored-by: Hamidreza <70919649+hamid-gh98@users.noreply.github.com> Co-authored-by: Ho3ein --- web/job/check_client_ip_job.go | 29 ++++- web/web.go | 4 +- x-ui.sh | 194 ++++++++++++++++++++++++++++++++- 3 files changed, 216 insertions(+), 11 deletions(-) diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go index c1b4ab34..758929e9 100644 --- a/web/job/check_client_ip_job.go +++ b/web/job/check_client_ip_job.go @@ -2,6 +2,7 @@ package job import ( "encoding/json" + "log" "os" "regexp" "x-ui/database" @@ -31,6 +32,18 @@ func (j *CheckClientIpJob) Run() { logger.Debug("Check Client IP Job...") if hasLimitIp() { + //create log file for Fail2ban IP Limit + logIpFile, err := os.OpenFile("/var/log/3xipl.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + checkError(err) + defer logIpFile.Close() + log.SetOutput(logIpFile) + log.SetFlags(log.LstdFlags) + + //create file to collect access.log to another file accessp.log (p=persistent) + logAccessP, err := os.OpenFile("/usr/local/x-ui/accessp.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + checkError(err) + defer logAccessP.Close() + processLogFile() } @@ -129,9 +142,18 @@ func processLogFile() { } - time.Sleep(time.Second * 5) - //added 5 seconds delay before cleaning logs to reduce chance of logging IP that already has been banned + time.Sleep(time.Second * 3) + //added 3 seconds delay before cleaning logs to reduce chance of logging IP that already has been banned if shouldCleanLog { + //copy log + logAccessP, err := os.OpenFile("/usr/local/x-ui/accessp.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) + checkError(err) + input, err := os.ReadFile(accessLogPath) + checkError(err) + if _, err := logAccessP.Write(input); err != nil { + checkError(err) + } + defer logAccessP.Close() // clean log if err := os.Truncate(GetAccessLogPath(), 0); err != nil { checkError(err) @@ -239,10 +261,9 @@ func updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmai shouldCleanLog = true if limitIp < len(ips) && inbound.Enable { - disAllowedIps = append(disAllowedIps, ips[limitIp:]...) for i := limitIp; i < len(ips); i++ { - logger.Notice("[LIMIT_IP] Email=", clientEmail, " SRC=", ips[i]) + log.Printf("[LIMIT_IP] Email = %s || SRC = %s", clientEmail, ips[i]) } } } diff --git a/web/web.go b/web/web.go index a70ae3c8..3372344a 100644 --- a/web/web.go +++ b/web/web.go @@ -250,8 +250,8 @@ func (s *Server) startTask() { // Check the inbound traffic every 30 seconds that the traffic exceeds and expires s.cron.AddJob("@every 30s", job.NewCheckInboundJob()) - // check client ips from log file every 30 sec - s.cron.AddJob("@every 30s", job.NewCheckClientIpJob()) + // check client ips from log file every 20 sec + s.cron.AddJob("@every 20s", job.NewCheckClientIpJob()) // Make a traffic condition every day, 8:30 var entry cron.EntryID diff --git a/x-ui.sh b/x-ui.sh index 5b8950cc..57e1c714 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -518,9 +518,9 @@ install_acme() { } ssl_cert_issue_main() { - echo "1) Get SSL" - echo "2) Revoke" - echo "3) Force Renew" + echo -e "${green}\t1.${plain} Get SSL" + echo -e "${green}\t2.${plain} Revoke" + echo -e "${green}\t3.${plain} Force Renew" read -p "Choose an option: " choice case "$choice" in 1) ssl_cert_issue ;; @@ -671,6 +671,186 @@ run_speedtest() { speedtest } +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} Check Logs" + echo -e "${green}\t5.${plain} Uninstall 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 5]: " NUM + if [[ $NUM =~ ^[0-9]+$ ]]; then + echo -e "\n[3x-ipl]\nenabled=true\nfilter=3x-ipl\naction=3x-ipl\nlogpath=/var/log/3xipl.log\nmaxretry=3\nfindtime=100\nbantime=${NUM}m" > /etc/fail2ban/jail.d/3x-ipl.conf + sudo systemctl restart fail2ban + echo -e "${green}Bantime set to ${NUM} minutes successfully.${plain}" + 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 + echo -e "${green}All users Unbanned successfully.${plain}" + iplimit_main + else + echo -e "${yellow}Cancelled.${plain}" + fi + iplimit_main ;; + 4) + if test -f "/var/log/3xipl-banned.log"; then + if [[ -s "/var/log/3xipl-banned.log" ]]; then + cat /var/log/3xipl-banned.log + 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" + iplimit_main + fi ;; + 5) + 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|debian) + sudo apt-get update && sudo apt-get install fail2ban -y ;; + centos) + sudo yum -y update && sudo yum -y install fail2ban ;; + fedora) + sudo dnf -y update && sudo dnf -y install fail2ban ;; + *) + echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n" + exit 1 ;; + esac + 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" + + #Check if [3x-ipl] exists in jail.local (just making sure there's no double config for jail) + if grep -qw '3x-ipl' /etc/fail2ban/jail.local || grep -qw '3x-ipl' /etc/fail2ban/jail.conf; then + echo -e "${red}Found conflicts in /etc/fail2ban/jail.conf or jail.local file!\nPlease manually remove anything related 3x-ipl in that files and try again.\nInstallation of IP Limit failed.${plain}\n" + exit 1 + fi + + #Check if log file exists + if ! test -f "/var/log/3xipl-banned.log"; then + touch /var/log/3xipl-banned.log + fi + + #Check if service log file exists so fail2ban won't return error + if ! test -f "/var/log/3xipl.log"; then + touch /var/log/3xipl.log + fi + + + echo -e "\n[3x-ipl]\nenabled=true\nfilter=3x-ipl\naction=3x-ipl\nlogpath=/var/log/3xipl.log\nmaxretry=3\nfindtime=100\nbantime=5m" > /etc/fail2ban/jail.d/3x-ipl.conf + + sudo cat > /etc/fail2ban/filter.d/3x-ipl.conf << EOF +[Definition] +datepattern = ^%%Y/%%m/%%d %%H:%%M:%%S +failregex = \[LIMIT_IP\]\s*Email\s*=\s*.+\s*\|\|\s*SRC\s*=\s* +ignoreregex = +EOF + + sudo cat > /etc/fail2ban/action.d/3x-ipl.conf << 'EOF' +[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." >> /var/log/3xipl-banned.log + +actionunban = -D f2b- -s -j + echo "$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = [IP] = unbanned." >> /var/log/3xipl-banned.log + +[Init] +EOF + + #Launching fail2ban + if ! sudo systemctl is-active --quiet fail2ban; then + sudo systemctl start fail2ban + else + systemctl restart fail2ban + fi + sudo 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 + sudo systemctl restart fail2ban + echo -e "${green}IP Limit removed successfully!${plain}\n" + before_show_menu ;; + 2) + 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 + sudo systemctl stop fail2ban + sudo systemctl disable fail2ban + case "${release}" in + ubuntu|debian) + sudo apt-get remove fail2ban -y ;; + centos) + sudo yum -y remove fail2ban ;; + fedora) + sudo dnf -y remove fail2ban ;; + *) + echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n" + exit 1 ;; + esac + rm -rf /etc/fail2ban/* + 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 +} show_usage() { echo "x-ui control menu usages: " @@ -718,9 +898,10 @@ show_menu() { ${green}18.${plain} Active Firewall and open ports ${green}19.${plain} Install WARP ${green}20.${plain} Speedtest by Ookla + ${green}21.${plain} IP Limit Management " show_status - echo && read -p "Please enter your selection [0-20]: " num + echo && read -p "Please enter your selection [0-21]: " num case "${num}" in 0) @@ -786,8 +967,11 @@ show_menu() { 20) run_speedtest ;; + 21) + iplimit_main + ;; *) - LOGE "Please enter the correct number [0-20]" + LOGE "Please enter the correct number [0-21]" ;; esac }