fix(tor-shield,security-threats): Preset persistence and firewall stats
Tor Shield: - Store current_preset in UCI when enabling with preset - Return current_preset in status response - Initialize currentPreset from stored UCI value on page load Security Threats: - Fix get_security_stats() firewall packet counting - Use correct nftables chain names (input_wan, handle_reject) - Fix grep -c exit code issue (returns 1 when no matches) - Improve numeric validation (use tr -cd to strip non-digits) - Add fallbacks for HAProxy socket paths Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
025a1085e9
commit
cf49c7d80b
@ -5,7 +5,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-secubox-security-threats
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=3
|
||||
PKG_RELEASE:=4
|
||||
PKG_ARCH:=all
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
|
||||
|
||||
@ -279,58 +279,116 @@ get_security_stats() {
|
||||
local wan_iface=$(uci -q get network.wan.device || uci -q get network.wan.ifname)
|
||||
[ -z "$wan_iface" ] && wan_iface="eth0"
|
||||
|
||||
# WAN dropped packets from nftables (actual firewall drops on input chain)
|
||||
# Count packets dropped/rejected on wan zone input
|
||||
# WAN dropped packets from nftables (use counter on drop rules)
|
||||
if command -v nft >/dev/null 2>&1; then
|
||||
# Get drop counters from firewall input chain for wan
|
||||
wan_drops=$(nft list chain inet fw4 input 2>/dev/null | grep -E "iifname.*$wan_iface.*drop|iifname.*$wan_iface.*reject" | grep -oE 'packets [0-9]+' | awk '{sum+=$2} END {print sum+0}')
|
||||
# Also count from forward chain drops (wan to lan blocked)
|
||||
local wan_fwd_drops=$(nft list chain inet fw4 forward 2>/dev/null | grep -E "iifname.*$wan_iface.*drop|iifname.*$wan_iface.*reject" | grep -oE 'packets [0-9]+' | awk '{sum+=$2} END {print sum+0}')
|
||||
wan_drops=$((${wan_drops:-0} + ${wan_fwd_drops:-0}))
|
||||
# Get all drop/reject counters from input_wan zone
|
||||
# nft format: "counter packets X bytes Y"
|
||||
wan_drops=$(nft list chain inet fw4 input_wan 2>/dev/null | \
|
||||
grep -E "counter packets" | \
|
||||
grep -oE 'packets [0-9]+' | \
|
||||
awk '{sum+=$2} END {print sum+0}')
|
||||
[ -z "$wan_drops" ] && wan_drops=0
|
||||
|
||||
# Also try reject chain
|
||||
local reject_drops=$(nft list chain inet fw4 reject_from_wan 2>/dev/null | \
|
||||
grep -E "counter packets" | \
|
||||
grep -oE 'packets [0-9]+' | \
|
||||
awk '{sum+=$2} END {print sum+0}')
|
||||
[ -n "$reject_drops" ] && wan_drops=$((wan_drops + reject_drops))
|
||||
fi
|
||||
wan_drops=${wan_drops:-0}
|
||||
|
||||
# Firewall rejects - count from nftables counters (more accurate than logs)
|
||||
# Firewall rejects - count from reject-specific chains
|
||||
if command -v nft >/dev/null 2>&1; then
|
||||
fw_rejects=$(nft list ruleset 2>/dev/null | grep -E "reject|drop" | grep -oE 'packets [0-9]+' | awk '{sum+=$2} END {print sum+0}')
|
||||
# Count from handle_reject chain which has actual reject rules
|
||||
fw_rejects=$(nft list chain inet fw4 handle_reject 2>/dev/null | \
|
||||
grep -E "counter packets" | \
|
||||
grep -oE 'packets [0-9]+' | \
|
||||
awk '{sum+=$2} END {print sum+0}')
|
||||
[ -z "$fw_rejects" ] && fw_rejects=0
|
||||
|
||||
# If no handle_reject, try counting reject rules in all chains
|
||||
if [ "$fw_rejects" = "0" ]; then
|
||||
fw_rejects=$(nft -a list ruleset 2>/dev/null | \
|
||||
grep -E "reject|drop" | grep "counter" | \
|
||||
grep -oE 'packets [0-9]+' | \
|
||||
awk '{sum+=$2} END {print sum+0}')
|
||||
fi
|
||||
else
|
||||
# Fallback to log parsing
|
||||
fw_rejects=$(logread 2>/dev/null | grep -c "reject\|DROP\|REJECT" || echo 0)
|
||||
fi
|
||||
fw_rejects=$(echo "$fw_rejects" | tr -d '\n')
|
||||
fw_rejects=${fw_rejects:-0}
|
||||
|
||||
# CrowdSec active bans
|
||||
if [ -x "$CSCLI" ]; then
|
||||
cs_bans=$($CSCLI decisions list -o json 2>/dev/null | grep -c '"id":' || echo 0)
|
||||
cs_bans=$(echo "$cs_bans" | tr -d '\n')
|
||||
# Use jq for proper JSON parsing if available, fallback to grep
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
cs_bans=$($CSCLI decisions list -o json 2>/dev/null | jq 'length' 2>/dev/null)
|
||||
[ -z "$cs_bans" ] && cs_bans=0
|
||||
else
|
||||
# Count JSON array items
|
||||
local cs_json=$($CSCLI decisions list -o json 2>/dev/null)
|
||||
if [ -n "$cs_json" ] && [ "$cs_json" != "null" ] && [ "$cs_json" != "[]" ]; then
|
||||
cs_bans=$(echo "$cs_json" | jsonfilter -e '@[*]' 2>/dev/null | wc -l)
|
||||
fi
|
||||
fi
|
||||
cs_bans=${cs_bans:-0}
|
||||
|
||||
# CrowdSec alerts in last 24h
|
||||
cs_alerts_24h=$($CSCLI alerts list -o json --since 24h 2>/dev/null | grep -c '"id":' || echo 0)
|
||||
cs_alerts_24h=$(echo "$cs_alerts_24h" | tr -d '\n')
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
cs_alerts_24h=$($CSCLI alerts list -o json --since 24h 2>/dev/null | jq 'length' 2>/dev/null)
|
||||
[ -z "$cs_alerts_24h" ] && cs_alerts_24h=0
|
||||
else
|
||||
local alerts_json=$($CSCLI alerts list -o json --since 24h 2>/dev/null)
|
||||
if [ -n "$alerts_json" ] && [ "$alerts_json" != "null" ] && [ "$alerts_json" != "[]" ]; then
|
||||
cs_alerts_24h=$(echo "$alerts_json" | jsonfilter -e '@[*]' 2>/dev/null | wc -l)
|
||||
fi
|
||||
fi
|
||||
cs_alerts_24h=${cs_alerts_24h:-0}
|
||||
fi
|
||||
|
||||
# Invalid connections (conntrack)
|
||||
# Invalid connections (conntrack) - only count INVALID, not UNREPLIED
|
||||
if [ -f /proc/net/nf_conntrack ]; then
|
||||
invalid_conns=$(grep -c "INVALID\|UNREPLIED" /proc/net/nf_conntrack 2>/dev/null || echo 0)
|
||||
# grep -c returns 1 exit code when no matches, so we can't use || echo 0
|
||||
invalid_conns=$(grep -c "\[INVALID\]" /proc/net/nf_conntrack 2>/dev/null)
|
||||
[ -z "$invalid_conns" ] && invalid_conns=0
|
||||
fi
|
||||
invalid_conns=$(echo "$invalid_conns" | tr -d '\n')
|
||||
invalid_conns=${invalid_conns:-0}
|
||||
|
||||
# HAProxy connections (if running in LXC)
|
||||
if lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING"; then
|
||||
# Try multiple socket paths (chroot-relative and absolute)
|
||||
# HAProxy connections
|
||||
# Try local haproxy first, then LXC
|
||||
if [ -S /var/run/haproxy/admin.sock ]; then
|
||||
haproxy_conns=$(echo "show stat" | socat stdio /var/run/haproxy/admin.sock 2>/dev/null | \
|
||||
tail -n+2 | awk -F, '{sum+=$5} END {print sum+0}')
|
||||
elif [ -S /var/lib/haproxy/stats ]; then
|
||||
haproxy_conns=$(echo "show stat" | socat stdio /var/lib/haproxy/stats 2>/dev/null | \
|
||||
tail -n+2 | awk -F, '{sum+=$5} END {print sum+0}')
|
||||
elif command -v lxc-info >/dev/null 2>&1 && lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING"; then
|
||||
# HAProxy in LXC container
|
||||
haproxy_conns=$(lxc-attach -n haproxy -- sh -c '
|
||||
for sock in /stats /run/haproxy.sock /var/lib/haproxy/stats /var/run/haproxy/admin.sock; do
|
||||
[ -S "$sock" ] && echo "show stat" | socat stdio "$sock" 2>/dev/null && break
|
||||
done | tail -n+2 | awk -F, "{sum+=\$5} END {print sum+0}"
|
||||
' 2>/dev/null || echo 0)
|
||||
[ -S "$sock" ] && { echo "show stat" | socat stdio "$sock" 2>/dev/null; break; }
|
||||
done' 2>/dev/null | tail -n+2 | awk -F, '{sum+=$5} END {print sum+0}')
|
||||
fi
|
||||
haproxy_conns=$(echo "$haproxy_conns" | tr -d '\n')
|
||||
haproxy_conns=${haproxy_conns:-0}
|
||||
|
||||
# Clean up and ensure numeric values (remove all non-digits)
|
||||
wan_drops=$(printf '%s' "$wan_drops" | tr -cd '0-9')
|
||||
fw_rejects=$(printf '%s' "$fw_rejects" | tr -cd '0-9')
|
||||
cs_bans=$(printf '%s' "$cs_bans" | tr -cd '0-9')
|
||||
cs_alerts_24h=$(printf '%s' "$cs_alerts_24h" | tr -cd '0-9')
|
||||
invalid_conns=$(printf '%s' "$invalid_conns" | tr -cd '0-9')
|
||||
haproxy_conns=$(printf '%s' "$haproxy_conns" | tr -cd '0-9')
|
||||
|
||||
# Default to 0 if empty
|
||||
: ${wan_drops:=0}
|
||||
: ${fw_rejects:=0}
|
||||
: ${cs_bans:=0}
|
||||
: ${cs_alerts_24h:=0}
|
||||
: ${invalid_conns:=0}
|
||||
: ${haproxy_conns:=0}
|
||||
|
||||
# Output JSON
|
||||
cat << EOF
|
||||
{
|
||||
|
||||
@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-tor-shield
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=9
|
||||
PKG_RELEASE:=10
|
||||
PKG_ARCH:=all
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
|
||||
@ -285,6 +285,9 @@ return view.extend({
|
||||
var presets = data.presets || [];
|
||||
var bandwidth = data.bandwidth || {};
|
||||
|
||||
// Initialize currentPreset from stored UCI value
|
||||
this.currentPreset = status.current_preset || 'anonymous';
|
||||
|
||||
var isActive = status.enabled && status.running;
|
||||
var isProtected = isActive && status.is_tor;
|
||||
var isConnecting = isActive && status.bootstrap < 100;
|
||||
|
||||
@ -54,17 +54,19 @@ get_bootstrap() {
|
||||
get_status() {
|
||||
json_init
|
||||
|
||||
local enabled mode dns_over_tor kill_switch
|
||||
local enabled mode dns_over_tor kill_switch current_preset
|
||||
config_load "$CONFIG"
|
||||
config_get enabled main enabled '0'
|
||||
config_get mode main mode 'transparent'
|
||||
config_get dns_over_tor main dns_over_tor '1'
|
||||
config_get kill_switch main kill_switch '1'
|
||||
config_get current_preset main current_preset 'anonymous'
|
||||
|
||||
json_add_boolean "enabled" "$enabled"
|
||||
json_add_string "mode" "$mode"
|
||||
json_add_boolean "dns_over_tor" "$dns_over_tor"
|
||||
json_add_boolean "kill_switch" "$kill_switch"
|
||||
json_add_string "current_preset" "$current_preset"
|
||||
|
||||
# Running state
|
||||
if is_running; then
|
||||
@ -164,6 +166,7 @@ do_enable() {
|
||||
uci set tor-shield.main.mode="$preset_mode"
|
||||
uci set tor-shield.main.dns_over_tor="$preset_dns"
|
||||
uci set tor-shield.main.kill_switch="$preset_kill"
|
||||
uci set tor-shield.main.current_preset="$preset"
|
||||
|
||||
if [ "$preset_bridges" = "1" ]; then
|
||||
uci set tor-shield.bridges.enabled='1'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user