The set_env/env_set helper escaped its value with
`esc=${val//\/\\}; esc=${esc//|/\|}; esc=${esc//&/\&}` and then
interpolated it into `sed -i -e "s|^KEY=.*|KEY=${esc}|"`. The escaping does
not do what it looks like. Tested on bash 5.2:
set_env K 'a&b' -> K=aK=seedb (sed expanded & to the whole match)
set_env K 'a|b' -> sed: unknown option to `s' (rc!=0, aborts under set -e)
So any value containing & is silently corrupted and any value containing the
s||| delimiter kills the run. That is reachable: headscale writes
OIDC_CLIENT_SECRET through this, pocket-id writes REDIRECT_URL, copyparty
writes DATA_DIR. A generated secret or a URL query string hits both cases.
The copies in headscale and pocket-id were additionally mangled when they
were introduced -- `${val//\/\}` (pattern `\/`, a literal SLASH) and a raw
newline inside `printf '%s=%s\n'`. The mangled form is a no-op rather than a
corrupter, so the practical failure mode was the same as the original.
Replace all of them with an awk rewrite that passes the key and value through
the ENVIRONMENT, so the value is never parsed as part of a script and needs no
escaping at all. ENVIRON and index() are POSIX, so busybox awk handles them.
Output goes to a temp file and is copied back with `cat >`, which preserves the
original mode and owner -- a .env holding secrets stays 0600. If awk fails,
set -e aborts before .env is touched, which `sed -i` could not promise.
Verified against plain, a&b, a|b, a\b, p@ss&w|rd\x, R&D, a URL with a query
string, s/foo/bar/, a trailing space and the empty string; plus the
append-when-key-absent path, the file-does-not-exist path, non-target lines
left intact, no line-count drift, and mode preservation.
copyparty/update.sh and the rebuilt copyparty payload are included because
update.sh is embedded; regenerated with build.sh.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
494 lines
25 KiB
Bash
494 lines
25 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# deploy.sh -- deploy the Squid SSL-bump caching forward proxy.
|
|
#
|
|
# What this does:
|
|
# 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. 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.
|
|
#
|
|
# Role: this is a FORWARD proxy, not an inbound web service -- there is no
|
|
# public hostname or Let's Encrypt cert. Clients set HTTP(S)_PROXY to this host
|
|
# and trust the generated CA. Only run it on networks/devices you own.
|
|
#
|
|
# Idempotent: re-run to apply config changes / rebuild the image.
|
|
#
|
|
# Self-contained: the compose file, Dockerfile, entrypoint, config template and
|
|
# domain lists are embedded as a base64 tar.gz at the bottom of this file.
|
|
# Rebuild with build.sh after editing the loose source files.
|
|
#
|
|
# Usage:
|
|
# bash deploy.sh # interactive prompts
|
|
# TRUSTED_CIDR=10.0.0.0/8 BIND_ADDR=100.64.0.1 \
|
|
# SKIP_PROMPTS=1 bash deploy.sh # non-interactive
|
|
# STACK_DIR=/opt/squid bash deploy.sh
|
|
# SKIP_DOCKER_INSTALL=1 bash deploy.sh
|
|
|
|
set -euo pipefail
|
|
|
|
: "${STACK_DIR:=/srv/squid}"
|
|
: "${SKIP_DOCKER_INSTALL:=0}"
|
|
: "${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}"
|
|
: "${CACHE_SIZE_MB:=}"
|
|
: "${CACHE_ONLY_LISTED:=}"
|
|
: "${CA_CN:=Squid Proxy CA}"
|
|
: "${CA_O:=automations}"
|
|
: "${CA_DAYS:=3650}"
|
|
: "${SQUID_IMAGE_TAG:=automations/squid:latest}"
|
|
|
|
log() { printf '\033[1;32m[+]\033[0m %s\n' "$*"; }
|
|
warn() { printf '\033[1;33m[!]\033[0m %s\n' "$*" >&2; }
|
|
die() { printf '\033[1;31m[x]\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
[[ $EUID -eq 0 ]] || die "Run as root."
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# OS detection + Docker install (Alpine / Debian / Alma). This deploy.sh is
|
|
# self-contained (scp'd standalone), so the OS logic is inlined here instead
|
|
# of sourced from scripts/oslib.sh.
|
|
# ---------------------------------------------------------------------------
|
|
osfam() {
|
|
local id="" like=""
|
|
if [[ -r /etc/os-release ]]; then
|
|
id="$(. /etc/os-release 2>/dev/null && echo "${ID:-}")"
|
|
like="$(. /etc/os-release 2>/dev/null && echo "${ID_LIKE:-}")"
|
|
fi
|
|
case " $id $like " in
|
|
*" alpine "*) echo alpine ;;
|
|
*" debian "*|*" ubuntu "*) echo debian ;;
|
|
*" rhel "*|*" fedora "*|*" centos "*) echo rhel ;;
|
|
*) echo "${id:-unknown}" ;;
|
|
esac
|
|
}
|
|
|
|
install_docker() {
|
|
if command -v docker >/dev/null 2>&1; then
|
|
log "Docker already installed: $(docker --version)"
|
|
else
|
|
log "Installing Docker (OS: $(osfam))..."
|
|
case "$(osfam)" in
|
|
alpine) apk add -q docker docker-cli-compose openrc ;;
|
|
debian|rhel) command -v curl >/dev/null 2>&1 || \
|
|
{ command -v apt-get >/dev/null 2>&1 && apt-get install -y -qq curl; } || \
|
|
{ command -v dnf >/dev/null 2>&1 && dnf install -y -q curl; }
|
|
curl -fsSL https://get.docker.com | sh ;;
|
|
*) die "Unsupported OS for auto Docker install. Set SKIP_DOCKER_INSTALL=1 and install Docker yourself." ;;
|
|
esac
|
|
fi
|
|
if command -v rc-update >/dev/null 2>&1; then
|
|
rc-update add docker default >/dev/null 2>&1 || true
|
|
rc-service docker status >/dev/null 2>&1 || rc-service docker start
|
|
elif command -v systemctl >/dev/null 2>&1; then
|
|
systemctl enable --now docker >/dev/null 2>&1 || systemctl start docker || true
|
|
fi
|
|
}
|
|
|
|
open_proxy_port() {
|
|
# Register the proxy port. Prefer the host firewall (harden-firewall.sh)
|
|
# when present; else ufw/firewalld if active.
|
|
#
|
|
# NOTE: a Docker-published port reaches the host through nat/FORWARD and
|
|
# BYPASSES the INPUT firewall, so this is belt-and-braces -- the real access
|
|
# gate is Squid's http_access (TRUSTED_CIDR) plus pinning BIND_ADDR to a
|
|
# trusted interface. The ports.d format is port-only (no source CIDR), so
|
|
# restrict the source via BIND_ADDR / upstream firewalling, not here.
|
|
local port="${PROXY_PORT:-3128}"
|
|
if [[ -d /etc/firewall/ports.d && -x /usr/local/sbin/firewall-apply ]]; then
|
|
log "Registering ${port}/tcp with host firewall..."
|
|
printf '%s/tcp\n' "$port" > /etc/firewall/ports.d/squid.rule
|
|
/usr/local/sbin/firewall-apply
|
|
elif command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q '^Status: active'; then
|
|
log "ufw active -- allowing ${port}/tcp..."
|
|
ufw allow "${port}/tcp" >/dev/null
|
|
elif command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then
|
|
log "firewalld active -- allowing ${port}/tcp..."
|
|
firewall-cmd -q --add-port="${port}/tcp" --permanent
|
|
firewall-cmd -q --reload
|
|
fi
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Extract embedded archive
|
|
# ----------------------------------------------------------------------------
|
|
SCRIPT_DIR=$(mktemp -d -t squid-deploy.XXXXXX)
|
|
trap 'rm -rf "$SCRIPT_DIR"' EXIT
|
|
|
|
extract_archive() {
|
|
grep -a -A 9999999 '^__ARCHIVE_BELOW__$' "$0" \
|
|
| tail -n +2 \
|
|
| base64 -d \
|
|
| tar -xz -C "$SCRIPT_DIR"
|
|
}
|
|
|
|
if grep -q -a '^__ARCHIVE_BELOW__$' "$0"; then
|
|
log "Extracting embedded deployment files..."
|
|
extract_archive
|
|
else
|
|
die "No embedded archive found. Run build.sh to embed deployment files."
|
|
fi
|
|
|
|
EMBEDDED=(docker-compose.yml Dockerfile entrypoint.sh squid.conf.tmpl
|
|
splice-domains.txt cache-domains.txt cache-domains.regex .env.example)
|
|
for f in "${EMBEDDED[@]}"; do
|
|
[[ -f "$SCRIPT_DIR/$f" ]] || die "Embedded archive missing $f"
|
|
done
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Prompt for required vars
|
|
# ----------------------------------------------------------------------------
|
|
prompt() {
|
|
local varname="$1" message="$2"
|
|
local -n ref="$varname"
|
|
if [[ -z "${ref:-}" ]]; then
|
|
[[ "$SKIP_PROMPTS" == "1" ]] && die "$varname required (set it in the environment; running with SKIP_PROMPTS=1)."
|
|
read -r -p "$message: " ref
|
|
[[ -n "$ref" ]] || die "$varname required."
|
|
fi
|
|
}
|
|
|
|
prompt TRUSTED_CIDR "Trusted client CIDR(s) allowed to use the proxy (e.g. 100.64.0.0/10)"
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Docker + firewall
|
|
# ----------------------------------------------------------------------------
|
|
if [[ "$SKIP_DOCKER_INSTALL" != "1" ]]; then
|
|
install_docker
|
|
fi
|
|
open_proxy_port
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Stack directory + files
|
|
# ----------------------------------------------------------------------------
|
|
log "Setting up $STACK_DIR..."
|
|
install -d -m 0750 "$STACK_DIR"
|
|
install -m 0644 "$SCRIPT_DIR/docker-compose.yml" "$STACK_DIR/docker-compose.yml"
|
|
install -m 0644 "$SCRIPT_DIR/Dockerfile" "$STACK_DIR/Dockerfile"
|
|
install -m 0755 "$SCRIPT_DIR/entrypoint.sh" "$STACK_DIR/entrypoint.sh"
|
|
install -m 0644 "$SCRIPT_DIR/squid.conf.tmpl" "$STACK_DIR/squid.conf.tmpl"
|
|
install -m 0644 "$SCRIPT_DIR/splice-domains.txt" "$STACK_DIR/splice-domains.txt"
|
|
install -m 0644 "$SCRIPT_DIR/cache-domains.txt" "$STACK_DIR/cache-domains.txt"
|
|
install -m 0644 "$SCRIPT_DIR/cache-domains.regex" "$STACK_DIR/cache-domains.regex"
|
|
|
|
ENV_FILE="$STACK_DIR/.env"
|
|
set_env() { # <KEY> <value>: update KEY in .env, or append if absent
|
|
# The value goes through the ENVIRONMENT, never interpolated into a sed
|
|
# script. Interpolating it corrupts any value containing & (sed expands it to
|
|
# the whole match) and aborts the run on one containing the s||| delimiter --
|
|
# which is reachable for an OIDC secret, a password or a URL query string.
|
|
local key="$1" val="$2" tmp
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
printf '%s=%s
|
|
' "$key" "$val" >> "$ENV_FILE"
|
|
return 0
|
|
fi
|
|
tmp="$(mktemp)"
|
|
_SE_KEY="$key" _SE_VAL="$val" awk '
|
|
BEGIN { k = ENVIRON["_SE_KEY"]; v = ENVIRON["_SE_VAL"]; seen = 0 }
|
|
!seen && index($0, k "=") == 1 { print k "=" v; seen = 1; next }
|
|
{ print }
|
|
END { if (!seen) print k "=" v }
|
|
' "$ENV_FILE" > "$tmp"
|
|
cat "$tmp" > "$ENV_FILE" # rewrite in place: keeps the original mode/owner
|
|
rm -f "$tmp"
|
|
}
|
|
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
log "Seeding $ENV_FILE..."
|
|
install -m 0600 "$SCRIPT_DIR/.env.example" "$ENV_FILE"
|
|
set_env TRUSTED_CIDR "$TRUSTED_CIDR"
|
|
if [[ -n "${BIND_ADDR:-}" ]]; then set_env BIND_ADDR "$BIND_ADDR"; fi
|
|
if [[ -n "${PROXY_PORT:-}" ]]; then set_env PROXY_PORT "$PROXY_PORT"; fi
|
|
if [[ -n "${CACHE_SIZE_MB:-}" ]]; then set_env CACHE_SIZE_MB "$CACHE_SIZE_MB"; fi
|
|
if [[ -n "${CACHE_ONLY_LISTED:-}" ]]; then set_env CACHE_ONLY_LISTED "$CACHE_ONLY_LISTED"; fi
|
|
set_env CA_CN "$CA_CN"
|
|
set_env CA_O "$CA_O"
|
|
set_env SQUID_IMAGE_TAG "$SQUID_IMAGE_TAG"
|
|
else
|
|
# 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:-<unset>} -> ${_new:-<empty>}"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Validate required value landed.
|
|
grep -E "^TRUSTED_CIDR=.+$" "$ENV_FILE" >/dev/null || die "TRUSTED_CIDR missing in $ENV_FILE."
|
|
|
|
# Use the image tag actually recorded in .env (keeps build + CA-gen in sync).
|
|
IMAGE=$(grep -E '^SQUID_IMAGE_TAG=' "$ENV_FILE" | head -n1 | cut -d= -f2-)
|
|
IMAGE=${IMAGE:-$SQUID_IMAGE_TAG}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Confirm (interception is a big deal)
|
|
# ----------------------------------------------------------------------------
|
|
if [[ "$FORCE" != "1" ]]; then
|
|
cat <<EOF
|
|
|
|
About to build the Squid image and start an SSL-bump forward proxy from
|
|
$STACK_DIR.
|
|
|
|
This generates a local CA and INTERCEPTS TLS for every client that trusts it
|
|
(except spliced domains). Only do this on networks/devices you own and are
|
|
authorized to inspect. Clients reach it at: http://<this-host>:${PROXY_PORT}
|
|
|
|
Continue? [y/N]
|
|
EOF
|
|
read -r ans
|
|
[[ "${ans,,}" == "y" || "${ans,,}" == "yes" ]] || { warn "Aborted."; exit 0; }
|
|
fi
|
|
|
|
cd "$STACK_DIR"
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Build image, then generate the CA (needs the built image's openssl)
|
|
# ----------------------------------------------------------------------------
|
|
log "Building image $IMAGE..."
|
|
docker compose build
|
|
|
|
SSL_DIR="$STACK_DIR/ssl"
|
|
install -d -m 0750 "$SSL_DIR"
|
|
if [[ ! -f "$SSL_DIR/squid-ca-key.pem" || ! -f "$SSL_DIR/squid-ca-cert.pem" ]]; then
|
|
log "Generating TLS interception CA (one-time; CN=${CA_CN})..."
|
|
docker run --rm -v "$SSL_DIR:/out" --entrypoint openssl "$IMAGE" \
|
|
req -x509 -newkey rsa:4096 -sha256 -days "$CA_DAYS" -nodes \
|
|
-keyout /out/squid-ca-key.pem -out /out/squid-ca-cert.pem \
|
|
-subj "/CN=${CA_CN}/O=${CA_O}" \
|
|
-addext "basicConstraints=critical,CA:TRUE" \
|
|
-addext "keyUsage=critical,keyCertSign,cRLSign"
|
|
chmod 0600 "$SSL_DIR/squid-ca-key.pem"
|
|
chmod 0644 "$SSL_DIR/squid-ca-cert.pem"
|
|
log "CA created. Distribute $SSL_DIR/squid-ca-cert.pem to client trust stores."
|
|
else
|
|
log "CA already present in $SSL_DIR; leaving it."
|
|
fi
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Bring up the stack
|
|
# ----------------------------------------------------------------------------
|
|
log "Starting stack..."
|
|
docker compose up -d --remove-orphans
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# Wait for health
|
|
# ----------------------------------------------------------------------------
|
|
log "Waiting for squid to become healthy (up to 120s)..."
|
|
deadline=$(( $(date +%s) + 120 ))
|
|
while (( $(date +%s) < deadline )); do
|
|
status=$(docker compose ps --format '{{.Service}} {{.Health}}' 2>/dev/null || true)
|
|
unhealthy=$(echo "$status" | awk '$2 != "healthy" && $2 != "" {print $1}')
|
|
if [[ -z "$unhealthy" && -n "$status" ]]; then
|
|
log "Squid healthy."
|
|
break
|
|
fi
|
|
sleep 5
|
|
done
|
|
|
|
echo
|
|
log "Stack status:"
|
|
docker compose ps
|
|
echo
|
|
CERT="$SSL_DIR/squid-ca-cert.pem"
|
|
cat <<EOF
|
|
================================================================
|
|
DEPLOYED -- Squid SSL-bump caching forward proxy
|
|
|
|
Proxy: http://<this-host>:${PROXY_PORT}
|
|
Stack dir: ${STACK_DIR}
|
|
CA cert: ${CERT}
|
|
|
|
1. Point clients at the proxy:
|
|
export http_proxy=http://<this-host>:${PROXY_PORT}
|
|
export https_proxy=http://<this-host>:${PROXY_PORT}
|
|
(apk: /etc/apk/repositories via http_proxy; apt: Acquire::http(s)::Proxy;
|
|
dnf: proxy= in /etc/dnf/dnf.conf)
|
|
|
|
2. Trust the CA on each client (so bumped HTTPS validates):
|
|
Debian/Ubuntu: cp ${CERT##*/} /usr/local/share/ca-certificates/squid-ca.crt && update-ca-certificates
|
|
Alpine: cp ${CERT##*/} /usr/local/share/ca-certificates/squid-ca.crt && update-ca-certificates
|
|
Alma/RHEL: cp ${CERT##*/} /etc/pki/ca-trust/source/anchors/squid-ca.pem && update-ca-trust
|
|
Browsers/Java keep their own trust stores -- import there too if needed.
|
|
|
|
3. Tune behavior, then re-render (lists are bind-mounted):
|
|
${STACK_DIR}/cache-domains.txt domains to cache hard (wildcards)
|
|
${STACK_DIR}/cache-domains.regex glob/regex cache patterns (optional)
|
|
${STACK_DIR}/splice-domains.txt domains to NOT decrypt
|
|
edit, then: cd ${STACK_DIR} && docker compose restart
|
|
|
|
Logs / cache stats:
|
|
docker compose logs -f
|
|
docker exec squid squid -k parse # validate config
|
|
docker exec squid tail -f /var/log/squid/access.log # TCP_HIT / TCP_MISS
|
|
|
|
Manage:
|
|
docker compose pull >/dev/null; docker compose build && docker compose up -d # update
|
|
docker compose down # stop, keep cache + CA
|
|
docker compose down -v # stop, WIPE cache + leaf-cert DB (CA on host survives)
|
|
|
|
SECURITY: keep ${SSL_DIR}/squid-ca-key.pem secret (it can MITM every client
|
|
that trusts it). It is 0600 root and never leaves this host. Only intercept
|
|
traffic on networks/devices you own.
|
|
|
|
Or just re-run this script -- it's idempotent.
|
|
================================================================
|
|
EOF
|
|
|
|
# IMPORTANT: do not put any code below this exit. Everything after the
|
|
# __ARCHIVE_BELOW__ marker is the embedded tar.gz payload (base64), appended by
|
|
# build.sh.
|
|
exit 0
|
|
__ARCHIVE_BELOW__
|
|
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
|