ab75b21cb4dc86477130f808de775045f2758dc2
The runtime API was an unauthenticated `level admin` channel on TCP 9999. `expose:` restricts nothing and docker networks have no per-port rules, so it was reachable by every container sharing a network — nginx included — and reaching it means installing your own certificate and key. It is now a unix socket on a volume shared with certbot alone, which also keeps the private key off the network on every renewal. `expose-fd listeners` is dropped: it hands the listening sockets themselves to a client of that socket and only serves seamless reloads, which this image never performs. HAProxy binds a unix socket by creating `<path>.<pid>.tmp` and renaming it, so it needs write access to the directory; the image now creates /var/lib/haproxy owned by uid 1001 and docker carries that onto an empty named volume. Without it HAProxy refuses to start. Also in this commit: - The visitor's address no longer leaves the process. `option forwardfor` is gone, X-Forwarded-For is deleted unconditionally, and X-Client-Id carries an HMAC of the address under the optional XFF_HMAC_KEY instead — one-to-one with the address, so a rate limit keyed on it is as precise, but not reversible. An empty key sends no header rather than one derived from an empty key. - Logging, which was absent entirely. To stdout for `docker logs`, errors-only via the existing dontlog-normal. Both log-format and error-log-format are hand-written: the built-in formats open with %ci:%cp and would have logged the addresses the rest of this works to avoid. The pseudonym is computed by a tcp-request connection rule so a refused handshake has one too. - TLS pinned: floor TLS 1.2, ECDHE-only in ECDSA and RSA variants, no session tickets, ALPN offering HTTP/2. - HTTP redirects to HTTPS, excepting the ACME challenge path, plus HSTS at one day — short deliberately, since the header cannot be recalled once sent. - timeout http-request, which `timeout client` cannot stand in for: that one resets on every byte, so a slow-drip client held a connection indefinitely. - Backends re-resolve through the declared `resolvers docker`, which nothing referenced. Names were resolved once at boot, so a container recreated on a new IP was never noticed. init-addr libc,none lets HAProxy start anyway. - The commented-out /ms deny rules are deleted. They would have blocked /ms/info and /ms/api/v2/whoami, both deliberately public. - compose: build context is the repository root, as the Dockerfile's COPY expects and as CI already did; restart policy added, since an empty certificate volume is a fatal start-up error; the volumes section, which was missing, so the file can actually come up. Verified on testnet2 against the real binary: config parses clean with the key set, empty, and rejects a non-base64 key at parse time; a live stack confirms the pseudonym is stable per address, differs between addresses, survives a forged header, and that the client address appears nowhere in the log.
Languages
Dockerfile
100%