- Generate CA certificate for SSL bumping - Initialize SSL certificate database with security_file_certgen - Selective SSL bump: only cache-worthy domains (Windows Update, Steam, etc.) - Exclude security-sensitive sites (banking, Google accounts, etc.) - Proper firewall integration for both HTTP and HTTPS redirect - RPCD methods for CA cert download and SSL bump control Ports: - 3128: Forward proxy - 3129: HTTP transparent intercept - 3130: HTTPS SSL bump intercept Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
409 lines
14 KiB
Bash
Executable File
409 lines
14 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
|
|
|
|
# === 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"
|
|
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"
|
|
}
|