secubox-openwrt/package/secubox/luci-app-cdn-cache/root/etc/init.d/cdn-cache
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

447 lines
16 KiB
Bash
Executable File

#!/bin/sh /etc/rc.common
# SPDX-License-Identifier: Apache-2.0
# CDN Cache Proxy Init Script - Squid Backend
# Copyright (C) 2025 CyberMind.fr
START=90
STOP=10
USE_PROCD=1
SQUID_CONF="/var/etc/cdn-cache-squid.conf"
CACHE_DIR="/var/cache/cdn-squid"
LOG_DIR="/var/log/cdn-cache"
SSL_DB="/var/lib/ssl_db"
CA_DIR="/etc/squid/ssl"
CA_CERT="$CA_DIR/ca.pem"
CA_KEY="$CA_DIR/ca.key"
validate_section() {
uci_load_validate cdn-cache main "$1" "$2" \
'enabled:bool:0' \
'cache_dir:string:/var/cache/cdn-squid' \
'cache_size:uinteger:2048' \
'max_object_size:uinteger:1024' \
'cache_valid:uinteger:10080' \
'listen_port:port:3128' \
'transparent:bool:1' \
'ssl_bump:bool:0' \
'log_level:string:1'
}
# Generate CA certificate for SSL bumping
generate_ca_cert() {
mkdir -p "$CA_DIR"
if [ ! -f "$CA_CERT" ] || [ ! -f "$CA_KEY" ]; then
logger -t cdn-cache "Generating CA certificate for SSL bumping..."
# Generate CA private key
openssl genrsa -out "$CA_KEY" 4096 2>/dev/null
# Generate self-signed CA certificate
openssl req -new -x509 -days 3650 -key "$CA_KEY" -out "$CA_CERT" \
-subj "/C=FR/ST=Paris/L=Paris/O=SecuBox/OU=CDN-Cache/CN=SecuBox CDN Cache CA" 2>/dev/null
# Set proper permissions
chmod 600 "$CA_KEY"
chmod 644 "$CA_CERT"
chown squid:squid "$CA_KEY" "$CA_CERT" 2>/dev/null
logger -t cdn-cache "CA certificate generated at $CA_CERT"
fi
}
# Initialize SSL certificate database
init_ssl_db() {
if [ ! -d "$SSL_DB/certs" ]; then
logger -t cdn-cache "Initializing SSL certificate database..."
rm -rf "$SSL_DB"
mkdir -p "$SSL_DB"
# Note: Size must be in bytes, not MB
/usr/lib/squid/security_file_certgen -s "$SSL_DB" -M 4194304 -c 2>/dev/null
chown -R squid:squid "$SSL_DB" 2>/dev/null
fi
}
generate_squid_config() {
local cache_dir="$1"
local cache_size="$2"
local max_object="$3"
local cache_valid="$4"
local listen_port="$5"
local transparent="$6"
local ssl_bump="$7"
mkdir -p "$(dirname $SQUID_CONF)"
mkdir -p "$cache_dir"
mkdir -p "$LOG_DIR"
chown squid:squid "$cache_dir" "$LOG_DIR" 2>/dev/null
# Build listen directives
# Need both a regular port (for client configuration) and optionally intercept port
local intercept_port=$((listen_port + 1))
local ssl_bump_port=$((listen_port + 2))
cat > "$SQUID_CONF" << EOF
# CDN Cache Squid Configuration
# Auto-generated by SecuBox - Do not edit manually
# === NETWORK ===
# Regular forward proxy port
http_port $listen_port
EOF
if [ "$transparent" = "1" ]; then
cat >> "$SQUID_CONF" << EOF
# Transparent proxy intercept port (HTTP)
http_port $intercept_port intercept
EOF
fi
# SSL Bump configuration for HTTPS caching
if [ "$ssl_bump" = "1" ] && [ -f "$CA_CERT" ] && [ -f "$CA_KEY" ]; then
cat >> "$SQUID_CONF" << EOF
# SSL Bump (HTTPS intercept) port
https_port $ssl_bump_port intercept ssl-bump \\
cert=$CA_CERT \\
key=$CA_KEY \\
generate-host-certificates=on \\
dynamic_cert_mem_cache_size=4MB
# SSL certificate database (size in bytes)
sslcrtd_program /usr/lib/squid/security_file_certgen -s $SSL_DB -M 4194304
sslcrtd_children 5
# SSL Bump ACLs
acl step1 at_step SslBump1
acl step2 at_step SslBump2
acl step3 at_step SslBump3
# Sites to NOT intercept (security sensitive)
acl ssl_exclude_domains ssl::server_name .paypal.com .stripe.com .braintreegateway.com
acl ssl_exclude_domains ssl::server_name .bank .banking .hsbc.com .chase.com .wellsfargo.com
acl ssl_exclude_domains ssl::server_name .google.com/accounts .accounts.google.com
acl ssl_exclude_domains ssl::server_name .apple.com/id .icloud.com
acl ssl_exclude_domains ssl::server_name .github.com .gitlab.com
# Sites to bump (cacheable content)
acl ssl_bump_domains ssl::server_name .windowsupdate.com .microsoft.com .officecdn.microsoft.com
acl ssl_bump_domains ssl::server_name .steamcontent.com .steampowered.com .steamcdn-a.akamaihd.net
acl ssl_bump_domains ssl::server_name .epicgames.com .epicgames-download1.akamaized.net
acl ssl_bump_domains ssl::server_name .download.nvidia.com .cdn.jsdelivr.net .cdnjs.cloudflare.com
acl ssl_bump_domains ssl::server_name .archive.ubuntu.com .security.ubuntu.com
acl ssl_bump_domains ssl::server_name .downloads.openwrt.org
# SSL bump rules
ssl_bump peek step1 all
ssl_bump splice ssl_exclude_domains
ssl_bump bump ssl_bump_domains
ssl_bump splice all
EOF
fi
cat >> "$SQUID_CONF" << EOF
visible_hostname cdn-cache.secubox.local
# === ACCESS CONTROL ===
acl localnet src 10.0.0.0/8
acl localnet src 172.16.0.0/12
acl localnet src 192.168.0.0/16
acl localnet src fc00::/7
acl localnet src fe80::/10
acl SSL_ports port 443
acl Safe_ports port 80 21 443 70 210 280 488 591 777 1025-65535
acl CONNECT method CONNECT
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localnet
http_access allow localhost
http_access deny all
# === CACHE STORAGE ===
cache_dir ufs $cache_dir $cache_size 16 256
cache_mem 128 MB
maximum_object_size ${max_object} MB
minimum_object_size 0 KB
cache_swap_low 90
cache_swap_high 95
# === CDN CONTENT REFRESH PATTERNS ===
# Windows Updates - cache for 7 days
refresh_pattern -i windowsupdate.com/.*\.(exe|msu|cab|msi)$ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-store ignore-private
refresh_pattern -i download.microsoft.com/.*\.(exe|msu|cab|msi)$ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-store ignore-private
refresh_pattern -i officecdn.microsoft.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
# Linux Package Repositories
refresh_pattern -i (deb|rpm|pkg\.tar\.(gz|xz|zst))$ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i archive\.ubuntu\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i security\.ubuntu\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i dl\.fedoraproject\.org/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i mirror\.centos\.org/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i downloads\.openwrt\.org/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
# Android/Play Store
refresh_pattern -i play\.googleapis\.com/.*\.apk$ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i android\.clients\.google\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
# Steam/Gaming
refresh_pattern -i \.steampowered\.com/depot/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-store ignore-private
refresh_pattern -i steamcdn-a\.akamaihd\.net/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-store ignore-private
refresh_pattern -i cdn\.cloudflare\.steamstatic\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i epicgames-download1\.akamaized\.net/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i origin-a\.akamaihd\.net/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i uplaypc-s-ubisoft\.cdn\.ubi\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
# Apple Updates
refresh_pattern -i swcdn\.apple\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i swscan\.apple\.com/ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
refresh_pattern -i \.itunes\.apple\.com/.*\.(ipa|pkg)$ ${cache_valid} 100% 43200 override-expire override-lastmod reload-into-ims ignore-reload
# Static Content CDNs
refresh_pattern -i \.cloudfront\.net/.*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ 1440 100% 10080 override-expire override-lastmod
refresh_pattern -i cdn\.jsdelivr\.net/ 1440 100% 10080 override-expire override-lastmod
refresh_pattern -i cdnjs\.cloudflare\.com/ 1440 100% 10080 override-expire override-lastmod
refresh_pattern -i ajax\.googleapis\.com/ 1440 100% 10080 override-expire override-lastmod
refresh_pattern -i fonts\.googleapis\.com/ 1440 100% 10080 override-expire override-lastmod
refresh_pattern -i fonts\.gstatic\.com/ 1440 100% 10080 override-expire override-lastmod
# General static assets
refresh_pattern -i \.(js|css)(\?.*)?$ 1440 50% 10080
refresh_pattern -i \.(png|jpg|jpeg|gif|ico|svg|webp)(\?.*)?$ 1440 50% 10080
refresh_pattern -i \.(woff|woff2|ttf|eot|otf)(\?.*)?$ 4320 50% 43200
refresh_pattern -i \.(mp3|mp4|webm|ogg|flac)(\?.*)?$ 1440 50% 10080
refresh_pattern -i \.(zip|tar|gz|bz2|xz|7z|rar)(\?.*)?$ 1440 50% 10080
# Default patterns
refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
refresh_pattern . 0 20% 4320
# === EXCLUSIONS (Never Cache) ===
# Banking/Financial - security
acl nocache_banking dstdomain .paypal.com .stripe.com .braintreegateway.com
acl nocache_banking dstdomain .visa.com .mastercard.com .americanexpress.com
# Video Streaming - too large, DRM protected
acl nocache_streaming dstdomain .netflix.com .nflxvideo.net
acl nocache_streaming dstdomain .youtube.com .googlevideo.com .ytimg.com
acl nocache_streaming dstdomain .twitch.tv .twitchcdn.net
acl nocache_streaming dstdomain .hulu.com .hulustream.com
acl nocache_streaming dstdomain .disneyplus.com .dssott.com
acl nocache_streaming dstdomain .hbomax.com .max.com
acl nocache_streaming dstdomain .primevideo.com .aiv-cdn.net
# Real-time APIs
acl nocache_realtime dstdomain .api.github.com .api.gitlab.com
acl nocache_realtime dstdomain .amazonaws.com .azure.com
cache deny nocache_banking
cache deny nocache_streaming
cache deny nocache_realtime
# === LOGGING ===
access_log daemon:$LOG_DIR/access.log squid
cache_log $LOG_DIR/cache.log
cache_store_log none
debug_options ALL,$log_level
# === API FAILOVER ===
EOF
# Add API failover configuration if enabled
local api_enabled stale_if_error offline_mode collapsed_forwarding connect_timeout read_timeout
api_enabled=$(uci -q get cdn-cache.api_failover.enabled || echo "1")
stale_if_error=$(uci -q get cdn-cache.api_failover.stale_if_error || echo "86400")
offline_mode=$(uci -q get cdn-cache.api_failover.offline_mode || echo "0")
collapsed_forwarding=$(uci -q get cdn-cache.api_failover.collapsed_forwarding || echo "1")
connect_timeout=$(uci -q get cdn-cache.api_failover.connect_timeout || echo "5")
read_timeout=$(uci -q get cdn-cache.api_failover.read_timeout || echo "30")
if [ "$api_enabled" = "1" ]; then
cat >> "$SQUID_CONF" << EOF
# API Failover - serve stale content on backend errors
refresh_pattern -i /api/ 1 20% 4320 stale-if-error=$stale_if_error ignore-no-store
refresh_pattern -i \.json\$ 1 20% 4320 stale-if-error=$stale_if_error
EOF
if [ "$collapsed_forwarding" = "1" ]; then
cat >> "$SQUID_CONF" << EOF
# Collapsed forwarding - combine duplicate requests during backend fetch
collapsed_forwarding on
EOF
fi
if [ "$offline_mode" = "1" ]; then
cat >> "$SQUID_CONF" << EOF
# Offline mode - serve stale content for all requests
offline_mode on
EOF
fi
fi
cat >> "$SQUID_CONF" << EOF
# === PERFORMANCE ===
dns_nameservers 8.8.8.8 8.8.4.4
connect_timeout $connect_timeout seconds
read_timeout $read_timeout seconds
request_timeout 60 seconds
client_lifetime 1 day
half_closed_clients off
pconn_timeout 60 seconds
quick_abort_min 0 KB
quick_abort_max 0 KB
quick_abort_pct 95
negative_ttl 30 seconds
# === CACHE MANAGER ===
cache_mgr secubox@local
cachemgr_passwd none all
# === MISC ===
cache_effective_user squid
coredump_dir /var/spool/squid
pid_filename none
shutdown_lifetime 3 seconds
EOF
}
setup_transparent() {
local listen_port="$1"
local transparent="$2"
local ssl_bump="$3"
# Intercept ports
local intercept_port=$((listen_port + 1))
local ssl_bump_port=$((listen_port + 2))
# Setup firewall redirect rules via UCI
if [ "$transparent" = "1" ]; then
# HTTP redirect rule
uci -q delete firewall.cdn_cache_redirect
uci set firewall.cdn_cache_redirect=redirect
uci set firewall.cdn_cache_redirect.name='CDN-Cache-HTTP'
uci set firewall.cdn_cache_redirect.src='lan'
uci set firewall.cdn_cache_redirect.proto='tcp'
uci set firewall.cdn_cache_redirect.src_dport='80'
uci set firewall.cdn_cache_redirect.dest_port="$intercept_port"
uci set firewall.cdn_cache_redirect.target='DNAT'
uci set firewall.cdn_cache_redirect.enabled='1'
logger -t cdn-cache "HTTP transparent proxy on port $intercept_port"
else
uci -q set firewall.cdn_cache_redirect.enabled='0'
fi
# HTTPS/SSL bump redirect rule
if [ "$ssl_bump" = "1" ] && [ "$transparent" = "1" ]; then
uci -q delete firewall.cdn_cache_ssl_redirect
uci set firewall.cdn_cache_ssl_redirect=redirect
uci set firewall.cdn_cache_ssl_redirect.name='CDN-Cache-HTTPS'
uci set firewall.cdn_cache_ssl_redirect.src='lan'
uci set firewall.cdn_cache_ssl_redirect.proto='tcp'
uci set firewall.cdn_cache_ssl_redirect.src_dport='443'
uci set firewall.cdn_cache_ssl_redirect.dest_port="$ssl_bump_port"
uci set firewall.cdn_cache_ssl_redirect.target='DNAT'
uci set firewall.cdn_cache_ssl_redirect.enabled='1'
logger -t cdn-cache "HTTPS SSL bump on port $ssl_bump_port"
else
uci -q set firewall.cdn_cache_ssl_redirect.enabled='0'
fi
uci commit firewall
/etc/init.d/firewall reload 2>/dev/null
}
remove_transparent() {
# Disable the firewall redirect rules
uci -q set firewall.cdn_cache_redirect.enabled='0'
uci -q set firewall.cdn_cache_ssl_redirect.enabled='0'
uci commit firewall
/etc/init.d/firewall reload 2>/dev/null
}
start_service() {
validate_section main start_cdn_cache
}
start_cdn_cache() {
[ "$2" = 0 ] || {
echo "Validation failed" >&2
return 1
}
[ "$enabled" = "1" ] || {
echo "CDN Cache is disabled"
return 0
}
# Check if Squid is installed
if ! command -v squid >/dev/null 2>&1; then
logger -t cdn-cache "ERROR: Squid is not installed"
echo "Squid is not installed. Install with: opkg install squid"
return 1
fi
# Setup required directories
mkdir -p "$cache_dir" "$LOG_DIR" /var/spool/squid
chown -R squid:squid "$cache_dir" "$LOG_DIR" /var/spool/squid 2>/dev/null
# Setup SSL if enabled
if [ "$ssl_bump" = "1" ]; then
generate_ca_cert
init_ssl_db
fi
generate_squid_config "$cache_dir" "$cache_size" "$max_object_size" \
"$cache_valid" "$listen_port" "$transparent" "$ssl_bump"
# Initialize cache if needed (blocking call, run once)
if [ ! -d "$cache_dir/00" ]; then
logger -t cdn-cache "Initializing Squid cache directory..."
/usr/sbin/squid -f "$SQUID_CONF" -z -N 2>/dev/null
fi
# Setup transparent proxy if enabled
setup_transparent "$listen_port" "$transparent" "$ssl_bump"
# Remove any stale PID file
rm -f /var/run/cdn-cache.pid
procd_open_instance cdn-cache
procd_set_param command /usr/sbin/squid -f "$SQUID_CONF" -sYC -N
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
logger -t cdn-cache "CDN Cache (Squid) started on port $listen_port"
}
stop_service() {
remove_transparent
logger -t cdn-cache "CDN Cache proxy stopped"
}
reload_service() {
# Send reconfigure signal to Squid
if [ -f /var/run/cdn-cache.pid ]; then
kill -HUP $(cat /var/run/cdn-cache.pid) 2>/dev/null
logger -t cdn-cache "CDN Cache configuration reloaded"
else
stop
start
fi
}
service_triggers() {
procd_add_reload_trigger "cdn-cache"
}