diff --git a/DOCKER.md b/DOCKER.md index 262738a3..3aeb947f 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -121,6 +121,13 @@ Without it every visitor appears to come from the proxy. The login rate limiter your users as one attacker, and the download log records the proxy's address instead of the person's. `compose.example.yaml` already sets this. +`"*"` means "trust whoever connected to me", so it belongs with a published port only the proxy can +reach — which is why `compose.example.yaml` publishes on `127.0.0.1`. If anybody can open the +container's port directly, they are the proxy as far as this setting is concerned, and the +`X-Forwarded-For` they send is the address the rate limiters and the download log will use. Where +the proxy runs on another host, publish on the interface it arrives from and name that address or +subnet here instead of `"*"`. + Leaving it unset does not cause a `502` — that means your proxy could not get a usable response out of the container at all, which is a different problem with a different fix. It does cause a **419 "page expired"**. Without it the application never learns the proxy terminated TLS, so it builds @@ -177,7 +184,7 @@ is the quickest way to separate "the app is down" from "the proxy cannot reach t during an outage, from the same machine: ```sh -curl -s -o /dev/null -w '%{http_code}\n' http://:8080/up # straight at the container +curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/up # straight at the container curl -s -o /dev/null -w '%{http_code}\n' https://files.example.com/up ``` diff --git a/docker/production/compose.example.yaml b/docker/production/compose.example.yaml index 66a160d9..c1abb213 100644 --- a/docker/production/compose.example.yaml +++ b/docker/production/compose.example.yaml @@ -17,7 +17,23 @@ services: # Put a TLS-terminating proxy in front of this in any real install. # ProjectSend issues download links and password-reset emails using # APP_URL, so that value — not this port — is what users must reach. - - "8080:80" + # + # Bound to the loopback address, not to every interface, because + # TRUSTED_PROXIES below is "*". That setting tells the application to + # believe the X-Forwarded-For header of whoever connects to it, which + # is correct behind a proxy and catastrophic when anybody can connect + # directly: a visitor who reaches this port themselves is then the + # "proxy", and can hand the application any client IP they like — + # which is enough to walk straight through the login lockout, every + # named rate limit, and the address recorded in the download log. + # + # Publishing on the loopback address keeps the proxy (on this host, + # or in this compose file) able to reach it while nothing off the + # machine can. If you move the proxy to another host, publish on the + # interface it comes from and narrow TRUSTED_PROXIES to that address + # or subnet at the same time — the two settings only make sense + # together. + - "127.0.0.1:8080:80" environment: APP_URL: https://files.example.com APP_ENV: production @@ -55,6 +71,10 @@ services: # Without it every visitor appears to come from the proxy: the login # rate limiter treats all of your users as one attacker, and the # download log records the proxy's address. + # + # "*" means "trust whoever connects to me", which is only safe when + # nothing but the proxy can — which is what the loopback binding + # above is for. Change one and you have to change the other. TRUSTED_PROXIES: "*" # Optional: uncomment these — with a password of your own — to create diff --git a/docker/production/dockerhub-overview.md b/docker/production/dockerhub-overview.md index cc14e17c..8c3933e3 100644 --- a/docker/production/dockerhub-overview.md +++ b/docker/production/dockerhub-overview.md @@ -40,7 +40,10 @@ services: restart: unless-stopped ports: # Put a TLS-terminating proxy in front of this in any real install. - - "8080:80" + # Bound to loopback because TRUSTED_PROXIES below is "*": the + # application then believes the X-Forwarded-For of whoever connects, + # so nobody but the proxy may be able to. + - "127.0.0.1:8080:80" environment: APP_URL: https://files.example.com APP_ENV: production @@ -58,6 +61,8 @@ services: # Required whenever anything sits between your visitors and this # container — including the reverse proxy you should be running. + # "*" trusts whoever connects, so it goes together with the loopback + # binding above: change one and you have to change the other. TRUSTED_PROXIES: "*" # Optional: uncomment these — with a password of your own — to create