#!/bin/bash # lib/iplimit.sh - Fail2ban IP limiting management # Include guard [[ -n "${__X_UI_IPLIMIT_INCLUDED:-}" ]] && return 0 __X_UI_IPLIMIT_INCLUDED=1 # Source dependencies source "${LIB_DIR}/common.sh" source "${LIB_DIR}/service.sh" ip_validation() { ipv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$" ipv4_regex="^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)$" } 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} Ban an IP Address" echo -e "${green}\t6.${plain} Unban an IP Address" echo -e "${green}\t7.${plain} Real-Time Logs" echo -e "${green}\t8.${plain} Service Status" echo -e "${green}\t9.${plain} Service Restart" echo -e "${green}\t10.${plain} Uninstall Fail2ban and IP Limit" echo -e "${green}\t0.${plain} Back to Main Menu" read -rp "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} if [[ $release == "alpine" ]]; then rc-service fail2ban restart else systemctl restart fail2ban fi 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) read -rp "Enter the IP address you want to ban: " ban_ip ip_validation if [[ $ban_ip =~ $ipv4_regex || $ban_ip =~ $ipv6_regex ]]; then fail2ban-client set 3x-ipl banip "$ban_ip" echo -e "${green}IP Address ${ban_ip} has been banned successfully.${plain}" else echo -e "${red}Invalid IP address format! Please try again.${plain}" fi iplimit_main ;; 6) read -rp "Enter the IP address you want to unban: " unban_ip ip_validation if [[ $unban_ip =~ $ipv4_regex || $unban_ip =~ $ipv6_regex ]]; then fail2ban-client set 3x-ipl unbanip "$unban_ip" echo -e "${green}IP Address ${unban_ip} has been unbanned successfully.${plain}" else echo -e "${red}Invalid IP address format! Please try again.${plain}" fi iplimit_main ;; 7) tail -f /var/log/fail2ban.log iplimit_main ;; 8) service fail2ban status iplimit_main ;; 9) if [[ $release == "alpine" ]]; then rc-service fail2ban restart else systemctl restart fail2ban fi iplimit_main ;; 10) 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) apt-get update if [[ "${os_version}" -ge 24 ]]; then apt-get install python3-pip -y python3 -m pip install pyasynchat --break-system-packages fi apt-get install fail2ban -y ;; debian) apt-get update if [ "$os_version" -ge 12 ]; then apt-get install -y python3-systemd fi apt-get install -y fail2ban ;; armbian) apt-get update && apt-get install fail2ban -y ;; fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol) dnf -y update && dnf -y install fail2ban ;; centos) if [[ "${VERSION_ID}" =~ ^7 ]]; then yum update -y && yum install epel-release -y yum -y install fail2ban else dnf -y update && dnf -y install fail2ban fi ;; arch | manjaro | parch) pacman -Syu --noconfirm fail2ban ;; alpine) apk add 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 [[ $release == "alpine" ]]; then if [[ $(rc-service fail2ban status | grep -F 'status: started' -c) == 0 ]]; then rc-service fail2ban start else rc-service fail2ban restart fi rc-update add fail2ban else if ! systemctl is-active --quiet fail2ban; then systemctl start fail2ban else systemctl restart fail2ban fi systemctl enable fail2ban fi 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 -rp "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 if [[ $release == "alpine" ]]; then rc-service fail2ban restart else systemctl restart fail2ban fi echo -e "${green}IP Limit removed successfully!${plain}\n" before_show_menu ;; 2) rm -rf /etc/fail2ban if [[ $release == "alpine" ]]; then rc-service fail2ban stop else systemctl stop fail2ban fi case "${release}" in ubuntu | debian | armbian) apt-get remove -y fail2ban apt-get purge -y fail2ban -y apt-get autoremove -y ;; fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol) dnf remove fail2ban -y dnf autoremove -y ;; centos) if [[ "${VERSION_ID}" =~ ^7 ]]; then yum remove fail2ban -y yum autoremove -y else dnf remove fail2ban -y dnf autoremove -y fi ;; arch | manjaro | parch) pacman -Rns --noconfirm fail2ban ;; alpine) apk del 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 } show_banlog() { local system_log="/var/log/fail2ban.log" echo -e "${green}Checking ban logs...${plain}\n" if [[ $release == "alpine" ]]; then if [[ $(rc-service fail2ban status | grep -F 'status: started' -c) == 0 ]]; then echo -e "${red}Fail2ban service is not running!${plain}\n" return 1 fi else if ! systemctl is-active --quiet fail2ban; then echo -e "${red}Fail2ban service is not running!${plain}\n" return 1 fi 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}" } create_iplimit_jails() { # Use default bantime if not passed => 30 minutes local bantime="${1:-30}" # 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=3x-ipl 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-allports.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] 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 }