diff --git a/package/secubox/luci-app-cdn-cache/root/etc/config/cdn-cache b/package/secubox/luci-app-cdn-cache/root/etc/config/cdn-cache index 7e956f49..03b1bff8 100644 --- a/package/secubox/luci-app-cdn-cache/root/etc/config/cdn-cache +++ b/package/secubox/luci-app-cdn-cache/root/etc/config/cdn-cache @@ -6,6 +6,7 @@ config cdn_cache 'main' option cache_valid '10080' option listen_port '3128' option transparent '1' + option ssl_bump '0' option log_level '1' config cache_policy 'windows_update' diff --git a/package/secubox/luci-app-cdn-cache/root/etc/init.d/cdn-cache b/package/secubox/luci-app-cdn-cache/root/etc/init.d/cdn-cache index 3b978ba9..5299f843 100755 --- a/package/secubox/luci-app-cdn-cache/root/etc/init.d/cdn-cache +++ b/package/secubox/luci-app-cdn-cache/root/etc/init.d/cdn-cache @@ -10,6 +10,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" \ @@ -20,9 +24,45 @@ validate_section() { '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" @@ -30,6 +70,7 @@ generate_squid_config() { local cache_valid="$4" local listen_port="$5" local transparent="$6" + local ssl_bump="$7" mkdir -p "$(dirname $SQUID_CONF)" mkdir -p "$cache_dir" @@ -39,6 +80,7 @@ generate_squid_config() { # 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 @@ -51,11 +93,54 @@ EOF if [ "$transparent" = "1" ]; then cat >> "$SQUID_CONF" << EOF -# Transparent proxy intercept port +# 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 @@ -194,37 +279,53 @@ EOF setup_transparent() { local listen_port="$1" local transparent="$2" + local ssl_bump="$3" - # Intercept port is listen_port + 1 + # Intercept ports local intercept_port=$((listen_port + 1)) + local ssl_bump_port=$((listen_port + 2)) - # Setup firewall redirect rule via UCI + # Setup firewall redirect rules via UCI if [ "$transparent" = "1" ]; then - # Create or update firewall redirect rule + # 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-Transparent' + 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' - uci commit firewall - /etc/init.d/firewall reload 2>/dev/null - logger -t cdn-cache "Transparent proxy enabled on port $intercept_port via firewall" + logger -t cdn-cache "HTTP transparent proxy on port $intercept_port" 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 + + # 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 rule + # 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 } @@ -255,8 +356,14 @@ start_cdn_cache() { 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" + "$cache_valid" "$listen_port" "$transparent" "$ssl_bump" # Initialize cache if needed (blocking call, run once) if [ ! -d "$cache_dir/00" ]; then @@ -265,7 +372,7 @@ start_cdn_cache() { fi # Setup transparent proxy if enabled - setup_transparent "$listen_port" "$transparent" + setup_transparent "$listen_port" "$transparent" "$ssl_bump" # Remove any stale PID file rm -f /var/run/cdn-cache.pid diff --git a/package/secubox/luci-app-cdn-cache/root/usr/libexec/rpcd/luci.cdn-cache b/package/secubox/luci-app-cdn-cache/root/usr/libexec/rpcd/luci.cdn-cache index 4fbaa9a5..91c4f698 100755 --- a/package/secubox/luci-app-cdn-cache/root/usr/libexec/rpcd/luci.cdn-cache +++ b/package/secubox/luci-app-cdn-cache/root/usr/libexec/rpcd/luci.cdn-cache @@ -22,6 +22,7 @@ STATS_FILE="/var/run/cdn-cache-stats.json" LOG_DIR="/var/log/cdn-cache" LOG_FILE="$LOG_DIR/cache.log" SQUID_CONF="/var/etc/cdn-cache-squid.conf" +CA_CERT="/etc/squid/ssl/ca.pem" # Initialize stats file if not exists init_stats() { @@ -84,6 +85,7 @@ get_status() { local listen_port=$(uci -q get cdn-cache.main.listen_port || echo "3128") local transparent=$(uci -q get cdn-cache.main.transparent || echo "1") + local ssl_bump=$(uci -q get cdn-cache.main.ssl_bump || echo "0") local max_size=$(uci -q get cdn-cache.main.cache_size || echo "2048") # Get Squid version if installed @@ -92,6 +94,10 @@ get_status() { squid_version=$(squid -v 2>/dev/null | head -1 | sed 's/.*Version //' | cut -d' ' -f1) fi + # Check if CA cert exists + local ca_cert_exists=0 + [ -f "$CA_CERT" ] && ca_cert_exists=1 + json_init json_add_string "version" "$PKG_VERSION" json_add_string "backend" "$backend" @@ -106,6 +112,8 @@ get_status() { json_add_int "max_size_mb" "$max_size" json_add_int "listen_port" "$listen_port" json_add_boolean "transparent" "$transparent" + json_add_boolean "ssl_bump" "$ssl_bump" + json_add_boolean "ca_cert_exists" "$ca_cert_exists" json_add_string "cache_dir" "$CACHE_DIR" json_dump } @@ -683,12 +691,54 @@ EOF # Restart service do_restart() { /etc/init.d/cdn-cache restart - + json_init json_add_boolean "success" 1 json_dump } +# Get CA certificate for download +get_ca_cert() { + json_init + + if [ -f "$CA_CERT" ]; then + local cert_content=$(cat "$CA_CERT" | base64 -w0) + local cert_fingerprint=$(openssl x509 -in "$CA_CERT" -noout -fingerprint -sha256 2>/dev/null | sed 's/.*=//') + local cert_subject=$(openssl x509 -in "$CA_CERT" -noout -subject 2>/dev/null | sed 's/subject=//') + local cert_expires=$(openssl x509 -in "$CA_CERT" -noout -enddate 2>/dev/null | sed 's/notAfter=//') + + json_add_boolean "success" 1 + json_add_string "certificate" "$cert_content" + json_add_string "fingerprint" "$cert_fingerprint" + json_add_string "subject" "$cert_subject" + json_add_string "expires" "$cert_expires" + else + json_add_boolean "success" 0 + json_add_string "error" "CA certificate not found" + fi + + json_dump +} + +# Enable/disable SSL bump +set_ssl_bump() { + local enabled="${1:-0}" + + uci set cdn-cache.main.ssl_bump="$enabled" + uci commit cdn-cache + + if [ "$enabled" = "1" ]; then + logger -t cdn-cache "SSL bump enabled - restart required" + else + logger -t cdn-cache "SSL bump disabled - restart required" + fi + + json_init + json_add_boolean "success" 1 + json_add_boolean "restart_required" 1 + json_dump +} + # Main dispatcher case "$1" in list) @@ -767,6 +817,11 @@ case "$1" in json_close_object json_add_object "restart" json_close_object + json_add_object "get_ca_cert" + json_close_object + json_add_object "set_ssl_bump" + json_add_boolean "enabled" false + json_close_object json_dump ;; call) @@ -872,6 +927,13 @@ case "$1" in ;; clear_stats) clear_stats ;; restart) do_restart ;; + get_ca_cert) get_ca_cert ;; + set_ssl_bump) + read -r input + json_load "$input" + json_get_var enabled enabled 0 + set_ssl_bump "$enabled" + ;; *) echo '{"error":"Unknown method"}' ;; esac ;;