feat(cdn-cache): Add MITM SSL bump support for HTTPS caching
- 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>
This commit is contained in:
parent
78f4fe4962
commit
1b6e9881c9
@ -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'
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
;;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user