#!/usr/bin/env bash # Measure what the nginx -> Traefik chain actually delivers to the application. # # docs/reverse-proxy-headers.md documents a single-hop nginx contract. The lab # runs two hops, so the forwarded headers are measured rather than assumed. # Run from anywhere that can resolve the lab hostnames. # # ./deploy/lab/scripts/measure-proxy-headers.sh set -euo pipefail HOST="${HOST:-app1.hyeonworks.com}" URL="https://${HOST}/api/echo" jqf() { if command -v jq >/dev/null 2>&1; then jq "$@"; else python3 -m json.tool; fi } echo "=== 1. baseline: what the app sees for a normal request ===" curl -s "$URL" | jqf '{ scheme, secure, serverName, serverPort, requestUrl, remoteAddr, forwarded: .headers | with_entries(select(.key | startswith("x-forwarded") or . == "x-real-ip" or . == "forwarded")) }' 2>/dev/null || curl -s "$URL" echo echo "=== 2. spoof test: client sends its own X-Forwarded-* ===" echo " a trusted boundary must overwrite these, not append to them" curl -s "$URL" \ -H 'X-Forwarded-For: 1.2.3.4' \ -H 'X-Forwarded-Proto: http' \ -H 'X-Forwarded-Host: evil.example.com' \ -H 'X-Real-IP: 1.2.3.4' \ | jqf '.headers | with_entries(select(.key | startswith("x-forwarded") or . == "x-real-ip"))' 2>/dev/null echo echo "=== 3. which pod answered (host nginx upstream distribution) ===" for _ in 1 2 3 4; do curl -s "$URL" | jqf -r '.headers["x-forwarded-server"] // "n/a"' 2>/dev/null done echo echo "=== 4. plain HTTP is redirected, not proxied ===" curl -s -o /dev/null -w ' http -> %{http_code} %{redirect_url}\n' "http://${HOST}/api/echo"