Docs: rproxy runs behind any TLS-terminating reverse proxy (closes #388)
check / check (push) Failing after 2m26s
check / check (push) Failing after 2m26s
README, SPEC and docs no longer present traefik as the required front proxy, and call it the TLS-terminating proxy. The README section of that name lists what the proxy must do: forward plain HTTP to port 8080, keep Host, set X-Forwarded-For and X-Forwarded-Proto, close an unused connection in under 120 seconds, and connect from an address in SWW_RPROXY_TRUSTED_PROXIES. traefik, Caddy and nginx appear as examples, with the Caddy and nginx settings their defaults lack. Model: opus-5-5
This commit was merged in pull request #391.
This commit is contained in:
1 parent
53d9bf6af6
commit
a05a82b1af
4 files changed
+100
-53
No files matched your search
@@ -10,10 +10,10 @@ 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 a
|
||||
TLS-terminating proxy, 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 +57,7 @@ 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 TLS-terminating proxy.
|
||||
- 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 +89,61 @@ 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:
|
||||
- `rproxy`, in front of the app, which the TLS-terminating proxy 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 TLS-terminating proxy
|
||||
|
||||
`rproxy` serves plain HTTP. It runs behind any TLS-terminating proxy, such as
|
||||
traefik, nginx or Caddy. The TLS-terminating 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;
|
||||
- close an unused connection to `rproxy` in under 120 seconds;
|
||||
- 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
|
||||
TLS-terminating 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.
|
||||
Caddy keeps an unused connection for 120 seconds by default, so its site
|
||||
shortens that to 90 seconds:
|
||||
|
||||
```caddyfile
|
||||
reverse_proxy rproxy:8080 {
|
||||
transport http {
|
||||
keepalive 90s
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By default nginx sends the host in `proxy_pass` as `Host` and sets neither
|
||||
forwarded header, so its site sets all three:
|
||||
|
||||
```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 +194,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's TCP peer is `rproxy`. The app sees the headers the TLS-terminating
|
||||
proxy sent, with that proxy's address added to `X-Forwarded-For`. 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,7 +208,7 @@ 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.
|
||||
- A request to the site through the TLS-terminating proxy 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
|
||||
|
||||
Reference in new issue
Block a user