Files
smallwebwaf/docs/settings.md
T
clawbot 344ce13c96
check / check (push) Failing after 14m44s
rproxy and servewaf replace the one-process smallwebwaf (closes #172, closes #165)
`smallwebwaf` with no command now prints the commands and exits with status 2.
The image runs rproxy, or servewaf with SMALLWEBWAF_COMMAND=servewaf. The
passing-on code, its settings and its log fields and metric go. internal/proxy
keeps the checks, answers, reports and own endpoints; its tests run through the
real rproxy. The documents describe the pair.

Judgement call: clients.json keeps token_refused, since state files refuse unknown fields.
Deviation: script/gitea-requests sets SWWAF_LOG_REQUEST_HEADERS, to label requests.
Partially verified: make example-app and make gitea-requests never built the image, as Ubuntu's snapshot service answered 5xx.

Model: opus-5-5
2026-11-05 13:15:05 +01:00

403 lines
20 KiB
Markdown

# Settings
Every setting, its default and the values it takes, in the groups of "Settings"
in [`SPEC.md`](../SPEC.md). That section gives the conventions, the settings
that take `off`, the `_FILE` form, and what each group does and why.
The settings that start with `SWWAF_` are `servewaf`'s. Those that start with
`SWRP_` are `rproxy`'s, and are under "`rproxy`" below. `SMALLWEBWAF_COMMAND`
chooses between the two (see "The image's command" below).
## Forms of values
- A duration is Go's syntax, with `d` for days: `90s`, `15m`, `24h`, `7d`. Zero
and negative durations are refused.
- A size is a number of bytes with an optional `K`, `M` or `G`, which are powers
of 1024, so `1K` is 1024 bytes. Examples: `512K`, `100M`, `5G`.
- A count is a whole number of requests, such as `1000`.
- A list is comma separated, with spaces around items ignored. An empty item is
refused.
- A netblock is an address and a prefix length, such as `10.0.0.0/8`. A bare
address stands for itself alone.
- A country is a two-letter code of ISO 3166-1, in either case: `de` and `DE`
are the same. `xk` stands for Kosovo. A code that is not a country is refused,
such as `nk` (North Korea is `kp`) or the withdrawn `su`.
- An AS number is `AS` and the number, in either case, such as `AS64496`.
- A percentage is a whole number from 0 to 100.
- A path prefix starts with `/`, such as `/assets/`.
## Core
- `SWWAF_LISTEN_ADDR` (default `:8080`): where `servewaf` listens: `:` and a
port, such as `:9000`. Leave the host out (see "Ports" in
[`SPEC.md`](../SPEC.md)).
- `SWWAF_INSTANCE_NAME` (default: the container's host name): the `instance`
name in each line `servewaf` logs, and in each alert and metric, such as
`fsn1/servewaf`. Valid UTF-8. No control character while
`SWWAF_ALERT_NTFY_URL` is set. 1 to 48 printable ASCII characters without a
space while `SWWAF_LOG_REMOTE_URL` is set and `SWWAF_LOG_REMOTE_APP_NAME` is
not.
- `SWWAF_MODE` (default `enforce`): `enforce` or `observe`.
- `SWWAF_IPV6_GROUP_PREFIX` (default `64`): the prefix length of an IPv6
client's group, from 32 to 128.
- `SWWAF_MAX_TRACKED_CLIENTS` (default `20000`): the most clients kept, a whole
number above zero.
- `SWWAF_MAX_BANS` (default `5000`): how many of its own bans `servewaf` keeps,
a whole number above zero.
## Tokens
- `SWWAF_ADMIN_TOKEN` (default unset): the token for the ban endpoints and
`/_smallwebwaf/clients/<ip>`. At least 32 characters.
- `SWWAF_METRICS_TOKEN` (default unset): the token for `/_smallwebwaf/metrics`.
At least 32 characters.
## State files
- `SWWAF_STATE_DIR` (default `/var/lib/smallwebwaf`): the directory of the state
files, an absolute path that exists and can be written.
- `SWWAF_STATE_WRITE_DELAY` (default `10s`): how long `bans.json` waits to be
written after a ban is made, lifted or made permanent. A duration.
- `SWWAF_STATE_COUNTER_INTERVAL` (default `15m`): how often every state file is
written. A duration.
## Logging
- `SWWAF_LOG_LEVEL` (default `info`): the least severe of `servewaf`'s own
messages that are written: `debug`, `info`, `warn` or `error`.
- `SWWAF_LOG_REQUEST_HEADERS` (default
`accept,accept-language,accept-encoding,content-type,origin,range`): the
request headers recorded in the log line, in either case. `Host` and
`Transfer-Encoding` are refused; the host is the field `host`.
- `SWWAF_LOG_REMOTE_URL` (default unset): a syslog server that also gets every
line written to stdout: `syslog+udp://`, `syslog+tcp://` or `syslog+tls://`,
each with a host and a port and nothing more.
- `SWWAF_LOG_REMOTE_TLS_CA_FILE` (default unset): a file of PEM certificates
that a `syslog+tls` server's certificate must chain to, instead of the host's
own. A file that cannot be read, or holds no certificate, is refused.
- `SWWAF_LOG_REMOTE_BUFFER` (default `10000`): the most lines held while they
wait to be sent, a whole number above zero.
- `SWWAF_LOG_REMOTE_FACILITY` (default `local0`): the syslog facility: `kern`,
`user`, `mail`, `daemon`, `auth`, `syslog`, `lpr`, `news`, `uucp`, `cron`,
`authpriv`, `ftp`, or `local0` to `local7`.
- `SWWAF_LOG_REMOTE_APP_NAME` (default: `SWWAF_INSTANCE_NAME`): the app name in
each syslog record, 1 to 48 printable ASCII characters without a space.
## Metrics
- `SWWAF_METRICS_TOP_N` (default `50`): how many AS numbers and how many
countries get series of their own. A whole number above zero.
## Static lists
- `SWWAF_ALLOW_NETS` (default empty): netblocks you trust, such as your
monitoring (see step 3 in [`SPEC.md`](../SPEC.md)).
- `SWWAF_RATE_LIMIT_EXEMPT_NETS` (default empty): netblocks whose clients are
exempt from rate limiting and the byte limits.
- `SWWAF_DENY_NETS` (default empty): netblocks whose clients are refused (see
step 3 in [`SPEC.md`](../SPEC.md)).
## Request rate limits, per client
- `SWWAF_RATE_LIMIT_PER_MINUTE` (default `500`, about 8 a second): the most
requests a client may make in a minute. A count or `off`. The default catches,
within seconds, a client firing requests as fast as it can. It leaves room for
a fleet of about 20 agents at its busiest, about 120 requests a minute. It
also leaves room for a few quick first visits to gitea with an empty cache,
about 40 files each.
- `SWWAF_RATE_LIMIT_PER_HOUR` (default `20000`, about 5.5 a second): the most
requests a client may make in an hour. A count or `off`. The default leaves
room for an automation fleet at work. It catches a client that keeps up more
than about 5.5 a second for an hour.
- `SWWAF_RATE_LIMIT_PER_DAY` (default `300000`, about 3.5 a second): the most
requests a client may make in a day. A count or `off`. The default catches a
scraper that keeps up 4 a second around the clock.
- Counting cannot catch a client that stays under all three limits (see "Request
rate limits, per client" in [`SPEC.md`](../SPEC.md)).
- `SWWAF_RATE_LIMIT_EXEMPT_PATHS` (default empty): path prefixes exempt from
rate limiting and the byte limits, such as `/assets/`.
## Byte limits, per client
- `SWWAF_BYTES_LIMIT_PER_MINUTE` (default `4G`), `SWWAF_BYTES_LIMIT_PER_HOUR`
(default `20G`) and `SWWAF_BYTES_LIMIT_PER_DAY` (default `200G`): the most
bytes counted for a client in each window. A size or `off`.
- `SWWAF_BYTES_COUNT` (default `both`): which body bytes the byte limits count:
`response`, `request` or `both`.
## Lookup of AS number and country
- `SWWAF_LOOKUP_SOURCE` (default `geojs`): `geojs`, `file` or `off`. `off` stops
the start while a setting that needs a lookup is set:
- the two country lists;
- `SWWAF_ADD_LOOKUP_HEADERS`;
- the four biased threshold lists and `SWWAF_ASN_LIMIT_PERCENT_URL`;
- `SWWAF_UNKNOWN_LIMIT_PERCENT` below `100`;
- an anomaly threshold per AS number.
- `SWWAF_LOOKUP_DB_PATH` (default empty): the IPinfo Lite database file, in its
`.mmdb` form, for `SWWAF_LOOKUP_SOURCE=file`.
- `SWWAF_LOOKUP_TIMEOUT` (default `800ms`): how long a request waits for its
client's first answer from GeoJS, and how long a request to GeoJS may take. A
duration; never `off`.
- `SWWAF_ADD_LOOKUP_HEADERS` (default `false`): `true` has `rproxy` pass the app
the client's AS number in `X-Client-ASN` and its country in
`X-Client-Country`.
## Country lists
- `SWWAF_DENIED_COUNTRIES` (default empty): countries whose clients are refused,
such as `cn,ru,kp`.
- `SWWAF_EXCLUSIVELY_ALLOWED_COUNTRIES` (default empty): when set, the only
countries whose clients get through, such as `us,de`. A country on both lists
stops the start.
## Biased thresholds
- `SWWAF_ASN_LIMIT_PERCENT` (default empty): a list of AS numbers, each with `:`
and a percentage, such as `AS14061:50,AS16276:50,AS45102:25`. An AS number
listed twice is refused.
- `SWWAF_COUNTRY_LIMIT_PERCENT` (default empty): the same by country, such as
`cn:25,ru:50`.
- `SWWAF_ASN_BYTES_PERCENT` and `SWWAF_COUNTRY_BYTES_PERCENT` (default empty):
the same, for the byte limits alone.
- `SWWAF_UNKNOWN_LIMIT_PERCENT` (default `100`): the percentage for a client the
lookup cannot place.
- `SWWAF_ASN_LIMIT_PERCENT_URL` (default unset): the `http` or `https` URL of a
text file with one AS number and percentage on each line, such as
`AS64496:50`. A URL that is also in `SWWAF_BLOCKLIST_URLS` is refused.
## Attack detection
- `SWWAF_RULES_DIR` (default `/etc/smallwebwaf/rules.d`): the directory of the
rule files.
- `SWWAF_RULES_ENABLED` (default `true`): `false` reads no rule file.
- `SWWAF_TRAP_PATHS` (default empty): paths only scanners ask for, such as
`/wp-login.php,/xmlrpc.php`. Each starts with `/` and holds no `?`.
- `SWWAF_WAF_MODE` (default `block`): `off`, `detect` or `block`.
- `SWWAF_WAF_PARANOIA_LEVEL` (default `1`): the Core Rule Set's paranoia level,
from 1 to 4.
- `SWWAF_WAF_ANOMALY_THRESHOLD` (default `5`): the score at which a request is a
match. A whole number above zero, or `off`.
- `SWWAF_WAF_DISABLED_RULES` (default
`920340,920420,920440,920640,930130,930140`): the ids of Core Rule Set rules
to switch off. Each a whole number above zero. An id from 900000 to 900999 is
refused: those are the rules that set the Core Rule Set up, and
`smallwebwaf`'s own.
- `SWWAF_WAF_EXEMPT_PATHS` (default empty): path prefixes the Core Rule Set does
not inspect.
- `SWWAF_WAF_BODY_LIMIT` (default `off`): `off`, or how much of a request body
the Core Rule Set reads, a size of at most `64K`. A question carries no more
of a body than 64 KiB, and the Core Rule Set reads at most 64 KiB less one
byte of it, whatever the value (see "The answer" in [`SPEC.md`](../SPEC.md)).
- `SWWAF_ERROR_BURST_THRESHOLD` (default `30`): the most refusals of one client
in a minute (see "Attack detection" in `SPEC.md`). A count or `off`.
## Ban lengths, response and scope
- `SWWAF_BAN_RESPONSE` (default `403`): `403`, `429` or `close`.
- `SWWAF_LIMIT_BAN_DURATION` (default `1h`): the ban for a first broken limit. A
duration.
- `SWWAF_LIMIT_BAN_REPEAT_WINDOW` (default `24h`): the time after a ban ends
within which breaking a limit again lengthens the next ban. A duration.
- `SWWAF_MAX_BAN_DURATION` (default `7d`): the longest ban for a broken limit;
one that would be longer is permanent. A duration.
- `SWWAF_ATTACK_BAN_DURATION` (default `7d`): the ban for a first clear sign of
attack. A duration.
- `SWWAF_BAN_SCOPE_V4_PREFIX` (default `32`): how much a ban on an IPv4 client
covers: the prefix length of its netblock, from 0 to 32.
## Reputation
- `SWWAF_BLOCKLIST_URLS` (default empty): blocklists to fetch, such as
`https://www.spamhaus.org/drop/drop.txt`. Each an `http` or `https` URL
without a user or a fragment. A URL listed twice is refused.
- `SWWAF_BLOCKLIST_REFRESH` (default `24h`): how long a list waits after its
last try before it is fetched again. A duration of `1h` or more.
- `SWWAF_BLOCKLIST_ACTION` (default `deny`): `deny`, `limit:<percent>` or `log`.
- `SWWAF_DNSBL_ZONES` (default empty): the DNS blocklists to ask, such as
`dnsbl.dronebl.org`. Each a DNS name of at most 189 characters. A Spamhaus
zone carries its key, as in `<key>.xbl.dq.spamhaus.net`. A zone listed twice
is refused, whatever its case or key.
- `SWWAF_DNSBL_RESOLVER` (default unset): the resolver to ask the zones through,
such as `192.0.2.53` or `[2001:db8::53]:5353`. An IP address with an optional
port, 53 when none is given. Unset, the host's resolver is used.
- `SWWAF_ABUSEIPDB_KEY` (default unset): the key of your AbuseIPDB account. A
control character in it is refused.
- `SWWAF_ABUSEIPDB_MIN_SCORE` (default `75`): the least abuse confidence score,
from 0 to 100, that is a hit.
- `SWWAF_ABUSEIPDB_DAILY_BUDGET` (default `900`): the most checks made in a day,
a whole number above zero. AbuseIPDB's free accounts may make 1,000.
- `SWWAF_CROWDSEC_LAPI_URL` (default unset): the local API of a CrowdSec engine
you run, such as `http://172.17.0.1:8080`. An `http` or `https` URL without a
user or a fragment; its decision list is at `v1/decisions` under it. Set
without the key, it is refused. So is a decision list URL that is also in
`SWWAF_BLOCKLIST_URLS`, or that is `SWWAF_ASN_LIMIT_PERCENT_URL`.
- `SWWAF_CROWDSEC_LAPI_KEY` (default unset): the key the engine gave for
`smallwebwaf`, as `cscli bouncers add smallwebwaf` prints it. Set without the
URL, it is refused.
- `SWWAF_REPUTATION_ACTION` (default `limit:25`): `deny`, `limit:<percent>` or
`log`, for DNS blocklist and AbuseIPDB hits.
- `SWWAF_REPUTATION_CACHE_TTL` (default `24h`): how long a zone's verdict or
AbuseIPDB's score is used. A duration.
- `SWWAF_REPUTATION_TIMEOUT` (default `2s`): how long a query to a zone or a
check with AbuseIPDB may take. A duration.
## Alerting
- `SWWAF_ALERT_WEBHOOK_URL` (default unset): the webhook that receives each
alert as a JSON object. An `http` or `https` URL without a user or a fragment.
- `SWWAF_ALERT_WEBHOOK_HEADERS` (default empty): headers sent with each post,
such as `Authorization:Bearer 0123456789abcdef`. A list of name, `:` and
value. A value cannot hold a comma.
- `SWWAF_ALERT_SLACK_WEBHOOK_URL` (default unset): the Slack incoming webhook
that receives each alert as a message, checked as the webhook URL is.
- `SWWAF_ALERT_NTFY_URL` (default unset): the ntfy topic that receives each
alert, as the topic's full URL, such as `https://ntfy.sh/my-alerts`, checked
as the webhook URL is.
- `SWWAF_ALERT_NTFY_TOKEN` (default unset): an ntfy access token. A control
character in it is refused.
- `SWWAF_ALERT_EVENTS` (default
`ban,permanent_ban,waf_block,anomaly,reputation_hit,source_failure,file_error`):
the event types alerts are sent for, from those in [`alerts.md`](alerts.md)
but `summary`. Any other name is refused.
- `SWWAF_ALERT_COOLDOWN` (default `15m`): how long after an alert is sent its
repeats are held back. A duration or `off`.
- `SWWAF_ALERT_MAX_PER_HOUR` (default `60`): the most alerts sent in an hour. A
whole number above zero, or `off`.
## Anomaly thresholds
Each scope has four thresholds: the most requests and the most bytes counted in
a minute and in an hour. Requests are counts, bytes are sizes. Each is `off` by
default. The four thresholds per client are:
- `SWWAF_ANOMALY_CLIENT_REQUESTS_PER_MINUTE`;
- `SWWAF_ANOMALY_CLIENT_REQUESTS_PER_HOUR`;
- `SWWAF_ANOMALY_CLIENT_BYTES_PER_MINUTE`;
- `SWWAF_ANOMALY_CLIENT_BYTES_PER_HOUR`.
The other scopes' names have another prefix before the same four endings, such
as `REQUESTS_PER_MINUTE`:
- `SWWAF_ANOMALY_NET_...`: per netblock around a client. Two settings give the
netblock's length:
- `SWWAF_ANOMALY_NET_V4_PREFIX` (default `24`, from 0 to 32) for an IPv4
client;
- `SWWAF_ANOMALY_NET_V6_PREFIX` (default `48`, from 0 to 128) for an IPv6
client.
- `SWWAF_ANOMALY_ASN_...`: per AS number.
- `SWWAF_ANOMALY_TOTAL_...`: the whole service.
- `SWWAF_WATCH_...`: each netblock in `SWWAF_WATCH_NETS`, as a whole.
`SWWAF_WATCH_NETS` (default empty) is a list of name, `=` and netblock, such
as `office=203.0.113.0/24,scraper-x=198.51.100.0/22`. An item without `=` or a
name, or with a bad netblock, is refused. So is a name used twice.
## Settings given as files
To give a token as a file, as "Tokens" under "Deployment" in
[`SPEC.md`](../SPEC.md) describes, make the file on the host:
```sh
mkdir -p /srv/app/tokens
openssl rand -hex 32 > /srv/app/tokens/metrics
chown 65532:65532 /srv/app/tokens/metrics
chmod 0400 /srv/app/tokens/metrics
```
Then mount its directory into the container read-only, and name the file:
```sh
docker run \
-v /srv/app/tokens:/etc/smallwebwaf/tokens:ro \
-e SMALLWEBWAF_COMMAND=servewaf \
-e SWWAF_METRICS_TOKEN_FILE=/etc/smallwebwaf/tokens/metrics \
...
```
## Fixed limits
A few limits are fixed rather than settings:
- at most 100,000 GeoJS answers are kept;
- at most 100,000 DNS blocklist verdicts and 100,000 AbuseIPDB scores are kept;
- at most 1,000 DNS blocklist queries are under way at once;
- at most 20,000 anomaly counters are kept;
- each alert destination's queue holds at most 1,000 alerts;
- `servewaf` holds at most 10,000 answered requests awaiting their reports;
- `servewaf` refuses a request with more than 50 cookies or more than 100 header
fields (see "Answering in time" under "`servewaf`" in
[`SPEC.md`](../SPEC.md));
- `rproxy` takes a request line and headers of at most 32 KiB, and closes a
kept-open connection idle for 120 seconds;
- `rproxy` reads at most 1 MiB of the body of `servewaf`'s answer, and answers
`502` to a longer one.
## `servewaf`'s answer and `rproxy`'s wait
`SWWAF_LOOKUP_TIMEOUT` is the longest a question waits on an outside source.
Keep it well under `rproxy`'s `SWRP_TIMEOUT_MS`, as the default is. No question
waits for `SWWAF_REPUTATION_TIMEOUT`, since the DNS blocklists and AbuseIPDB are
asked in the background (see "Answering in time" under "`servewaf`" in
[`SPEC.md`](../SPEC.md)).
## `rproxy`
`smallwebwaf rproxy` reads the settings below and no others. They are read once
at start, and an invalid value stops the start. None can be given as a file,
since `rproxy` never touches disk. "`rproxy`" in [`SPEC.md`](../SPEC.md) says
what they do.
A time here is a whole number of milliseconds above zero, such as `2000`.
- `SWRP_WAF_BACKEND` (required, no default): `servewaf`'s URL. An `http` or
`https` URL with a host and an optional port, and nothing more. `rproxy`
speaks HTTP/1.1 to it.
- `SWRP_TIMEOUT_MS` (default `2000`): how long `rproxy` waits for `servewaf`'s
answer, to a question and to a report. A time; never `off`.
- `SWRP_DEFAULT_DISPOSITION` (default `allow`): what is done with a request when
`servewaf` cannot be connected to, does not answer at all, or does not answer
within `SWRP_TIMEOUT_MS`. `allow` passes it to the app, and `deny` answers
`503`. It does not apply to an answer `servewaf` gives, such as an error.
- `SWRP_MAX_REQUEST_SIZE` (default `100M`): the largest request body. A size or
`off`.
- `SWRP_MAX_RESPONSE_SIZE` (default `1G`): the largest response body. A size or
`off`.
- `SWRP_MAX_REQUEST_TIME_MS` (default `60000`): how long the visitor may take to
send its request line and headers. It then also bounds, from the end of the
headers, the time until the app has been sent the whole request. A time or
`off`.
- `SWRP_MAX_RESPONSE_TIME_MS` (default `1800000`): how long the answer may take,
from the end of the request to its last byte reaching the visitor. A time or
`off`.
- `SWRP_LISTEN_ADDR` (default `:8080`): where `rproxy` listens, such as `:9000`.
- `SWRP_UPSTREAM_URL` (default `http://127.0.0.1:8081`): the app. An `http` or
`https` URL with a host and an optional port, and nothing more.
- `SWRP_TRUSTED_PROXIES` (default `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16`):
the netblocks whose `X-Forwarded-For` is believed. Set but empty, it trusts
nothing. A client outside these netblocks cannot claim another address. Where
other containers reach `rproxy` directly, or visitors on a private network
reach traefik, they can name another address in the header. Narrowing the
setting to traefik's own network closes that gap.
## The image's command
`SMALLWEBWAF_COMMAND` (default `rproxy`) chooses what the image runs and what
its health check checks: `rproxy` or `servewaf` (see "Deployment" in
[`SPEC.md`](../SPEC.md)). It cannot be given as a file.
## Where earlier settings went
Nothing reads these names any more:
- `SWWAF_UPSTREAM_URL` is `SWRP_UPSTREAM_URL`.
- `SWWAF_TRUSTED_PROXIES` is `SWRP_TRUSTED_PROXIES`.
- `SWWAF_REQUEST_MAX_BYTES` is `SWRP_MAX_REQUEST_SIZE`.
- `SWWAF_RESPONSE_MAX_BYTES` is `SWRP_MAX_RESPONSE_SIZE`.
- `SWWAF_CLIENT_REQUEST_TIMEOUT` and `SWWAF_UPSTREAM_REQUEST_TIMEOUT` are
`SWRP_MAX_REQUEST_TIME_MS`, in milliseconds.
- `SWWAF_CLIENT_RESPONSE_TIMEOUT` and `SWWAF_UPSTREAM_RESPONSE_TIMEOUT` are
`SWRP_MAX_RESPONSE_TIME_MS`, in milliseconds.
- `SWWAF_CLIENT_IDLE_TIMEOUT` and `SWWAF_CLIENT_REQUEST_HEADER_MAX_BYTES` have
no setting. `rproxy` closes a kept-open connection idle for 120 seconds, and
takes a request line and headers of at most 32 KiB.