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
|
CLOUDFLARE_API_KEY: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', // https://dash.cloudflare.com/profile/api-tokens
|
||||||
ABUSEIPDB_API_KEY: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', // API key for reporting malicious IPs to AbuseIPDB
|
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?
|
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: {
|
CYCLES: {
|
||||||
// Schedule for running cron jobs for reporting to AbuseIPDB.
|
// CRON: Schedule for running cron jobs for reporting to AbuseIPDB.
|
||||||
REPORT_SCHEDULE: '0 */2 * * *',
|
REPORT_SCHEDULE: '0 */2 * * *',
|
||||||
|
|
||||||
// The minimum time (in hours) that must pass after reporting an IP address before it can be reported again.
|
// 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.
|
// Additional delay (in milliseconds) after each successful IP report to avoid overloading the AbuseIPDB API.
|
||||||
SUCCESS_COOLDOWN: 20,
|
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.
|
// 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: {
|
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 SefinekAPI = require('./services/SefinekAPI.js');
|
||||||
const headers = require('./utils/headers.js');
|
const headers = require('./utils/headers.js');
|
||||||
const { logToCSV, readReportedIPs } = require('./services/csv.js');
|
const { logToCSV, readReportedIPs } = require('./services/csv.js');
|
||||||
const formatDelay = require('./utils/formatDelay.js');
|
const { refreshServerIPs, getServerIPs } = require('./services/ipFetcher.js');
|
||||||
const fetchServerIP = require('./services/fetchServerIP.js');
|
|
||||||
const getFilters = require('./services/getFilters.js');
|
const getFilters = require('./services/getFilters.js');
|
||||||
const log = require('./utils/log.js');
|
const log = require('./utils/log.js');
|
||||||
|
|
||||||
const fetchBlockedIPs = async whitelist => {
|
const fetchCloudflareEvents = async whitelist => {
|
||||||
try {
|
try {
|
||||||
const { data, status } = await axios.post('https://api.cloudflare.com/client/v4/graphql', PAYLOAD(), { headers: headers.CLOUDFLARE });
|
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;
|
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)}`);
|
if (!events) throw new Error(`Failed to retrieve data from Cloudflare (status ${status}): ${JSON.stringify(data?.errors)}`);
|
||||||
|
|
||||||
const isWhitelisted = event =>
|
const isWhitelisted = event =>
|
||||||
event.ip === fetchServerIP() ||
|
getServerIPs().includes(event.ip) ||
|
||||||
whitelist.userAgents.some(ua => event.userAgent.includes(ua)) ||
|
whitelist.userAgents.some(ua => event.userAgent.includes(ua)) ||
|
||||||
whitelist.imgExtensions.some(ext => event.clientRequestPath.endsWith(ext)) ||
|
whitelist.imgExtensions.some(ext => event.clientRequestPath.endsWith(ext)) ||
|
||||||
whitelist.domains.some(domain => event.clientRequestHTTPHost?.includes(domain)) ||
|
whitelist.domains.some(domain => event.clientRequestHTTPHost?.includes(domain)) ||
|
||||||
|
|
@ -60,7 +59,7 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount
|
||||||
return false;
|
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');
|
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}`);
|
log(0, `Your IP address (${event.clientIP}) was unexpectedly received from Cloudflare. URI: ${uri}`);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -104,23 +103,27 @@ const reportIP = async (event, uri, country, hostname, endpoint, cycleErrorCount
|
||||||
let cycleId = 1;
|
let cycleId = 1;
|
||||||
|
|
||||||
const cron = async () => {
|
const cron = async () => {
|
||||||
|
log(0, `======================== Reporting Cycle No. ${cycleId} ========================`);
|
||||||
|
|
||||||
|
// Fetch cloudflare events
|
||||||
const whitelist = await getFilters();
|
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} =====================`);
|
// IP
|
||||||
|
await refreshServerIPs();
|
||||||
const blockedIPEvents = await fetchBlockedIPs(whitelist);
|
const ips = getServerIPs();
|
||||||
if (!blockedIPEvents) return log(1, 'No events fetched, skipping cycle...');
|
if (!Array.isArray(ips)) return log(2, 'For some reason, \'ips\' is not an array');
|
||||||
|
log(0, `Fetched ${getServerIPs()?.length} of your IP addresses`);
|
||||||
const serverIP = fetchServerIP();
|
|
||||||
if (!serverIP) log(1, `Server IP address is missing! Received: ${serverIP}`);
|
|
||||||
|
|
||||||
|
// Cycle
|
||||||
let cycleProcessedCount = 0, cycleReportedCount = 0, cycleSkippedCount = 0;
|
let cycleProcessedCount = 0, cycleReportedCount = 0, cycleSkippedCount = 0;
|
||||||
const cycleErrorCounts = { blocked: 0, otherErrors: 0 };
|
const cycleErrorCounts = { blocked: 0, otherErrors: 0 };
|
||||||
|
|
||||||
for (const event of blockedIPEvents) {
|
for (const event of events) {
|
||||||
cycleProcessedCount++;
|
cycleProcessedCount++;
|
||||||
const ip = event.clientIP;
|
const ip = event.clientIP;
|
||||||
if (ip === serverIP) {
|
if (getServerIPs().includes(ip)) {
|
||||||
log(0, `The IP address ${ip} belongs to this machine. Ignoring...`);
|
log(0, `The IP address ${ip} belongs to this machine. Ignoring...`);
|
||||||
cycleSkippedCount++;
|
cycleSkippedCount++;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -164,11 +167,12 @@ const cron = async () => {
|
||||||
new CronJob(CONFIG.SEFINEK_API.REPORT_SCHEDULE, SefinekAPI, null, true, 'UTC');
|
new CronJob(CONFIG.SEFINEK_API.REPORT_SCHEDULE, SefinekAPI, null, true, 'UTC');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ready
|
|
||||||
process.send && process.send('ready');
|
|
||||||
log(0, 'The integration is ready!');
|
|
||||||
|
|
||||||
// AbuseIPDB
|
// AbuseIPDB
|
||||||
new CronJob(CONFIG.CYCLES.REPORT_SCHEDULE, cron, null, true, 'UTC');
|
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();
|
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",
|
"name": "cf-waf-to-abuseipdb",
|
||||||
"version": "1.5.1",
|
"version": "1.6.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cf-waf-to-abuseipdb",
|
"name": "cf-waf-to-abuseipdb",
|
||||||
"version": "1.5.1",
|
"version": "1.6.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.8.4",
|
"axios": "^1.8.4",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cf-waf-to-abuseipdb",
|
"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.",
|
"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",
|
"homepage": "https://github.com/sefinek/Cloudflare-WAF-To-AbuseIPDB",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const axios = require('./axios.js');
|
const axios = require('./axios.js');
|
||||||
const { readReportedIPs, updateSefinekAPIInCSV } = require('./csv.js');
|
const { readReportedIPs, updateSefinekAPIInCSV } = require('./csv.js');
|
||||||
const log = require('../utils/log.js');
|
const log = require('../utils/log.js');
|
||||||
const fetchServerIP = require('./fetchServerIP.js');
|
const fetchServerIP = require('./ipFetcher.js');
|
||||||
const { SEFINEK_API } = require('../config.js').CONFIG;
|
const { SEFINEK_API } = require('../config.js').CONFIG;
|
||||||
|
|
||||||
module.exports = async () => {
|
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