mirror of
https://github.com/wahyd4/links.git
synced 2026-08-08 21:04:53 +10:00
Exclude ips
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
Fetch nginx ingress logs — from the Kubernetes API (production) or a local file (dev/testing).
|
||||
Deduplicates by request_id so overlapping windows don't double-insert.
|
||||
"""
|
||||
import ipaddress
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
@@ -35,6 +36,7 @@ def fetch_and_store() -> int:
|
||||
_touch(settings)
|
||||
return 0
|
||||
|
||||
excluder = _build_excluder(settings.excluded_ips)
|
||||
since_seconds = settings.fetch_interval_seconds + _OVERLAP
|
||||
existing_ids = set(
|
||||
NginxAccessLog.objects.filter(
|
||||
@@ -88,11 +90,16 @@ def ingest_raw(text: str) -> int:
|
||||
if not entries:
|
||||
return 0
|
||||
|
||||
settings = NginxSettings.get()
|
||||
excluder = _build_excluder(settings.excluded_ips)
|
||||
|
||||
existing_ids = set(
|
||||
NginxAccessLog.objects.values_list('request_id', flat=True)
|
||||
)
|
||||
new_logs, seen = [], set()
|
||||
for e in entries:
|
||||
if excluder(e['remote_addr']):
|
||||
continue
|
||||
key = e['request_id'] or (
|
||||
f"{e['timestamp'].isoformat()}|{e['remote_addr']}|{e['request_uri']}"
|
||||
)
|
||||
@@ -196,3 +203,95 @@ def cleanup_old_logs(days: int = 7):
|
||||
deleted, _ = NginxAccessLog.objects.filter(timestamp__lt=cutoff).delete()
|
||||
if deleted:
|
||||
logger.info('nginxmon: pruned %d old log entries (>%d days)', deleted, days)
|
||||
|
||||
|
||||
def _build_excluder(excluded_ips_text: str):
|
||||
"""
|
||||
Build and return a callable(ip: str) -> bool that returns True when the
|
||||
given IP should be excluded from ingestion.
|
||||
|
||||
Supported entry formats (one per line):
|
||||
192.168.1.218 exact IP
|
||||
192.168.1.x wildcard — any IP whose first 3 octets match
|
||||
192.168.1.0/24 CIDR range
|
||||
"""
|
||||
exact: set[str] = set()
|
||||
wildcards: list[str] = [] # prefixes like '192.168.1.'
|
||||
networks: list[ipaddress.IPv4Network | ipaddress.IPv6Network] = []
|
||||
|
||||
for raw_line in (excluded_ips_text or '').splitlines():
|
||||
entry = raw_line.strip()
|
||||
if not entry or entry.startswith('#'):
|
||||
continue
|
||||
if entry.endswith('.x') or entry.endswith('.*'):
|
||||
# Wildcard: everything before .x/.* becomes a dotted prefix
|
||||
wildcards.append(entry[:-1]) # keep trailing '.'
|
||||
elif '/' in entry:
|
||||
try:
|
||||
networks.append(ipaddress.ip_network(entry, strict=False))
|
||||
except ValueError:
|
||||
logger.warning('nginxmon: invalid CIDR in excluded_ips: %r', entry)
|
||||
else:
|
||||
exact.add(entry)
|
||||
|
||||
def _is_excluded(ip: str) -> bool:
|
||||
if ip in exact:
|
||||
return True
|
||||
for prefix in wildcards:
|
||||
if ip.startswith(prefix):
|
||||
return True
|
||||
if networks:
|
||||
try:
|
||||
addr = ipaddress.ip_address(ip)
|
||||
return any(addr in net for net in networks)
|
||||
except ValueError:
|
||||
pass
|
||||
return False
|
||||
|
||||
return _is_excluded
|
||||
|
||||
|
||||
def _build_excluder(excluded_ips_text: str):
|
||||
"""
|
||||
Build and return a callable(ip: str) -> bool that returns True when the
|
||||
given IP should be excluded from ingestion.
|
||||
|
||||
Supported entry formats (one per line):
|
||||
192.168.1.218 exact IP
|
||||
192.168.1.x wildcard — any IP whose first 3 octets match
|
||||
192.168.1.0/24 CIDR range
|
||||
"""
|
||||
exact: set[str] = set()
|
||||
wildcards: list[str] = [] # prefixes like '192.168.1.'
|
||||
networks: list[ipaddress.IPv4Network | ipaddress.IPv6Network] = []
|
||||
|
||||
for raw_line in (excluded_ips_text or '').splitlines():
|
||||
entry = raw_line.strip()
|
||||
if not entry or entry.startswith('#'):
|
||||
continue
|
||||
if entry.endswith('.x') or entry.endswith('.*'):
|
||||
# Wildcard: treat everything before .x as a prefix
|
||||
wildcards.append(entry[:-1]) # keep trailing '.'
|
||||
elif '/' in entry:
|
||||
try:
|
||||
networks.append(ipaddress.ip_network(entry, strict=False))
|
||||
except ValueError:
|
||||
logger.warning('nginxmon: invalid CIDR in excluded_ips: %r', entry)
|
||||
else:
|
||||
exact.add(entry)
|
||||
|
||||
def _is_excluded(ip: str) -> bool:
|
||||
if ip in exact:
|
||||
return True
|
||||
for prefix in wildcards:
|
||||
if ip.startswith(prefix):
|
||||
return True
|
||||
if networks:
|
||||
try:
|
||||
addr = ipaddress.ip_address(ip)
|
||||
return any(addr in net for net in networks)
|
||||
except ValueError:
|
||||
pass
|
||||
return False
|
||||
|
||||
return _is_excluded
|
||||
|
||||
@@ -12,7 +12,15 @@ class NginxSettingsForm(forms.ModelForm):
|
||||
'fetch_interval_seconds',
|
||||
'log_file_path',
|
||||
'enabled',
|
||||
'excluded_ips',
|
||||
]
|
||||
widgets = {
|
||||
'excluded_ips': forms.Textarea(attrs={
|
||||
'rows': 4,
|
||||
'placeholder': '192.168.1.218\n192.168.1.x\n192.168.1.0/24',
|
||||
'class': 'font-mono text-xs',
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
class NginxAlertProfileForm(forms.ModelForm):
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.12 on 2026-04-01 08:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('nginxmon', '0002_covering_index'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='nginxsettings',
|
||||
name='excluded_ips',
|
||||
field=models.TextField(blank=True, default='', help_text='One entry per line. Supports exact IPs (192.168.1.218), wildcards (192.168.1.x), and CIDR ranges (192.168.1.0/24). Matching requests will not be ingested.'),
|
||||
),
|
||||
]
|
||||
@@ -30,6 +30,15 @@ class NginxSettings(models.Model):
|
||||
)
|
||||
enabled = models.BooleanField(default=True)
|
||||
last_fetch_at = models.DateTimeField(null=True, blank=True)
|
||||
excluded_ips = models.TextField(
|
||||
blank=True,
|
||||
default='',
|
||||
help_text=(
|
||||
'One entry per line. Supports exact IPs (192.168.1.218), '
|
||||
'wildcards (192.168.1.x), and CIDR ranges (192.168.1.0/24). '
|
||||
'Matching requests will not be ingested.'
|
||||
),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Nginx Settings'
|
||||
|
||||
Reference in New Issue
Block a user