From 73c8a834d30c68134a4dd7a10982530e60079180 Mon Sep 17 00:00:00 2001 From: Sefinek Date: Sun, 2 Feb 2025 01:51:39 +0100 Subject: [PATCH] 1.3.0 --- .env.default | 42 ------------------ .gitignore | 2 +- README.md | 28 +++++++----- config.default.js | 64 ++++++++++++++++++++++++++++ index.js | 46 +++++++++----------- package-lock.json | 35 +++++++-------- package.json | 7 +-- scripts/config.js | 25 ----------- scripts/generateComment.js | 30 ------------- scripts/headers.js | 17 -------- services/SefinekAPI.js | 17 ++++---- services/axios.js | 15 ++++--- services/clientIp.js | 23 ---------- services/csv.js | 2 +- services/fetchServerIP.js | 41 ++++++++++++++++++ services/payload.js | 4 +- {scripts => utils}/formatDelay.js | 0 utils/headers.js | 18 ++++++++ {scripts => utils}/isImageRequest.js | 0 utils/isLocalIP.js | 10 +++++ {scripts => utils}/log.js | 0 {scripts => utils}/whitelist.js | 0 22 files changed, 214 insertions(+), 212 deletions(-) delete mode 100644 .env.default create mode 100644 config.default.js delete mode 100644 scripts/config.js delete mode 100644 scripts/generateComment.js delete mode 100644 scripts/headers.js delete mode 100644 services/clientIp.js create mode 100644 services/fetchServerIP.js rename {scripts => utils}/formatDelay.js (100%) create mode 100644 utils/headers.js rename {scripts => utils}/isImageRequest.js (100%) create mode 100644 utils/isLocalIP.js rename {scripts => utils}/log.js (100%) rename {scripts => utils}/whitelist.js (100%) diff --git a/.env.default b/.env.default deleted file mode 100644 index 2883e87..0000000 --- a/.env.default +++ /dev/null @@ -1,42 +0,0 @@ -# production or development -NODE_ENV=production - -############################### TOKENS ############################### -# Cloudflare (https://dash.cloudflare.com/profile/api-tokens) -CLOUDFLARE_ZONE_ID=00000000000000000000000000000000 -CLOUDFLARE_API_KEY=0000000000000000000000000000000000000000 - -# AbuseIPDB (https://www.abuseipdb.com/account/api) -ABUSEIPDB_API_KEY=00000000000000000000000000000000000000000000000000000000000000000000000000000000 - -############################### CYCLES ############################### -# Main interval (in minutes) of each cycle. For production 2h; development 8s. -CYCLE_INTERVAL=120 - -# The minimum time (in hours) 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=7 - -# 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 miliseconds) after each successful IP report to avoid overloading the AbuseIPDB API. -SUCCESS_COOLDOWN=80 - -# 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/sefinek/Malicious-IP-Addresses. SEFINEK_API_SECRET is required if true. -REPORT_TO_SEFINEK_API=false - -# Secret key for api.sefinek.net -SEFINEK_API_SECRET= - -# How often should the log (reported_ips.csv) be analyzed and sent to the Sefinek API? In hours. -SEFINEK_API_INTERVAL=1 - -# Sefinek API v2 URL -SEFINEK_API_URL=https://api.sefinek.net/api/v2 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0724280..d475b68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ node_modules -.env +config.js reported_ips.csv \ No newline at end of file diff --git a/README.md b/README.md index 8c20d1c..2dc1594 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ Triggered Cloudflare WAF (securitylevel) from T1. Action taken: MANAGED_CHALLENGE ASN: 53667 (PONYNET) Protocol: HTTP/1.0 (method GET) -Zone: blocklist.sefinek.net Endpoint: / Timestamp: 2024-11-09T19:20:18Z Ray ID: 8e0028cb79ab3a96 @@ -47,33 +46,40 @@ https://github.com/sefinek/Cloudflare-WAF-To-AbuseIPDB ```bash npm install ``` -3. Environment variables. Create a new `.env.default` file with the same content, then rename it to `.env`. Fill it with your tokens, etc. Remember to set `NODE_ENV` to `production`! -4. Run the script. +3. Create a new configuration file. + ```bash + cp config.default.js config.js + ``` +4. Paste the tokens into the `config.js` file. Make sure that `NODE_ENV` is set to `production`. + ```bash + nano config.js + ``` +5. Run the script. ```bash node . ``` -5. If you want to run the process 24/7, install the [PM2](https://www.npmjs.com/package/pm2) module. +6. If you want to run the process 24/7, install the [PM2](https://www.npmjs.com/package/pm2) module. ```bash npm install pm2 -g ``` -6. Modify the log paths in the `ecosystem.config.js` file to be correct and existing. You don't need to create `.log` files, just ensure the directory structure is accurate. -7. Run the process continuously using `PM2` to ensure constant operation and automatic restart in case of a failure. +7. Modify the log paths in the `ecosystem.config.js` file to be correct and existing. You don't need to create `.log` files, just ensure the directory structure is accurate. +8. Run the process continuously using `PM2` to ensure constant operation and automatic restart in case of a failure. ```bash pm2 start ``` -8. Save a snapshot of the currently running `Node.js` processes. +9. Save a snapshot of the currently running `Node.js` processes. ```bash pm2 save ``` -9. Add `PM2` to startup. +10. Add `PM2` to startup. ```bash pm2 startup ``` -10. Execute the command generated by PM2, e.g.: +11. Execute the command generated by PM2, e.g.: ```bash sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sefinek --hp /home/sefinek ``` -11. That’s it! Monitor logs using the `pm2 logs` command. +12. That’s it! Monitor logs using the `pm2 logs` command. ## 🔤 How to Get Tokens? @@ -100,4 +106,4 @@ I'm not particularly fond of Python and usually try to avoid using this programm ## 📑 MIT License -Copyright 2024 © by [Sefinek](https://sefinek.net). All Rights Reserved. +Copyright 2024-2025 © by [Sefinek](https://sefinek.net). All Rights Reserved. diff --git a/config.default.js b/config.default.js new file mode 100644 index 0000000..8bd6f0e --- /dev/null +++ b/config.default.js @@ -0,0 +1,64 @@ +exports.CONFIG = { + MAIN: { + NODE_ENV: 'production', // Environment mode: 'production' or 'development' + CLOUDFLARE_ZONE_ID: '00000000000000000000000000000000', // https://dash.cloudflare.com/profile/api-tokens + CLOUDFLARE_API_KEY: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', // API key for Cloudflare access + ABUSEIPDB_API_KEY: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', // API key for reporting malicious IPs to AbuseIPDB + }, + + CYCLES: { + // Main interval (in minutes) of each cycle + CYCLE_INTERVAL: 120 * 60 * 1000, + + // The minimum time (in hours) 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: 6 * 60 * 60 * 1000, + + // The maximum URI length that can be reported to AbuseIPDB. + // If Cloudflare returns a longer URI, the API request will fail. + MAX_URL_LENGTH: 780, + + // Additional delay (in milliseconds) after each successful IP report to avoid overloading the AbuseIPDB API. + SUCCESS_COOLDOWN: 30, + + // 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 * 60 * 1000, + }, + + SEFINEK_API: { + // Report IP addresses to api.sefinek.net to support the development of the repository at https://github.com/sefinek/Malicious-IP-Addresses. SECRET_TOKEN is required if true. + REPORT_TO_SEFIN_API: true, + + // Secret key for api.sefinek.net + SECRET_TOKEN: 'HKKAUZHTDAH7W87SyL6XsWkV8UeUFVA9VvvXhn6H9Wn6kfDW6ZsXCtbahmkaYcLbxZGyrAKPmSkXb3AJ6pCU3VuDyTjUSehMyDZ', + + // How often should the log (reported_ips.csv) be analyzed and sent to the Sefinek API? In hours. + INTERVAL: 60 * 60 * 1000, // Frequency for analyzing and submitting logs to the Sefinek API + }, +}; + +exports.GENERATE_COMMENT = ({ action, clientAsn, clientASNDescription, clientRequestHTTPProtocol, clientRequestHTTPMethodName, clientRequestHTTPHost, clientRequestPath, clientRequestQuery, datetime, rayName, ruleId, userAgent, source, clientCountryName }) => { + const fields = [ + { label: 'Action taken', value: action?.toUpperCase() }, + { label: 'ASN', value: `${clientAsn} (${clientASNDescription})` }, + { label: 'Protocol', value: `${clientRequestHTTPProtocol} (${clientRequestHTTPMethodName} method)` }, + // { label: 'Zone', value: clientRequestHTTPHost }, + { label: 'Endpoint', value: clientRequestPath }, + { label: 'Query', value: clientRequestQuery }, + { label: 'Timestamp', value: datetime }, + { label: 'Ray ID', value: rayName }, + // { label: 'Rule ID', value: ruleId }, + { label: 'UA', value: userAgent || 'Empty string' }, + ]; + + const reportLines = fields + .filter(({ value }) => value) + .map(({ label, value }) => `${label}: ${value}`); + + return `Triggered Cloudflare WAF (${source}) from ${clientCountryName}. +${reportLines.join('\n')} + +Report generated by Cloudflare-WAF-To-AbuseIPDB: +https://github.com/sefinek/Cloudflare-WAF-To-AbuseIPDB`; // Please do not remove the repository URL. I'd really appreciate it (: +}; \ No newline at end of file diff --git a/index.js b/index.js index 0fb611a..5d2fc28 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,14 @@ -require('dotenv').config(); - -const { axios } = require('./services/axios.js'); -const { CYCLE_INTERVAL, REPORTED_IP_COOLDOWN, MAX_URL_LENGTH, SUCCESS_COOLDOWN, SEFINEK_API_INTERVAL, REPORT_TO_SEFINEK_API } = require('./scripts/config.js'); +const axios = require('./services/axios.js'); +const { CONFIG, GENERATE_COMMENT } = require('./config.js'); const PAYLOAD = require('./services/payload.js'); -const generateComment = require('./scripts/generateComment.js'); const SefinekAPI = require('./services/SefinekAPI.js'); -const isImageRequest = require('./scripts/isImageRequest.js'); -const headers = require('./scripts/headers.js'); +const isImageRequest = require('./utils/isImageRequest.js'); +const headers = require('./utils/headers.js'); const { logToCSV, readReportedIPs, wasImageRequestLogged } = require('./services/csv.js'); -const formatDelay = require('./scripts/formatDelay.js'); -const clientIp = require('./services/clientIp.js'); -const whitelist = require('./scripts/whitelist.js'); -const log = require('./scripts/log.js'); +const formatDelay = require('./utils/formatDelay.js'); +const fetchServerIP = require('./services/fetchServerIP.js'); +const whitelist = require('./utils/whitelist.js'); +const log = require('./utils/log.js'); const fetchBlockedIPs = async () => { try { @@ -19,7 +16,7 @@ const fetchBlockedIPs = async () => { const events = data?.data?.viewer?.zones[0]?.firewallEventsAdaptive; if (events) { const filtered = events.filter(x => - x.ip !== clientIp.getAddress() && + x.ip !== fetchServerIP() && ( x.source === 'securitylevel' || x.source === 'badscore' || @@ -52,7 +49,7 @@ const isIPReportedRecently = (rayId, ip, reportedIPs) => { return latest; }, null); - if (lastReport && (Date.now() - lastReport.timestamp) < REPORTED_IP_COOLDOWN) { + if (lastReport && (Date.now() - lastReport.timestamp) < CONFIG.CYCLES.REPORTED_IP_COOLDOWN) { return { recentlyReported: true, timeDifference: Date.now() - lastReport.timestamp, reason: lastReport.status === 'TOO_MANY_REQUESTS' ? 'RATE-LIMITED' : 'REPORTED' }; } @@ -66,13 +63,13 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount return false; } - if (event.clientIP === clientIp.address) { + if (event.clientIP === fetchServerIP()) { logToCSV(event.rayName, event.clientIP, country, hostname, endpoint, event.userAgent, event.action, 'YOUR_IP_ADDRESS'); log(0, `Your IP address (${event.clientIP}) was unexpectedly received from Cloudflare. URI: ${uri}`); return false; } - if (uri.length > MAX_URL_LENGTH) { + if (uri.length > CONFIG.CYCLES.MAX_URL_LENGTH) { logToCSV(event.rayName, event.clientIP, country, hostname, endpoint, event.userAgent, event.action, 'URI_TOO_LONG'); // log(0, `URI too long ${event.clientIP}; Received: ${uri}`); return false; @@ -82,7 +79,7 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount await axios.post('https://api.abuseipdb.com/api/v2/report', { ip: event.clientIP, categories: '19', - comment: generateComment(event), + comment: GENERATE_COMMENT(event), }, { headers: headers.ABUSEIPDB }); logToCSV(event.rayName, event.clientIP, country, hostname, endpoint, event.userAgent, event.action, 'REPORTED'); @@ -105,15 +102,14 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount (async () => { log(0, 'Loading data, please wait...'); - await clientIp.fetchIPAddress(); // Sefinek API - if (REPORT_TO_SEFINEK_API && SEFINEK_API_INTERVAL && process.env.SEFINEK_API_SECRET) { - setInterval(SefinekAPI, SEFINEK_API_INTERVAL); + if (CONFIG.SEFINEK_API.REPORT_TO_SEFIN_API && CONFIG.SEFINEK_API.INTERVAL && CONFIG.SEFINEK_API.SECRET_TOKEN) { + setInterval(SefinekAPI, CONFIG.SEFINEK_API.INTERVAL); } // Ready - if (process.env.NODE_ENV === 'production') { + if (CONFIG.MAIN.NODE_ENV === 'production') { try { process.send('ready'); } catch (err) { @@ -132,7 +128,7 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount continue; } - const userIp = clientIp.getAddress(); + const userIp = fetchServerIP(); if (!userIp) log(1, `Your IP address is missing! Received: ${userIp}`); let cycleImageSkippedCount = 0, cycleProcessedCount = 0, cycleReportedCount = 0, cycleSkippedCount = 0; @@ -153,7 +149,7 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount const reportedIPs = readReportedIPs(); const { recentlyReported } = isIPReportedRecently(event.rayName, ip, reportedIPs); if (recentlyReported) { - // if (process.env.NODE_ENV === 'development') { + // if (MAIN.NODE_ENV === 'development') { // const hoursAgo = Math.floor(timeDifference / (1000 * 60 * 60)); // const minutesAgo = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60)); // const secondsAgo = Math.floor((timeDifference % (1000 * 60)) / 1000); @@ -178,7 +174,7 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount const wasReported = await reportIP(event, `${event.clientRequestHTTPHost}${event.clientRequestPath}`, event.clientCountryName, event.clientRequestHTTPHost, event.clientRequestPath, cycleErrorCounts); if (wasReported) { cycleReportedCount++; - await new Promise(resolve => setTimeout(resolve, SUCCESS_COOLDOWN)); + await new Promise(resolve => setTimeout(resolve, CONFIG.CYCLES.SUCCESS_COOLDOWN)); } } @@ -190,8 +186,8 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount log(0, `- Other errors: ${cycleErrorCounts.otherErrors}`); log(0, '===================== End of Reporting Cycle ====================='); - log(0, `Waiting ${formatDelay(CYCLE_INTERVAL)}...`); + log(0, `Waiting ${formatDelay(CONFIG.CYCLES.CYCLE_INTERVAL)}...`); cycleId++; - await new Promise(resolve => setTimeout(resolve, CYCLE_INTERVAL)); + await new Promise(resolve => setTimeout(resolve, CONFIG.CYCLES.CYCLE_INTERVAL)); } })(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e827b07..2d4e6b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,26 +1,26 @@ { "name": "cf-waf-to-abuseipdb", - "version": "1.2.3", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cf-waf-to-abuseipdb", - "version": "1.2.3", + "version": "1.3.0", "license": "MIT", "dependencies": { "axios": "^1.7.9", - "dotenv": "^16.4.7" + "ipaddr.js": "^2.2.0" }, "devDependencies": { - "@eslint/js": "^9.18.0", + "@eslint/js": "^9.19.0", "globals": "^15.14.0" } }, "node_modules/@eslint/js": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", - "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", + "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", "dev": true, "license": "MIT", "engines": { @@ -65,18 +65,6 @@ "node": ">=0.4.0" } }, - "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", @@ -124,6 +112,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", diff --git a/package.json b/package.json index ad18ed6..297beb6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cf-waf-to-abuseipdb", - "version": "1.2.3", + "version": "1.3.0", "description": "Node.js script for automatically reporting incidents to AbuseIPDB using data obtained from Cloudflare WAF.", "keywords": [ "abuseipdb", @@ -21,6 +21,7 @@ }, "license": "MIT", "author": "Sefinek (https://sefinek.net)", + "type": "commonjs", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", @@ -28,10 +29,10 @@ }, "dependencies": { "axios": "^1.7.9", - "dotenv": "^16.4.7" + "ipaddr.js": "^2.2.0" }, "devDependencies": { - "@eslint/js": "^9.18.0", + "@eslint/js": "^9.19.0", "globals": "^15.14.0" } } diff --git a/scripts/config.js b/scripts/config.js deleted file mode 100644 index 6d706b0..0000000 --- a/scripts/config.js +++ /dev/null @@ -1,25 +0,0 @@ -const CYCLE_INTERVAL = process.env.NODE_ENV === 'production' ? - parseInt(process.env.CYCLE_INTERVAL || '120') * 60 * 1000 : 8 * 1000; - -const REPORTED_IP_COOLDOWN = parseInt(process.env.REPORTED_IP_COOLDOWN || '6') * 60 * 60 * 1000; - -const MAX_URL_LENGTH = parseInt(process.env.MAX_URL_LENGTH || '920'); - -const SUCCESS_COOLDOWN = parseInt(process.env.SUCCESS_COOLDOWN || '200'); - -const IP_REFRESH_INTERVAL = parseInt(process.env.IP_REFRESH_INTERVAL || '80') * 60 * 1000; - -const REPORT_TO_SEFINEK_API = process.env.REPORT_TO_SEFINEK_API === 'true'; - -const SEFINEK_API_INTERVAL = process.env.NODE_ENV === 'production' ? - parseInt(process.env.SEFINEK_API_INTERVAL || '1') * 60 * 60 * 1000 : 5 * 1000; - -module.exports = { - CYCLE_INTERVAL, - REPORTED_IP_COOLDOWN, - MAX_URL_LENGTH, - SUCCESS_COOLDOWN, - IP_REFRESH_INTERVAL, - REPORT_TO_SEFINEK_API, - SEFINEK_API_INTERVAL, -}; \ No newline at end of file diff --git a/scripts/generateComment.js b/scripts/generateComment.js deleted file mode 100644 index 5e25797..0000000 --- a/scripts/generateComment.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = ({ action, clientAsn, clientASNDescription, clientRequestHTTPProtocol, clientRequestHTTPMethodName, clientRequestHTTPHost, clientRequestPath, clientRequestQuery, datetime, rayName, ruleId, userAgent, source, clientCountryName }) => { - const fields = [ - { label: 'Action taken', value: action?.toUpperCase() }, - { label: 'ASN', value: `${clientAsn} (${clientASNDescription})` }, - { label: 'Protocol', value: `${clientRequestHTTPProtocol} (${clientRequestHTTPMethodName} method)` }, - // { label: 'Zone', value: clientRequestHTTPHost }, - { label: 'Endpoint', value: clientRequestPath }, - { label: 'Query', value: clientRequestQuery }, - { label: 'Timestamp', value: datetime }, - { label: 'Ray ID', value: rayName }, - // { label: 'Rule ID', value: ruleId }, - { label: 'UA', value: userAgent || 'Empty string' }, - ]; - - const reportLines = fields - .filter(({ value }) => value) - .map(({ label, value }) => `${label}: ${value}`); - - return `Triggered Cloudflare WAF (${source}) from ${clientCountryName}. -${reportLines.join('\n')} - -Report generated by Cloudflare-WAF-To-AbuseIPDB: -https://github.com/sefinek/Cloudflare-WAF-To-AbuseIPDB`; -}; - -/* - * Hello! 👋 I'm really glad you're here. - * Please do not remove the repository URL in the comment above. - * I’d really appreciate it (: - */ \ No newline at end of file diff --git a/scripts/headers.js b/scripts/headers.js deleted file mode 100644 index 7317a5b..0000000 --- a/scripts/headers.js +++ /dev/null @@ -1,17 +0,0 @@ -const { name, version, homepage } = require('../package.json'); - -const UserAgent = `Mozilla/5.0 (compatible; ${name}/${version}; +${homepage})`; - -const CLOUDFLARE = { - 'User-Agent': UserAgent, - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${process.env.CLOUDFLARE_API_KEY}`, -}; - -const ABUSEIPDB = { - 'User-Agent': UserAgent, - 'Content-Type': 'application/json', - 'Key': process.env.ABUSEIPDB_API_KEY, -}; - -module.exports = { UserAgent, CLOUDFLARE, ABUSEIPDB }; \ No newline at end of file diff --git a/services/SefinekAPI.js b/services/SefinekAPI.js index 74e2872..5b474a9 100644 --- a/services/SefinekAPI.js +++ b/services/SefinekAPI.js @@ -1,15 +1,14 @@ -const { axios } = require('./axios.js'); +const axios = require('./axios.js'); const { readReportedIPs, updateSefinekAPIInCSV } = require('./csv.js'); -const log = require('../scripts/log.js'); -const clientIp = require('./clientIp.js'); -const whitelist = require('../scripts/whitelist.js'); - -const API_URL = `${process.env.SEFINEK_API_URL}/cloudflare-waf-abuseipdb/post`; +const log = require('../utils/log.js'); +const fetchServerIP = require('./fetchServerIP.js'); +const whitelist = require('../utils/whitelist.js'); +const { MAIN } = require('../config.js').CONFIG; module.exports = async () => { const reportedIPs = (readReportedIPs() || []).filter(x => x.status === 'REPORTED' && - x.ip !== clientIp.getAddress() && + x.ip !== fetchServerIP() && !x.sefinekAPI && ( x.source === 'securitylevel' || @@ -35,7 +34,7 @@ module.exports = async () => { if (!uniqueLogs.length) return log(0, 'No unique IPs to send to Sefinek API'); try { - const res = await axios.post(API_URL, { + const res = await axios.post('https://api.sefinek.net/api/v2/cloudflare-waf-abuseipdb/post', { reportedIPs: uniqueLogs.map(ip => ({ rayId: ip.rayId, ip: ip.ip, @@ -45,7 +44,7 @@ module.exports = async () => { country: ip.country, timestamp: ip.timestamp, })), - }, { headers: { 'Authorization': process.env.SEFINEK_API_SECRET } }); + }, { headers: { 'Authorization': MAIN.SECRET_TOKEN } }); log(0, `Successfully sent ${uniqueLogs.length} logs to Sefinek API. Status: ${res.status}`); diff --git a/services/axios.js b/services/axios.js index 9c2e555..cdfb6e2 100644 --- a/services/axios.js +++ b/services/axios.js @@ -1,8 +1,13 @@ const axios = require('axios'); -const { UserAgent } = require('../scripts/headers.js'); -const { version } = require('../package.json'); +const { UserAgent } = require('../utils/headers.js'); -axios.defaults.headers.common['User-Agent'] = UserAgent; -axios.defaults.timeout = 25000; +axios.defaults.headers.common = { + 'User-Agent': UserAgent, + 'Accept': 'application/json', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', +}; -module.exports = { axios, moduleVersion: version }; \ No newline at end of file +axios.defaults.timeout = 30000; + +module.exports = axios; \ No newline at end of file diff --git a/services/clientIp.js b/services/clientIp.js deleted file mode 100644 index 5a874f6..0000000 --- a/services/clientIp.js +++ /dev/null @@ -1,23 +0,0 @@ -const { axios } = require('./axios.js'); -const { IP_REFRESH_INTERVAL } = require('../scripts/config.js'); -const log = require('../scripts/log.js'); - -let address = null; // Holds the IP address - -const fetchIPAddress = async () => { - try { - const { data } = await axios.get('https://api.sefinek.net/api/v2/ip'); - if (data?.success) { - address = data.message; - } else { - log(2, 'Failed to retrieve your IP'); - } - } catch (err) { - log(2, 'Error fetching your IP:', err.stack); - } -}; - - -setInterval(fetchIPAddress, IP_REFRESH_INTERVAL); - -module.exports = { fetchIPAddress, getAddress: () => address }; \ No newline at end of file diff --git a/services/csv.js b/services/csv.js index e0604a2..956e52b 100644 --- a/services/csv.js +++ b/services/csv.js @@ -1,6 +1,6 @@ const fs = require('node:fs'); const path = require('node:path'); -const log = require('../scripts/log.js'); +const log = require('../utils/log.js'); const CSV_FILE_PATH = path.join(__dirname, '..', 'reported_ips.csv'); const MAX_CSV_SIZE_BYTES = 3 * 1024 * 1024; // 3 MB diff --git a/services/fetchServerIP.js b/services/fetchServerIP.js new file mode 100644 index 0000000..bebe2c0 --- /dev/null +++ b/services/fetchServerIP.js @@ -0,0 +1,41 @@ +const { networkInterfaces } = require('node:os'); +const axios = require('./axios.js'); +const isLocalIP = require('../utils/isLocalIP.js'); +const log = require('../utils/log.js'); +const { CYCLES } = require('../config.js').CONFIG; + +const ipAddrList = new Set(); + +const fetchIPv4Address = async () => { + try { + const { data } = await axios.get('https://api.sefinek.net/api/v2/ip'); + if (data?.success && data?.message) ipAddrList.add(data.message); + } catch (err) { + log(2, `Error fetching IPv4 address: ${err.message}`); + } +}; + +const fetchIPv6Address = () => { + try { + Object.values(networkInterfaces()).flat().forEach(({ address, internal }) => { + if (!internal && address && !isLocalIP(address)) ipAddrList.add(address); + }); + } catch (err) { + log(2, `Error fetching IPv6 address: ${err.message}`); + } +}; + +const fetchServerIPs = async () => { + ipAddrList.clear(); + await fetchIPv4Address(); + fetchIPv6Address(); +}; + +(async () => { + await fetchServerIPs(); + setInterval(fetchServerIPs, CYCLES.IP_REFRESH_INTERVAL); + + // console.debug(ipAddrList); +})(); + +module.exports = () => Array.from(ipAddrList); \ No newline at end of file diff --git a/services/payload.js b/services/payload.js index 7c6ee1d..f5af5c9 100644 --- a/services/payload.js +++ b/services/payload.js @@ -1,3 +1,5 @@ +const { MAIN } = require('../config.js').CONFIG; + const query = `query ListFirewallEvents($zoneTag: string, $filter: FirewallEventsAdaptiveFilter_InputObject) { viewer { zones(filter: { zoneTag: $zoneTag }) { @@ -28,7 +30,7 @@ const query = `query ListFirewallEvents($zoneTag: string, $filter: FirewallEvent module.exports = () => { const variables = { - zoneTag: process.env.CLOUDFLARE_ZONE_ID, + zoneTag: MAIN.CLOUDFLARE_ZONE_ID, filter: { datetime_geq: new Date(Date.now() - (60 * 60 * 12 * 1000)).toISOString(), // datetime_leq: new Date(Date.now() - (60 * 60 * 8 * 1000)).toISOString(), diff --git a/scripts/formatDelay.js b/utils/formatDelay.js similarity index 100% rename from scripts/formatDelay.js rename to utils/formatDelay.js diff --git a/utils/headers.js b/utils/headers.js new file mode 100644 index 0000000..ec30e92 --- /dev/null +++ b/utils/headers.js @@ -0,0 +1,18 @@ +const { MAIN } = require('../config.js').CONFIG; +const { version, homepage } = require('../package.json'); + +const UserAgent = `Mozilla/5.0 (compatible; Cloudflare-WAF-To-AbuseIPDB/${version}; +${homepage})`; + +const CLOUDFLARE = { + 'User-Agent': UserAgent, + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${MAIN.CLOUDFLARE_API_KEY}`, +}; + +const ABUSEIPDB = { + 'User-Agent': UserAgent, + 'Content-Type': 'application/json', + 'Key': MAIN.ABUSEIPDB_API_KEY, +}; + +module.exports = { UserAgent, CLOUDFLARE, ABUSEIPDB }; \ No newline at end of file diff --git a/scripts/isImageRequest.js b/utils/isImageRequest.js similarity index 100% rename from scripts/isImageRequest.js rename to utils/isImageRequest.js diff --git a/utils/isLocalIP.js b/utils/isLocalIP.js new file mode 100644 index 0000000..7d7ab80 --- /dev/null +++ b/utils/isLocalIP.js @@ -0,0 +1,10 @@ +const ipaddr = require('ipaddr.js'); + +module.exports = ip => { + const range = ipaddr.parse(ip).range(); + return [ + 'unspecified', 'multicast', 'linkLocal', 'loopback', 'reserved', 'benchmarking', + 'amt', 'broadcast', 'carrierGradeNat', 'private', 'as112', 'uniqueLocal', + 'ipv4Mapped', 'rfc6145', '6to4', 'teredo', 'as112v6', 'orchid2', 'droneRemoteIdProtocolEntityTags', + ].includes(range); +}; \ No newline at end of file diff --git a/scripts/log.js b/utils/log.js similarity index 100% rename from scripts/log.js rename to utils/log.js diff --git a/scripts/whitelist.js b/utils/whitelist.js similarity index 100% rename from scripts/whitelist.js rename to utils/whitelist.js