Docs: rproxy runs behind any TLS-terminating reverse proxy (closes #388)
check / check (push) Successful in 4m13s
check / check (push) Successful in 4m13s
README, SPEC and docs no longer present traefik as the required front proxy. The README gains "The reverse proxy in front", listing what that proxy must do: forward plain HTTP to port 8080, keep Host, set X-Forwarded-For and X-Forwarded-Proto, and connect from an address in SWW_RPROXY_TRUSTED_PROXIES. traefik, Caddy and nginx appear as examples, with the nginx headers spelled out since it sets none by default. Model: opus-5-5
This commit is contained in:
1 parent
53d9bf6af6
commit
cddcf3d065
4 files changed
+95
-57
No files matched your search
@@ -3,7 +3,8 @@
|
||||
`smallwebwaf` is a web application firewall for self-hosted apps. It is one
|
||||
binary, `sww`, and two of its commands do the work:
|
||||
|
||||
- `sww rproxy` is a reverse proxy between traefik and one app.
|
||||
- `sww rproxy` is a reverse proxy between one app and the reverse proxy that
|
||||
terminates TLS.
|
||||
- `sww waf` decides what happens to each request, for many sites.
|
||||
|
||||
This document says what it must do and why, each requirement once. Everything in
|
||||
@@ -45,7 +46,8 @@ never as a time.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- TLS, certificates and routing by host name. Those are traefik's job.
|
||||
- TLS, certificates and routing by host name. Those are the job of the reverse
|
||||
proxy in front of `rproxy`, such as traefik, nginx or Caddy.
|
||||
- More than one app behind one `rproxy`. Each app has its own.
|
||||
- Browser challenges such as captchas or proof of work. If wanted, chain Anubis
|
||||
between `rproxy` and the app.
|
||||
@@ -1943,10 +1945,10 @@ leave out a cookie only when its name matches exactly, case included.
|
||||
### Ban lengths, response and scope
|
||||
|
||||
`SWW_WAF_BAN_RESPONSE` is `403` by default, which makes a ban easy to recognise
|
||||
when debugging; `429` and `close` are the alternatives. Behind traefik, `close`
|
||||
does not leave the client unanswered: traefik answers `502`, as it does whenever
|
||||
its backend drops a connection. `SWW_WAF_BAN_SCOPE_V4_PREFIX` widens an IPv4 ban
|
||||
to the surrounding netblock. The four ban lengths are under "Bans".
|
||||
when debugging; `429` and `close` are the alternatives. `close` does not leave
|
||||
the client unanswered: the reverse proxy in front answers `502`, as it does
|
||||
whenever its backend drops a connection. `SWW_WAF_BAN_SCOPE_V4_PREFIX` widens an
|
||||
IPv4 ban to the surrounding netblock. The four ban lengths are under "Bans".
|
||||
|
||||
### Reputation
|
||||
|
||||
@@ -2771,9 +2773,10 @@ its whole body. The visitor gets that answer at once, and the connection closes
|
||||
after it.
|
||||
|
||||
The largest request line and headers is 32 KiB. A kept-open connection is closed
|
||||
after 120 seconds idle. Neither has a setting. traefik, by default, closes an
|
||||
unused connection after 90 seconds. So traefik closes first, and never sends a
|
||||
request on a connection `rproxy` is closing.
|
||||
after 120 seconds idle. Neither has a setting. The reverse proxy in front should
|
||||
close an unused connection sooner, so that it never sends a request on a
|
||||
connection `rproxy` is closing. traefik closes one after 90 seconds by default.
|
||||
nginx, by default, opens a new connection for each request.
|
||||
|
||||
What the visitor gets when a limit is passed:
|
||||
|
||||
@@ -2824,14 +2827,16 @@ chooses: `waf` runs `waf`, and with it unset the image runs `rproxy`.
|
||||
The usual setup is three containers on one private network:
|
||||
|
||||
- the app;
|
||||
- `rproxy`, which traefik routes to, with `SWW_RPROXY_UPSTREAM_URL` naming the
|
||||
app and `SWW_RPROXY_WAF_BACKEND` naming `waf`;
|
||||
- `rproxy`, which the reverse proxy that terminates TLS forwards to, with
|
||||
`SWW_RPROXY_UPSTREAM_URL` naming the app and `SWW_RPROXY_WAF_BACKEND` naming
|
||||
`waf`;
|
||||
- `waf`, with `SWW_COMMAND=waf` and a volume for its state files.
|
||||
|
||||
Each further app adds its own container and its own `rproxy`, whose
|
||||
`SWW_RPROXY_WAF_BACKEND` names the same `waf` (see "Sites"). Anything on that
|
||||
network can reach an app around `rproxy`, and can ask `waf` questions or send it
|
||||
reports. So the network holds only what you trust.
|
||||
reports. So the network holds only what you trust. "The reverse proxy in front"
|
||||
in `README.md` lists what that proxy must do.
|
||||
|
||||
### `rproxy` in the app's container
|
||||
|
||||
@@ -2903,10 +2908,10 @@ process that stops at start is started again every second, not in a tight loop.
|
||||
container's output, which `docker logs` shows.
|
||||
- When a service exits, runit runs its `run` script again, and the container
|
||||
does not stop. Beside an app, the other process keeps running.
|
||||
- While `rproxy` is down, traefik answers `502`. While the app is down, `rproxy`
|
||||
answers `502`, which is not a client offence. While `waf` is down,
|
||||
`SWW_RPROXY_WAF_BACKEND_NO_ANSWER_DISPOSITION` decides (see "Without an answer
|
||||
from `waf`").
|
||||
- While `rproxy` is down, the reverse proxy in front answers `502`. While the
|
||||
app is down, `rproxy` answers `502`, which is not a client offence. While
|
||||
`waf` is down, `SWW_RPROXY_WAF_BACKEND_NO_ANSWER_DISPOSITION` decides (see
|
||||
"Without an answer from `waf`").
|
||||
- Only faults found at start stop `waf`:
|
||||
- invalid configuration;
|
||||
- a lookup database that is missing or unreadable;
|
||||
@@ -2939,23 +2944,26 @@ checks:
|
||||
- With `SWW_COMMAND` unset, empty or `rproxy`, it passes while `rproxy` answers
|
||||
that request on `127.0.0.1`, at the port in `SWW_RPROXY_LISTEN_ADDR`, and the
|
||||
app accepts connections at `SWW_RPROXY_UPSTREAM_URL`. It does not ask `waf`,
|
||||
so that the site stays in traefik's routes while `waf` is down.
|
||||
so that the site stays in the routes of the reverse proxy in front while `waf`
|
||||
is down.
|
||||
- With any other value, it fails with a message naming `SWW_COMMAND` and its
|
||||
value.
|
||||
|
||||
traefik sends a container no requests until it shows as healthy. So the check
|
||||
runs every second from the container's start until it first passes, for up to a
|
||||
minute, and every 30 seconds after that. An app with a health check of its own
|
||||
can replace the image's `HEALTHCHECK` with one that checks both.
|
||||
A reverse proxy that reads Docker's health status, as traefik does, sends a
|
||||
container no requests until it shows as healthy. So the check runs every second
|
||||
from the container's start until it first passes, for up to a minute, and every
|
||||
30 seconds after that. An app with a health check of its own can replace the
|
||||
image's `HEALTHCHECK` with one that checks both.
|
||||
|
||||
### Ports
|
||||
|
||||
`rproxy` listens on no port but the one in `SWW_RPROXY_LISTEN_ADDR`, 8080 by
|
||||
default, which traefik routes to. It passes requests to the app at
|
||||
`SWW_RPROXY_UPSTREAM_URL`, `http://127.0.0.1:8081` by default. Another port may
|
||||
be set: the health check follows the setting, and traefik's port label
|
||||
(`traefik.http.services.<name>.loadbalancer.server.port`) must name it. An app
|
||||
beside `rproxy` must leave that port free.
|
||||
default, which the reverse proxy in front forwards to. It passes requests to the
|
||||
app at `SWW_RPROXY_UPSTREAM_URL`, `http://127.0.0.1:8081` by default. Another
|
||||
port may be set: the health check follows the setting, and the reverse proxy in
|
||||
front must name it, as traefik's port label
|
||||
(`traefik.http.services.<name>.loadbalancer.server.port`) does. An app beside
|
||||
`rproxy` must leave that port free.
|
||||
|
||||
`waf` listens on no port but the one in `SWW_WAF_LISTEN_ADDR`, 8080 by default.
|
||||
The questions, the reports and its own endpoints all come there.
|
||||
@@ -2996,9 +3004,9 @@ in `docs/settings.md` shows the commands.
|
||||
### Forwarded headers
|
||||
|
||||
The app's TCP peer is `rproxy`, and the `X-Forwarded-For` the app receives ends
|
||||
with traefik's address, which `rproxy` adds after the visitor's. So the app must
|
||||
trust `rproxy`'s address for forwarded headers, besides the private address
|
||||
ranges traefik is on.
|
||||
with the address of the reverse proxy in front, which `rproxy` adds after the
|
||||
visitor's. So the app must trust `rproxy`'s address for forwarded headers,
|
||||
besides the private address ranges that proxy is on.
|
||||
|
||||
- In a container of its own, `rproxy`'s address is on the private network. The
|
||||
usual default for a trusted-proxy setting, the private ranges, covers it.
|
||||
|
||||
Reference in new issue
Block a user