Allow per-app allow/deny.

This commit is contained in:
Greyson Parrelli
2026-08-06 13:53:24 -04:00
parent c15a1cc14c
commit 8ce2919627
19 changed files with 1151 additions and 187 deletions
+81
View File
@@ -60,6 +60,32 @@ grep -q 'proxy_set_header X-Forwarded-Email \$google_auth_email;' "$CONF" || fai
grep -q 'proxy_set_header X-Forwarded-Email "";' "$CONF" || fail "excluded paths should strip identity headers"
echo "ok: conf contents"
# --- the app name must be stamped on every request that reaches the service ---
# Checking the invariant rather than a fixed list of locations: any location
# that proxies to the auth service must set X-Google-Auth-App to this app, or
# the service would fall back to the global lists (silently widening access for
# an app whose own allow list is narrower). Any location that proxies to the
# app must blank it, so clients cannot pass one through.
awk -v app="$APP" '
/^location/ { block = $0; inside = 1; to_service = 0; to_app = 0; stamped = 0; blanked = 0; next }
inside && /^}/ {
if (to_service && !stamped) { printf "location reaching the auth service without the app header: %s\n", block; bad = 1 }
if (to_app && !blanked) { printf "location reaching the app without blanking the app header: %s\n", block; bad = 1 }
inside = 0; next
}
inside {
if ($0 ~ /proxy_pass http:\/\/127\.0\.0\.1:/) to_service = 1
if ($0 ~ /proxy_pass http:\/\/myapp-5000;/) to_app = 1
if ($0 == sprintf(" proxy_set_header X-Google-Auth-App \"%s\";", app)) stamped = 1
if ($0 == " proxy_set_header X-Google-Auth-App \"\";") blanked = 1
}
END { exit bad }
' "$CONF" || fail "X-Google-Auth-App is not handled consistently across locations"
# Guard the guard: the awk above must actually see both kinds of location.
[[ "$(grep -c 'proxy_set_header X-Google-Auth-App "myapp";' "$CONF")" -eq 3 ]] ||
fail "expected the app header on the three auth-service locations"
echo "ok: app name stamped for the auth service, blanked for the app"
# --- write/remove behavior ---
[[ "$(fn-ga-write-conf "$APP")" == "changed" ]] || fail "first write should report changed"
[[ "$(fn-ga-write-conf "$APP")" == "unchanged" ]] || fail "second write should report unchanged"
@@ -100,6 +126,61 @@ EOF
else
fail "nginx -t rejected the generated config"
fi
# --- run it for real, and try to spoof the app name ---
# Per-app access lists are only as trustworthy as X-Google-Auth-App, so prove
# with a live nginx that a client-supplied value never reaches the auth
# service. Stand-ins for the two backends run inside the same nginx:
# :2999 pretends to be the auth service and reports the app name it saw,
# :8081 pretends to be the app and echoes what it was forwarded.
cat >"$WORK/nginx-runtime.conf" <<EOF
events {}
http {
access_log off;
upstream myapp-5000 {
server 127.0.0.1:8081;
}
server {
listen 8080 default_server;
include /work/google-auth.conf;
}
server {
listen 2999;
location ${GOOGLE_AUTH_ROUTE_PREFIX}/verify {
add_header X-Auth-Request-Email "app-seen=\$http_x_google_auth_app" always;
return 204;
}
location / { return 404; }
}
server {
listen 8081;
location / {
default_type text/plain;
return 200 "forwarded-email=\$http_x_forwarded_email\n";
}
}
}
EOF
runtime_out="$(docker run --rm -v "$WORK:/work:ro" nginx:alpine sh -c '
nginx -c /work/nginx-runtime.conf -g "daemon off;" &
i=0
while [ $i -lt 40 ]; do
wget -q -O /dev/null http://127.0.0.1:8081/ 2>/dev/null && break
i=$((i + 1)); sleep 0.1
done
echo "--- plain ---"
wget -q -O - http://127.0.0.1:8080/some/page 2>&1
echo "--- spoofed ---"
wget -q -O - --header "X-Google-Auth-App: spoofed-app" http://127.0.0.1:8080/some/page 2>&1
' 2>/dev/null)" || fail "could not run the live nginx spoofing check: $runtime_out"
if [[ "$(grep -c 'forwarded-email=app-seen=myapp' <<<"$runtime_out")" -ne 2 ]]; then
fail "nginx should have told the auth service the app name on both requests, got: $runtime_out"
fi
if grep -q "spoofed-app" <<<"$runtime_out"; then
fail "a client-supplied X-Google-Auth-App reached the auth service: $runtime_out"
fi
echo "ok: live nginx sends the real app name and discards a spoofed one"
else
echo "skip: docker unavailable, skipped real nginx validation"
fi