check / check (push) Waiting to run
docs/settings.md is now the one settings reference; the spec keeps the conventions, the off list and each group's requirements and reasons, and links to it for the per-setting entries. The endpoint list, the ban notes, the state file contents, the rule file format and the alert object likewise live in docs/ and are linked from the spec. The package list from the README's Design section joins "Architecture". The nine docs files are byte-identical to those on the README branch. Package comments that named "Alert webhook schema", "Request log" and "Metrics endpoint" for their field lists now name the docs files. Model: fable-5-1
484 lines
26 KiB
Markdown
484 lines
26 KiB
Markdown
# Settings
|
|
|
|
Every setting is an environment variable whose name starts with `SWWAF_`. Every
|
|
setting has a default, so none has to be set. Settings are read once, at start.
|
|
To change one, restart the container.
|
|
|
|
A setting that is set to a value it cannot take stops the start. The process
|
|
writes one line, `"type":"process"` with `"msg":"invalid setting"`, naming the
|
|
variable and what is wrong with the value. It exits with status 1, and runit
|
|
starts it again every second until the value is fixed. The entries below say
|
|
which values each setting takes. Any other value stops the start. At start, the
|
|
settings in effect are logged on one line. Tokens, keys and the paths of
|
|
webhooks are shown as `********`.
|
|
|
|
The 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: `1K` is 1024 bytes, `512K`, `100M`, `5G`.
|
|
- A count is a whole number of requests, such as `1000`.
|
|
- `off` switches a setting off, where its entry below says it takes `off`.
|
|
- A list is comma separated, with spaces around items ignored. An empty item is
|
|
refused. A list set but empty is an empty list, and replaces the default.
|
|
- 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/`.
|
|
|
|
## Where it listens and what it protects
|
|
|
|
- `SWWAF_LISTEN_ADDR` (default `:8080`): where `smallwebwaf` listens, an
|
|
optional host and a port. Keep the host empty: `:9000`, never
|
|
`127.0.0.1:9000`. The app must not listen on that port. The health check
|
|
follows the setting, and traefik's label must name the port.
|
|
- `SWWAF_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: no path, user
|
|
or query.
|
|
- `SWWAF_INSTANCE_NAME` (default: the container's host name, the first 12
|
|
characters of the container's id): the `instance` name in every log line,
|
|
alert and metric. Set it to a name that survives deploys and tells instances
|
|
apart, such as `fsn1app1/gitea`. It must be valid UTF-8. While
|
|
`SWWAF_ALERT_NTFY_URL` is set, it may hold no control character. While
|
|
`SWWAF_LOG_REMOTE_URL` is set and `SWWAF_LOG_REMOTE_APP_NAME` is not, it must
|
|
be 1 to 48 printable ASCII characters without a space.
|
|
- `SWWAF_MODE` (default `enforce`): `enforce`, or `observe` to ban no one and
|
|
log what `enforce` would have done. Observe mode still refuses a request over
|
|
a size or time limit, and one without the right token.
|
|
- `SWWAF_TRUSTED_PROXIES` (default `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16`,
|
|
the private address ranges): the netblocks whose `X-Forwarded-For` is
|
|
believed, normally where traefik comes from. Set but empty, it trusts nothing:
|
|
every TCP peer is then the client.
|
|
- `SWWAF_IPV6_GROUP_PREFIX` (default `64`): the length of the netblock that is
|
|
one IPv6 client, from 32 to 128. After a change, each IPv6 client starts
|
|
afresh. Existing bans keep refusing their netblocks until they end.
|
|
- `SWWAF_MAX_TRACKED_CLIENTS` (default `20000`): the most clients kept in memory
|
|
and in `clients.json`, a whole number above zero. Past it, the least recently
|
|
seen client is dropped first.
|
|
|
|
## Time and size limits
|
|
|
|
Each request has limits on both sides: between the client and `smallwebwaf`, and
|
|
between `smallwebwaf` and the app. Bodies stream through in both directions,
|
|
except the part the Core Rule Set reads while `SWWAF_WAF_BODY_LIMIT` is set.
|
|
|
|
- `SWWAF_CLIENT_REQUEST_TIMEOUT` (default `60s`): how long a client may take to
|
|
send its request line and headers, and then, from the end of the headers, its
|
|
body. A duration or `off`.
|
|
- `SWWAF_CLIENT_REQUEST_HEADER_MAX_BYTES` (default `32K`): the largest request
|
|
line and headers. A size over `4K`. It cannot be `off`: Go's HTTP server
|
|
always has such a limit.
|
|
- `SWWAF_CLIENT_IDLE_TIMEOUT` (default `120s`): how long a kept-open connection
|
|
may wait for its next request before it is closed. A duration or `off`. The
|
|
default is longer than the 90 seconds after which traefik closes an unused
|
|
connection, so traefik closes first.
|
|
- `SWWAF_CLIENT_RESPONSE_TIMEOUT` (default `30m`): how long the answer may take
|
|
to reach the client, from the end of the request to the last byte. A duration
|
|
or `off`.
|
|
- `SWWAF_UPSTREAM_REQUEST_TIMEOUT` (default `60s`): how long connecting to the
|
|
app and sending it the whole request may take. A duration or `off`.
|
|
- `SWWAF_UPSTREAM_RESPONSE_TIMEOUT` (default `30m`): how long the app may take
|
|
to send its whole answer. A duration or `off`.
|
|
- `SWWAF_REQUEST_MAX_BYTES` (default `100M`): the largest request body. A size
|
|
or `off`. A request that announces a larger body is refused before anything
|
|
reaches the app.
|
|
- `SWWAF_RESPONSE_MAX_BYTES` (default `5G`): the largest response body. A size
|
|
or `off`.
|
|
|
|
In front of a code forge, a clone is a response and a push is a request. At the
|
|
defaults, an upload over 100 MiB or slower than 60 seconds is cut off. A forge
|
|
that takes large uploads needs `SWWAF_REQUEST_MAX_BYTES`,
|
|
`SWWAF_CLIENT_REQUEST_TIMEOUT` and `SWWAF_UPSTREAM_REQUEST_TIMEOUT` raised to
|
|
fit.
|
|
|
|
What the client gets when a limit is passed is under "Size and time limits" in
|
|
[`SPEC.md`](../SPEC.md).
|
|
|
|
## Netblock lists
|
|
|
|
- `SWWAF_ALLOW_NETS` (default empty): netblocks whose clients skip every check,
|
|
such as your monitoring or your own network. The size and time limits still
|
|
apply to them. They are not looked up. They still need a token for the admin
|
|
endpoints.
|
|
- `SWWAF_RATE_LIMIT_EXEMPT_NETS` (default empty): netblocks whose clients are
|
|
exempt from rate limiting and the byte limits, such as a machine that talks to
|
|
the app all day. Everything else still applies to them.
|
|
- `SWWAF_DENY_NETS` (default empty): netblocks whose clients are always refused.
|
|
A client in both lists is let through.
|
|
|
|
## Rate limiting and byte limits
|
|
|
|
- `SWWAF_RATE_LIMIT_PER_MINUTE` (default `1000`), `SWWAF_RATE_LIMIT_PER_HOUR`
|
|
(default `10000`) and `SWWAF_RATE_LIMIT_PER_DAY` (default `50000`): the most
|
|
requests a client may make in each window. A count or `off`. The defaults are
|
|
several times what one busy person produces. A browser loading a heavy page
|
|
makes a few hundred requests, and several people often share one address.
|
|
- `SWWAF_RATE_LIMIT_EXEMPT_PATHS` (default empty): path prefixes exempt from
|
|
rate limiting, such as `/assets/`. Their bytes are not counted either. The
|
|
path is matched exactly as sent, without the query, and is not
|
|
percent-decoded. `/assets/` matches `/assets/app.js`. It does not match
|
|
`/Assets/app.js`, `/%61ssets/app.js` or `/static/assets/app.js`. There are no
|
|
wildcards. A path holding `..`, a backslash or an encoded slash (`%2F`) is
|
|
never exempt, since the app may read it as a path outside the prefix.
|
|
- `SWWAF_BYTES_LIMIT_PER_MINUTE` (default `10G`), `SWWAF_BYTES_LIMIT_PER_HOUR`
|
|
(default `20G`) and `SWWAF_BYTES_LIMIT_PER_DAY` (default `50G`): the most
|
|
bytes a client may have counted in each window. A size or `off`. Bytes are
|
|
counted once the answer has ended, so a byte limit never cuts an answer short.
|
|
The client's next request is refused instead. Each default is above the
|
|
largest request and answer together, so at the defaults no single download
|
|
breaks a limit.
|
|
- `SWWAF_BYTES_COUNT` (default `both`): which body bytes the byte limits count:
|
|
`response`, `request` or `both`. For a WebSocket, what it carried each way
|
|
counts once it closes.
|
|
|
|
## Looking up the AS number and country
|
|
|
|
- `SWWAF_LOOKUP_SOURCE` (default `geojs`): `geojs`, `file` or `off`. `geojs`
|
|
asks the GeoJS web service, which is then told the address of every new
|
|
visitor. `file` looks clients up in the IPinfo Lite database file at
|
|
`SWWAF_LOOKUP_DB_PATH`, and sends nothing off the host. `off` looks up no
|
|
client. With `off`, a setting that needs lookups stops the start:
|
|
- a country list;
|
|
- `SWWAF_ADD_LOOKUP_HEADERS`;
|
|
- an entry in `SWWAF_ASN_LIMIT_PERCENT`, `SWWAF_COUNTRY_LIMIT_PERCENT`,
|
|
`SWWAF_ASN_BYTES_PERCENT` or `SWWAF_COUNTRY_BYTES_PERCENT`;
|
|
- `SWWAF_UNKNOWN_LIMIT_PERCENT` below 100;
|
|
- `SWWAF_ASN_LIMIT_PERCENT_URL`;
|
|
- 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`. `file` without it stops the
|
|
start, and so does setting it with any other source. A file that is missing or
|
|
cannot be read stops the start too.
|
|
- `SWWAF_LOOKUP_TIMEOUT` (default `1s`): how long a request waits for its
|
|
client's first answer from GeoJS when a setting needs it, and how long a
|
|
request to GeoJS may take. A duration; it cannot be `off`.
|
|
- `SWWAF_ADD_LOOKUP_HEADERS` (default `false`): `true` passes the app the
|
|
client's AS number in `X-Client-ASN` and its country in `X-Client-Country`. A
|
|
header whose value is unknown is left out. Whatever this setting says, any
|
|
such header the client sent is removed.
|
|
|
|
See [`lookup.md`](lookup.md) for what each source sends where, and how long
|
|
answers are kept.
|
|
|
|
## 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 client whose country
|
|
cannot be found is refused too. Otherwise every new client would be let in
|
|
whenever GeoJS stops answering. A client on a private address has no country,
|
|
so it is refused unless it is in `SWWAF_ALLOW_NETS`.
|
|
|
|
A country on both lists stops the start. What a refused request gets, and why
|
|
the lists are empty by default, is under "Country lists" in
|
|
[`SPEC.md`](../SPEC.md).
|
|
|
|
## Lower limits by AS number and country
|
|
|
|
Clients of the AS numbers and countries listed here get a percentage of every
|
|
rate limit and byte limit. A lowered limit is rounded down. "Biased thresholds"
|
|
in [`SPEC.md`](../SPEC.md) says why the lists are empty by default and why no
|
|
budget is shared by a whole AS number or country.
|
|
|
|
- `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. For the AS numbers and countries they
|
|
list, they take the place of the two settings above.
|
|
- `SWWAF_UNKNOWN_LIMIT_PERCENT` (default `100`): the percentage of every limit
|
|
for a client without a country:
|
|
- one the lookup cannot place;
|
|
- one on a private address;
|
|
- one whose answer from GeoJS has not come in time.
|
|
- `SWWAF_ASN_LIMIT_PERCENT_URL` (default unset): the `http` or `https` URL of a
|
|
text file of such entries, one to a line, such as `AS14061:50`. One list can
|
|
then serve several instances. It is fetched and kept as a blocklist is. Where
|
|
it and `SWWAF_ASN_LIMIT_PERCENT` both give a percentage, the lower applies. A
|
|
URL that is also in `SWWAF_BLOCKLIST_URLS` stops the start.
|
|
|
|
A client to which several percentages apply gets the lowest. The log line of
|
|
each counted request, and the notes of each ban, say which setting lowered the
|
|
limit.
|
|
|
|
## Reputation sources
|
|
|
|
None is on by default. A reputation source judges a client by what others saw it
|
|
do. The defaults judge it by what it does to your service.
|
|
|
|
- `SWWAF_BLOCKLIST_URLS` (default empty): blocklists to fetch, such as
|
|
`https://www.spamhaus.org/drop/drop.txt`. Each is 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 fetch, or failed try, before it is fetched again. A duration of `1h` or
|
|
more; it cannot be `off`. The Spamhaus lists may be fetched no more than once
|
|
an hour.
|
|
- `SWWAF_BLOCKLIST_ACTION` (default `deny`): what is done with a client on a
|
|
blocklist. `deny` refuses its requests. `limit:<percent>`, such as `limit:25`,
|
|
gives it that percentage of every limit. `log` only notes the list in the log
|
|
line and raises an alert.
|
|
- `SWWAF_DNSBL_ZONES` (default empty): the DNS blocklists to ask about each
|
|
client, such as `dnsbl.dronebl.org`. A DNS blocklist answers a DNS query for a
|
|
name made from an address, and so says whether the address is on it. A zone is
|
|
a DNS name of at most 189 characters. A Spamhaus zone carries its key, as in
|
|
`<key>.xbl.dq.spamhaus.net`, and is shown with the key masked. A zone listed
|
|
twice is refused, whatever its case or key. Do not name a zone meant for mail:
|
|
such a zone holds the residential addresses ordinary visitors come from.
|
|
- `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. Several zones
|
|
refuse queries that come through a public resolver.
|
|
- `SWWAF_ABUSEIPDB_KEY` (default unset): the key of your AbuseIPDB account.
|
|
While it is set, a client that has committed an offence is checked. 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,
|
|
in UTC, 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 that URL, and
|
|
is fetched every minute. Set without the key, it stops the start. So does 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 stops the start.
|
|
- `SWWAF_REPUTATION_ACTION` (default `limit:25`): what is done with a client on
|
|
a DNS blocklist, or one whose AbuseIPDB score is a hit. `deny`,
|
|
`limit:<percent>` or `log`, as for `SWWAF_BLOCKLIST_ACTION`. Such a verdict is
|
|
less certain than the Spamhaus DROP list, so by default the client gets a
|
|
quarter of every limit.
|
|
- `SWWAF_REPUTATION_CACHE_TTL` (default `24h`): how long a zone's verdict or
|
|
AbuseIPDB's score is used. A duration; it cannot be `off`.
|
|
- `SWWAF_REPUTATION_TIMEOUT` (default `2s`): how long a query to a zone or a
|
|
check with AbuseIPDB may take. A duration; it cannot be `off`.
|
|
|
|
See [`reputation.md`](reputation.md) for how each source is asked and what it
|
|
does with the answer.
|
|
|
|
## Attack detection
|
|
|
|
- `SWWAF_RULES_DIR` (default `/etc/smallwebwaf/rules.d`): the directory of the
|
|
rule files (see [`rules.md`](rules.md)). A directory that cannot be read stops
|
|
the start. An empty one is no error.
|
|
- `SWWAF_RULES_ENABLED` (default `true`): `false` reads no rule file.
|
|
- `SWWAF_TRAP_PATHS` (default empty): paths the app never serves and only
|
|
scanners ask for, such as `/wp-login.php,/xmlrpc.php` in front of a code
|
|
forge. A request for one bans the client, as a `ban` rule does. Each starts
|
|
with `/` and holds no `?`. A trap path is matched whole, without the query,
|
|
against the path as sent and once percent-decoded. `/wp-login.php` matches
|
|
`/wp-login.php?x=1` and `/%77p-login.php`. It does not match `/wp-login.php/`
|
|
or `/blog/wp-login.php`.
|
|
- `SWWAF_WAF_MODE` (default `block`): what the Core Rule Set does with a request
|
|
it takes for an attack. `block` refuses it with `403`. `detect` lets it
|
|
through, logged and alerted. `off` inspects no request.
|
|
- `SWWAF_WAF_PARANOIA_LEVEL` (default `1`): the Core Rule Set's paranoia level,
|
|
from 1 to 4. Each level up runs more rules. Those find more attacks, and take
|
|
more ordinary requests for attacks.
|
|
- `SWWAF_WAF_ANOMALY_THRESHOLD` (default `5`): the score at which a request is a
|
|
match. Each rule that matches adds to the request's score, and a rule rated
|
|
critical adds 5. So by default one such rule is enough. A whole number above
|
|
zero, or `off`. With `off` no request is a match, and scores are still logged.
|
|
- `SWWAF_WAF_DISABLED_RULES` (default
|
|
`920340,920420,920440,920640,930130,930140`): the ids of Core Rule Set rules
|
|
to switch off. The log line names the rules a request matched, so a rule that
|
|
refuses your app's ordinary requests can be found there. "Attack detection" in
|
|
[`SPEC.md`](../SPEC.md) says what the six default ids refuse and why they are
|
|
off. A list you give replaces the default, so name these six ids in it to keep
|
|
them off. An id must be 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, matched as `SWWAF_RATE_LIMIT_EXEMPT_PATHS` is.
|
|
- `SWWAF_WAF_BODY_LIMIT` (default `off`): `off` reads no request body. A size,
|
|
at most `1G`, switches body inspection on. The Core Rule Set then reads a form
|
|
data or multipart body up to that size, and the rest streams on to the app. It
|
|
reads a JSON or XML body no larger than that size, since those cannot be read
|
|
in part. Any other body, such as a git push, reaches the app uninspected. See
|
|
[`core-rule-set.md`](core-rule-set.md).
|
|
- `SWWAF_ERROR_BURST_THRESHOLD` (default `30`): the most refusals of one client
|
|
in a minute for a rule match, a trap path, a Core Rule Set match or a failed
|
|
token check. One more bans the client, as a broken rate limit does. A count or
|
|
`off`. The app's own `401` and `404` answers are not counted.
|
|
|
|
## How long bans last and what they cover
|
|
|
|
- `SWWAF_BAN_RESPONSE` (default `403`): how a refused client is answered: `403`,
|
|
`429`, or `close` to close the connection without an answer. A `block` rule
|
|
and the Core Rule Set always answer `403`.
|
|
- `SWWAF_LIMIT_BAN_DURATION` (default `1h`): the ban for a first broken rate
|
|
limit, byte limit or error burst. A duration; it cannot be `off`.
|
|
- `SWWAF_LIMIT_BAN_REPEAT_WINDOW` (default `24h`): a client that breaks a limit
|
|
again within this time after its ban ended is banned three times as long. A
|
|
duration; it cannot be `off`.
|
|
- `SWWAF_MAX_BAN_DURATION` (default `7d`): a ban for a broken limit that would
|
|
be longer is permanent instead. A duration; it cannot be `off`.
|
|
- `SWWAF_ATTACK_BAN_DURATION` (default `7d`): the ban for a first clear sign of
|
|
attack. A duration; it cannot be `off`.
|
|
- `SWWAF_MAX_BANS` (default `5000`): the most bans `smallwebwaf` made that are
|
|
kept, whether past, active or permanent. A whole number above zero. Bans whose
|
|
cause is `admin` are kept besides and never dropped (see
|
|
[`bans.md`](bans.md)).
|
|
- `SWWAF_BAN_SCOPE_V4_PREFIX` (default `32`): the length of the netblock around
|
|
an IPv4 client that a ban covers, from 0 to 32. `24` bans the surrounding /24.
|
|
An IPv6 ban covers the client's group.
|
|
|
|
## Where and how often the state files are written
|
|
|
|
- `SWWAF_STATE_DIR` (default `/var/lib/smallwebwaf`): the directory of the state
|
|
files, an absolute path. A directory that does not exist or cannot be written
|
|
stops the start.
|
|
- `SWWAF_STATE_WRITE_DELAY` (default `10s`): how long `bans.json` waits to be
|
|
written after a ban is made, lifted or made permanent. Every change in between
|
|
goes into the same write. A duration; it cannot be `off`.
|
|
- `SWWAF_STATE_COUNTER_INTERVAL` (default `15m`): how often every state file is
|
|
written. A duration; it cannot be `off`.
|
|
|
|
## Logging
|
|
|
|
- `SWWAF_LOG_REQUEST_HEADERS` (default
|
|
`accept,accept-language,accept-encoding,content-type,origin,range`): the
|
|
request headers to record in the log line, in either case. `Authorization`,
|
|
`Cookie` and `Set-Cookie` are never logged, even if listed. `Host` and
|
|
`Transfer-Encoding` are refused: Go's HTTP server takes them out of the
|
|
request. The host is the field `host`.
|
|
- `SWWAF_LOG_LEVEL` (default `info`): the least severe of `smallwebwaf`'s own
|
|
messages that are written: `debug`, `info`, `warn` or `error`. No message is
|
|
at `debug`. It holds back no request log line. `warn` and `error` hold back
|
|
the line of settings at start.
|
|
- `SWWAF_LOG_REMOTE_URL` (default unset): a syslog server every line on stdout
|
|
is also sent to, such as `syslog+tls://logs.example:6514`. The forms are
|
|
`syslog+udp://`, `syslog+tcp://` and `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, stops the start.
|
|
- `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.
|
|
|
|
## Tokens and metrics
|
|
|
|
- `SWWAF_ADMIN_TOKEN` (default unset): the token for the ban endpoints and
|
|
`/_smallwebwaf/clients/<ip>`. A long random value of at least 32 characters.
|
|
Unset, those endpoints are off. Give it as a file to keep it from the app (see
|
|
"Settings given as files" below).
|
|
- `SWWAF_METRICS_TOKEN` (default unset): the token a scraper sends for
|
|
`/_smallwebwaf/metrics`, at least 32 characters. Unset, the metrics are off.
|
|
It is separate from the admin token, so a scraper cannot manage bans.
|
|
- `SWWAF_METRICS_TOP_N` (default `50`): how many AS numbers and how many
|
|
countries get series of their own in the metrics. The rest count as `other`. A
|
|
whole number above zero.
|
|
|
|
## Alert destinations
|
|
|
|
- `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.
|
|
The settings logged at start show its path and query as `********`, since many
|
|
webhooks carry their secret there.
|
|
- `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. It is checked and masked 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`. It is
|
|
checked and masked as the webhook URL is.
|
|
- `SWWAF_ALERT_NTFY_TOKEN` (default unset): an ntfy access token, sent as
|
|
`Authorization: Bearer <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 events alerts are sent for. Any other name is refused.
|
|
- `SWWAF_ALERT_COOLDOWN` (default `15m`): how long a repeat of an alert is held
|
|
back. A duration or `off`.
|
|
- `SWWAF_ALERT_MAX_PER_HOUR` (default `60`): the most alerts sent in an hour.
|
|
The rest are rolled into one summary. A whole number above zero, or `off`.
|
|
|
|
See [`alerts.md`](alerts.md) for the events and what each destination gets.
|
|
|
|
## Anomaly thresholds
|
|
|
|
These only alert. They refuse and ban nothing. 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. What is unusual
|
|
depends on each service's normal traffic, which the metrics show. 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. Needs lookups on.
|
|
- `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.
|
|
|
|
Every request but the health check is counted, whatever was done with it,
|
|
clients in `SWWAF_ALLOW_NETS` included. At most 20,000 counters are kept.
|
|
`alerts.json` keeps them across a restart.
|
|
|
|
## Settings given as files
|
|
|
|
Any setting may be given as a file holding its value. Add `_FILE` to the
|
|
setting's name: `SWWAF_METRICS_TOKEN_FILE` names the file that holds
|
|
`SWWAF_METRICS_TOKEN`. The file's contents are the value, less one newline at
|
|
the end. They are checked as the value itself would be. Setting both forms stops
|
|
the start, and so does naming a file that cannot be read.
|
|
`SWWAF_LOG_REMOTE_TLS_CA_FILE` already holds a file name, so it has no `_FILE`
|
|
form.
|
|
|
|
A token given as a file is out of the app's reach only while the `smallwebwaf`
|
|
user alone can read the file. Make it on the host, owned by uid 65532 with mode
|
|
`0400`:
|
|
|
|
```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
|
|
```
|
|
|
|
Mount its directory into the container read-only, and name the file:
|
|
|
|
```sh
|
|
docker run \
|
|
-v /srv/app/tokens:/etc/smallwebwaf/tokens:ro \
|
|
-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, for 7 days each;
|
|
- 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;
|
|
- the CrowdSec decision list is fetched again a minute after the last fetch.
|