Commit Graph

47 Commits

Author SHA1 Message Date
Cobra 5077e30c66 Add multi-source device identity store and zero-config enrollment (#671)
Track devices across MAC rotations using BLE Handoff sequence numbers
and DHCP Option 55 fingerprinting. Auto-enroll personal devices when
seen on 2+ independent signal types. Handle iOS MAC rotation silently.
Confidence-based departure only fires for enrolled devices.

Changes:
- New device_store dict for stable device identity tracking (keyed by
  handoff seq anchor or DHCP fingerprint hash)
- _dhcp_fingerprint_hash() computes stable DHCP Option 55 fingerprint
- _create_identity_record() initializes device records
- _on_device_enrolled() triggers silent enrollment when signal_count >= 2
- _correlate_or_create_identity() finds or creates identities via BLE seq
  or DHCP fingerprint, handles MAC rotation detection
- _ingest_signal() ingests signals from multiple sources (BLE, DHCP, ARP,
  mDNS) and triggers enrollment when criteria met
- ble_sniffer() thread for Apple Continuity Protocol parsing (HCI socket +
  hcidump fallback); graceful failure on missing hardware
- Modified parse_frame() to extract DHCP Option 55 and ingest signals for
  ARP, mDNS, DHCP packets
- Updated main() to start BLE sniffer thread and log device_store stats
2026-04-14 12:38:32 -04:00
Cobra e49ca1ace5 Add tests for iOS departure detection features
New tests for net_alerter iOS departure detection:
- test_update_last_seen_updates_dict: verifies _update_last_seen dict updates
- test_personal_watchdog_fires_on_departure_after_timeout: timeout detection
- test_personal_watchdog_does_not_fire_within_timeout: no false positives
- test_mdns_name_matching_adds_mac_to_personal_devices: mDNS name discovery
- test_load_personal_macs_from_env_does_not_log_individual_macs: privacy logging

All 21 tests passing (16 existing + 5 new).
Updated cleanup fixture to include last_seen, personal_devices, personal_names.
2026-04-13 20:30:49 -04:00
Cobra 3f4e79dd68 Add iOS departure detection via passive ARP/mDNS capture and watchdog
Implement passive observation tracking for iOS devices:
- Add last_seen dict and lock for tracking passive frame observations
- Parse ARP frames (ethertype 0x0806) for MAC observation
- Parse mDNS frames (UDP port 5353) for device name auto-discovery
- Rename parse_dhcp to parse_frame to reflect broader scope
- Add personal_names set for device name matching
- Parse NET_ALERTER_PERSONAL_NAMES env var (comma-separated device names)
- Add mDNS DNS message parsing to extract PTR/SRV/A record names
- Auto-add MACs to personal_devices when mDNS name matches personal_names
- Call _update_last_seen on all frame types (ARP, mDNS, DHCP)
- Add personal_watchdog thread to fire on_departure after WATCHDOG_TIMEOUT_SEC
- Add NET_ALERTER_WATCHDOG_TIMEOUT env config (default 900s)
- Fix load_personal_macs_from_env logging to show counts instead of MACs

Changes are minimal and focused:
- No socket changes (existing AF_PACKET socket already sees all frames)
- No new dependencies (stdlib only)
- Deadlock prevention (watchdog does not hold locks when calling on_departure)
- Backward compatible (personal_names optional, watchdog is passive)
2026-04-13 20:27:00 -04:00
Cobra 979ebe8441 fix _check_churn self-deadlock — remove redundant known_lock nesting
on_departure() holds known_lock when calling _check_churn(). When churn
threshold is hit, the nested `with known_lock:` inside _check_churn tried
to reacquire an already-held non-reentrant lock → permanent deadlock.

This killed the production process on the OPi Zero 3 after ~15 hours of
runtime on 2026-04-12 at ~02:12 UTC. infrastructure_ips.add() is safe
without the inner lock since the caller already holds known_lock.

Also: reset _churn_tracker between tests to prevent cross-test state
accumulation that triggered the churn threshold in test isolation.
2026-04-13 17:07:25 -04:00
Cobra 13900f9045 Remove Matrix credentials from global memory — load at call time
Fix #605: Remove MATRIX_HOMESERVER, MATRIX_ACCESS_TOKEN, MATRIX_ROOM_ID from module-level globals.
Credentials are now read at call time in send_alert() via _read_env_value() to minimize
credential lifetime in process memory.

Fix #606: Update deploy.sh to use Infisical creds CLI instead of .secrets file.
Fetches NET_ALERTER_PASSWORD from matrix folder in Infisical vault, then generates
access token at deploy time.

All 16 net_alerter tests pass when run individually or in groups.
2026-04-13 04:00:56 -04:00
Cobra c87967f2c4 Fix timer.cancel() deadlock and add MAC format validation
Two P1 bugs fixed:

1. Timer cancel deadlock: timer.cancel() was called while holding locks, which would deadlock if the callback was running/paused waiting for those same locks. Fixed by extracting timers outside of all locks before calling cancel().

2. MAC validation: _normalize_mac() now validates format with regex and returns empty string for invalid MACs. Callers (load_personal_macs_from_env, is_personal_device) skip empty results and log warnings.

All 16 tests pass.
2026-04-12 08:41:04 -04:00
Cobra 0b468ee548 Fix P1 bugs in occupancy tracking: lock ordering deadlock, MAC normalization, and race condition
- P1-1: Fix lock ordering deadlock by establishing canonical order (known_lock → departure_lock → occupancy_lock). Refactor on_arrival() to acquire locks in correct order. Fix send_departure_alert() callback to hold occupancy_lock while updating state.
- P1-2: Add _normalize_mac() helper to strip colons/hyphens, apply to load_personal_macs_from_env() and is_personal_device() for consistent MAC comparison.
- P1-3: Remove occupancy_lock acquisition from _update_occupancy_state_unlocked() - callers must hold lock. Update all callers to acquire occupancy_lock before calling.

All individual tests pass. Fixes allow on_arrival() and timer callbacks to execute without deadlock.
2026-04-12 07:57:37 -04:00
Cobra f690370402 Implement occupancy tracking with personal device detection and deadlock fix
- Add MOBILE_DEVICE_OUIS set with 55+ OUI prefixes for personal devices
- Add is_personal_device() function to detect personal devices by OUI or manual config
- Add load_personal_macs_from_env() to parse NET_ALERTER_PERSONAL_MACS
- Add location_occupancy state tracking (VACANT/OCCUPIED/UNKNOWN)
- Add update_occupancy_state() function with thread-safe alerts on transitions
- Add _update_occupancy_state_unlocked() helper to prevent deadlock
- Fix deadlock in on_arrival() by using unlocked version when holding known_lock
- Update test suite with 5 new occupancy tests
- Fix test assertions to exclude occupancy alerts for device arrival tests
2026-04-12 07:26:57 -04:00
Cobra 92c85f4e43 Apply Delta Reviewer QA corrections to net_alerter security review
- Correction 1: Add severity justification notes to all 15 CRITICAL findings explaining uprates from source agent JSON
- Correction 2: Complete triage mapping with explicit (FIX-NOW)/(FIX-DETECTION)/(FIX-OPSEC) labels on all findings
- Correction 3: Add Appendix A deduplication mapping showing raw→consolidated finding ID merges (54 raw → 29 consolidated HIGH+CRITICAL)
- Correction 4: Preserve agent-specific structured fields in consolidated findings:
  - RT findings: Tools, Exploitation Time, Attack Chain steps
  - IR findings: Failure Mode scenarios, Impact window
  - BT findings: Detection Source, Theoretical TTD minutes
- Correction 5: Add explicit triage labels to High-Severity section findings

All 5 Delta Reviewer QA corrections applied. Report now ready for gate review.
2026-04-10 23:34:42 -04:00
Cobra f243e1e049 Append Delta Reviewer QA report to consolidated security findings 2026-04-10 23:28:24 -04:00
Cobra 1af00e7da9 Consolidate 6-agent security review into unified report
- Deduplicate 105 raw findings into 52 unique consolidated issues
- Apply severity calibration rubric across SA, OA, BT, APT, RT, IR agents
- Map findings to 6 triage categories (FIX-NOW, FIX-DETECTION, FIX-OPSEC, FIX-QUALITY, NOT-FIXING, DEFERRED)
- Prioritize 4 user-specified concerns: flap suppression blind spot, gateway exclusion, debounce aggressiveness, flap detection OPSEC
- Include cross-agent consensus table (28 multi-agent findings, 54% of total)
- Provide pre-deployment checklist and remediation effort estimates
- Executive summary with 15 CRITICAL, 22 HIGH, 13 MEDIUM, 2 LOW findings
2026-04-10 23:23:44 -04:00
Cobra d1e89bddf5 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)
2026-04-10 22:57:40 -04:00
Cobra 39ae11f3d1 Add flap state cleanup to test_net_alerter for test isolation
Tests now call cleanup_flap_state() at the start of each test to properly
reset the interface flap detection state (_flap_window, _interface_recovering,
_recovery_timer) between tests, preventing state carryover failures.
2026-04-10 22:46:07 -04:00
Cobra 91a84b9d6f Add interface flap detection and env var for infrastructure IPs in net_alerter
- Fix 1: Interface flap detection suppresses departure storms when wlan0 briefly loses association and triggers simultaneous RTM_DELNEIGH events for all cached neighbors
  - Tracks 3-second window of departure MACs
  - Locks recovery state for 60 seconds if 3+ departures occur in rapid succession
  - Prevents false positive alerts during interface recovery

- Fix 2: Additional infrastructure IPs via NET_ALERTER_INFRA_IPS env var
  - Allows operator to suppress alerts for devices like access points that cycle regularly
  - Comma-separated list of IP addresses to be seeded as infrastructure at startup
  - Example: NET_ALERTER_INFRA_IPS=10.0.0.0,10.0.0.0

Addresses DevTrack #467 (net_alerter false positive alerts)
2026-04-10 22:45:15 -04:00
Cobra 722a632cee Revert "Add presence DB schema; fix BLE passive scan mode (bleak #1440)"
This reverts commit 2499db6147be65c76e7e202190b81855ba7b9100.
2026-04-10 15:49:03 -04:00
Cobra 3ddb854685 Revert "Add Phase 1 WiFi presence daemon — mDNS + ARP fusion with exponential decay and presence anchor"
This reverts commit b9adf498106c52f1c7508a791a7aac30d5ca1955.
2026-04-10 15:49:03 -04:00
Cobra f7460b0416 Add Phase 1 WiFi presence daemon — mDNS + ARP fusion with exponential decay and presence anchor 2026-04-10 15:49:03 -04:00
Cobra 0994385f7c Add presence DB schema; fix BLE passive scan mode (bleak #1440) 2026-04-10 15:49:03 -04:00
Cobra fa0e2491cf Fix ble_alerter: MAC name filter, Matrix 429 backoff, active scanner
- Reject dash/colon MAC-format device names (e.g. 45-48-39-7F-73-C7)
  from open mode — were slipping past is_generic_name()
- Add exponential backoff (1s, 2s) on Matrix 429/5xx, max 3 attempts —
  startup burst was rate-limiting and dropping alerts
- Commit active scanner flag (scanning_mode=active) that was in working
  tree but not in prior commit c0b91e9 despite the commit message
- Add 3 tests: MAC format filter, 429 retry success, 429 give-up

Fixes DevTrack #518
2026-04-10 06:44:13 -04:00
Cobra c0b91e9b0b Switch BLE scanner to active mode — bleak 0.20 passive requires or_patterns on BlueZ
Passive mode fails on Debian bookworm bleak 0.20.2 without or_patterns filter.
Active mode is functionally equivalent for building-level presence detection
and actually yields more complete advertisement data including device names.
2026-04-10 06:37:30 -04:00
Cobra 85e53fed68 Fix test_too_short test (abc/abd are hex characters) 2026-04-10 06:33:38 -04:00
Cobra 7ef6fc7c3f Update deploy-daemon.sh to deploy both net_alerter and ble_alerter 2026-04-10 06:31:55 -04:00
Cobra 485b124c11 Add unit tests for BLE alerter (mocked bleak, async scenarios) 2026-04-10 06:31:41 -04:00
Cobra a40e86be10 Add systemd unit for BLE alerter (after bluetooth.target) 2026-04-10 06:31:39 -04:00
Cobra 77b0cd6512 Add BLE alerter daemon (passive scan, named-device tracking, Matrix alerts) 2026-04-10 06:31:38 -04:00
Cobra 71cd3332d2 Fix ARRIVED spam — mark departing instead of popping from known_devices
When on_departure popped the device from known_devices, rapid RTM_NEWNEIGH
events during ARP cache rebuild each triggered on_arrival as if the device
was brand new, flooding the Matrix room with 5-10+ duplicate ARRIVED alerts.

Fix: keep device in known_devices with departing=True flag. on_arrival
silently cancels pending departure timers and returns without alerting when
it sees this flag. Timer callback checks flag before sending the alert and
only pops the device when departure is confirmed after 15 minutes.
2026-04-09 14:34:46 -04:00
Cobra b66380f25d Fix on_arrival() to always cancel departure timer on device re-arrival
- Bug: device departs → timer starts, but last_departed_time not set yet
  If device re-arrives before timer fires, timer never cancelled
  Result: false DEPARTED alert sent after re-arrival debounce expires

- Fix: Cancel any pending departure timer immediately on re-arrival,
  before checking last_departed_time

- Test: Added regression test that verifies timer is cancelled when
  device re-arrives before 15-min debounce expires, preventing false alerts

- All 10 tests passing
2026-04-09 13:28:34 -04:00
Cobra 0e178e34d4 Deploy OUI database as part of net_alerter daemon installation
- scp oui.db to /opt/net_alerter/oui.db after daemon script
- Verify DB on remote via Python/sqlite3
- Confirm row count on target host
2026-04-09 13:05:34 -04:00
Cobra 4ffe29d4d3 Add SQLite OUI database lookup to net_alerter
- lookup_oui() now tries SQLite first (/opt/net_alerter/oui.db)
- Falls back to inline OUI_DICT if DB missing or query fails
- No network calls, local SQLite reads only
- Debug-level logging for failures (no output noise)
2026-04-09 13:05:18 -04:00
Cobra a3c9353d3a Add false positive suppression to net_alerter — fix for #467
- Infrastructure IPs (gateway, self, broadcast) silently ignored in
  on_arrival() and on_departure(); seeded at startup from 'ip route'
- 15-minute departure debounce: alert only fires if device is still
  absent after 900s, eliminating ARP cache flush false positives
- 5-minute re-arrival suppression: ARRIVED alert skipped when device
  returns within 5 min of a recorded departure (covers interface flap)
- DHCP renewal dedup: known device re-announcing within 30 min skips
  ARRIVED alert
- Tests added for all four suppression paths (9 total, all passing)
2026-04-09 09:44:37 -04:00
Cobra d4354074c8 Make net_alerter alerts readable — fix hostname dedup and inline OUI lookup
- Replace broken OUI file lookup with inline dict of 200+ common vendors (works on Armbian)
- Fix hostname/IP duplication when reverse DNS fails (show IP once, not twice)
- Add format_arrival() and format_departure() helpers for consistent alert formatting
- Update on_arrival/on_departure to use new formatting functions
- Add comprehensive tests for hostname dedup and OUI lookup
2026-04-08 22:40:35 -04:00
Cobra b34aab7986 Add net_alerter README with quick start guide 2026-04-08 21:56:28 -04:00
Cobra 424b4f4552 Add Orange Pi Zero 3 specific deployment guide 2026-04-08 21:56:03 -04:00
Cobra a54acf32b8 Add net_alerter daemon deployment documentation 2026-04-08 21:55:39 -04:00
Cobra 52872794ee Add deployment script for net_alerter daemon 2026-04-08 21:55:18 -04:00
Cobra 431571dd82 Rebuild net_alerter as persistent daemon — DHCP sniffer + Netlink RTM_NEWNEIGH/DELNEIGH; removes 5-min cron, zero active probing 2026-04-08 21:55:02 -04:00
Cobra 402c4e849b Replace nmap ARP scan with passive /proc/net/arp read — no active probing (#449)
- Removed get_local_subnets() and scan_subnet() — no longer needed
- Added read_arp_cache() to read kernel ARP table from /proc/net/arp
- Added fallback to 'ip neigh show' if /proc/net/arp unavailable
- Added lookup_oui() to query system /usr/share/hwdata/oui.txt for vendor names
- Removed subprocess nmap call and re import (no longer needed)
- run() now directly reads ARP cache instead of scanning subnets
- Zero active network traffic — purely passive monitoring
- Removed sudo requirement
2026-04-08 21:01:12 -04:00
Cobra 967fac5ce5 Enrich net_alerter alerts with OUI vendor and reverse DNS hostname
Parse nmap vendor name from MAC Address line (filtering out 'Unknown').
Fall back to socket.gethostbyaddr for IPs nmap didn't resolve a hostname
for. Alert label is now: 'hostname (vendor)' | 'hostname' | 'vendor' |
'unknown', in that priority order.
2026-04-08 20:31:49 -04:00
Cobra d9c27eaf66 Fix Cloudflare bot detection blocking net_alerter Matrix sends
urllib's default Python-urllib user agent is flagged by Cloudflare Bot
Fight Mode (error 1010) when m.example.org homeserver sits behind
Cloudflare. Add a browser-like User-Agent to Matrix PUT/POST requests so
Cloudflare passes the traffic through.
2026-04-08 20:19:24 -04:00
n0mad1k 516b7908a6 Add absent_count column and commented-out grace period threshold for tripwire resets 2026-04-06 08:52:30 -04:00
n0mad1k 0156101f11 Rewrite net_alerter with presence tracking: baseline on first run, alert on join and rejoin 2026-04-06 08:47:48 -04:00
n0mad1k 532f5f4a33 Fix net_alerter: don't treat 403 as token expiry — room permission errors were silently dropping all alerts 2026-04-06 08:27:42 -04:00
n0mad1k 75703d7ef6 Auto-refresh Matrix token on 401 instead of silently dropping alerts 2026-03-25 09:33:15 -04:00
n0mad1k 59cfb6b07a Use sudo nmap to allow ARP scan with restricted sudoers 2026-03-25 06:28:48 -04:00
n0mad1k 12f8a37dc4 Use dedicated net-alerter Matrix service account, keep credentials out of repo 2026-03-25 06:20:37 -04:00
n0mad1k 94abb6f2ce Fix deploy.sh to use SSH config alias instead of raw key/host 2026-03-25 06:05:32 -04:00
n0mad1k d7cdd489ce Add net_alerter: new device join alerts via Matrix to condo Pi 2026-03-24 20:29:24 -04:00