1.3.0
This commit is contained in:
parent
a0eb452044
commit
73c8a834d3
22 changed files with 214 additions and 212 deletions
42
.env.default
42
.env.default
|
|
@ -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
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,3 +1,3 @@
|
|||
node_modules
|
||||
.env
|
||||
config.js
|
||||
reported_ips.csv
|
||||
28
README.md
28
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.
|
||||
|
|
|
|||
64
config.default.js
Normal file
64
config.default.js
Normal file
|
|
@ -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 (:
|
||||
};
|
||||
46
index.js
46
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));
|
||||
}
|
||||
})();
|
||||
35
package-lock.json
generated
35
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 <contact@sefinek.net> (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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
@ -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 (:
|
||||
*/
|
||||
|
|
@ -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 };
|
||||
|
|
@ -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}`);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
axios.defaults.timeout = 30000;
|
||||
|
||||
module.exports = axios;
|
||||
|
|
@ -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 };
|
||||
|
|
@ -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
|
||||
|
|
|
|||
41
services/fetchServerIP.js
Normal file
41
services/fetchServerIP.js
Normal file
|
|
@ -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);
|
||||
|
|
@ -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(),
|
||||
|
|
|
|||
18
utils/headers.js
Normal file
18
utils/headers.js
Normal file
|
|
@ -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 };
|
||||
10
utils/isLocalIP.js
Normal file
10
utils/isLocalIP.js
Normal file
|
|
@ -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);
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue