Submodule
This commit is contained in:
parent
293fbfeb94
commit
b153df4fca
12 changed files with 12 additions and 294 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "scripts"]
|
||||
path = scripts
|
||||
url = https://github.com/sefinek/UFW-Integration-Scripts
|
||||
16
index.js
16
index.js
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
const fs = require('node:fs');
|
||||
const chokidar = require('chokidar');
|
||||
const isLocalIP = require('./utils/isLocalIP.js');
|
||||
const { reportedIPs, loadReportedIPs, saveReportedIPs, isIPReportedRecently, markIPAsReported } = require('./services/cache.js');
|
||||
const log = require('./utils/log.js');
|
||||
const axios = require('./services/axios.js');
|
||||
const serverAddress = require('./services/fetchServerIP.js');
|
||||
const discordWebhooks = require('./services/discord.js');
|
||||
const isLocalIP = require('./scripts/utils/isLocalIP.js');
|
||||
const { reportedIPs, loadReportedIPs, saveReportedIPs, isIPReportedRecently, markIPAsReported } = require('./scripts/services/cache.js');
|
||||
const log = require('./scripts/utils/log.js');
|
||||
const axios = require('./scripts/services/axios.js');
|
||||
const serverAddress = require('./scripts/services/fetchServerIP.js');
|
||||
const discordWebhooks = require('./scripts/services/discord.js');
|
||||
const config = require('./config.js');
|
||||
const { version } = require('./package.json');
|
||||
const { UFW_LOG_FILE, ABUSEIPDB_API_KEY, SERVER_ID, AUTO_UPDATE_ENABLED, AUTO_UPDATE_SCHEDULE, DISCORD_WEBHOOKS_ENABLED, DISCORD_WEBHOOKS_URL } = config.MAIN;
|
||||
|
|
@ -140,8 +140,8 @@ const processLogLine = async line => {
|
|||
});
|
||||
|
||||
// Auto updates
|
||||
if (AUTO_UPDATE_ENABLED && AUTO_UPDATE_SCHEDULE) await require('./services/updates.js')();
|
||||
if (DISCORD_WEBHOOKS_ENABLED && DISCORD_WEBHOOKS_URL) await require('./services/summaries.js')();
|
||||
if (AUTO_UPDATE_ENABLED && AUTO_UPDATE_SCHEDULE) await require('./scripts/services/updates.js')();
|
||||
if (DISCORD_WEBHOOKS_ENABLED && DISCORD_WEBHOOKS_URL) await require('./scripts/services/summaries.js')();
|
||||
|
||||
await discordWebhooks(0, `[UFW-AbuseIPDB-Reporter](https://github.com/sefinek/UFW-AbuseIPDB-Reporter) has been successfully launched on the device \`${SERVER_ID}\`.`);
|
||||
|
||||
|
|
|
|||
1
scripts
Submodule
1
scripts
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit b18b33a087f03b901bcddae3dd8094c66ab436a5
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
const axios = require('axios');
|
||||
const { version } = require('../package.json');
|
||||
|
||||
axios.defaults.headers.common = {
|
||||
'User-Agent': `Mozilla/5.0 (compatible; UFW-AbuseIPDB-Reporter/${version}; +https://github.com/sefinek/UFW-AbuseIPDB-Reporter)`,
|
||||
'Accept': 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
};
|
||||
|
||||
axios.defaults.timeout = 30000;
|
||||
|
||||
module.exports = axios;
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
const { existsSync, readFileSync, writeFileSync } = require('node:fs');
|
||||
const { CACHE_FILE, IP_REPORT_COOLDOWN } = require('../config.js').MAIN;
|
||||
const log = require('../utils/log.js');
|
||||
|
||||
const reportedIPs = new Map();
|
||||
|
||||
const loadReportedIPs = () => {
|
||||
if (existsSync(CACHE_FILE)) {
|
||||
readFileSync(CACHE_FILE, 'utf8')
|
||||
.split('\n')
|
||||
.forEach(line => {
|
||||
const [ip, time] = line.split(' ');
|
||||
if (ip && time) reportedIPs.set(ip, Number(time));
|
||||
});
|
||||
log(0, `Loaded ${reportedIPs.size} IPs from ${CACHE_FILE}`);
|
||||
} else {
|
||||
log(0, `${CACHE_FILE} does not exist. No data to load.`);
|
||||
}
|
||||
};
|
||||
|
||||
const saveReportedIPs = () => writeFileSync(CACHE_FILE, Array.from(reportedIPs).map(([ip, time]) => `${ip} ${time}`).join('\n'), 'utf8');
|
||||
|
||||
const isIPReportedRecently = ip => {
|
||||
const reportedTime = reportedIPs.get(ip);
|
||||
return reportedTime && (Date.now() / 1000 - reportedTime < IP_REPORT_COOLDOWN / 1000);
|
||||
};
|
||||
|
||||
const markIPAsReported = ip => reportedIPs.set(ip, Math.floor(Date.now() / 1000));
|
||||
|
||||
module.exports = { reportedIPs, loadReportedIPs, saveReportedIPs, isIPReportedRecently, markIPAsReported };
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
const axios = require('axios');
|
||||
const log = require('../utils/log.js');
|
||||
const { SERVER_ID, DISCORD_WEBHOOKS_ENABLED, DISCORD_WEBHOOKS_URL } = require('../config.js').MAIN;
|
||||
|
||||
const TYPES = {
|
||||
0: { type: 'SUCCESS', emoji: '\\✅', color: 0x60D06D },
|
||||
1: { type: 'WARN', emoji: '\\⚠️', color: 0xFFB02E },
|
||||
2: { type: 'ERROR', emoji: '\\❌', color: 0xF92F60 },
|
||||
3: { type: 'FAIL', emoji: '\\🔴', color: 0xF8312F },
|
||||
4: { type: 'INFO', emoji: '\\📄', color: 0xF2EEF8 },
|
||||
5: { type: 'DEBUG', emoji: '\\🛠️', color: 0xB4ACBC },
|
||||
6: { type: 'CRITICAL', emoji: '\\🔴', color: 0xF8312F },
|
||||
7: { type: 'NOTICE', emoji: '\\📝', color: 0xF3EEF8 },
|
||||
};
|
||||
|
||||
module.exports = async (id, description) => {
|
||||
if (!DISCORD_WEBHOOKS_ENABLED || !DISCORD_WEBHOOKS_URL) return;
|
||||
|
||||
const logType = TYPES[id];
|
||||
if (!logType) return log(1, 'Invalid log type ID provided!');
|
||||
|
||||
const config = {
|
||||
method: 'POST',
|
||||
url: DISCORD_WEBHOOKS_URL,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: {
|
||||
embeds: [{
|
||||
title: `${logType.emoji} ${SERVER_ID}: ${logType.type} [ID ${id}]`,
|
||||
description,
|
||||
color: logType.color,
|
||||
footer: {
|
||||
text: `Date: ${new Date().toLocaleString()} | sefinek/UFW-AbuseIPDB-Reporter`,
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await axios(config);
|
||||
if (res.status !== 204) log(1, 'Failed to deliver Discord Webhook');
|
||||
} catch (err) {
|
||||
log(2, `Failed to send Discord Webhook! ${err.stack}`);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
const { networkInterfaces } = require('node:os');
|
||||
const axios = require('./axios.js');
|
||||
const isLocalIP = require('../utils/isLocalIP.js');
|
||||
const log = require('../utils/log.js');
|
||||
const { IP_REFRESH_INTERVAL } = require('../config.js').MAIN;
|
||||
|
||||
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, IP_REFRESH_INTERVAL);
|
||||
|
||||
// console.debug(ipAddrList);
|
||||
})();
|
||||
|
||||
module.exports = () => Array.from(ipAddrList);
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
const { exec } = require('node:child_process');
|
||||
const ecosystem = require('../ecosystem.config.js');
|
||||
const discordWebhooks = require('./discord.js');
|
||||
const log = require('../utils/log.js');
|
||||
|
||||
const executeCmd = cmd =>
|
||||
new Promise((resolve, reject) => {
|
||||
exec(cmd, (err, stdout, stderr) => {
|
||||
if (err || stderr) reject(err || stderr);
|
||||
else resolve(stdout);
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = async () => {
|
||||
const process = ecosystem.apps[0].name;
|
||||
await discordWebhooks(4, `Restarting the ${process} process...`);
|
||||
|
||||
try {
|
||||
console.log(await executeCmd('npm install --omit=dev'));
|
||||
console.log(await executeCmd(`pm2 restart ${process}`));
|
||||
} catch (err) {
|
||||
log(2, err);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
const { CronJob } = require('cron');
|
||||
const fs = require('node:fs/promises');
|
||||
const discordWebhooks = require('./discord.js');
|
||||
const log = require('../utils/log.js');
|
||||
const { CACHE_FILE } = require('../config.js').MAIN;
|
||||
|
||||
const formatHourRange = hour => `${hour.toString().padStart(2, '0')}:00-${hour.toString().padStart(2, '0')}:59`;
|
||||
const pluralizeReport = count => (count === 1 ? 'report' : 'reports');
|
||||
|
||||
const sendWebhook = async () => {
|
||||
try {
|
||||
await fs.access(CACHE_FILE);
|
||||
} catch {
|
||||
return log(2, `Cache file not found: ${CACHE_FILE}`);
|
||||
}
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = (await fs.readFile(CACHE_FILE, 'utf8')).trim();
|
||||
} catch (err) {
|
||||
return log(2, `Error reading file: ${err.message}`);
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
log(0, `Cache file is empty: ${CACHE_FILE}`);
|
||||
return discordWebhooks(4, `Cache file is empty: \`${CACHE_FILE}\``);
|
||||
}
|
||||
|
||||
try {
|
||||
const yesterday = new Date();
|
||||
yesterday.setUTCDate(yesterday.getUTCDate() - 1);
|
||||
const yesterdayString = yesterday.toISOString().split('T')[0];
|
||||
|
||||
const hourlySummary = {};
|
||||
const uniqueEntries = new Set();
|
||||
|
||||
data.split('\n').forEach((line) => {
|
||||
const [ip, timestamp] = line.split(' ');
|
||||
if (!ip || isNaN(timestamp)) return;
|
||||
|
||||
const entryKey = `${ip}_${timestamp}`;
|
||||
if (uniqueEntries.has(entryKey)) return;
|
||||
uniqueEntries.add(entryKey);
|
||||
|
||||
const dateObj = new Date(parseInt(timestamp, 10) * 1000);
|
||||
if (dateObj.toISOString().split('T')[0] !== yesterdayString) return;
|
||||
|
||||
const hour = dateObj.getUTCHours();
|
||||
hourlySummary[hour] = (hourlySummary[hour] || 0) + 1;
|
||||
});
|
||||
|
||||
const totalReports = Object.values(hourlySummary).reduce((sum, count) => sum + count, 0);
|
||||
const sortedEntries = Object.entries(hourlySummary).sort((a, b) => b[1] - a[1]);
|
||||
const maxReports = sortedEntries.length > 0 ? sortedEntries[0][1] : 0;
|
||||
const topHours = sortedEntries
|
||||
.filter(([, count]) => count === maxReports && count > 1)
|
||||
.map(([hour]) => parseInt(hour));
|
||||
|
||||
const summaryString = Object.entries(hourlySummary)
|
||||
.map(([hour, count]) => `${formatHourRange(parseInt(hour))}: ${count} ${pluralizeReport(count)}${topHours.includes(parseInt(hour)) ? ' 🔥' : ''}`)
|
||||
.join('\n');
|
||||
|
||||
await discordWebhooks(7, `Midnight. Summary of IP address reports (${totalReports}) from yesterday (${yesterdayString}).\nGood night to you, sleep well! 😴\n\`\`\`${summaryString}\`\`\``);
|
||||
log(0, `Reported IPs yesterday by hour:\n${summaryString}\nTotal reported IPs: ${totalReports} ${pluralizeReport(totalReports)}`);
|
||||
} catch (err) {
|
||||
log(2, err);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = async () => {
|
||||
// await sendWebhook();
|
||||
new CronJob('0 0 * * *', sendWebhook, null, true, 'UTC');
|
||||
};
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
const { AUTO_UPDATE_SCHEDULE } = require('../config.js').MAIN;
|
||||
|
||||
const simpleGit = require('simple-git');
|
||||
const { CronJob } = require('cron');
|
||||
const restartApp = require('./reloadApp.js');
|
||||
const log = require('../utils/log.js');
|
||||
const discordWebhooks = require('./discord.js');
|
||||
|
||||
const git = simpleGit();
|
||||
|
||||
const pull = async () => {
|
||||
await discordWebhooks(4, 'Updating the local repository in progress `(git pull)`...');
|
||||
log(0, 'Running git pull...');
|
||||
|
||||
try {
|
||||
const { summary } = await git.pull();
|
||||
log(0, `Changes: ${summary.changes}; Deletions: ${summary.insertions}; Insertions: ${summary.insertions}`);
|
||||
await discordWebhooks(4, `**Changes:** ${summary.changes}; **Deletions:** ${summary.insertions}; **Insertions:** ${summary.insertions}`);
|
||||
} catch (err) {
|
||||
log(2, err);
|
||||
}
|
||||
};
|
||||
|
||||
const pullAndRestart = async () => {
|
||||
try {
|
||||
await pull();
|
||||
await restartApp();
|
||||
} catch (err) {
|
||||
log(2, err);
|
||||
}
|
||||
};
|
||||
|
||||
// https://crontab.guru
|
||||
new CronJob(AUTO_UPDATE_SCHEDULE, pullAndRestart, null, true, 'UTC');
|
||||
|
||||
module.exports = pull;
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
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);
|
||||
};
|
||||
14
utils/log.js
14
utils/log.js
|
|
@ -1,14 +0,0 @@
|
|||
const discordWebhooks = require('../services/discord.js');
|
||||
|
||||
const levels = {
|
||||
0: { method: 'log', label: '[INFO]' },
|
||||
1: { method: 'warn', label: '[WARN]' },
|
||||
2: { method: 'error', label: '[FAIL]' },
|
||||
};
|
||||
|
||||
module.exports = (level, msg) => {
|
||||
const { method, label } = levels[level] || { method: 'log', label: '[N/A]' };
|
||||
console[method](`${label} ${msg}`);
|
||||
|
||||
if (level >= 1) discordWebhooks(level, msg).catch(console.error);
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue