nginx: trust X-Forwarded-For only from TRUSTED_PROXIES (closes #64)
check / check (push) Successful in 49s

nginx trusted X-Forwarded-For from every RFC1918 address, so a client
reaching it from one could write a new address on each request and
get a fresh rate-limit allowance. The container's TRUSTED_PROXIES now
names the reverse proxies nginx trusts, none by default.
bin/entrypoint.sh refuses a value with a character no IP address or
CIDR has, and writes one set_real_ip_from line per entry into
/etc/nginx/trusted-proxies.conf, which nginx.conf includes. The
backend is started with TRUSTED_PROXIES=127.0.0.1/32, since nginx is
its only client. The viewport test mounts an empty file there.

Model: opus-5-5
This commit is contained in:
2026-09-29 05:16:37 +00:00
parent d2f219ca19
commit 16d01d5326
7 changed files with 76 additions and 23 deletions
+27 -4
View File
@@ -32,16 +32,39 @@ if [ "$PORT" -eq 8081 ]; then
exit 1
fi
# TRUSTED_PROXIES names the reverse proxies in front of the container,
# as IP addresses or CIDRs separated by commas. nginx takes the client
# address from X-Forwarded-For only on a request from one of them, so
# unset or empty, it trusts no one. nginx would look up a hostname at
# start and trust whatever address it found, so a value with a
# character no address has stops the container here. An entry such as
# 999.1.1.1 gets past this, and nginx refuses it at start as a
# hostname it cannot find.
TRUSTED_PROXIES="${TRUSTED_PROXIES:-}"
case "$TRUSTED_PROXIES" in
*[!0-9A-Fa-f.:/,\ ]*)
echo "entrypoint: TRUSTED_PROXIES must be IP addresses or CIDRs" \
"separated by commas, not '$TRUSTED_PROXIES'" >&2
exit 1
;;
esac
# nginx.conf includes this file; an empty one trusts no proxy.
for proxy in $(echo "$TRUSTED_PROXIES" | tr ',' ' '); do
echo "set_real_ip_from $proxy;"
done > /etc/nginx/trusted-proxies.conf
# A stop signal is only noted here; the loop below acts on it.
stop_requested=""
trap 'stop_requested=yes' TERM INT
# netwatch-server runs as the netwatch user and listens on loopback
# only, on a port other than the public one; nginx.conf proxies to this
# address. The netwatch user has no login shell, hence -s /bin/sh.
# busybox su replaces itself with the command instead of staying on as
# its parent, so $! is the server's own PID.
BIND_ADDRESS=127.0.0.1 PORT=8081 \
# address. Its only client is nginx, so it takes the client address
# nginx passes on from 127.0.0.1 alone, whatever TRUSTED_PROXIES the
# container has. The netwatch user has no login shell, hence -s
# /bin/sh. busybox su replaces itself with the command instead of
# staying on as its parent, so $! is the server's own PID.
BIND_ADDRESS=127.0.0.1 PORT=8081 TRUSTED_PROXIES=127.0.0.1/32 \
su -s /bin/sh netwatch -c 'exec netwatch-server' &
backend=$!