web: /healthz is answered only to local probes (audit I6)

Security audit 2026-10-17, finding I6 (low), stage 3. /healthz is
registered outside auth and every proxy fragment passed it through: the
live server answered "ok" to the internet, and "unhealthy: postfix"
would have been a free tell.

Now handleHealth answers only a request with no X-Forwarded-For (nor
Forwarded) from loopback or a private address: the image's HEALTHCHECK
(127.0.0.1), the host through the loopback-published port (the Docker
gateway), a monitor on the Docker network. Every supported proxy appends
X-Forwarded-For, so through it the endpoint is 404. The plan named
"trusted-proxy peers"; that would have let the internet in again through
the trusted proxy, hence this shape. The e2e's readiness probe is the
host's, and stays answered.

Test: TestHealthzAnswersOnlyLocalProbes. Docs: guide § Container health,
architecture.md; CHANGELOG; plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-17 22:17:27 +03:00
co-authored by Claude Opus 5.5
parent 8aad5b6223
commit a327962008
6 changed files with 76 additions and 8 deletions
+7 -4
View File
@@ -819,15 +819,18 @@ again whole and loses the keys of domains added under 2.8.x.
### Container health
The image declares a Docker `HEALTHCHECK` that probes `GET /healthz` on port
8080 (unauthenticated). It returns `200 ok` when the panel - whose roles
8080 (unauthenticated, and answered only to a request that did not come
through a reverse proxy - no `X-Forwarded-For` - from loopback or a private
address; anyone else gets `404`). It returns `200 ok` when the panel - whose roles
include the DKIM signer - and Postfix are both `RUNNING` under supervisord;
otherwise `503 unhealthy`. This
catches a dead mail path that would still leave the HTTP server up, but it
does **not** verify TLS certificates, DNS records, or end-to-end delivery —
use the authenticated [Overview and Health](#overview-and-health) pages for
that. External monitoring
can use the same endpoint through the reverse proxy if you expose it, or poll
`docker inspect` health state on the host.
that. External monitoring runs on the host - `curl -fsS
http://127.0.0.1:8080/healthz` - or polls `docker inspect` health state;
through the reverse proxy the endpoint is not found, so its answer tells a
stranger nothing.
### Server-level DNS (PTR/rDNS)