From b1d079fc24fa765afe6a2189892a963cebb9d55b Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Thu, 4 Jun 2026 17:05:27 +0200 Subject: [PATCH] fix(fail2ban): exempt SSH and panel ports from IP-limit ban (#4896) The 3x-ipl action used iptables-allports, so a banned IP lost all TCP access including SSH and the panel, locking admins out (especially with dynamic-IP clients). The ban now blocks every TCP port except the SSH and panel ports via a multiport negation, derived at jail-creation time in both x-ui.sh and DockerEntrypoint.sh. This keeps IP-limit working for all current and future inbounds without per-port config. --- DockerEntrypoint.sh | 15 +++++++++++++-- x-ui.sh | 17 +++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/DockerEntrypoint.sh b/DockerEntrypoint.sh index 38786b14..9105f965 100644 --- a/DockerEntrypoint.sh +++ b/DockerEntrypoint.sh @@ -27,6 +27,16 @@ failregex = \[LIMIT_IP\]\s*Email\s*=\s*.+\s*\|\|\s*Disconnect ignoreregex = EOF + # Ports to exempt from the ban so an over-limit proxy client can never lock + # the administrator out of SSH or the panel. The ban still covers every other + # TCP port (including all Xray inbounds), so IP-limit keeps working for inbounds + # added later without regenerating these files. + SSH_PORTS=$(grep -oE '^[[:space:]]*Port[[:space:]]+[0-9]+' /etc/ssh/sshd_config 2>/dev/null | grep -oE '[0-9]+' | paste -sd, -) + [ -z "$SSH_PORTS" ] && SSH_PORTS="22" + PANEL_PORT=$(/app/x-ui setting -show true 2>/dev/null | grep -Eo 'port: .+' | awk '{print $2}') + EXEMPT_PORTS="$SSH_PORTS" + [ -n "$PANEL_PORT" ] && EXEMPT_PORTS="$EXEMPT_PORTS,$PANEL_PORT" + cat > /etc/fail2ban/action.d/3x-ipl.conf << EOF [INCLUDES] before = iptables-allports.conf @@ -42,16 +52,17 @@ actionstop = -D -p -j f2b- actioncheck = -n -L | grep -q 'f2b-[ \t]' -actionban = -I f2b- 1 -s -j +actionban = -I f2b- 1 -s -p -m multiport ! --dports -j echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") BAN [Email] = [IP] = banned for seconds." >> $LOG_FOLDER/3xipl-banned.log -actionunban = -D f2b- -s -j +actionunban = -D f2b- -s -p -m multiport ! --dports -j echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = [IP] = unbanned." >> $LOG_FOLDER/3xipl-banned.log [Init] name = default protocol = tcp chain = INPUT +exemptports = $EXEMPT_PORTS EOF fail2ban-client -x start diff --git a/x-ui.sh b/x-ui.sh index 7bcc5022..9b79d933 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -2248,6 +2248,18 @@ failregex = \[LIMIT_IP\]\s*Email\s*=\s*.+\s*\|\|\s*Disconnect ignoreregex = EOF + # Ports to exempt from the ban so an over-limit proxy client can never lock + # the administrator out of SSH or the panel. The ban still covers every other + # TCP port (including all Xray inbounds), so IP-limit keeps working for inbounds + # added later without regenerating these files. + local ssh_ports + ssh_ports=$(grep -oP '^[[:space:]]*Port[[:space:]]+\K[0-9]+' /etc/ssh/sshd_config 2>/dev/null | paste -sd, -) + [[ -z "${ssh_ports}" ]] && ssh_ports="22" + local panel_port + panel_port=$(${xui_folder}/x-ui setting -show true 2>/dev/null | grep -Eo 'port: .+' | awk '{print $2}') + local exempt_ports="${ssh_ports}" + [[ -n "${panel_port}" ]] && exempt_ports="${exempt_ports},${panel_port}" + cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf [INCLUDES] before = iptables-allports.conf @@ -2263,16 +2275,17 @@ actionstop = -D -p -j f2b- actioncheck = -n -L | grep -q 'f2b-[ \t]' -actionban = -I f2b- 1 -s -j +actionban = -I f2b- 1 -s -p -m multiport ! --dports -j echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") BAN [Email] = [IP] = banned for seconds." >> ${iplimit_banned_log_path} -actionunban = -D f2b- -s -j +actionunban = -D f2b- -s -p -m multiport ! --dports -j echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = [IP] = unbanned." >> ${iplimit_banned_log_path} [Init] name = default protocol = tcp chain = INPUT +exemptports = ${exempt_ports} EOF echo -e "${green}Ip Limit jail files created with a bantime of ${bantime} minutes.${plain}"