#!/bin/sh # # entrypoint.sh -- container start-up for the Squid SSL-bump caching proxy. # # 1. Render /etc/squid/squid.conf from squid.conf.tmpl (@VAR@ -> env). # 2. Generate the cache-policy include from cache-domains.txt / .regex and the # CACHE_ONLY_LISTED toggle (boost ACLs, storage gate, per-domain TTLs). # 3. Stage the CA into a squid-readable tmpfs copy (host key stays 0600 root). # 4. Init the dynamic-cert DB + cache_dir structure (idempotent). # 5. Validate the config, then exec squid in the foreground. # # Runs as root (needs chown/install); squid then drops to cache_effective_user. set -eu TMPL=/etc/squid/squid.conf.tmpl CONF=/etc/squid/squid.conf GEN_DIR=/etc/squid/conf.d GEN="$GEN_DIR/cache-policy.conf" SSL_SRC=/etc/squid/ssl SSL_RUN=/run/squid SSL_DB=/var/lib/squid/ssl_db CACHE_DOMAINS=/etc/squid/cache-domains.txt CACHE_REGEX=/etc/squid/cache-domains.regex # Tunables -- docker-compose passes these from .env; defaults keep the image # runnable on its own for a smoke test. : "${TRUSTED_CIDR:=127.0.0.1/32}" : "${CACHE_SIZE_MB:=5000}" : "${MAX_OBJECT_SIZE_MB:=256}" : "${CACHE_MEM_MB:=256}" : "${DYNAMIC_CERT_MEM_MB:=8}" : "${CACHE_ONLY_LISTED:=0}" : "${VISIBLE_HOSTNAME:=squid-proxy}" log() { printf '[entrypoint] %s\n' "$*" >&2; } # A list file "has entries" if it holds >=1 non-blank, non-comment line. has_entries() { [ -f "$1" ] && grep -qE '^[[:space:]]*[^#[:space:]]' "$1"; } # --------------------------------------------------------------------------- # 1. Render the static config from the template (@VAR@ placeholders). # --------------------------------------------------------------------------- sed \ -e "s|@TRUSTED_CIDR@|${TRUSTED_CIDR}|g" \ -e "s|@CACHE_SIZE_MB@|${CACHE_SIZE_MB}|g" \ -e "s|@MAX_OBJECT_SIZE_MB@|${MAX_OBJECT_SIZE_MB}|g" \ -e "s|@CACHE_MEM_MB@|${CACHE_MEM_MB}|g" \ -e "s|@DYNAMIC_CERT_MEM_MB@|${DYNAMIC_CERT_MEM_MB}|g" \ -e "s|@VISIBLE_HOSTNAME@|${VISIBLE_HOSTNAME}|g" \ "$TMPL" > "$CONF" # --------------------------------------------------------------------------- # 2. Generate the cache-policy include from the domain lists + toggle. # Storage gate runs first (whether to store); per-domain refresh_patterns # only tune freshness of what survived the gate. # --------------------------------------------------------------------------- mkdir -p "$GEN_DIR" { echo "# AUTO-GENERATED by entrypoint.sh at container start -- do not edit." echo "# Source: cache-domains.txt / cache-domains.regex + CACHE_ONLY_LISTED." echo # Boost ACLs are only emitted when their backing file is non-empty, else # squid would error on an empty ACL. exc="" if has_entries "$CACHE_DOMAINS"; then echo 'acl boost dstdomain "/etc/squid/cache-domains.txt"' exc="$exc !boost" fi if has_entries "$CACHE_REGEX"; then echo 'acl boost_re dstdom_regex "/etc/squid/cache-domains.regex"' exc="$exc !boost_re" fi cat <<'ACLS' acl has_query urlpath_regex \? acl dyn_ext urlpath_regex (/cgi-bin/|\.(cgi|php|aspx?|jsp)$) acl html_ext urlpath_regex \.html?(\?|$) acl html_ct rep_mime_type -i ^text/html # Storage gate: never store HTML (by ext or content-type) or dynamic content. cache deny html_ext cache deny dyn_ext ACLS # Query strings are dynamic EXCEPT on boosted domains (versioned static # assets like app.js?v=123 should still cache there). echo "cache deny has_query${exc}" echo 'store_miss deny html_ct' if [ "$CACHE_ONLY_LISTED" = "1" ]; then echo echo "# CACHE_ONLY_LISTED=1: store ONLY the listed domains." if has_entries "$CACHE_DOMAINS"; then echo 'cache allow boost'; fi if has_entries "$CACHE_REGEX"; then echo 'cache allow boost_re'; fi echo 'cache deny all' fi if has_entries "$CACHE_DOMAINS"; then echo echo "# Boost: long TTL + force-cache for each listed wildcard domain." grep -E '^[[:space:]]*[^#[:space:]]' "$CACHE_DOMAINS" | while read -r dom _rest; do # ".example.com" / "example.com" -> "example[.]com" ([.] matches a # literal dot portably -- avoids sed backslash-escaping quirks). d=$(printf '%s' "$dom" | sed 's/^\.//; s/\./[.]/g') printf 'refresh_pattern -i %s 1440 100%% 43200 override-expire ignore-private ignore-no-store override-lastmod\n' \ "^https?://([^/]+[.])?${d}/" done fi } > "$GEN" # --------------------------------------------------------------------------- # 3. Stage the CA into a squid-readable tmpfs copy. The host key is 0600 root # (mounted read-only); root copies it to /run owned by squid so the signer # can read it without loosening the host file. # --------------------------------------------------------------------------- if [ ! -f "$SSL_SRC/squid-ca-cert.pem" ] || [ ! -f "$SSL_SRC/squid-ca-key.pem" ]; then log "FATAL: CA missing in $SSL_SRC (need squid-ca-cert.pem + squid-ca-key.pem)." exit 1 fi install -d -m 0710 -o squid -g squid "$SSL_RUN" install -m 0444 -o squid -g squid "$SSL_SRC/squid-ca-cert.pem" "$SSL_RUN/ca-cert.pem" install -m 0400 -o squid -g squid "$SSL_SRC/squid-ca-key.pem" "$SSL_RUN/ca-key.pem" # --------------------------------------------------------------------------- # 4. Init the dynamic-cert DB + cache_dir structure (idempotent). # --------------------------------------------------------------------------- if [ ! -s "$SSL_DB/index.txt" ]; then log "Initializing TLS cert DB at $SSL_DB..." find "$SSL_DB" -mindepth 1 -delete 2>/dev/null || true /usr/lib/squid/security_file_certgen -c -s "$SSL_DB" -M "${DYNAMIC_CERT_MEM_MB}MB" fi chown -R squid:squid "$SSL_DB" /var/cache/squid /var/log/squid 2>/dev/null || true if [ ! -d /var/cache/squid/00 ]; then log "Initializing cache_dir structure..." squid -f "$CONF" -z --foreground || squid -f "$CONF" -z || true fi # --------------------------------------------------------------------------- # 5. Validate the rendered + generated config, then hand off to squid. # --------------------------------------------------------------------------- squid -k parse -f "$CONF" log "Starting squid (visible_hostname=${VISIBLE_HOSTNAME})..." exec squid -N -f "$CONF" "$@"