0.4.0-beta.4
This commit is contained in:
1 parent
c8f8da8a6c
commit
ab207030b5
6 files changed
+77
-63
No files matched your search
@@ -1,19 +1,20 @@
|
||||
// Copyright 2024-2025 (c) by Sefinek All rights reserved.
|
||||
// Copyright 2024-2025 © by Sefinek. All Rights Reserved.
|
||||
// https://sefinek.net
|
||||
|
||||
const fs = require('node:fs');
|
||||
const chokidar = require('chokidar');
|
||||
const { parseUfwLog } = require('ufw-log-parser');
|
||||
const banner = require('./scripts/banners/ufw.js');
|
||||
const axios = require('./scripts/services/axios.js');
|
||||
const { saveBufferToFile, loadBufferFromFile, sendBulkReport, BULK_REPORT_BUFFER } = require('./scripts/services/bulk.js');
|
||||
const { reportedIPs, loadReportedIPs, saveReportedIPs, isIPReportedRecently, markIPAsReported } = require('./scripts/services/cache.js');
|
||||
const { refreshServerIPs, getServerIPs } = require('./scripts/services/ipFetcher.js');
|
||||
const { name, version, authorEmailWebsite, repoFullUrl } = require('./scripts/repo.js');
|
||||
const { name, version, repoFullUrl } = require('./scripts/repo.js');
|
||||
const sendWebhook = require('./scripts/services/discordWebhooks.js');
|
||||
const isLocalIP = require('./scripts/isLocalIP.js');
|
||||
const log = require('./scripts/log.js');
|
||||
const isSpecialPurposeIP = require('./scripts/isSpecialPurposeIP.js');
|
||||
const logger = require('./scripts/logger.js');
|
||||
const config = require('./config.js');
|
||||
const { UFW_LOG_FILE, ABUSEIPDB_API_KEY, SERVER_ID, EXTENDED_LOGS, AUTO_UPDATE_ENABLED, AUTO_UPDATE_SCHEDULE, DISCORD_WEBHOOKS_ENABLED, DISCORD_WEBHOOKS_URL } = config.MAIN;
|
||||
const { UFW_LOG_FILE, ABUSEIPDB_API_KEY, SERVER_ID, EXTENDED_LOGS, AUTO_UPDATE_ENABLED, AUTO_UPDATE_SCHEDULE, DISCORD_WEBHOOK_ENABLED, DISCORD_WEBHOOK_URL } = config.MAIN;
|
||||
|
||||
const ABUSE_STATE = { isLimited: false, isBuffering: false, sentBulk: false };
|
||||
const RATE_LIMIT_LOG_INTERVAL = 10 * 60 * 1000;
|
||||
@@ -37,17 +38,17 @@ const checkRateLimit = async () => {
|
||||
if (!ABUSE_STATE.sentBulk && BULK_REPORT_BUFFER.size > 0) await sendBulkReport();
|
||||
RATELIMIT_RESET = nextRateLimitReset();
|
||||
ABUSE_STATE.sentBulk = false;
|
||||
log(`Rate limit reset. Next reset scheduled at ${RATELIMIT_RESET.toISOString()}`, 1);
|
||||
logger.log(`Rate limit reset. Next reset scheduled at ${RATELIMIT_RESET.toISOString()}`, 1);
|
||||
} else if (now - LAST_RATELIMIT_LOG >= RATE_LIMIT_LOG_INTERVAL) {
|
||||
const minutesLeft = Math.ceil((RATELIMIT_RESET.getTime() - now) / 60000);
|
||||
log(`Rate limit is still active. Collected ${BULK_REPORT_BUFFER.size} IPs. Waiting for reset in ${minutesLeft} minute(s) (${RATELIMIT_RESET.toISOString()})`, 1);
|
||||
logger.log(`Rate limit is still active. Collected ${BULK_REPORT_BUFFER.size} IPs. Waiting for reset in ${minutesLeft} minute(s) (${RATELIMIT_RESET.toISOString()})`, 1);
|
||||
LAST_RATELIMIT_LOG = now;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const reportIp = async ({ srcIp, dpt = 'N/A', proto = 'N/A', id, timestamp }, categories, comment) => {
|
||||
if (!srcIp) return log('Missing source IP (srcIp)', 3);
|
||||
if (!srcIp) return logger.log('Missing source IP (srcIp)', 3);
|
||||
|
||||
await checkRateLimit();
|
||||
|
||||
@@ -55,7 +56,7 @@ const reportIp = async ({ srcIp, dpt = 'N/A', proto = 'N/A', id, timestamp }, ca
|
||||
if (BULK_REPORT_BUFFER.has(srcIp)) return;
|
||||
BULK_REPORT_BUFFER.set(srcIp, { categories, timestamp, comment });
|
||||
await saveBufferToFile();
|
||||
log(`Queued ${srcIp} for bulk report (collected ${BULK_REPORT_BUFFER.size} IPs)`, 1);
|
||||
logger.log(`Queued ${srcIp} for bulk report (collected ${BULK_REPORT_BUFFER.size} IPs)`, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -66,7 +67,7 @@ const reportIp = async ({ srcIp, dpt = 'N/A', proto = 'N/A', id, timestamp }, ca
|
||||
comment,
|
||||
}), { headers: { 'Key': ABUSEIPDB_API_KEY } });
|
||||
|
||||
log(`Reported ${srcIp} [${dpt}/${proto}]; ID: ${id}; Categories: ${categories}; Abuse: ${res.data.abuseConfidenceScore}%`, 1);
|
||||
logger.log(`Reported ${srcIp} [${dpt}/${proto}]; ID: ${id}; Categories: ${categories}; Abuse: ${res.data.abuseConfidenceScore}%`, 1);
|
||||
return true;
|
||||
} catch (err) {
|
||||
const status = err.response?.status ?? 'unknown';
|
||||
@@ -77,37 +78,34 @@ const reportIp = async ({ srcIp, dpt = 'N/A', proto = 'N/A', id, timestamp }, ca
|
||||
ABUSE_STATE.sentBulk = false;
|
||||
LAST_RATELIMIT_LOG = Date.now();
|
||||
RATELIMIT_RESET = nextRateLimitReset();
|
||||
log(`Daily AbuseIPDB limit reached. Buffering reports until ${RATELIMIT_RESET.toLocaleString()}`, 0, true);
|
||||
logger.log(`Daily AbuseIPDB limit reached. Buffering reports until ${RATELIMIT_RESET.toLocaleString()}`, 0, true);
|
||||
}
|
||||
|
||||
if (BULK_REPORT_BUFFER.has(srcIp)) {
|
||||
log(`${srcIp} is already in buffer, skipping`);
|
||||
return;
|
||||
if (!BULK_REPORT_BUFFER.has(srcIp)) {
|
||||
BULK_REPORT_BUFFER.set(srcIp, { timestamp, categories, comment });
|
||||
await saveBufferToFile();
|
||||
logger.log(`Queued ${srcIp} for bulk report due to rate limit`, 1);
|
||||
}
|
||||
|
||||
BULK_REPORT_BUFFER.set(srcIp, { timestamp, categories, comment });
|
||||
await saveBufferToFile();
|
||||
log(`Queued ${srcIp} for bulk report due to rate limit`);
|
||||
} else {
|
||||
log(`Failed to report ${srcIp} [${dpt}/${proto}]; ${err.response?.data?.errors ? JSON.stringify(err.response.data.errors) : err.message}`, status === 429 ? 0 : 3);
|
||||
logger.log(`Failed to report ${srcIp} [${dpt}/${proto}]; ${err.response?.data?.errors ? JSON.stringify(err.response.data.errors) : err.message}`, status === 429 ? 0 : 3);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const processLogLine = async (line, test = false) => {
|
||||
if (!line.includes('[UFW BLOCK]')) return log(`Ignoring invalid line: ${line}`, 2);
|
||||
if (!line.includes('[UFW BLOCK]')) return logger.log(`Ignoring invalid line: ${line}`, 2);
|
||||
|
||||
const data = parseUfwLog(line);
|
||||
const { srcIp, proto, dpt } = data;
|
||||
if (!srcIp) return log(`Missing SRC in the log line: ${line}`, 3);
|
||||
if (!srcIp) return logger.log(`Missing SRC in the log line: ${line}`, 3);
|
||||
|
||||
const ips = getServerIPs();
|
||||
if (!Array.isArray(ips)) return log(`For some reason, 'ips' from 'getServerIPs()' is not an array. Received: ${ips}`, 3, true);
|
||||
if (!Array.isArray(ips)) return logger.log(`For some reason, 'ips' from 'getServerIPs()' is not an array. Received: ${ips}`, 3, true);
|
||||
|
||||
if (ips.includes(srcIp)) return log(`Ignoring own IP address: PROTO=${proto?.toLowerCase()} SRC=${srcIp} DPT=${dpt} ID=${data.id}`, 0, true);
|
||||
if (isLocalIP(srcIp)) return log(`Ignoring local IP address: PROTO=${proto?.toLowerCase()} SRC=${srcIp} DPT=${dpt} ID=${data.id}`, 0, true);
|
||||
if (ips.includes(srcIp)) return logger.log(`Ignoring own IP address: PROTO=${proto?.toLowerCase()} SRC=${srcIp} DPT=${dpt} ID=${data.id}`, 0, true);
|
||||
if (isSpecialPurposeIP(srcIp)) return logger.log(`Ignoring local IP address: PROTO=${proto?.toLowerCase()} SRC=${srcIp} DPT=${dpt} ID=${data.id}`, 0, true);
|
||||
if (proto === 'UDP') {
|
||||
if (EXTENDED_LOGS) log(`Skipping UDP traffic: SRC=${srcIp} DPT=${dpt} ID=${data.id}`);
|
||||
if (EXTENDED_LOGS) logger.log(`Skipping UDP traffic: SRC=${srcIp} DPT=${dpt} ID=${data.id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,7 +125,7 @@ const processLogLine = async (line, test = false) => {
|
||||
(seconds || (!days && !hours && !minutes)) && `${seconds}s`,
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
if (EXTENDED_LOGS) log(`${srcIp} was last reported on ${new Date(lastReportedTime * 1000).toLocaleString()} (${timeAgo} ago)`);
|
||||
if (EXTENDED_LOGS) logger.log(`${srcIp} was last reported on ${new Date(lastReportedTime * 1000).toLocaleString()} (${timeAgo} ago)`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,7 +139,7 @@ const processLogLine = async (line, test = false) => {
|
||||
};
|
||||
|
||||
(async () => {
|
||||
log(`${repoFullUrl} - v${version} | Author: ${authorEmailWebsite}`);
|
||||
banner(`UFW To AbuseIPDB (v${version})`);
|
||||
|
||||
// Auto updates
|
||||
if (AUTO_UPDATE_ENABLED && AUTO_UPDATE_SCHEDULE && SERVER_ID !== 'development') {
|
||||
@@ -150,23 +148,24 @@ const processLogLine = async (line, test = false) => {
|
||||
await require('./scripts/services/version.js');
|
||||
}
|
||||
|
||||
// Bulk
|
||||
// Load cache
|
||||
await loadReportedIPs();
|
||||
await loadBufferFromFile();
|
||||
|
||||
// Bulk
|
||||
await loadBufferFromFile();
|
||||
if (BULK_REPORT_BUFFER.size > 0 && !ABUSE_STATE.isLimited) {
|
||||
log(`Found ${BULK_REPORT_BUFFER.size} IPs in buffer after restart. Sending bulk report...`);
|
||||
logger.log(`Found ${BULK_REPORT_BUFFER.size} IPs in buffer after restart. Sending bulk report...`);
|
||||
await sendBulkReport();
|
||||
}
|
||||
|
||||
|
||||
// Fetch IPs
|
||||
log('Trying to fetch your IPv4 and IPv6 address from api.sefinek.net...');
|
||||
logger.log('Trying to fetch your IPv4 and IPv6 address from api.sefinek.net...');
|
||||
await refreshServerIPs();
|
||||
log(`Fetched ${getServerIPs()?.length} of your IP addresses. If any of them accidentally appear in the UFW logs, they will be ignored.`, 1);
|
||||
logger.log(`Fetched ${getServerIPs()?.length} of your IP addresses. If any of them accidentally appear in the UFW logs, they will be ignored.`, 1);
|
||||
|
||||
// Check UFW_LOG_FILE
|
||||
if (!fs.existsSync(UFW_LOG_FILE)) {
|
||||
log(`Log file ${UFW_LOG_FILE} does not exist`, 3);
|
||||
logger.log(`Log file ${UFW_LOG_FILE} does not exist`, 3);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -177,7 +176,7 @@ const processLogLine = async (line, test = false) => {
|
||||
const stats = fs.statSync(path);
|
||||
if (stats.size < fileOffset) {
|
||||
fileOffset = 0;
|
||||
log('The file has been truncated, and the offset has been reset');
|
||||
logger.log('The file has been truncated, and the offset has been reset');
|
||||
}
|
||||
|
||||
fs.createReadStream(path, { start: fileOffset, encoding: 'utf8' }).on('data', chunk => {
|
||||
@@ -188,11 +187,11 @@ const processLogLine = async (line, test = false) => {
|
||||
});
|
||||
|
||||
// Summaries
|
||||
if (DISCORD_WEBHOOKS_ENABLED && DISCORD_WEBHOOKS_URL) await require('./scripts/services/summaries.js')();
|
||||
if (DISCORD_WEBHOOK_ENABLED && DISCORD_WEBHOOK_URL) await require('./scripts/services/summaries.js')();
|
||||
|
||||
// Ready
|
||||
await sendWebhook(`[${name}](${repoFullUrl}) was successfully started!`, 0x59D267);
|
||||
log(`Ready! Now monitoring: ${UFW_LOG_FILE}`, 1);
|
||||
logger.log(`Ready! Now monitoring: ${UFW_LOG_FILE}`, 1);
|
||||
process.send?.('ready');
|
||||
})();
|
||||
|
||||
|
||||
Reference in new issue
Block a user