1.1.6: Migrate to .env, bugfixes

This commit is contained in:
Sefinek 2024-09-18 12:29:19 +02:00
parent 6fef5548e5
commit 140a142243
6 changed files with 74 additions and 44 deletions

View file

@ -1,13 +1,40 @@
# production or development # production or development
NODE_ENV=production NODE_ENV=production
# Cloudflare: https://dash.cloudflare.com/profile/api-tokens ############################### TOKENS ###############################
# Cloudflare (https://dash.cloudflare.com/profile/api-tokens)
CLOUDFLARE_EMAIL= CLOUDFLARE_EMAIL=
CLOUDFLARE_ZONE_ID= CLOUDFLARE_ZONE_ID=00000000000000000000000000000000
CLOUDFLARE_API_KEY= CLOUDFLARE_API_KEY=0000000000000000000000000000000000000000
# AbuseIPDB: https://www.abuseipdb.com/account/api # AbuseIPDB (https://www.abuseipdb.com/account/api)
ABUSEIPDB_API_KEY= ABUSEIPDB_API_KEY=00000000000000000000000000000000000000000000000000000000000000000000000000000000
# API key for api.sefinek.net. Contact me at contact@sefinek.net to obtain it and contribute to the sefinek24/Malicious-IP-Addresses. Leave empty if you don't have a token. # API key for api.sefinek.net. Contact me at contact@sefinek.net to obtain it and contribute to the sefinek24/Malicious-IP-Addresses. Leave empty if you don't have a token.
SEFINEK_API_SECRET= SEFINEK_API_SECRET=
############################### CYCLES ###############################
# Main interval (in minutes) of each cycle.
CYCLE_INTERVAL=120
# The minimum time that must pass after reporting an IP address before it can be reported again.
# The required time is >= 15 minutes, according to AbuseIPDB API limits.
REPORTED_IP_COOLDOWN_MS=6
# The maximum URI length that can be reported to AbuseIPDB.
# If Cloudflare returns a longer URI, the API request will fail.
MAX_URL_LENGTH=920
# Additional delay (in seconds) after each successful IP report to avoid overloading the AbuseIPDB API.
SUCCESS_COOLDOWN_MS=2
# Interval for refreshing your IP address (in minutes).
# This ensures that WAF violations originating from your IP address are not reported to AbuseIPDB.
IP_REFRESH_INTERVAL=80
############################### SEFINEK API ###############################
# Report IP addresses to api.sefinek.net to support the development of the repository at https://github.com/sefinek24/Malicious-IP-Addresses. SEFINEK_API_SECRET is required if true.
REPORT_TO_SEFINEK_API=true
# How often should the log (reported_ips.csv) be analyzed and sent to the Sefinek API? In minutes.
SEFINEK_API_INTERVAL=60

View file

@ -1,26 +1,25 @@
// Main interval of each cycle. const CYCLE_INTERVAL = process.env.NODE_ENV === 'production' ?
// In production mode, it's 2 hours, and in development mode, it's 8 seconds. parseInt(process.env.CYCLE_INTERVAL) * 60 * 1000 : 8 * 1000;
const CYCLE_INTERVAL = process.env.NODE_ENV === 'production' ? 2 * 60 * 60 * 1000 : 8 * 1000;
// The minimum time that must pass after reporting an IP address before it can be reported again. const REPORTED_IP_COOLDOWN_MS = parseInt(process.env.REPORTED_IP_COOLDOWN_MS) * 60 * 60 * 1000;
// The required time is >= 15 minutes, according to AbuseIPDB API limits.
const REPORTED_IP_COOLDOWN_MS = 6 * 60 * 60 * 1000; // 6h
// The maximum URI length that can be reported to AbuseIPDB. const MAX_URL_LENGTH = parseInt(process.env.MAX_URL_LENGTH);
// If Cloudflare returns a longer URI, the API request will fail.
const MAX_URL_LENGTH = 920;
// Additional delay after each successful IP report to avoid overloading the AbuseIPDB API. const SUCCESS_COOLDOWN_MS = parseInt(process.env.SUCCESS_COOLDOWN_MS);
const SUCCESS_COOLDOWN_MS = 2 * 1000; // 2s
// Interval for refreshing your IP address. const IP_REFRESH_INTERVAL = parseInt(process.env.IP_REFRESH_INTERVAL) * 60 * 1000;
// This ensures that WAF violations originating from your IP address are not reported to AbuseIPDB.
const IP_REFRESH_INTERVAL = 80 * 60 * 1000; // 80m
// Report IP addresses to api.sefinek.net to support the development of the repository at https://github.com/sefinek24/Malicious-IP-Addresses. If true, please see the .env file. const REPORT_TO_SEFINEK_API = process.env.REPORT_TO_SEFINEK_API === 'true';
const REPORT_TO_SEFINEK_API = true;
// How often should the log (reported_ips.csv) be analyzed and sent to the Sefinek API? const SEFINEK_API_INTERVAL = process.env.NODE_ENV === 'production' ?
const SEFINEK_API_INTERVAL = process.env.NODE_ENV === 'production' ? 60 * 60 * 1000 : 5 * 1000; parseInt(process.env.SEFINEK_API_INTERVAL) * 60 * 1000 : 5 * 1000;
module.exports = { CYCLE_INTERVAL, REPORTED_IP_COOLDOWN_MS, MAX_URL_LENGTH, SUCCESS_COOLDOWN_MS, IP_REFRESH_INTERVAL, REPORT_TO_SEFINEK_API, SEFINEK_API_INTERVAL }; module.exports = {
CYCLE_INTERVAL,
REPORTED_IP_COOLDOWN_MS,
MAX_URL_LENGTH,
SUCCESS_COOLDOWN_MS,
IP_REFRESH_INTERVAL,
REPORT_TO_SEFINEK_API,
SEFINEK_API_INTERVAL
};

View file

@ -30,16 +30,20 @@ const fetchBlockedIPs = async () => {
}; };
const isIPReportedRecently = (rayId, ip, reportedIPs) => { const isIPReportedRecently = (rayId, ip, reportedIPs) => {
const lastReport = reportedIPs.find(entry => const lastReport = reportedIPs.reduce((latest, entry) => {
if (
(entry.rayId === rayId || entry.ip === ip) && (entry.rayId === rayId || entry.ip === ip) &&
(entry.action === 'REPORTED' || entry.action === 'TOO_MANY_REQUESTS') (entry.status === 'TOO_MANY_REQUESTS' || entry.status === 'REPORTED') &&
); (!latest || entry.timestamp > latest.timestamp)
) return entry;
return latest;
}, null);
if (lastReport) { if (lastReport) {
const lastTimestamp = new Date(lastReport.timestamp).getTime(); const timeDifference = Date.now() - lastReport.timestamp;
const currentTime = Date.now(); if (timeDifference < REPORTED_IP_COOLDOWN_MS) {
const timeDifference = currentTime - lastTimestamp; return { recentlyReported: true, timeDifference, reason: lastReport.status === 'TOO_MANY_REQUESTS' ? 'RATE-LIMITED' : 'REPORTED' };
if (timeDifference < REPORTED_IP_COOLDOWN_MS) return { recentlyReported: true, timeDifference }; }
} }
return { recentlyReported: false }; return { recentlyReported: false };
@ -96,7 +100,7 @@ const reportIP = async (event, country, hostname, endpoint, userAgent, cycleErro
// Sefinek API // Sefinek API
if (REPORT_TO_SEFINEK_API && SEFINEK_API_INTERVAL && process.env.SEFINEK_API_SECRET) { if (REPORT_TO_SEFINEK_API && SEFINEK_API_INTERVAL && process.env.SEFINEK_API_SECRET) {
setInterval(async () => await SefinekAPI(), SEFINEK_API_INTERVAL); setInterval(SefinekAPI, SEFINEK_API_INTERVAL);
} }
// Ready // Ready
@ -111,7 +115,7 @@ const reportIP = async (event, country, hostname, endpoint, userAgent, cycleErro
// AbuseIPDB // AbuseIPDB
let cycleId = 1; let cycleId = 1;
while (true) { while (true) {
log('log', `===================== New Reporting Cycle (v${moduleVersion}) =====================`); log('log', `================ New Reporting Cycle v${moduleVersion}; ID: ${cycleId} ================`);
const blockedIPEvents = await fetchBlockedIPs(); const blockedIPEvents = await fetchBlockedIPs();
if (!blockedIPEvents) { if (!blockedIPEvents) {
@ -136,12 +140,13 @@ const reportIP = async (event, country, hostname, endpoint, userAgent, cycleErro
} }
const reportedIPs = readReportedIPs(); const reportedIPs = readReportedIPs();
const { recentlyReported, timeDifference } = isIPReportedRecently(event.rayName, ip, reportedIPs); const { recentlyReported, timeDifference, reason } = isIPReportedRecently(event.rayName, ip, reportedIPs);
if (recentlyReported) { if (recentlyReported) {
const hoursAgo = Math.floor(timeDifference / (1000 * 60 * 60)); const hoursAgo = Math.floor(timeDifference / (1000 * 60 * 60));
const minutesAgo = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60)); const minutesAgo = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
const secondsAgo = Math.floor((timeDifference % (1000 * 60)) / 1000); const secondsAgo = Math.floor((timeDifference % (1000 * 60)) / 1000);
log('log', `${ip} was reported or rate-limited ${hoursAgo}h ${minutesAgo}m ${secondsAgo}s ago. Skipping...`);
log('log', `${ip} was ${reason} ${hoursAgo}h ${minutesAgo}m ${secondsAgo}s ago. Skipping...`);
cycleSkippedCount++; cycleSkippedCount++;
continue; continue;
} }
@ -164,7 +169,6 @@ const reportIP = async (event, country, hostname, endpoint, userAgent, cycleErro
} }
} }
log('log', `Cycle Summary [${cycleId}]:`);
log('log', `- Reported IPs: ${cycleReportedCount}`); log('log', `- Reported IPs: ${cycleReportedCount}`);
log('log', `- Total IPs processed: ${cycleProcessedCount}`); log('log', `- Total IPs processed: ${cycleProcessedCount}`);
log('log', `- Skipped IPs: ${cycleSkippedCount}`); log('log', `- Skipped IPs: ${cycleSkippedCount}`);
@ -172,7 +176,7 @@ const reportIP = async (event, country, hostname, endpoint, userAgent, cycleErro
log('log', `- 429 Too Many Requests: ${cycleErrorCounts.blocked}`); log('log', `- 429 Too Many Requests: ${cycleErrorCounts.blocked}`);
log('log', `- No response errors: ${cycleErrorCounts.noResponse}`); log('log', `- No response errors: ${cycleErrorCounts.noResponse}`);
log('log', `- Other errors: ${cycleErrorCounts.otherErrors}`); log('log', `- Other errors: ${cycleErrorCounts.otherErrors}`);
log('log', '==================== End of Reporting Cycle ===================='); log('log', '===================== End of Reporting Cycle =====================');
log('log', `Waiting ${formatDelay(CYCLE_INTERVAL)}...`); log('log', `Waiting ${formatDelay(CYCLE_INTERVAL)}...`);
cycleId++; cycleId++;

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "waf-to-abuseipdb", "name": "waf-to-abuseipdb",
"version": "1.1.5", "version": "1.1.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "waf-to-abuseipdb", "name": "waf-to-abuseipdb",
"version": "1.1.5", "version": "1.1.6",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"axios": "^1.7.7", "axios": "^1.7.7",

View file

@ -1,6 +1,6 @@
{ {
"name": "waf-to-abuseipdb", "name": "waf-to-abuseipdb",
"version": "1.1.5", "version": "1.1.6",
"description": "A Node.js project that enables automatic reporting of incidents detected by Cloudflare WAF to the AbuseIPDB database.", "description": "A Node.js project that enables automatic reporting of incidents detected by Cloudflare WAF to the AbuseIPDB database.",
"keywords": [ "keywords": [
"abuseipdb", "abuseipdb",
@ -20,7 +20,7 @@
"url": "git+https://github.com/sefinek24/Node-Cloudflare-WAF-AbuseIPDB.git" "url": "git+https://github.com/sefinek24/Node-Cloudflare-WAF-AbuseIPDB.git"
}, },
"license": "MIT", "license": "MIT",
"author": "Sefinek <contact@nekosia.cat> (https://sefinek.net)", "author": "Sefinek <contact@sefinek.net> (https://sefinek.net)",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",

View file

@ -8,7 +8,7 @@ const SEFINEK_API_URL = process.env.SEFINEK_API_URL || `${process.env.NODE_ENV =
module.exports = async () => { module.exports = async () => {
const userIp = clientIp.getAddress(); const userIp = clientIp.getAddress();
const reportedIPs = readReportedIPs().filter(x => x.status === 'REPORTED' && x.ip !== userIp && !x.sefinekAPI); const reportedIPs = readReportedIPs().filter(x => x.status === 'REPORTED' && x.ip !== userIp && !x.sefinekAPI);
if (reportedIPs.length === 0) return log('log', 'No IPs with `action Reported` and `SefinekAPI false` to send to Sefinek API'); if (reportedIPs.length === 0) return;
const uniqueLogs = reportedIPs.reduce((acc, ip) => { const uniqueLogs = reportedIPs.reduce((acc, ip) => {
if (acc.seen.has(ip.ip)) return acc; if (acc.seen.has(ip.ip)) return acc;