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
@@ -10,10 +10,11 @@ A web application firewall reads each request on its way to a web app and
|
||||
refuses the ones that look like abuse. `smallwebwaf` is one program, `sww`,
|
||||
shipped in one image, with two commands that do this together:
|
||||
|
||||
- `sww rproxy` is a small reverse proxy in front of one app. traefik routes to
|
||||
it. It asks `waf` about each request, passes the allowed ones to the app, and
|
||||
passes the app's answers back. It keeps nothing between requests and writes
|
||||
nothing to disk.
|
||||
- `sww rproxy` is a small reverse proxy in front of one app. It sits behind the
|
||||
reverse proxy that terminates TLS, such as traefik, nginx or Caddy. It asks
|
||||
`waf` about each request, passes the allowed ones to the app, and passes the
|
||||
app's answers back. It keeps nothing between requests and writes nothing to
|
||||
disk.
|
||||
- `sww waf` decides. It runs the checks and keeps the bans, the counters and
|
||||
each client's history. One `waf` serves many sites: a client banned through
|
||||
one site is refused on all of them, and its requests on all of them count
|
||||
@@ -57,8 +58,8 @@ For the operator, it:
|
||||
|
||||
Not built, and not planned:
|
||||
|
||||
- TLS and routing by host name. Both stay with traefik, the reverse proxy in
|
||||
front of `rproxy`.
|
||||
- TLS and routing by host name. Both stay with the reverse proxy in front of
|
||||
`rproxy`.
|
||||
- Browser challenges such as captchas.
|
||||
- A web console, or a configuration file.
|
||||
- Defence against floods large enough to fill the host's network link.
|
||||
@@ -90,20 +91,48 @@ accepts connections, whether or not `waf` answers; in `waf`'s container, once
|
||||
The usual setup is three containers on one private network:
|
||||
|
||||
- the app;
|
||||
- `rproxy`, in front of the app, which traefik routes to:
|
||||
`SWW_RPROXY_WAF_BACKEND` names `waf` and `SWW_RPROXY_UPSTREAM_URL` names the
|
||||
app;
|
||||
- `rproxy`, in front of the app, which the reverse proxy that terminates TLS
|
||||
forwards to: `SWW_RPROXY_WAF_BACKEND` names `waf` and
|
||||
`SWW_RPROXY_UPSTREAM_URL` names the app;
|
||||
- `waf`, with `SWW_COMMAND=waf` and a volume at `/var/lib/sww` for its state
|
||||
files.
|
||||
|
||||
Point traefik's labels at port 8080 of `rproxy`
|
||||
(`traefik.http.services.<name>.loadbalancer.server.port=8080`). Publish no port
|
||||
of the app or of `waf`. Anything on that network can reach the app around
|
||||
`rproxy`, and can ask `waf` questions, so the network holds only what you trust.
|
||||
Each further app adds its own container and its own `rproxy`, whose
|
||||
Publish no port of the app or of `waf`. Anything on that network can reach the
|
||||
app around `rproxy`, and can ask `waf` questions, so the network holds only what
|
||||
you trust. Each further app adds its own container and its own `rproxy`, whose
|
||||
`SWW_RPROXY_WAF_BACKEND` names the same `waf`. The "Smallest setup" example
|
||||
below shows this shape.
|
||||
|
||||
### The reverse proxy in front
|
||||
|
||||
`rproxy` serves plain HTTP. It runs behind any reverse proxy that terminates
|
||||
TLS, such as traefik, nginx or Caddy. That proxy must:
|
||||
|
||||
- forward each request to port 8080 of `rproxy` over plain HTTP;
|
||||
- keep the visitor's `Host` header;
|
||||
- add the visitor's address to `X-Forwarded-For`;
|
||||
- set `X-Forwarded-Proto` to the scheme the visitor used;
|
||||
- connect to `rproxy` from an address in `SWW_RPROXY_TRUSTED_PROXIES`, by
|
||||
default `10.0.0.0/8`, `172.16.0.0/12` or `192.168.0.0/16`.
|
||||
|
||||
From any other address, `rproxy` ignores the forwarded headers and takes the
|
||||
proxy for the visitor. Every visitor then shares one address, and one ban.
|
||||
|
||||
traefik and Caddy keep the `Host` header and set both forwarded headers by
|
||||
default. traefik's Docker label
|
||||
`traefik.http.services.<name>.loadbalancer.server.port=8080` names the port, and
|
||||
Caddy's `reverse_proxy rproxy:8080` needs nothing more. nginx sets none of the
|
||||
headers by default, so its site names them:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
proxy_pass http://rproxy:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
```
|
||||
|
||||
### `rproxy` in the app's container
|
||||
|
||||
An app's image can instead be built on the `smallwebwaf` image, so that `rproxy`
|
||||
@@ -154,13 +183,13 @@ entrypoint starts `rproxy` and the app side by side, each as its own user. Run
|
||||
the app's image with `SWW_RPROXY_WAF_BACKEND` set. `deploy/example-app` is such
|
||||
an app, and the "`rproxy` beside the app" example below runs one.
|
||||
|
||||
The app sees the headers traefik sent, with traefik's address added to
|
||||
`X-Forwarded-For`, from a TCP peer that is `rproxy`. So the app must trust
|
||||
`rproxy`'s address for forwarded headers. In a container of its own, `rproxy` is
|
||||
on the private network, which the usual default of a trusted-proxy setting
|
||||
covers. Beside the app, its address is `127.0.0.1` or `::1`, which the private
|
||||
ranges leave out, so the app's own setting must name them too, as `app.run`
|
||||
does.
|
||||
The app sees the headers the reverse proxy in front sent, with that proxy's
|
||||
address added to `X-Forwarded-For`, from a TCP peer that is `rproxy`. So the app
|
||||
must trust `rproxy`'s address for forwarded headers. In a container of its own,
|
||||
`rproxy` is on the private network, which the usual default of a trusted-proxy
|
||||
setting covers. Beside the app, its address is `127.0.0.1` or `::1`, which the
|
||||
private ranges leave out, so the app's own setting must name them too, as
|
||||
`app.run` does.
|
||||
|
||||
### Checking that it works
|
||||
|
||||
@@ -168,9 +197,9 @@ does.
|
||||
`healthy` for each container of `smallwebwaf`.
|
||||
- A log line with `"msg":"starting"` gives the version, the address and every
|
||||
setting in effect, with secrets masked.
|
||||
- A request to the site through traefik gets the app's answer.
|
||||
`https://<site>/_sww/healthz` answers `ok`. `waf`'s log lines about the
|
||||
request have `"action":"forward"`.
|
||||
- A request to the site through the reverse proxy in front gets the app's
|
||||
answer. `https://<site>/_sww/healthz` answers `ok`. `waf`'s log lines about
|
||||
the request have `"action":"forward"`.
|
||||
- To see a refusal without banning yourself, run `waf` with
|
||||
`SWW_WAF_MODE=observe` and ask for `/.env`. The request reaches the app.
|
||||
`waf`'s log lines have `"would_action":"temporarily_banned"` and
|
||||
|
||||
Reference in new issue
Block a user