diff --git a/deployments/squid/README.md b/deployments/squid/README.md index 7c3f4cb..d9c45e7 100644 --- a/deployments/squid/README.md +++ b/deployments/squid/README.md @@ -44,6 +44,15 @@ The deploy is idempotent. On first run it builds the local image, generates the CA into `ssl/` (never overwritten), seeds `.env`, registers the port with the host firewall if present, and brings the stack up. +On a **re-run**, an existing `.env` is kept — except for values you pass +explicitly that run (`TRUSTED_CIDR`, `BIND_ADDR`, `PROXY_PORT`, `CACHE_SIZE_MB`, +`CACHE_ONLY_LISTED`), which are written through and logged. That matters because +Compose reads the shell environment *before* `.env`: without the write-through, a +re-run that narrowed `BIND_ADDR` would apply only to that run, and the next plain +`docker compose up -d` — which has no such environment — would fall back to the +old `.env` and republish the intercepting proxy on `0.0.0.0`. Values you do not +pass are left untouched, so hand-edits to `.env` survive. + ## Point clients at it ```bash diff --git a/deployments/squid/deploy.sh b/deployments/squid/deploy.sh index f15db96..fe2161e 100644 --- a/deployments/squid/deploy.sh +++ b/deployments/squid/deploy.sh @@ -6,7 +6,9 @@ # 1. Installs docker + compose if missing. # 2. Lays down the stack files in $STACK_DIR and builds the local image. # 3. Generates the TLS interception CA on first run (never overwritten). -# 4. Generates .env on first run; existing .env is never overwritten. +# 4. Generates .env on first run. An existing .env is kept, except that values +# passed explicitly to a re-run are written through (they are access +# controls -- a stale .env is what a later `docker compose up -d` reads). # 5. Prompts for required values not preset (TRUSTED_CIDR). # 6. Registers the proxy port with the host firewall if present. # 7. Brings the stack up and waits for health. @@ -35,6 +37,16 @@ set -euo pipefail : "${FORCE:=0}" : "${SKIP_PROMPTS:=0}" # non-interactive: require values via env, no prompts [[ "$SKIP_PROMPTS" == "1" ]] && FORCE=1 +# Runtime keys the operator supplies and that compose interpolates. Which of them +# actually arrived in this script's ENVIRONMENT has to be recorded BEFORE the +# ":=" defaults below, which make an unset variable look supplied -- PROXY_PORT +# especially, since its default is a non-empty 3128. +RUNTIME_KEYS=(TRUSTED_CIDR BIND_ADDR PROXY_PORT CACHE_SIZE_MB CACHE_ONLY_LISTED) +KEYS_FROM_ENV=() +for _k in "${RUNTIME_KEYS[@]}"; do + [[ -n "${!_k+x}" ]] && KEYS_FROM_ENV+=("$_k") +done + : "${TRUSTED_CIDR:=}" : "${BIND_ADDR:=}" : "${PROXY_PORT:=3128}" @@ -201,7 +213,23 @@ if [[ ! -f "$ENV_FILE" ]]; then set_env CA_O "$CA_O" set_env SQUID_IMAGE_TAG "$SQUID_IMAGE_TAG" else - log ".env exists; leaving it alone." + # Keep the file, but do NOT let it drift from what the operator passed this + # run. compose reads the shell environment before .env, so an exported value + # silently wins now while .env still holds the old one -- and a later plain + # `docker compose up -d` (the documented update path) has no such environment + # and falls back to .env. That is how a deliberately narrowed BIND_ADDR + # reverts to 0.0.0.0 on the next image update, republishing an intercepting + # proxy on every interface. These keys are access controls, so write them + # through and say so; anything not passed this run is left untouched. + log ".env exists; keeping it (only values passed this run are updated)." + for _k in ${KEYS_FROM_ENV[@]+"${KEYS_FROM_ENV[@]}"}; do + _cur=$(sed -n "s/^${_k}=//p" "$ENV_FILE" | tail -n1) + _new="${!_k}" + if [[ "$_cur" != "$_new" ]]; then + set_env "$_k" "$_new" + log " ${_k}: ${_cur:-} -> ${_new:-}" + fi + done fi # Validate required value landed.