Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

services/pufferpanel/test-ingress-runtime.sh

Raw
#!/usr/bin/env bash
set -euo pipefail

[[ "$#" -eq 1 && "$1" = /* ]] || {
  echo "usage: test-ingress-runtime.sh NEW_OS_TEMP_DIRECTORY" >&2
  exit 2
}
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
work="$(realpath -m "$1")"
case "${work}" in
  /tmp/* | /var/tmp/*) ;;
  *)
    echo "runtime test requires an OS temporary directory" >&2
    exit 2
    ;;
esac
# Refuse existing data; retain this run's isolated storage/evidence for inspection.
mkdir -m 0700 "${work}"
mkdir "${work}/tmp" "${work}/socket"
export TMPDIR="${work}/tmp"
p() { podman --root "${work}/store" --runroot "${work}/run" --storage-driver vfs "$@"; }
cleanup() {
  p rm -f ingress-test pufferpanel >/dev/null 2>&1 || true
  p network rm ingress-test >/dev/null 2>&1 || true
}
trap cleanup EXIT

cp "${repo_root}/services/pufferpanel/deploy/gateway.Caddyfile" "${work}/gateway.Caddyfile"
cat >"${work}/backend.Caddyfile" <<'CONFIG'
{
	admin off
	auto_https off
	persist_config off
}
http://:8080 {
	respond "{http.request.header.X-Forwarded-Proto}" 200
}
CONFIG

# Stock Caddy's file capability needs a matching rootless capability bound.
if p run --rm --network none --read-only --image-volume ignore --cap-drop all \
  --security-opt no-new-privileges docker.io/library/caddy:2 caddy version \
  >"${work}/no-capability.log" 2>&1; then
  echo "stock image no longer needs NET_BIND_SERVICE; review gateway capability" >&2
  exit 1
fi
grep -F 'Operation not permitted' "${work}/no-capability.log" >/dev/null
p run --rm --network none --read-only --image-volume ignore --cap-drop all \
  --cap-add NET_BIND_SERVICE --security-opt no-new-privileges \
  docker.io/library/caddy:2 caddy version >"${work}/version.log"
p network create --internal ingress-test >/dev/null
p run -d --name pufferpanel --network ingress-test --read-only --image-volume ignore \
  --cap-drop all --cap-add NET_BIND_SERVICE --security-opt no-new-privileges \
  -v "${work}/backend.Caddyfile:/etc/caddy/Caddyfile:ro" docker.io/library/caddy:2 >/dev/null
p run -d --name ingress-test --network ingress-test --read-only --image-volume ignore \
  --cap-drop all --cap-add NET_BIND_SERVICE --security-opt no-new-privileges \
  -v "${work}/gateway.Caddyfile:/etc/caddy/Caddyfile:ro" \
  -v "${work}/socket:/run/pufferpanel-ingress" docker.io/library/caddy:2 >/dev/null
for _ in $(seq 1 100); do
  [[ -S "${work}/socket/http.sock" ]] && break
  sleep 0.1
done
[[ -S "${work}/socket/http.sock" ]]
response="$(curl --fail --silent --show-error --unix-socket "${work}/socket/http.sock" \
  -H 'Host: games.bugabinga.net' http://localhost/api/proof)"
[[ "${response}" == https ]]
status="$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' \
  --unix-socket "${work}/socket/http.sock" -H 'Host: invalid.example' http://localhost/api/proof)"
[[ "${status}" == 404 ]]
p inspect ingress-test | jq -e '.[0] | .HostConfig.ReadonlyRootfs and (.Mounts | length == 2)' >/dev/null
printf '%s\n' 'Stock Caddy capability control, private-network DNS, Unix HTTP, HTTPS forwarding, host filter, and mount boundaries passed'
printf 'Retained isolated test evidence: %s\n' "${work}"