secubox-openwrt/package/secubox/secubox-iot-guard/root/usr/lib/secubox/iot-guard/anomaly.sh
CyberMind-FR 8ef0c70d0f feat(iot-guard): Add IoT device isolation and security monitoring
Backend (secubox-iot-guard):
- OUI-based device classification with 100+ IoT vendor prefixes
- 10 device classes: camera, thermostat, lighting, plug, assistant, etc.
- Risk scoring (0-100) with auto-isolation threshold
- Anomaly detection: bandwidth spikes, port scans, time anomalies
- Integration with Client Guardian, MAC Guardian, Vortex Firewall
- iot-guardctl CLI for status/list/scan/isolate/trust/block
- SQLite database for devices, anomalies, cloud dependencies
- Traffic baseline profiles for common device classes

Frontend (luci-app-iot-guard):
- KISS-style overview dashboard with security score
- Device management with isolate/trust/block actions
- Vendor classification rules editor
- Settings form for UCI configuration
- RPCD handler with 11 methods
- Public ACL for unauthenticated dashboard access

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-11 10:36:04 +01:00

199 lines
6.1 KiB
Bash

#!/bin/sh
#
# IoT Guard - Anomaly Detection Module
#
# Detects behavioral anomalies in IoT device traffic patterns.
#
# Sensitivity levels
SENSITIVITY_LOW=3
SENSITIVITY_MEDIUM=2
SENSITIVITY_HIGH=1.5
# ============================================================================
# Baseline Management
# ============================================================================
get_device_baseline() {
local mac="$1"
local db="${2:-/var/lib/iot-guard/iot-guard.db}"
sqlite3 -json "$db" \
"SELECT avg_bps_in, avg_bps_out, peak_bps_in, peak_bps_out, common_ports
FROM traffic_baseline WHERE mac='$mac';" 2>/dev/null
}
update_device_baseline() {
local mac="$1"
local bps_in="$2"
local bps_out="$3"
local ports="$4"
local db="${5:-/var/lib/iot-guard/iot-guard.db}"
local now=$(date -Iseconds)
# Get current baseline
local current=$(sqlite3 "$db" \
"SELECT avg_bps_in, avg_bps_out, sample_count FROM traffic_baseline WHERE mac='$mac';" 2>/dev/null)
if [ -z "$current" ]; then
# Create new baseline
sqlite3 "$db" "INSERT INTO traffic_baseline
(mac, avg_bps_in, avg_bps_out, peak_bps_in, peak_bps_out, common_ports, sample_count, last_update)
VALUES ('$mac', $bps_in, $bps_out, $bps_in, $bps_out, '$ports', 1, '$now');"
else
# Update with exponential moving average
local old_in=$(echo "$current" | cut -d'|' -f1)
local old_out=$(echo "$current" | cut -d'|' -f2)
local count=$(echo "$current" | cut -d'|' -f3)
# EMA with alpha = 0.1
local new_in=$(awk "BEGIN {printf \"%.2f\", $old_in * 0.9 + $bps_in * 0.1}")
local new_out=$(awk "BEGIN {printf \"%.2f\", $old_out * 0.9 + $bps_out * 0.1}")
sqlite3 "$db" "UPDATE traffic_baseline SET
avg_bps_in = $new_in,
avg_bps_out = $new_out,
peak_bps_in = MAX(peak_bps_in, $bps_in),
peak_bps_out = MAX(peak_bps_out, $bps_out),
sample_count = sample_count + 1,
last_update = '$now'
WHERE mac = '$mac';"
fi
}
# ============================================================================
# Anomaly Detection
# ============================================================================
check_bandwidth_anomaly() {
local mac="$1"
local current_bps="$2"
local direction="$3"
local db="${4:-/var/lib/iot-guard/iot-guard.db}"
local sensitivity
config_load iot-guard
config_get sensitivity main anomaly_sensitivity "medium"
local threshold
case "$sensitivity" in
low) threshold=$SENSITIVITY_LOW ;;
high) threshold=$SENSITIVITY_HIGH ;;
*) threshold=$SENSITIVITY_MEDIUM ;;
esac
# Get baseline average
local avg_field="avg_bps_in"
[ "$direction" = "out" ] && avg_field="avg_bps_out"
local baseline=$(sqlite3 "$db" "SELECT $avg_field FROM traffic_baseline WHERE mac='$mac';" 2>/dev/null)
[ -z "$baseline" ] && return 1
# Check if current is threshold times the baseline
local ratio=$(awk "BEGIN {printf \"%.2f\", $current_bps / ($baseline + 0.01)}")
local is_anomaly=$(awk "BEGIN {print ($ratio > $threshold) ? 1 : 0}")
if [ "$is_anomaly" = "1" ]; then
echo "bandwidth_spike|high|Traffic ${direction} ${ratio}x above baseline"
return 0
fi
return 1
}
check_new_destination() {
local mac="$1"
local domain="$2"
local db="${3:-/var/lib/iot-guard/iot-guard.db}"
# Check if this is a new destination for this device
local exists=$(sqlite3 "$db" \
"SELECT COUNT(*) FROM cloud_deps WHERE mac='$mac' AND domain='$domain';" 2>/dev/null)
if [ "$exists" = "0" ]; then
echo "new_destination|low|First connection to $domain"
return 0
fi
return 1
}
check_port_scan() {
local mac="$1"
local port_count="$2"
local time_window="$3"
# If device contacts many ports in short time, flag as port scan
if [ "$port_count" -gt 10 ]; then
echo "port_scan|high|Contacted $port_count ports in ${time_window}s"
return 0
fi
return 1
}
check_time_anomaly() {
local mac="$1"
local db="${2:-/var/lib/iot-guard/iot-guard.db}"
# Get device class to determine expected active hours
local device_class=$(sqlite3 "$db" "SELECT device_class FROM devices WHERE mac='$mac';" 2>/dev/null)
local hour=$(date +%H)
# Cameras/doorbells should be active 24/7
# Thermostats should have some activity
# Lights/plugs typically quiet at night
case "$device_class" in
lighting|plug)
if [ "$hour" -ge 2 ] && [ "$hour" -lt 6 ]; then
echo "time_anomaly|medium|Unusual activity at night (${hour}:00)"
return 0
fi
;;
esac
return 1
}
# ============================================================================
# Main Anomaly Check
# ============================================================================
run_anomaly_detection() {
local db="${1:-/var/lib/iot-guard/iot-guard.db}"
# Get all active devices
sqlite3 "$db" "SELECT mac FROM devices WHERE status='active';" 2>/dev/null | while read -r mac; do
[ -z "$mac" ] && continue
# Check various anomaly types
# These would normally be called with real-time data from traffic monitoring
# Time-based anomaly
local time_result=$(check_time_anomaly "$mac" "$db")
if [ -n "$time_result" ]; then
local atype=$(echo "$time_result" | cut -d'|' -f1)
local severity=$(echo "$time_result" | cut -d'|' -f2)
local desc=$(echo "$time_result" | cut -d'|' -f3)
record_anomaly "$mac" "$atype" "$severity" "$desc"
fi
done
}
record_anomaly() {
local mac="$1"
local atype="$2"
local severity="$3"
local desc="$4"
local db="${5:-/var/lib/iot-guard/iot-guard.db}"
local now=$(date -Iseconds)
sqlite3 "$db" "INSERT INTO anomalies (mac, timestamp, anomaly_type, severity, description)
VALUES ('$mac', '$now', '$atype', '$severity', '$desc');"
logger -t "iot-guard" "Anomaly detected: $mac - $atype ($severity): $desc"
}