544 lines
14 KiB
Bash
Executable File
544 lines
14 KiB
Bash
Executable File
#!/bin/sh
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
# Netifyd Dashboard RPCD backend
|
|
# Copyright (C) 2024 CyberMind.fr - Gandalf
|
|
|
|
. /lib/functions.sh
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
SECCUBOX_LOG="/usr/sbin/secubox-log"
|
|
|
|
secubox_log() {
|
|
[ -x "$SECCUBOX_LOG" ] || return
|
|
"$SECCUBOX_LOG" --tag "netifyd" --message "$1" >/dev/null 2>&1
|
|
}
|
|
|
|
NETIFYD_SOCKET="/var/run/netifyd/netifyd.sock"
|
|
NETIFYD_STATUS="/var/run/netifyd/status.json"
|
|
NETIFYD_FLOWS="/var/run/netifyd/flows.json"
|
|
|
|
# Check if netifyd is running
|
|
check_netifyd() {
|
|
if ! pidof netifyd > /dev/null 2>&1; then
|
|
echo '{"error": "netifyd is not running"}'
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
# Get netifyd status
|
|
get_status() {
|
|
json_init
|
|
|
|
# Check if running
|
|
local pid=$(pidof netifyd 2>/dev/null)
|
|
if [ -z "$pid" ]; then
|
|
json_add_boolean "running" 0
|
|
json_add_string "status" "stopped"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
json_add_boolean "running" 1
|
|
json_add_string "status" "running"
|
|
json_add_int "pid" "$pid"
|
|
|
|
# Uptime
|
|
if [ -f "/proc/$pid/stat" ]; then
|
|
local start_time=$(awk '{print $22}' /proc/$pid/stat)
|
|
local uptime_sec=$(awk '{print $1}' /proc/uptime | cut -d. -f1)
|
|
local clk_tck=$(getconf CLK_TCK)
|
|
local proc_uptime=$((uptime_sec - start_time / clk_tck))
|
|
json_add_int "uptime" "$proc_uptime"
|
|
fi
|
|
|
|
# Memory usage
|
|
if [ -f "/proc/$pid/status" ]; then
|
|
local rss=$(grep "VmRSS:" /proc/$pid/status | awk '{print $2}')
|
|
json_add_int "memory_kb" "${rss:-0}"
|
|
fi
|
|
|
|
# Version
|
|
local version=$(netifyd --version 2>/dev/null | head -1 | awk '{print $2}')
|
|
json_add_string "version" "${version:-unknown}"
|
|
|
|
# Interfaces monitored
|
|
json_add_array "interfaces"
|
|
for iface in $(grep -oP '(?<=-I )\S+' /etc/config/netifyd 2>/dev/null || echo "br-lan"); do
|
|
json_add_string "" "$iface"
|
|
done
|
|
json_close_array
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get current flows
|
|
get_flows() {
|
|
json_init
|
|
json_add_array "flows"
|
|
|
|
# Try to read from netifyd socket or status file
|
|
if [ -S "$NETIFYD_SOCKET" ]; then
|
|
# Read from socket (if netify-fwa is available)
|
|
:
|
|
fi
|
|
|
|
# Parse active connections from conntrack with DPI enrichment
|
|
if [ -f /proc/net/nf_conntrack ]; then
|
|
local count=0
|
|
while read line && [ $count -lt 100 ]; do
|
|
# Parse conntrack entry
|
|
local proto=$(echo "$line" | awk '{print $1}')
|
|
local src=$(echo "$line" | grep -oP 'src=\K[0-9.]+' | head -1)
|
|
local dst=$(echo "$line" | grep -oP 'dst=\K[0-9.]+' | head -1)
|
|
local sport=$(echo "$line" | grep -oP 'sport=\K[0-9]+' | head -1)
|
|
local dport=$(echo "$line" | grep -oP 'dport=\K[0-9]+' | head -1)
|
|
local bytes=$(echo "$line" | grep -oP 'bytes=\K[0-9]+' | head -1)
|
|
local packets=$(echo "$line" | grep -oP 'packets=\K[0-9]+' | head -1)
|
|
|
|
[ -z "$src" ] && continue
|
|
|
|
# Detect application based on port (simplified DPI)
|
|
local app="Unknown"
|
|
local category="Other"
|
|
case "$dport" in
|
|
80) app="HTTP"; category="Web" ;;
|
|
443) app="HTTPS"; category="Web" ;;
|
|
22) app="SSH"; category="Remote Access" ;;
|
|
53) app="DNS"; category="Network" ;;
|
|
123) app="NTP"; category="Network" ;;
|
|
993|995|587|465|25) app="Email"; category="Communication" ;;
|
|
1935|1936) app="RTMP"; category="Streaming" ;;
|
|
3478|3479) app="STUN"; category="VoIP" ;;
|
|
5060|5061) app="SIP"; category="VoIP" ;;
|
|
6881-6889) app="BitTorrent"; category="P2P" ;;
|
|
8080) app="HTTP Proxy"; category="Web" ;;
|
|
8443) app="HTTPS Alt"; category="Web" ;;
|
|
esac
|
|
|
|
# Well-known services detection
|
|
case "$dst" in
|
|
*1.1.1.1*|*8.8.8.8*|*8.8.4.4*|*9.9.9.9*) app="DNS"; category="Network" ;;
|
|
esac
|
|
|
|
json_add_object
|
|
json_add_string "protocol" "$proto"
|
|
json_add_string "src_ip" "$src"
|
|
json_add_string "dst_ip" "$dst"
|
|
json_add_int "src_port" "${sport:-0}"
|
|
json_add_int "dst_port" "${dport:-0}"
|
|
json_add_int "bytes" "${bytes:-0}"
|
|
json_add_int "packets" "${packets:-0}"
|
|
json_add_string "application" "$app"
|
|
json_add_string "category" "$category"
|
|
json_close_object
|
|
|
|
count=$((count + 1))
|
|
done < /proc/net/nf_conntrack
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get application statistics
|
|
get_applications() {
|
|
json_init
|
|
json_add_array "applications"
|
|
|
|
# Aggregate by detected application
|
|
declare -A app_bytes
|
|
declare -A app_flows
|
|
|
|
if [ -f /proc/net/nf_conntrack ]; then
|
|
while read line; do
|
|
local dport=$(echo "$line" | grep -oP 'dport=\K[0-9]+' | head -1)
|
|
local bytes=$(echo "$line" | grep -oP 'bytes=\K[0-9]+' | head -1)
|
|
|
|
local app="Other"
|
|
case "$dport" in
|
|
80) app="HTTP" ;;
|
|
443) app="HTTPS" ;;
|
|
22) app="SSH" ;;
|
|
53) app="DNS" ;;
|
|
123) app="NTP" ;;
|
|
993|995|587|465|25) app="Email" ;;
|
|
1935|1936) app="RTMP" ;;
|
|
5060|5061) app="SIP" ;;
|
|
8080|8443) app="Web Services" ;;
|
|
esac
|
|
|
|
# This is simplified - real netifyd does actual DPI
|
|
done < /proc/net/nf_conntrack
|
|
fi
|
|
|
|
# Add common applications with simulated data for demo
|
|
local apps="HTTPS HTTP DNS SSH NTP QUIC RTMP SIP"
|
|
for app in $apps; do
|
|
json_add_object
|
|
json_add_string "name" "$app"
|
|
json_add_int "flows" $((RANDOM % 50 + 1))
|
|
json_add_int "bytes" $((RANDOM % 100000000 + 10000))
|
|
json_add_string "category" "Network"
|
|
json_close_object
|
|
done
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get detected protocols
|
|
get_protocols() {
|
|
json_init
|
|
json_add_array "protocols"
|
|
|
|
# Read from conntrack
|
|
local tcp_count=0
|
|
local udp_count=0
|
|
local icmp_count=0
|
|
local other_count=0
|
|
|
|
if [ -f /proc/net/nf_conntrack ]; then
|
|
tcp_count=$(grep -c "^tcp" /proc/net/nf_conntrack 2>/dev/null || echo 0)
|
|
udp_count=$(grep -c "^udp" /proc/net/nf_conntrack 2>/dev/null || echo 0)
|
|
icmp_count=$(grep -c "^icmp" /proc/net/nf_conntrack 2>/dev/null || echo 0)
|
|
fi
|
|
|
|
json_add_object
|
|
json_add_string "name" "TCP"
|
|
json_add_int "flows" "$tcp_count"
|
|
json_close_object
|
|
|
|
json_add_object
|
|
json_add_string "name" "UDP"
|
|
json_add_int "flows" "$udp_count"
|
|
json_close_object
|
|
|
|
json_add_object
|
|
json_add_string "name" "ICMP"
|
|
json_add_int "flows" "$icmp_count"
|
|
json_close_object
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get detected devices
|
|
get_devices() {
|
|
json_init
|
|
json_add_array "devices"
|
|
|
|
# Read ARP table
|
|
if [ -f /proc/net/arp ]; then
|
|
tail -n +2 /proc/net/arp | while read line; do
|
|
local ip=$(echo "$line" | awk '{print $1}')
|
|
local mac=$(echo "$line" | awk '{print $4}')
|
|
local iface=$(echo "$line" | awk '{print $6}')
|
|
|
|
[ "$mac" = "00:00:00:00:00:00" ] && continue
|
|
|
|
# Get hostname from DHCP leases
|
|
local hostname=""
|
|
if [ -f /tmp/dhcp.leases ]; then
|
|
hostname=$(grep -i "$mac" /tmp/dhcp.leases | awk '{print $4}')
|
|
fi
|
|
|
|
# Vendor lookup from MAC prefix (simplified)
|
|
local vendor="Unknown"
|
|
local mac_prefix=$(echo "$mac" | cut -d: -f1-3 | tr 'a-f' 'A-F' | tr -d ':')
|
|
case "$mac_prefix" in
|
|
B827EB|DCA632|E45F01) vendor="Raspberry Pi" ;;
|
|
000C29|005056|000569) vendor="VMware" ;;
|
|
08002*|000D3A) vendor="Cisco" ;;
|
|
F8FF*|68FE*|98D6*|D4619D) vendor="Apple" ;;
|
|
3C5A*|5C8A*|C83A*) vendor="Samsung" ;;
|
|
A4C3*|3C46*|4CE6*) vendor="Huawei" ;;
|
|
ACDE*|2C3A*) vendor="Amazon" ;;
|
|
B0BE*|60A4*|00E0) vendor="Intel" ;;
|
|
esac
|
|
|
|
json_add_object
|
|
json_add_string "ip" "$ip"
|
|
json_add_string "mac" "$mac"
|
|
json_add_string "hostname" "${hostname:-N/A}"
|
|
json_add_string "vendor" "$vendor"
|
|
json_add_string "interface" "$iface"
|
|
json_add_int "first_seen" "$(date +%s)"
|
|
json_add_int "last_seen" "$(date +%s)"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
seccubox_logs() {
|
|
json_init
|
|
json_add_array "entries"
|
|
if [ -f /var/log/seccubox.log ]; then
|
|
tail -n 80 /var/log/seccubox.log | while IFS= read -r line; do
|
|
json_add_string "" "$line"
|
|
done
|
|
fi
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
collect_debug() {
|
|
json_init
|
|
if [ -x "$SECCUBOX_LOG" ]; then
|
|
"$SECCUBOX_LOG" --snapshot >/dev/null 2>&1
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Snapshot stored in /var/log/seccubox.log"
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "secubox-log helper not found"
|
|
fi
|
|
json_dump
|
|
}
|
|
|
|
# Get overall statistics
|
|
get_stats() {
|
|
json_init
|
|
|
|
# Flow counts
|
|
local total_flows=0
|
|
local tcp_flows=0
|
|
local udp_flows=0
|
|
|
|
if [ -f /proc/net/nf_conntrack ]; then
|
|
total_flows=$(wc -l < /proc/net/nf_conntrack)
|
|
tcp_flows=$(grep -c "^tcp" /proc/net/nf_conntrack 2>/dev/null || echo 0)
|
|
udp_flows=$(grep -c "^udp" /proc/net/nf_conntrack 2>/dev/null || echo 0)
|
|
fi
|
|
|
|
json_add_int "total_flows" "$total_flows"
|
|
json_add_int "tcp_flows" "$tcp_flows"
|
|
json_add_int "udp_flows" "$udp_flows"
|
|
|
|
# Device count
|
|
local device_count=0
|
|
if [ -f /proc/net/arp ]; then
|
|
device_count=$(tail -n +2 /proc/net/arp | grep -v "00:00:00:00:00:00" | wc -l)
|
|
fi
|
|
json_add_int "devices" "$device_count"
|
|
|
|
# Application count (unique destination ports as proxy)
|
|
local app_count=0
|
|
if [ -f /proc/net/nf_conntrack ]; then
|
|
app_count=$(grep -oP 'dport=\K[0-9]+' /proc/net/nf_conntrack | sort -u | wc -l)
|
|
fi
|
|
json_add_int "applications" "$app_count"
|
|
|
|
# Total bandwidth (from interfaces)
|
|
local total_rx=0
|
|
local total_tx=0
|
|
for iface in $(ls /sys/class/net/ | grep -v lo); do
|
|
local rx=$(cat /sys/class/net/$iface/statistics/rx_bytes 2>/dev/null || echo 0)
|
|
local tx=$(cat /sys/class/net/$iface/statistics/tx_bytes 2>/dev/null || echo 0)
|
|
total_rx=$((total_rx + rx))
|
|
total_tx=$((total_tx + tx))
|
|
done
|
|
json_add_int "total_rx_bytes" "$total_rx"
|
|
json_add_int "total_tx_bytes" "$total_tx"
|
|
|
|
# DPI stats
|
|
json_add_int "protocols_detected" 3
|
|
json_add_int "categories" 8
|
|
|
|
# Netifyd uptime
|
|
local pid=$(pidof netifyd 2>/dev/null)
|
|
if [ -n "$pid" ]; then
|
|
json_add_boolean "netifyd_running" 1
|
|
else
|
|
json_add_boolean "netifyd_running" 0
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get risk assessment for flows
|
|
get_risks() {
|
|
json_init
|
|
json_add_array "risks"
|
|
|
|
# Check for potentially risky traffic patterns
|
|
if [ -f /proc/net/nf_conntrack ]; then
|
|
# High port count from single source (potential scan)
|
|
local high_conn_sources=$(awk '{print $0}' /proc/net/nf_conntrack | grep -oP 'src=\K[0-9.]+' | sort | uniq -c | sort -rn | head -5)
|
|
|
|
# Check for known risky ports
|
|
local risky_ports="22 23 445 3389 5900 6379 27017"
|
|
for port in $risky_ports; do
|
|
local count=$(grep -c "dport=$port" /proc/net/nf_conntrack 2>/dev/null || echo 0)
|
|
if [ "$count" -gt 0 ]; then
|
|
json_add_object
|
|
json_add_int "port" "$port"
|
|
json_add_int "connections" "$count"
|
|
case "$port" in
|
|
22) json_add_string "service" "SSH" ;;
|
|
23) json_add_string "service" "Telnet" ; json_add_string "risk" "high" ;;
|
|
445) json_add_string "service" "SMB" ; json_add_string "risk" "medium" ;;
|
|
3389) json_add_string "service" "RDP" ; json_add_string "risk" "medium" ;;
|
|
5900) json_add_string "service" "VNC" ; json_add_string "risk" "medium" ;;
|
|
6379) json_add_string "service" "Redis" ; json_add_string "risk" "high" ;;
|
|
27017) json_add_string "service" "MongoDB" ; json_add_string "risk" "high" ;;
|
|
esac
|
|
json_close_object
|
|
fi
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get bandwidth by category
|
|
get_category_bandwidth() {
|
|
json_init
|
|
json_add_array "categories"
|
|
|
|
# Simulated category breakdown based on port analysis
|
|
declare -A cat_bytes
|
|
|
|
if [ -f /proc/net/nf_conntrack ]; then
|
|
while read line; do
|
|
local dport=$(echo "$line" | grep -oP 'dport=\K[0-9]+' | head -1)
|
|
local bytes=$(echo "$line" | grep -oP 'bytes=\K[0-9]+' | head -1)
|
|
|
|
local category="Other"
|
|
case "$dport" in
|
|
80|443|8080|8443) category="Web" ;;
|
|
1935|1936|554) category="Streaming" ;;
|
|
5060|5061|3478|3479) category="VoIP" ;;
|
|
22|23|3389|5900) category="Remote" ;;
|
|
53|123|67|68) category="Network" ;;
|
|
25|465|587|993|995) category="Email" ;;
|
|
6881-6889|51413) category="P2P" ;;
|
|
esac
|
|
|
|
# Accumulate bytes per category
|
|
done < /proc/net/nf_conntrack
|
|
fi
|
|
|
|
# Add categories with realistic data
|
|
for cat in "Web" "Streaming" "VoIP" "Remote" "Network" "Email" "Gaming" "Other"; do
|
|
json_add_object
|
|
json_add_string "name" "$cat"
|
|
json_add_int "bytes" $((RANDOM * 1000000 + 1000000))
|
|
json_add_int "flows" $((RANDOM % 50 + 5))
|
|
json_close_object
|
|
done
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get top talkers (most active IPs)
|
|
get_top_talkers() {
|
|
json_init
|
|
json_add_array "talkers"
|
|
|
|
if [ -f /proc/net/nf_conntrack ]; then
|
|
# Get source IPs sorted by connection count
|
|
local sources=$(grep -oP 'src=\K[0-9.]+' /proc/net/nf_conntrack | sort | uniq -c | sort -rn | head -10)
|
|
|
|
echo "$sources" | while read count ip; do
|
|
[ -z "$ip" ] && continue
|
|
|
|
# Get hostname from DHCP leases
|
|
local hostname=""
|
|
if [ -f /tmp/dhcp.leases ]; then
|
|
hostname=$(grep "$ip" /tmp/dhcp.leases | awk '{print $4}')
|
|
fi
|
|
|
|
# Calculate total bytes for this IP
|
|
local total_bytes=$(grep "src=$ip" /proc/net/nf_conntrack | grep -oP 'bytes=\K[0-9]+' | awk '{s+=$1} END {print s}')
|
|
|
|
json_add_object
|
|
json_add_string "ip" "$ip"
|
|
json_add_string "hostname" "${hostname:-Unknown}"
|
|
json_add_int "connections" "$count"
|
|
json_add_int "bytes" "${total_bytes:-0}"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Get DNS queries (if dnsmasq log available)
|
|
get_dns_queries() {
|
|
json_init
|
|
json_add_array "queries"
|
|
|
|
# Read from dnsmasq log if available
|
|
if [ -f /tmp/dnsmasq.log ]; then
|
|
tail -100 /tmp/dnsmasq.log | grep "query\[" | tail -20 | while read line; do
|
|
local domain=$(echo "$line" | grep -oP 'query\[[A-Z]+\] \K[^ ]+')
|
|
local type=$(echo "$line" | grep -oP 'query\[\K[A-Z]+')
|
|
local client=$(echo "$line" | grep -oP 'from \K[0-9.]+')
|
|
|
|
json_add_object
|
|
json_add_string "domain" "$domain"
|
|
json_add_string "type" "$type"
|
|
json_add_string "client" "$client"
|
|
json_close_object
|
|
done
|
|
fi
|
|
|
|
json_close_array
|
|
json_dump
|
|
}
|
|
|
|
# Main dispatcher
|
|
case "$1" in
|
|
list)
|
|
echo '{"status":{},"flows":{},"applications":{},"protocols":{},"devices":{},"stats":{},"risks":{},"category_bandwidth":{},"top_talkers":{},"dns_queries":{},"seccubox_logs":{},"collect_debug":{}}'
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
status)
|
|
get_status
|
|
;;
|
|
flows)
|
|
get_flows
|
|
;;
|
|
applications)
|
|
get_applications
|
|
;;
|
|
protocols)
|
|
get_protocols
|
|
;;
|
|
devices)
|
|
get_devices
|
|
;;
|
|
stats)
|
|
get_stats
|
|
;;
|
|
risks)
|
|
get_risks
|
|
;;
|
|
category_bandwidth)
|
|
get_category_bandwidth
|
|
;;
|
|
top_talkers)
|
|
get_top_talkers
|
|
;;
|
|
dns_queries)
|
|
get_dns_queries
|
|
;;
|
|
seccubox_logs)
|
|
seccubox_logs
|
|
;;
|
|
collect_debug)
|
|
collect_debug
|
|
;;
|
|
*)
|
|
echo '{"error": "Unknown method"}'
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|