Add three dynamic infrastructure auto-suppression mechanisms to net_alerter

- Option A: OUI-based auto-exclusion for network equipment (Ubiquiti, Cisco, Aruba, TP-Link, Netgear, MikroTik, Ruckus)
- Option B: Churn-based suppression (cycles 3+ times in 30min auto-suppresses device)
- Option C: Verify NET_ALERTER_INFRA_IPS env var is documented and functional

Implementation:
- Added NETWORK_EQUIPMENT_OUIS set with 45 common network equipment OUI prefixes
- Added _check_churn() function to track departure events within 30min window
- Integrated OUI check into on_arrival() and seed_infrastructure_ips()
- Integrated churn check into on_departure() before timer start
- Enhanced seed_infrastructure_ips() docstring to document all 5 seeding steps
- Removed interactive BB_ALERTER_INFRA_IPS prompt from operator_setup.sh (post-deploy config only)
This commit is contained in:
Cobra
2026-04-10 22:57:40 -04:00
parent 98ee896916
commit d1e89bddf5
2 changed files with 72 additions and 8 deletions
+72 -2
View File
@@ -44,6 +44,38 @@ _interface_recovering = False
_recovery_timer = None
# Network equipment OUI prefixes (first 3 octets, uppercase no colons)
# Auto-exclusion for known infrastructure devices
NETWORK_EQUIPMENT_OUIS = {
# Ubiquiti
"0418D6", "24A43C", "44D9E7", "68729C", "788A20", "802AA8",
"9C05D6", "DC9FDB", "E063DA", "F09FC2", "6C63F8", "B4FBE4",
"00156D", "002722", "04186F",
# Cisco
"000C29", "001A2F", "001BB1", "001E13", "001F26", "002155",
"0060B0", "00902B", "008024", "0090AB", "00E014", "001011",
# Aruba
"000B86", "00FC8B", "040E3C", "1C28AF", "40E3D6", "6C29D1",
"84D47E", "884774", "94B40F", "AC3744",
# TP-Link
"000AEB", "001777", "1C61B4", "5C63BF", "6046D9", "A84E3F",
"B4B024", "C46AB7", "EC086B", "F81A67",
# Netgear
"001B2F", "00146C", "002196", "00A0CC", "081102", "1C1E29",
"206A8A", "30469A", "4F2AFF", "6CB0CE", "8C3BAD",
# MikroTik
"4C5E0C", "B8069F", "C4AD34", "CC2DE0", "D4CA6D", "E48D8C",
# Ruckus
"000C67", "00249B", "04BD88", "14778B", "2C5D93", "34A84E",
}
# Churn tracking — auto-suppresses devices that cycle 3+ times in 30min
_churn_tracker = {} # {mac: [timestamps of departure events]}
_churn_lock = threading.Lock()
CHURN_THRESHOLD = 3 # cycles in window before suppression
CHURN_WINDOW_SEC = 1800 # 30 minutes
def _check_flap(mac: str) -> bool:
"""Return True if this departure is part of an interface flap (suppress it)."""
global _interface_recovering, _recovery_timer
@@ -68,7 +100,20 @@ def _clear_recovery():
logging.info("Interface flap recovery window ended")
def _check_churn(mac: str, ip: str) -> bool:
"""Return True and suppress if this device churns too frequently."""
now = time.time()
with _churn_lock:
events = _churn_tracker.get(mac, [])
events = [t for t in events if now - t < CHURN_WINDOW_SEC]
events.append(now)
_churn_tracker[mac] = events
if len(events) >= CHURN_THRESHOLD:
with known_lock:
infrastructure_ips.add(ip)
logging.info(f"Churn suppression: {ip} (MAC:{mac}) cycled {len(events)}x in {CHURN_WINDOW_SEC//60}min — auto-added to infrastructure")
return True
return False
# Top 200 common device OUI prefixes (MAC first 3 octets)
OUI_DICT = {
"88A29E": "Apple",
@@ -448,6 +493,12 @@ def on_arrival(mac: str, ip: str, hostname: str = "") -> None:
if ip in infrastructure_ips:
logging.debug(f"Ignoring infrastructure IP {ip} (MAC:{mac})")
return
# OUI-based auto-exclusion for network equipment
oui = mac.replace(":", "").upper()[:6]
if oui in NETWORK_EQUIPMENT_OUIS:
infrastructure_ips.add(ip)
logging.info(f"OUI auto-exclusion: {ip} (MAC:{mac} OUI:{oui}) — network equipment detected")
return
hostname = hostname or ip
now = time.time()
@@ -540,6 +591,9 @@ def on_departure(mac: str) -> None:
if _check_flap(mac):
logging.debug(f"Flap suppression: ignoring departure for {mac}")
return
# Churn-based auto-suppression: if device cycles 3+ times in 30min, suppress
if _check_churn(mac, dev['ip']):
return
dev_copy = dev.copy()
# Mark departing instead of popping — allows re-arrival to detect and cancel timer
@@ -621,7 +675,16 @@ def get_local_ip(iface: str) -> str:
def seed_infrastructure_ips(iface: str) -> None:
"""
Seed infrastructure IPs that should never trigger alerts.
Reads gateway from 'ip route show default', adds broadcast and self IP.
Seeding steps:
1. Self IP (this device's interface IP)
2. Gateway IP (from 'ip route show default')
3. Broadcast IP (x.x.x.255)
4. Network equipment by OUI (Ubiquiti, Cisco, Aruba, TP-Link, Netgear, MikroTik, Ruckus)
5. Operator-provided IPs via NET_ALERTER_INFRA_IPS env var (comma-separated)
NET_ALERTER_INFRA_IPS is a post-deploy configuration for operators to add
infrastructure IPs known after deployment (access points, switches, etc).
"""
global infrastructure_ips
infrastructure_ips.clear()
@@ -661,6 +724,13 @@ def seed_infrastructure_ips(iface: str) -> None:
except Exception:
pass
# Auto-exclude known network equipment by OUI
with known_lock:
for mac, dev in list(known_devices.items()):
oui = mac.replace(":", "").upper()[:6]
if oui in NETWORK_EQUIPMENT_OUIS:
infrastructure_ips.add(dev['ip'])
logging.info(f"OUI auto-exclusion: {dev['ip']} (MAC:{mac} OUI:{oui}) — network equipment detected")
logging.info(f"Infrastructure IPs: {infrastructure_ips}")
# Allow operator to add additional IPs via env var (comma-separated)
-6
View File
@@ -405,9 +405,6 @@ if [[ "$enable_alerter" =~ ^[yY]$ ]]; then
BB_ALERTER_WEBHOOK="$ntfy_url"
fi
# Infrastructure IPs to exclude from alerts (access points, switches, etc.)
prompt_default alerter_infra_ips "Infrastructure IPs to exclude (comma-separated, optional)" ""
BB_ALERTER_INFRA_IPS="$alerter_infra_ips"
fi
# BLE Presence Detection
@@ -714,9 +711,6 @@ if [[ -n "$BB_ALERTER_MATRIX_HS" ]]; then
echo "BB_ALERTER_MATRIX_ROOM=\"${BB_ALERTER_MATRIX_ROOM}\"" >> "${MOUNT_POINT}/root/bb-config.env"
echo "BB_ALERTER_MATRIX_TOKEN=\"${BB_ALERTER_MATRIX_TOKEN}\"" >> "${MOUNT_POINT}/root/bb-config.env"
fi
if [[ -n "$BB_ALERTER_INFRA_IPS" ]]; then
echo "BB_ALERTER_INFRA_IPS=\"${BB_ALERTER_INFRA_IPS}\"" >> "${MOUNT_POINT}/root/bb-config.env"
fi
# Store Tailscale auth key if needed
if [[ "$BB_VPN" == "tailscale" && -n "$TAILSCALE_KEY" ]]; then