From 539c33f511bc753aeead0fc201a3e6de3e5cd1df Mon Sep 17 00:00:00 2001 From: gitea Date: Fri, 20 Feb 2026 18:11:35 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20isWhitelisted()=20reads=20only=20jail.lo?= =?UTF-8?q?cal=20=E2=80=94=20single=20source=20of=20truth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously isWhitelisted() checked SUBNETS_TO_IGNORE env var in addition to jail.local ignoreip. This meant removing a subnet from the dashboard whitelist updated fail2ban but the dashboard scan still excluded those IPs, creating a visible disconnect. Now isWhitelisted() reads and does CIDR matching only against jail.local. Dashboard whitelist changes are immediately reflected in scan/records, 1:1 with what fail2ban enforces. Co-Authored-By: Claude Sonnet 4.6 --- dashboard/server.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dashboard/server.js b/dashboard/server.js index 7e460a4..717a6fa 100644 --- a/dashboard/server.js +++ b/dashboard/server.js @@ -19,7 +19,6 @@ const JAIL_LOCAL = process.env.JAIL_LOCAL || '/etc/fail2ban/jail.local'; const CF_SYNC = process.env.CF_SYNC || '/usr/local/bin/cloudflare-whitelist-sync.sh'; const MANUAL_JAIL = process.env.MANUAL_JAIL || 'manual-bans'; const BAN_HIST_FILE = process.env.BAN_HIST_FILE || '/data/ban-history.json'; -const SUBNETS = (process.env.SUBNETS_TO_IGNORE || '10.0.0.0/8,172.16.0.0/12').split(',').map(s => s.trim()); const DEFAULT_DAYS = 3; const ABUSE_KEY = process.env.ABUSEIPDB_API_KEY; const AUTOBAN_THR = 75; @@ -220,11 +219,13 @@ async function refreshBanHistory() { // ── Nginx log scanner ───────────────────────────────────────────────────────── function isWhitelisted(ip) { const wl = readIgnoreIP(); - if (wl.includes(ip)) return true; try { - return SUBNETS.some(s => { - const [range, bits] = ipaddr.parseCIDR(s); - return ipaddr.parse(ip).match(range, bits); + return wl.some(entry => { + if (entry.includes('/')) { + const [range, bits] = ipaddr.parseCIDR(entry); + return ipaddr.parse(ip).match(range, bits); + } + return entry === ip; }); } catch { return false; } }