Improved the service fetching the device's IP address
This commit is contained in:
parent
1c4f916480
commit
1ae07a1ca2
7 changed files with 103 additions and 66 deletions
|
|
@ -5,10 +5,11 @@ exports.CONFIG = {
|
|||
CLOUDFLARE_API_KEY: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', // https://dash.cloudflare.com/profile/api-tokens
|
||||
ABUSEIPDB_API_KEY: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', // API key for reporting malicious IPs to AbuseIPDB
|
||||
RUN_ON_START: true, // Should the reporting function run immediately after the script starts?
|
||||
IPv6_SUPPORT: true, // Specifies whether the device has been assigned an IPv6 address.
|
||||
},
|
||||
|
||||
CYCLES: {
|
||||
// Schedule for running cron jobs for reporting to AbuseIPDB.
|
||||
// CRON: Schedule for running cron jobs for reporting to AbuseIPDB.
|
||||
REPORT_SCHEDULE: '0 */2 * * *',
|
||||
|
||||
// The minimum time (in hours) that must pass after reporting an IP address before it can be reported again.
|
||||
|
|
@ -22,9 +23,9 @@ exports.CONFIG = {
|
|||
// Additional delay (in milliseconds) after each successful IP report to avoid overloading the AbuseIPDB API.
|
||||
SUCCESS_COOLDOWN: 20,
|
||||
|
||||
// Interval for refreshing your IP address (in minutes).
|
||||
// CRON: Interval for refreshing your IP address. Default: every 6 hours
|
||||
// This ensures that WAF violations originating from your IP address are not reported to AbuseIPDB.
|
||||
IP_REFRESH_INTERVAL: 8 * 60 * 1000,
|
||||
IP_REFRESH_SCHEDULE: '0 */6 * * *',
|
||||
},
|
||||
|
||||
SEFINEK_API: {
|
||||
|
|
|
|||
40
index.js
40
index.js
|
|
@ -5,19 +5,18 @@ const PAYLOAD = require('./services/payload.js');
|
|||
const SefinekAPI = require('./services/SefinekAPI.js');
|
||||
const headers = require('./utils/headers.js');
|
||||
const { logToCSV, readReportedIPs } = require('./services/csv.js');
|
||||
const formatDelay = require('./utils/formatDelay.js');
|
||||
const fetchServerIP = require('./services/fetchServerIP.js');
|
||||
const { refreshServerIPs, getServerIPs } = require('./services/ipFetcher.js');
|
||||
const getFilters = require('./services/getFilters.js');
|
||||
const log = require('./utils/log.js');
|
||||
|
||||
const fetchBlockedIPs = async whitelist => {
|
||||
const fetchCloudflareEvents = async whitelist => {
|
||||
try {
|
||||
const { data, status } = await axios.post('https://api.cloudflare.com/client/v4/graphql', PAYLOAD(), { headers: headers.CLOUDFLARE });
|
||||
const events = data?.data?.viewer?.zones?.[0]?.firewallEventsAdaptive;
|
||||
if (!events) throw new Error(`Failed to retrieve data from Cloudflare (status ${status}): ${JSON.stringify(data?.errors)}`);
|
||||
|
||||
const isWhitelisted = event =>
|
||||
event.ip === fetchServerIP() ||
|
||||
getServerIPs().includes(event.ip) ||
|
||||
whitelist.userAgents.some(ua => event.userAgent.includes(ua)) ||
|
||||
whitelist.imgExtensions.some(ext => event.clientRequestPath.endsWith(ext)) ||
|
||||
whitelist.domains.some(domain => event.clientRequestHTTPHost?.includes(domain)) ||
|
||||
|
|
@ -60,7 +59,7 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount
|
|||
return false;
|
||||
}
|
||||
|
||||
if (event.clientIP === fetchServerIP()) {
|
||||
if (getServerIPs().includes(event.clientIP)) {
|
||||
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;
|
||||
|
|
@ -104,23 +103,27 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount
|
|||
let cycleId = 1;
|
||||
|
||||
const cron = async () => {
|
||||
log(0, `======================== Reporting Cycle No. ${cycleId} ========================`);
|
||||
|
||||
// Fetch cloudflare events
|
||||
const whitelist = await getFilters();
|
||||
const events = await fetchCloudflareEvents(whitelist);
|
||||
if (!events) return log(1, 'No events fetched, skipping cycle...');
|
||||
|
||||
log(0, `===================== Reporting Cycle No. ${cycleId} =====================`);
|
||||
|
||||
const blockedIPEvents = await fetchBlockedIPs(whitelist);
|
||||
if (!blockedIPEvents) return log(1, 'No events fetched, skipping cycle...');
|
||||
|
||||
const serverIP = fetchServerIP();
|
||||
if (!serverIP) log(1, `Server IP address is missing! Received: ${serverIP}`);
|
||||
// IP
|
||||
await refreshServerIPs();
|
||||
const ips = getServerIPs();
|
||||
if (!Array.isArray(ips)) return log(2, 'For some reason, \'ips\' is not an array');
|
||||
log(0, `Fetched ${getServerIPs()?.length} of your IP addresses`);
|
||||
|
||||
// Cycle
|
||||
let cycleProcessedCount = 0, cycleReportedCount = 0, cycleSkippedCount = 0;
|
||||
const cycleErrorCounts = { blocked: 0, otherErrors: 0 };
|
||||
|
||||
for (const event of blockedIPEvents) {
|
||||
for (const event of events) {
|
||||
cycleProcessedCount++;
|
||||
const ip = event.clientIP;
|
||||
if (ip === serverIP) {
|
||||
if (getServerIPs().includes(ip)) {
|
||||
log(0, `The IP address ${ip} belongs to this machine. Ignoring...`);
|
||||
cycleSkippedCount++;
|
||||
continue;
|
||||
|
|
@ -164,11 +167,12 @@ const cron = async () => {
|
|||
new CronJob(CONFIG.SEFINEK_API.REPORT_SCHEDULE, SefinekAPI, null, true, 'UTC');
|
||||
}
|
||||
|
||||
// Ready
|
||||
process.send && process.send('ready');
|
||||
log(0, 'The integration is ready!');
|
||||
|
||||
// AbuseIPDB
|
||||
new CronJob(CONFIG.CYCLES.REPORT_SCHEDULE, cron, null, true, 'UTC');
|
||||
|
||||
// Ready
|
||||
process.send && process.send('ready');
|
||||
|
||||
// Run on start?
|
||||
if (CONFIG.MAIN.RUN_ON_START) await cron();
|
||||
})();
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "cf-waf-to-abuseipdb",
|
||||
"version": "1.5.1",
|
||||
"version": "1.6.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cf-waf-to-abuseipdb",
|
||||
"version": "1.5.1",
|
||||
"version": "1.6.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.8.4",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cf-waf-to-abuseipdb",
|
||||
"version": "1.5.1",
|
||||
"version": "1.6.0",
|
||||
"description": "Node.js script for automatically reporting incidents to AbuseIPDB using data obtained from Cloudflare WAF.",
|
||||
"homepage": "https://github.com/sefinek/Cloudflare-WAF-To-AbuseIPDB",
|
||||
"bugs": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const axios = require('./axios.js');
|
||||
const { readReportedIPs, updateSefinekAPIInCSV } = require('./csv.js');
|
||||
const log = require('../utils/log.js');
|
||||
const fetchServerIP = require('./fetchServerIP.js');
|
||||
const fetchServerIP = require('./ipFetcher.js');
|
||||
const { SEFINEK_API } = require('../config.js').CONFIG;
|
||||
|
||||
module.exports = async () => {
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
const { networkInterfaces } = require('node:os');
|
||||
const { get } = 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 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);
|
||||
73
services/ipFetcher.js
Normal file
73
services/ipFetcher.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
const { networkInterfaces } = require('node:os');
|
||||
const https = require('node:https');
|
||||
const { CronJob } = require('cron');
|
||||
const { get } = require('./axios.js');
|
||||
const isLocalIP = require('../utils/isLocalIP.js');
|
||||
const log = require('../utils/log.js');
|
||||
const { CYCLES, IPv6_SUPPORT } = require('../config.js').CONFIG;
|
||||
|
||||
const ipAddresses = new Set();
|
||||
let ipv6ErrorCount = 0, ipv6ErrorLogged = false;
|
||||
const IPv6Failed = 'It looks like your ISP hasn\'t assigned you any IPv6 address. I won\'t attempt to fetch it again.';
|
||||
|
||||
const fetchIPAddress = async family => {
|
||||
if (family === 6 && (ipv6ErrorLogged || !IPv6_SUPPORT)) return;
|
||||
|
||||
try {
|
||||
const { data } = await get('https://api.sefinek.net/api/v2/ip', {
|
||||
httpsAgent: new https.Agent({ family }),
|
||||
});
|
||||
|
||||
if (data?.success && data?.message) {
|
||||
ipAddresses.add(data.message);
|
||||
|
||||
if (family === 6) {
|
||||
if (ipv6ErrorCount > 0) {
|
||||
const IPv6Success = `Uh, it looks like IPv6 has started working! It only succeeded after ${ipv6ErrorCount} attempts.`;
|
||||
log(0, IPv6Success);
|
||||
}
|
||||
|
||||
ipv6ErrorCount = 0;
|
||||
}
|
||||
} else {
|
||||
log(2, `Unexpected API response: ${JSON.stringify(data)}`);
|
||||
}
|
||||
} catch (err) {
|
||||
log(2, `Error fetching IPv${family} address: ${err.message}`);
|
||||
|
||||
if (family === 6 && err.code === 'ENOENT') {
|
||||
ipv6ErrorCount++;
|
||||
|
||||
if (ipv6ErrorCount >= 6 && !ipv6ErrorLogged) {
|
||||
ipv6ErrorLogged = true;
|
||||
log(0, IPv6Failed);
|
||||
} else {
|
||||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||||
await fetchIPAddress(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const fetchLocalIPs = () => {
|
||||
for (const iface of Object.values(networkInterfaces()).flat()) {
|
||||
if (iface && !iface.internal && iface.address && !isLocalIP(iface.address)) {
|
||||
ipAddresses.add(iface.address);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const refreshServerIPs = async () => {
|
||||
await Promise.all([fetchIPAddress(4), fetchIPAddress(6)]);
|
||||
fetchLocalIPs();
|
||||
};
|
||||
|
||||
(async () => {
|
||||
new CronJob(CYCLES.IP_REFRESH_SCHEDULE || '0 */6 * * *', refreshServerIPs, null, true, 'UTC');
|
||||
await refreshServerIPs();
|
||||
})();
|
||||
|
||||
module.exports = {
|
||||
refreshServerIPs,
|
||||
getServerIPs: () => [...ipAddresses],
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue