From 136b912b8196b9874b7dd932a72b37e8fc4ce532 Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Tue, 29 Sep 2026 01:44:32 +0000 Subject: [PATCH] nginx: listen on PORT, default 8080; server_tokens off (closes #26) nginx.conf is now a template the nginx image renders into conf.d at container start. bin/entrypoint.sh sets PORT to 8080 when unset or empty, and stops with an error before starting anything when PORT is not digits only: nginx would take a value such as localhost or unix:/tmp/x.sock as an address and start anyway. NGINX_ENVSUBST_FILTER limits the rendering to PORT, so $uri, $host and every other nginx variable pass through unchanged. server_tokens off drops the version from the Server header and error pages. script/frontend-viewport-test renders the template the same way. EXPOSE still documents 8080; the backend stays on 127.0.0.1:8081. Model: opus-5-5 --- Dockerfile | 5 ++++- TODO.md | 6 ++++++ bin/entrypoint.sh | 21 +++++++++++++++++++-- nginx.conf | 8 +++++++- script/frontend-viewport-test | 5 ++++- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3046f01..d9b8879 100644 --- a/Dockerfile +++ b/Dockerfile @@ -68,8 +68,10 @@ FROM nginx@sha256:15e96e59aa3b0aada3a121296e3bce117721f42d88f5f64217ef4b18f458c6 RUN addgroup -g 1000 -S netwatch && \ adduser -u 1000 -S netwatch -G netwatch +# At start-up the nginx image renders every template here into +# conf.d; bin/entrypoint.sh says how. RUN rm /etc/nginx/conf.d/default.conf -COPY nginx.conf /etc/nginx/conf.d/netwatch.conf +COPY nginx.conf /etc/nginx/templates/netwatch.conf.template COPY --from=frontend /app/dist /usr/share/nginx/html COPY --from=builder /src/netwatch-server /usr/local/bin/netwatch-server COPY bin/entrypoint.sh /usr/local/bin/entrypoint.sh @@ -78,6 +80,7 @@ ENV DATA_DIR=/data/reports RUN mkdir -p /data/reports && chown -R netwatch:netwatch /data VOLUME /data +# The default public port; PORT changes it. EXPOSE 8080 # The nginx image stops its container with SIGQUIT; the entrypoint diff --git a/TODO.md b/TODO.md index adbc424..c275f1e 100644 --- a/TODO.md +++ b/TODO.md @@ -23,6 +23,12 @@ latest run passes. # Completed Steps +- 2026-09-29: nginx listens on `PORT` (issue #26), 8080 when unset or empty: the + nginx image renders `nginx.conf` as a template at container start, filling in + `PORT` and no other variable. `bin/entrypoint.sh` refuses to start when `PORT` + is not digits only. `server_tokens off` keeps the nginx version out of + responses. `script/frontend-viewport-test` renders the template the same way. + Gzip and a `50x.html` error page are not added - 2026-09-29: bounded the report endpoint (issue #20): `POST /api/v1/reports` still needs no credentials, but each client address, as resolved through `TRUSTED_PROXIES`, may send `REPORTS_PER_MINUTE` (default 60) reports a diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index 3dece27..b100e75 100755 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -8,6 +8,18 @@ # No set -e: kill and wait return non-zero here in normal operation. set -u +# PORT is the public port nginx listens on, 8080 when unset or empty. +# nginx would take a value such as localhost or unix:/tmp/x.sock as an +# address and start anyway, so anything but digits stops the container +# here, before either process starts. +export PORT="${PORT:-8080}" +case "$PORT" in + *[!0-9]*) + echo "entrypoint: PORT must be a port number, not '$PORT'" >&2 + exit 1 + ;; +esac + # A stop signal is only noted here; the loop below acts on it. stop_requested="" trap 'stop_requested=yes' TERM INT @@ -23,8 +35,13 @@ backend=$! # nginx starts through the nginx image's own entrypoint, which applies # the image's start-up configuration and then replaces itself with -# nginx. -/docker-entrypoint.sh nginx -g 'daemon off;' & +# nginx. Part of that start-up configuration renders nginx.conf into +# conf.d with nginx listening on PORT. NGINX_ENVSUBST_FILTER limits +# that rendering to PORT: a variable nginx itself uses, such as $uri, +# would otherwise be replaced by an environment variable of the same +# name. +NGINX_ENVSUBST_FILTER='^PORT$' \ + /docker-entrypoint.sh nginx -g 'daemon off;' & nginx=$! running() { diff --git a/nginx.conf b/nginx.conf index 65e8aaf..697340b 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,7 +1,13 @@ +# A template: the nginx image renders it into conf.d at container start, +# filling in PORT and nothing else. bin/entrypoint.sh sets PORT and that +# limit. server { - listen 8080; + listen ${PORT}; server_name _; + # Keep the nginx version out of the Server header and error pages. + server_tokens off; + root /usr/share/nginx/html; index index.html; diff --git a/script/frontend-viewport-test b/script/frontend-viewport-test index 53a2ee1..5a97c99 100755 --- a/script/frontend-viewport-test +++ b/script/frontend-viewport-test @@ -61,10 +61,13 @@ main() { # host. docker network create --internal "$NETWORK" > /dev/null + # nginx.conf is a template: the image renders it over its own + # default.conf, with the same port and limit bin/entrypoint.sh uses. docker run -d --rm --name "$SERVER" \ --network "$NETWORK" --network-alias netwatch \ + -e PORT=8080 -e NGINX_ENVSUBST_FILTER='^PORT$' \ -v "$ROOT/dist:/usr/share/nginx/html:ro" \ - -v "$ROOT/nginx.conf:/etc/nginx/conf.d/default.conf:ro" \ + -v "$ROOT/nginx.conf:/etc/nginx/templates/default.conf.template:ro" \ "$SERVER_IMAGE" > /dev/null # The image's own entrypoint already exposes CDP on 9222 and passes -- 2.54.0