fix: isWhitelisted() reads only jail.local — single source of truth

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 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 18:11:35 +00:00
parent 572e8bbe4e
commit 539c33f511

View File

@@ -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 CF_SYNC = process.env.CF_SYNC || '/usr/local/bin/cloudflare-whitelist-sync.sh';
const MANUAL_JAIL = process.env.MANUAL_JAIL || 'manual-bans'; const MANUAL_JAIL = process.env.MANUAL_JAIL || 'manual-bans';
const BAN_HIST_FILE = process.env.BAN_HIST_FILE || '/data/ban-history.json'; 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 DEFAULT_DAYS = 3;
const ABUSE_KEY = process.env.ABUSEIPDB_API_KEY; const ABUSE_KEY = process.env.ABUSEIPDB_API_KEY;
const AUTOBAN_THR = 75; const AUTOBAN_THR = 75;
@@ -220,11 +219,13 @@ async function refreshBanHistory() {
// ── Nginx log scanner ───────────────────────────────────────────────────────── // ── Nginx log scanner ─────────────────────────────────────────────────────────
function isWhitelisted(ip) { function isWhitelisted(ip) {
const wl = readIgnoreIP(); const wl = readIgnoreIP();
if (wl.includes(ip)) return true;
try { try {
return SUBNETS.some(s => { return wl.some(entry => {
const [range, bits] = ipaddr.parseCIDR(s); if (entry.includes('/')) {
return ipaddr.parse(ip).match(range, bits); const [range, bits] = ipaddr.parseCIDR(entry);
return ipaddr.parse(ip).match(range, bits);
}
return entry === ip;
}); });
} catch { return false; } } catch { return false; }
} }