#!/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" 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' \ 'log_level:string:1' } 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" 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)) 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_port $intercept_port intercept 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 # === PERFORMANCE === dns_nameservers 8.8.8.8 8.8.4.4 connect_timeout 30 seconds read_timeout 90 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 5 minutes # === 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" # Intercept port is listen_port + 1 local intercept_port=$((listen_port + 1)) # Setup firewall redirect rule via UCI if [ "$transparent" = "1" ]; then # Create or update firewall 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-Transparent' 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' uci commit firewall /etc/init.d/firewall reload 2>/dev/null logger -t cdn-cache "Transparent proxy enabled on port $intercept_port via firewall" else # Disable transparent redirect uci -q set firewall.cdn_cache_redirect.enabled='0' uci commit firewall /etc/init.d/firewall reload 2>/dev/null logger -t cdn-cache "Transparent proxy disabled" fi } remove_transparent() { # Disable the firewall redirect rule uci -q set firewall.cdn_cache_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 generate_squid_config "$cache_dir" "$cache_size" "$max_object_size" \ "$cache_valid" "$listen_port" "$transparent" # 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" # 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" }