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

20 KiB

Settings

Every setting, its default and the values it takes, in the groups of "Settings" in 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).
  • 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).
  • 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).

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).
  • 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).
  • 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 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 describes, make the file on the host:

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:

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);
  • 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).

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 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). 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.