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}"