SPEC and README: bodies unread by default, one listener, GeoJS by default (closes #6)

Address the third review of the spec update and sneak's two new rulings.
By default the Core Rule Set reads the URL, query string and headers but
no request body, since a code forge's bodies carry code it takes for
attacks; `WAF_BODY_LIMIT` switches body inspection on. `Content-Encoding`
is allowed only on unread bodies, and OAuth sign-in from local tools
passes. The default rule file bans common probes for secrets, version
control, backups and logs at the site root. The spec names the Core Rule
Set 4.25.0. One listener answers the sidecar's own endpoints under
`/_smallwebwaf/`, behind tokens. GeoJS is the default lookup source. Size
suffixes are powers of 1024.

Model: opus-5-5
This commit is contained in:
2026-09-28 18:50:00 +00:00
parent 2d15e7e746
commit 4020ff83c6
2 changed files with 312 additions and 199 deletions
+35 -27
View File
@@ -55,15 +55,16 @@ goes through the candidates one by one.
counted by /64 by default.
- Size and time limits on requests and responses, both between the client and
`smallwebwaf` and between `smallwebwaf` and the app: by default a request may
take 60 seconds and 100 MB, a response 30 minutes and 5 GB.
take 60 seconds and 100 MiB, a response 30 minutes and 5 GiB.
- Rate limits per client on requests per minute, per hour and per day, and on
bytes per minute, per hour and per day, on by default and set well above what
real visitors need.
- Netblocks that bypass rate limiting, netblocks that bypass everything, and
netblocks that are always refused.
- AS number and country lookup for every client, off until you choose a source:
the IPinfo Lite database file, which you download and mount, or the free GeoJS
web service (see "Country and AS number lookup" below).
- AS number and country lookup for every client, on by default through the free
GeoJS web service, which is sent the address of every new visitor. The IPinfo
Lite database file, which you download and mount, can be used instead, or
lookups switched off (see "Country and AS number lookup" below).
- Country lists: `DENIED_COUNTRIES` refuses every request from the countries
listed, `EXCLUSIVELY_ALLOWED_COUNTRIES` every request from anywhere else. Such
a request gets the answer a banned client gets as soon as the client's address
@@ -78,7 +79,9 @@ goes through the candidates one by one.
scanning and penetration probes; easy to edit by hand, and picked up while
running;
- the OWASP Core Rule Set, run by the Coraza engine, refusing the requests
it flags;
it flags; it reads the URL and headers of every request, and request
bodies only once you switch that on, since on a code forge they are full
of code it would take for attacks;
- trap paths, and a ban for a client that the rule files or the Core Rule
Set refuse again and again.
- Bans:
@@ -102,14 +105,17 @@ goes through the candidates one by one.
- Request log: one JSON object per request on stdout with the usual web log
fields, the decision taken and why, AS number and country, and timings.
Optionally also sent to a remote syslog server.
- Prometheus metrics on their own port.
- Prometheus metrics, for a scraper that holds the metrics token.
- State (bans with their notes, each client's counters and history, the GeoJS
answers, the reputation cache, the alerting state) held in memory and kept in
readable JSON files, written regularly and at every stop, so a restart loses
nothing. Edit a file, or add a rule file, and the running `smallwebwaf` picks
up the change. Nothing is read from disk while serving a request.
- A small admin endpoint for health checks, listing, adding and lifting bans,
and asking why a given address was refused.
- Health checks, the metrics, and listing, adding and lifting bans or asking why
a given address was refused, all on the one port every request uses: under
`/_smallwebwaf/` on the app's own address, through traefik like any other
request. The metrics need the metrics token, and the ban management the admin
token.
Not planned: TLS termination, routing for several apps, browser challenges
(captcha or proof of work), a web console, or defence against floods large
@@ -182,30 +188,32 @@ failure behaviour and the build order.
## Country and AS number lookup
AS number and country lookups, and the country lists and biased limits that use
them, are off until you choose one of two sources with `LOOKUP_SOURCE`.
`LOOKUP_SOURCE=file` reads the free IPinfo Lite database (`ipinfo_lite.mmdb`).
You download it with your own IPinfo account, mount the directory that holds it
into the container, point `LOOKUP_DB_PATH` at the file and refresh it when you
choose; `smallwebwaf` never downloads it itself, and reads it again when you
replace it. It has to be the directory rather than the file itself: docker does
not show a single mounted file being replaced, so a refresh would go unseen.
IPinfo releases it under the Creative Commons Attribution-ShareAlike 4.0
International License and asks for attribution, in its own words on
https://ipinfo.io/lite: "The attribution requirements can be met by giving our
service credit as your data source. Simply place a link to IPinfo on the
website, application, or social media account that uses our data." Its example
of such a credit is a link mentioning "IP address data is powered by IPinfo". A
service that uses the database through `smallwebwaf` should carry that link.
`LOOKUP_SOURCE=geojs` asks the free GeoJS web service instead, with no account
and no file. Every new visitor's address is sent to GeoJS. Each answer is kept
`smallwebwaf` looks up the AS number and country of every client, for the
request log, the metrics and the ban notes, and for the country lists and biased
limits when you set them. It works with no setup: by default it asks the free
GeoJS web service, which needs no account and no file. This means that, by
default, the address of every new visitor is sent to GeoJS. Each answer is kept
for seven days, across restarts, and many addresses are asked about in one
request. GeoJS publishes no rate limit but may block a caller it thinks asks too
much; while it is not answering, new visitors count as coming from an unknown
country, which `EXCLUSIVELY_ALLOWED_COUNTRIES` refuses.
To keep your visitors' addresses on your own host, set `LOOKUP_SOURCE=off`, or
use the database file instead of GeoJS: `LOOKUP_SOURCE=file` reads the free
IPinfo Lite database (`ipinfo_lite.mmdb`). You download it with your own IPinfo
account, mount the directory that holds it into the container, point
`LOOKUP_DB_PATH` at the file and refresh it when you choose; `smallwebwaf` never
downloads it itself, and reads it again when you replace it. It has to be the
directory rather than the file itself: docker does not show a single mounted
file being replaced, so a refresh would go unseen. IPinfo releases it under the
Creative Commons Attribution-ShareAlike 4.0 International License and asks for
attribution, in its own words on https://ipinfo.io/lite: "The attribution
requirements can be met by giving our service credit as your data source. Simply
place a link to IPinfo on the website, application, or social media account that
uses our data." Its example of such a credit is a link mentioning "IP address
data is powered by IPinfo". A service that uses the database through
`smallwebwaf` should carry that link.
Neither source can place a private address, so a client on one, such as a
visitor on your local network, another container or your monitoring, has no
country: `EXCLUSIVELY_ALLOWED_COUNTRIES` refuses it unless you list it in