151
index.js
151
index.js
@@ -10,6 +10,8 @@ const decoders = require('cap').decoders;
|
||||
const PROTOCOL = decoders.PROTOCOL;
|
||||
const { Client } = require('@elastic/elasticsearch');
|
||||
const os = require('os');
|
||||
const https = require('https');
|
||||
const geoip = require('geoip-lite');
|
||||
const config = require('./config');
|
||||
|
||||
// Initialize Elasticsearch client
|
||||
@@ -25,6 +27,9 @@ let esAvailable = true;
|
||||
let lastESCheckTime = Date.now();
|
||||
const ES_CHECK_INTERVAL = config.cache.checkInterval;
|
||||
|
||||
// Public IP detection
|
||||
let publicIP = null;
|
||||
|
||||
// Statistics tracking
|
||||
const stats = {
|
||||
packetsProcessed: 0,
|
||||
@@ -120,6 +125,106 @@ function buildBPFFilter() {
|
||||
return filters.length > 0 ? filters.join(' and ') : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get public IP address from external service
|
||||
*/
|
||||
async function getPublicIP() {
|
||||
return new Promise((resolve, reject) => {
|
||||
https.get('https://api.ipify.org?format=json', (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const json = JSON.parse(data);
|
||||
resolve(json.ip);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}).on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if IP address is private/local
|
||||
*/
|
||||
function isPrivateIP(ip) {
|
||||
if (!ip) return true;
|
||||
|
||||
// IPv4 private ranges
|
||||
const ipv4PrivateRanges = [
|
||||
/^10\./, // 10.0.0.0/8
|
||||
/^172\.(1[6-9]|2[0-9]|3[0-1])\./, // 172.16.0.0/12
|
||||
/^192\.168\./, // 192.168.0.0/16
|
||||
/^127\./, // Loopback
|
||||
/^169\.254\./, // Link-local
|
||||
/^0\./ // Invalid
|
||||
];
|
||||
|
||||
// Check IPv4 private ranges
|
||||
if (ipv4PrivateRanges.some(regex => regex.test(ip))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// IPv6 local addresses
|
||||
if (ip.includes(':')) {
|
||||
if (ip.startsWith('fe80:') || // Link-local
|
||||
ip.startsWith('fc00:') || // Unique local
|
||||
ip.startsWith('fd00:') || // Unique local
|
||||
ip === '::1') { // Loopback
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if IP is remote (not local, not private, not our public IP)
|
||||
*/
|
||||
function isRemoteIP(ip) {
|
||||
if (!ip || isPrivateIP(ip)) return false;
|
||||
if (publicIP && ip === publicIP) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GeoIP information for an IP address
|
||||
*/
|
||||
function getGeoIPData(ip) {
|
||||
if (!ip || !isRemoteIP(ip)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const geo = geoip.lookup(ip);
|
||||
|
||||
if (!geo) return null;
|
||||
|
||||
return {
|
||||
country: geo.country || null,
|
||||
country_name: geo.country || null,
|
||||
region: geo.region || null,
|
||||
city: geo.city || null,
|
||||
timezone: geo.timezone || null,
|
||||
location: geo.ll ? {
|
||||
lat: geo.ll[0],
|
||||
lon: geo.ll[1]
|
||||
} : null,
|
||||
coordinates: geo.ll ? [geo.ll[1], geo.ll[0]] : null // [lon, lat] for Elasticsearch
|
||||
};
|
||||
} catch (error) {
|
||||
logger.debug(`Failed to get GeoIP data for ${ip}:`, error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and format IP address for Elasticsearch
|
||||
* Returns null if invalid
|
||||
@@ -484,6 +589,21 @@ async function processPacket(buffer, interfaceInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
// Add GeoIP data for remote IPs
|
||||
if (packet.ip && packet.ip.src && packet.ip.dst) {
|
||||
// Get GeoIP for source IP
|
||||
const srcGeoIP = getGeoIPData(packet.ip.src);
|
||||
if (srcGeoIP) {
|
||||
packet.geoip_src = srcGeoIP;
|
||||
}
|
||||
|
||||
// Get GeoIP for destination IP
|
||||
const dstGeoIP = getGeoIPData(packet.ip.dst);
|
||||
if (dstGeoIP) {
|
||||
packet.geoip_dst = dstGeoIP;
|
||||
}
|
||||
}
|
||||
|
||||
// Index to Elasticsearch (with cache fallback)
|
||||
await indexDocument(packet);
|
||||
|
||||
@@ -623,6 +743,28 @@ async function initializeElasticsearch() {
|
||||
protocol: { type: 'keyword' }
|
||||
}
|
||||
},
|
||||
geoip_src: {
|
||||
properties: {
|
||||
country: { type: 'keyword' },
|
||||
country_name: { type: 'keyword' },
|
||||
region: { type: 'keyword' },
|
||||
city: { type: 'keyword' },
|
||||
timezone: { type: 'keyword' },
|
||||
location: { type: 'geo_point' },
|
||||
coordinates: { type: 'geo_point' }
|
||||
}
|
||||
},
|
||||
geoip_dst: {
|
||||
properties: {
|
||||
country: { type: 'keyword' },
|
||||
country_name: { type: 'keyword' },
|
||||
region: { type: 'keyword' },
|
||||
city: { type: 'keyword' },
|
||||
timezone: { type: 'keyword' },
|
||||
location: { type: 'geo_point' },
|
||||
coordinates: { type: 'geo_point' }
|
||||
}
|
||||
},
|
||||
content: { type: 'text' },
|
||||
content_length: { type: 'integer' },
|
||||
content_type: { type: 'keyword' }
|
||||
@@ -683,6 +825,15 @@ async function main() {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Get public IP address for GeoIP filtering
|
||||
try {
|
||||
publicIP = await getPublicIP();
|
||||
logger.info(`Detected public IP: ${publicIP}`);
|
||||
} catch (error) {
|
||||
logger.warn('Failed to detect public IP address:', error.message);
|
||||
logger.warn('GeoIP will be applied to all non-private IPs');
|
||||
}
|
||||
|
||||
// Determine interfaces to capture
|
||||
let interfaces = config.capture.interfaces;
|
||||
|
||||
|
||||
Referencia en una nueva incidencia
Block a user