secubox-openwrt/package/secubox/secubox-cookie-tracker/root/usr/sbin/cookie-trackerctl
CyberMind-FR 8055bca368 feat(interceptor): Add InterceptoR transparent traffic interception
The Gandalf Proxy - unified traffic interception with 5 pillars:

New packages:
- secubox-cookie-tracker: HTTP cookie classification with mitmproxy addon
  - SQLite database for cookie tracking
  - 100+ known tracker domains (Google Analytics, Facebook, etc.)
  - CLI: cookie-trackerctl status/list/block/report

- luci-app-interceptor: Unified dashboard aggregating all pillars
  - Health score (0-100%) based on active pillars
  - Status cards: WPAD, mitmproxy, CDN Cache, Cookie Tracker, API Failover

Enhanced modules:
- luci-app-network-tweaks: WPAD enforcement via iptables redirect
  - setWpadEnforce/getWpadEnforce RPCD methods
  - Catches clients ignoring WPAD auto-discovery

- luci-app-cdn-cache: API failover and offline mode
  - stale-if-error patterns for /api/ and .json endpoints
  - WAN hotplug script (99-cdn-offline) toggles offline mode
  - collapsed_forwarding for duplicate request handling

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

588 lines
17 KiB
Bash

#!/bin/sh
#
# cookie-trackerctl - CLI controller for SecuBox Cookie Tracker
#
# Usage: cookie-trackerctl <command> [options]
#
. /lib/functions.sh
DB_PATH="/var/lib/cookie-tracker/cookies.db"
TRACKER_TSV="/usr/lib/secubox/cookie-tracker/known-trackers.tsv"
VORTEX_DB="/var/lib/vortex-firewall/blocklist.db"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
usage() {
cat <<EOF
SecuBox Cookie Tracker - HTTP cookie classification and tracking
Usage: cookie-trackerctl <command> [options]
Commands:
status Show statistics summary
init Initialize/reset database
reload Reload tracker rules from UCI
list [options] List cookies
--domain <d> Filter by domain
--category <c> Filter by category
--limit <n> Limit results (default: 100)
show <domain> Show cookies for domain
classify <domain> <name> <category>
Manually classify a cookie
block <domain> Block all cookies from domain
unblock <domain> Unblock domain
report [--json] Generate cookie report
export [file] Export database to CSV
import <file> Import tracker rules from TSV
feed-vortex Feed blocked domains to Vortex Firewall
stats Detailed statistics
Categories:
essential Required for site functionality
functional User preferences/settings
analytics Usage tracking
advertising Ad targeting
tracking Cross-site tracking
Examples:
cookie-trackerctl list --category tracking
cookie-trackerctl classify google.com _ga analytics
cookie-trackerctl block doubleclick.net
cookie-trackerctl report --json
EOF
exit 1
}
log_info() {
logger -t cookie-tracker -p info "$1"
}
log_warn() {
logger -t cookie-tracker -p warn "$1"
}
log_error() {
logger -t cookie-tracker -p err "$1"
}
# Initialize database
init_db() {
local force="$1"
mkdir -p "$(dirname "$DB_PATH")"
if [ -f "$DB_PATH" ] && [ "$force" != "force" ]; then
echo "Database exists at $DB_PATH"
echo "Use 'init force' to reset"
return 0
fi
rm -f "$DB_PATH"
sqlite3 "$DB_PATH" <<-EOF
CREATE TABLE IF NOT EXISTS cookies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT NOT NULL,
name TEXT NOT NULL,
category TEXT DEFAULT 'unknown',
first_seen INTEGER DEFAULT (strftime('%s', 'now')),
last_seen INTEGER DEFAULT (strftime('%s', 'now')),
count INTEGER DEFAULT 1,
client_mac TEXT,
blocked INTEGER DEFAULT 0,
UNIQUE(domain, name)
);
CREATE TABLE IF NOT EXISTS tracker_domains (
domain TEXT PRIMARY KEY,
category TEXT NOT NULL,
source TEXT DEFAULT 'manual',
added INTEGER DEFAULT (strftime('%s', 'now'))
);
CREATE TABLE IF NOT EXISTS blocked_domains (
domain TEXT PRIMARY KEY,
reason TEXT,
blocked_at INTEGER DEFAULT (strftime('%s', 'now'))
);
CREATE INDEX IF NOT EXISTS idx_cookies_domain ON cookies(domain);
CREATE INDEX IF NOT EXISTS idx_cookies_category ON cookies(category);
CREATE INDEX IF NOT EXISTS idx_cookies_last_seen ON cookies(last_seen);
EOF
echo "Database initialized at $DB_PATH"
# Import known trackers
if [ -f "$TRACKER_TSV" ]; then
import_trackers "$TRACKER_TSV"
fi
log_info "Database initialized"
}
# Import tracker rules from TSV
import_trackers() {
local file="${1:-$TRACKER_TSV}"
if [ ! -f "$file" ]; then
echo "File not found: $file"
return 1
fi
local count=0
while IFS=$'\t' read -r domain category source; do
# Skip comments and empty lines
case "$domain" in
"#"*|"") continue ;;
esac
sqlite3 "$DB_PATH" "INSERT OR REPLACE INTO tracker_domains (domain, category, source) VALUES ('$domain', '$category', '$source');"
count=$((count + 1))
done < "$file"
echo "Imported $count tracker domains"
log_info "Imported $count tracker domains from $file"
}
# Reload UCI rules into database
reload_rules() {
config_load cookie-tracker
local count=0
reload_tracker_rule() {
local section="$1"
local pattern domain_pattern category source
config_get pattern "$section" pattern
config_get domain_pattern "$section" domain_pattern
config_get category "$section" category "tracking"
config_get source "$section" source "uci"
# For domain patterns, extract and add domains
if [ -n "$domain_pattern" ]; then
# Convert regex pattern to domain list (simplified)
local domains=$(echo "$domain_pattern" | sed 's/\\\././' | tr '|' '\n')
for d in $domains; do
sqlite3 "$DB_PATH" "INSERT OR REPLACE INTO tracker_domains (domain, category, source) VALUES ('$d', '$category', '$source');"
count=$((count + 1))
done
fi
}
config_foreach reload_tracker_rule tracker_rule
echo "Reloaded $count tracker rules from UCI"
log_info "Reloaded $count tracker rules"
}
# Show status/statistics
show_status() {
local json="$1"
if [ ! -f "$DB_PATH" ]; then
echo "Database not initialized. Run: cookie-trackerctl init"
return 1
fi
local total=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies;")
local domains=$(sqlite3 "$DB_PATH" "SELECT COUNT(DISTINCT domain) FROM cookies;")
local blocked=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE blocked=1;")
local trackers=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM tracker_domains;")
local blocked_domains=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM blocked_domains;")
# Category breakdown
local essential=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE category='essential';")
local functional=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE category='functional';")
local analytics=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE category='analytics';")
local advertising=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE category='advertising';")
local tracking=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE category='tracking';")
local unknown=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE category='unknown';")
# Last 24h activity
local today=$(date +%s)
local yesterday=$((today - 86400))
local new_today=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE first_seen > $yesterday;")
local seen_today=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE last_seen > $yesterday;")
if [ "$json" = "--json" ]; then
cat <<EOF
{
"total_cookies": $total,
"unique_domains": $domains,
"blocked_cookies": $blocked,
"known_trackers": $trackers,
"blocked_domains": $blocked_domains,
"categories": {
"essential": $essential,
"functional": $functional,
"analytics": $analytics,
"advertising": $advertising,
"tracking": $tracking,
"unknown": $unknown
},
"last_24h": {
"new_cookies": $new_today,
"seen_cookies": $seen_today
}
}
EOF
else
echo "============================================"
echo " SecuBox Cookie Tracker Status"
echo "============================================"
echo ""
printf "Total Cookies: %s\n" "$total"
printf "Unique Domains: %s\n" "$domains"
printf "Blocked Cookies: %s\n" "$blocked"
printf "Known Trackers: %s\n" "$trackers"
printf "Blocked Domains: %s\n" "$blocked_domains"
echo ""
echo "--- Category Breakdown ---"
printf " Essential: ${GREEN}%s${NC}\n" "$essential"
printf " Functional: ${BLUE}%s${NC}\n" "$functional"
printf " Analytics: ${YELLOW}%s${NC}\n" "$analytics"
printf " Advertising: ${RED}%s${NC}\n" "$advertising"
printf " Tracking: ${RED}%s${NC}\n" "$tracking"
printf " Unknown: %s\n" "$unknown"
echo ""
echo "--- Last 24 Hours ---"
printf " New Cookies: %s\n" "$new_today"
printf " Seen Cookies: %s\n" "$seen_today"
echo ""
fi
}
# List cookies
list_cookies() {
local domain="" category="" limit=100 json=""
while [ $# -gt 0 ]; do
case "$1" in
--domain) domain="$2"; shift 2 ;;
--category) category="$2"; shift 2 ;;
--limit) limit="$2"; shift 2 ;;
--json) json="1"; shift ;;
*) shift ;;
esac
done
local where=""
[ -n "$domain" ] && where="WHERE domain LIKE '%$domain%'"
[ -n "$category" ] && {
[ -n "$where" ] && where="$where AND" || where="WHERE"
where="$where category='$category'"
}
if [ "$json" = "1" ]; then
echo "["
sqlite3 -json "$DB_PATH" "SELECT domain, name, category, count, blocked, datetime(last_seen, 'unixepoch') as last_seen FROM cookies $where ORDER BY last_seen DESC LIMIT $limit;" 2>/dev/null || \
sqlite3 "$DB_PATH" "SELECT domain, name, category, count, blocked, datetime(last_seen, 'unixepoch') as last_seen FROM cookies $where ORDER BY last_seen DESC LIMIT $limit;" | \
awk -F'|' 'BEGIN{first=1} {
if(!first) print ","
first=0
printf "{\"domain\":\"%s\",\"name\":\"%s\",\"category\":\"%s\",\"count\":%s,\"blocked\":%s,\"last_seen\":\"%s\"}", $1, $2, $3, $4, $5, $6
}'
echo "]"
else
printf "%-30s %-25s %-12s %6s %s\n" "DOMAIN" "COOKIE" "CATEGORY" "COUNT" "BLOCKED"
echo "--------------------------------------------------------------------------------"
sqlite3 "$DB_PATH" "SELECT domain, name, category, count, CASE WHEN blocked=1 THEN 'YES' ELSE '' END FROM cookies $where ORDER BY last_seen DESC LIMIT $limit;" | \
while IFS='|' read -r d n c cnt b; do
printf "%-30s %-25s %-12s %6s %s\n" "${d:0:30}" "${n:0:25}" "$c" "$cnt" "$b"
done
fi
}
# Show cookies for specific domain
show_domain() {
local domain="$1"
if [ -z "$domain" ]; then
echo "Usage: cookie-trackerctl show <domain>"
return 1
fi
echo "Cookies for domain: $domain"
echo "========================================"
sqlite3 "$DB_PATH" "SELECT name, category, count, blocked, datetime(first_seen, 'unixepoch'), datetime(last_seen, 'unixepoch') FROM cookies WHERE domain LIKE '%$domain%' ORDER BY count DESC;" | \
while IFS='|' read -r name cat cnt blocked first last; do
printf "\n Name: %s\n" "$name"
printf " Category: %s\n" "$cat"
printf " Count: %s\n" "$cnt"
printf " Blocked: %s\n" "$([ "$blocked" = "1" ] && echo "Yes" || echo "No")"
printf " First seen: %s\n" "$first"
printf " Last seen: %s\n" "$last"
done
}
# Manually classify a cookie
classify_cookie() {
local domain="$1"
local name="$2"
local category="$3"
if [ -z "$domain" ] || [ -z "$name" ] || [ -z "$category" ]; then
echo "Usage: cookie-trackerctl classify <domain> <name> <category>"
return 1
fi
# Validate category
case "$category" in
essential|functional|analytics|advertising|tracking|unknown) ;;
*) echo "Invalid category: $category"; return 1 ;;
esac
sqlite3 "$DB_PATH" "UPDATE cookies SET category='$category' WHERE domain='$domain' AND name='$name';"
if [ $? -eq 0 ]; then
echo "Cookie classified: $domain/$name -> $category"
log_info "Cookie classified: $domain/$name -> $category"
else
echo "Failed to classify cookie"
return 1
fi
}
# Block domain
block_domain() {
local domain="$1"
local reason="${2:-manual}"
if [ -z "$domain" ]; then
echo "Usage: cookie-trackerctl block <domain>"
return 1
fi
sqlite3 "$DB_PATH" "INSERT OR REPLACE INTO blocked_domains (domain, reason) VALUES ('$domain', '$reason');"
sqlite3 "$DB_PATH" "UPDATE cookies SET blocked=1 WHERE domain LIKE '%$domain%';"
echo "Domain blocked: $domain"
log_info "Domain blocked: $domain"
}
# Unblock domain
unblock_domain() {
local domain="$1"
if [ -z "$domain" ]; then
echo "Usage: cookie-trackerctl unblock <domain>"
return 1
fi
sqlite3 "$DB_PATH" "DELETE FROM blocked_domains WHERE domain='$domain';"
sqlite3 "$DB_PATH" "UPDATE cookies SET blocked=0 WHERE domain LIKE '%$domain%';"
echo "Domain unblocked: $domain"
log_info "Domain unblocked: $domain"
}
# Generate report
generate_report() {
local json="$1"
# Top 10 domains by cookie count
local top_domains=$(sqlite3 "$DB_PATH" "SELECT domain, COUNT(*) as cnt FROM cookies GROUP BY domain ORDER BY cnt DESC LIMIT 10;")
# Top tracking domains
local top_trackers=$(sqlite3 "$DB_PATH" "SELECT domain, COUNT(*) as cnt FROM cookies WHERE category IN ('tracking', 'advertising') GROUP BY domain ORDER BY cnt DESC LIMIT 10;")
# Recent cookies
local recent=$(sqlite3 "$DB_PATH" "SELECT domain, name, category FROM cookies ORDER BY last_seen DESC LIMIT 10;")
if [ "$json" = "--json" ]; then
# Build JSON report
echo "{"
echo " \"generated\": \"$(date -Iseconds)\","
# Top domains
echo " \"top_domains\": ["
echo "$top_domains" | awk -F'|' 'BEGIN{first=1} {
if(!first) print ","
first=0
printf " {\"domain\":\"%s\",\"count\":%s}", $1, $2
}'
echo ""
echo " ],"
# Top trackers
echo " \"top_trackers\": ["
echo "$top_trackers" | awk -F'|' 'BEGIN{first=1} {
if(!first) print ","
first=0
printf " {\"domain\":\"%s\",\"count\":%s}", $1, $2
}'
echo ""
echo " ],"
# Recent
echo " \"recent\": ["
echo "$recent" | awk -F'|' 'BEGIN{first=1} {
if(!first) print ","
first=0
printf " {\"domain\":\"%s\",\"name\":\"%s\",\"category\":\"%s\"}", $1, $2, $3
}'
echo ""
echo " ]"
echo "}"
else
echo "============================================"
echo " Cookie Tracker Report"
echo " Generated: $(date)"
echo "============================================"
echo ""
echo "--- Top 10 Domains by Cookie Count ---"
echo "$top_domains" | while IFS='|' read -r d c; do
printf " %-40s %s cookies\n" "$d" "$c"
done
echo ""
echo "--- Top Tracking/Advertising Domains ---"
echo "$top_trackers" | while IFS='|' read -r d c; do
printf " ${RED}%-40s${NC} %s cookies\n" "$d" "$c"
done
echo ""
echo "--- Recently Seen Cookies ---"
echo "$recent" | while IFS='|' read -r d n c; do
printf " %-30s %-20s [%s]\n" "$d" "$n" "$c"
done
echo ""
fi
}
# Export to CSV
export_csv() {
local file="${1:-/tmp/cookies-export.csv}"
echo "domain,name,category,count,blocked,first_seen,last_seen" > "$file"
sqlite3 -csv "$DB_PATH" "SELECT domain, name, category, count, blocked, datetime(first_seen, 'unixepoch'), datetime(last_seen, 'unixepoch') FROM cookies ORDER BY domain, name;" >> "$file"
echo "Exported to $file"
}
# Feed blocked domains to Vortex Firewall
feed_vortex() {
if [ ! -f "$VORTEX_DB" ]; then
echo "Vortex Firewall database not found: $VORTEX_DB"
echo "Make sure secubox-vortex-firewall is installed"
return 1
fi
local count=0
# Get blocked domains
sqlite3 "$DB_PATH" "SELECT domain FROM blocked_domains;" | while read -r domain; do
sqlite3 "$VORTEX_DB" "INSERT OR IGNORE INTO domains (domain, category, source) VALUES ('$domain', 'cookie_tracker', 'cookie-tracker');"
count=$((count + 1))
done
# Get tracking/advertising domains
sqlite3 "$DB_PATH" "SELECT DISTINCT domain FROM cookies WHERE category IN ('tracking', 'advertising') AND blocked=1;" | while read -r domain; do
sqlite3 "$VORTEX_DB" "INSERT OR IGNORE INTO domains (domain, category, source) VALUES ('$domain', 'cookie_tracker', 'cookie-tracker');"
count=$((count + 1))
done
echo "Fed $count domains to Vortex Firewall"
log_info "Fed blocked domains to Vortex Firewall"
# Reload Vortex if available
[ -x /etc/init.d/vortex-firewall ] && /etc/init.d/vortex-firewall reload
}
# Detailed statistics
detailed_stats() {
echo "============================================"
echo " Cookie Tracker Detailed Statistics"
echo "============================================"
echo ""
echo "--- Cookies by Category ---"
sqlite3 "$DB_PATH" "SELECT category, COUNT(*) as cnt, SUM(count) as total FROM cookies GROUP BY category ORDER BY cnt DESC;" | \
while IFS='|' read -r cat cnt total; do
printf " %-15s %5s cookies, %6s total hits\n" "$cat" "$cnt" "$total"
done
echo ""
echo "--- Top 20 Most Frequent Cookies ---"
sqlite3 "$DB_PATH" "SELECT domain, name, count, category FROM cookies ORDER BY count DESC LIMIT 20;" | \
while IFS='|' read -r d n c cat; do
printf " %-30s %-20s %6s [%s]\n" "${d:0:30}" "${n:0:20}" "$c" "$cat"
done
echo ""
echo "--- Cookie Age Distribution ---"
local now=$(date +%s)
local hour=$((now - 3600))
local day=$((now - 86400))
local week=$((now - 604800))
local last_hour=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE last_seen > $hour;")
local last_day=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE last_seen > $day;")
local last_week=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE last_seen > $week;")
local older=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM cookies WHERE last_seen <= $week;")
printf " Last hour: %5s\n" "$last_hour"
printf " Last day: %5s\n" "$last_day"
printf " Last week: %5s\n" "$last_week"
printf " Older: %5s\n" "$older"
}
# Main command dispatcher
case "${1:-status}" in
status)
show_status "$2"
;;
init)
init_db "$2"
;;
reload)
reload_rules
;;
list)
shift
list_cookies "$@"
;;
show)
show_domain "$2"
;;
classify)
classify_cookie "$2" "$3" "$4"
;;
block)
block_domain "$2" "$3"
;;
unblock)
unblock_domain "$2"
;;
report)
generate_report "$2"
;;
export)
export_csv "$2"
;;
import)
import_trackers "$2"
;;
feed-vortex)
feed_vortex
;;
stats)
detailed_stats
;;
-h|--help|help)
usage
;;
*)
echo "Unknown command: $1"
usage
;;
esac