CrowdSec decision list fetched, kept, and its clients banned until the decision ends (closes #106)
check / check (push) Waiting to run
check / check (push) Waiting to run
SWWAF_CROWDSEC_LAPI_URL and SWWAF_CROWDSEC_LAPI_KEY name an engine whose decision list, <url>/v1/decisions, is fetched every minute with the key in X-Api-Key and kept as a blocklist is: used while a fetch fails, and across restarts through reputation.json. Ban decisions on an Ip or a Range end at the fetch time plus their duration. A listed client's request is refused and bans its netblock with the cause crowdsec until the decision ends; bans.json, ban notes and metrics take the cause. Judgement call: fetched every minute, not a setting. Judgement call: a crowdsec ban never lengthens a limit ban. Judgement call: a lifted crowdsec ban is remade while its decision lasts. Model: opus-5-5
This commit is contained in:
@@ -26,11 +26,12 @@ Slack and ntfy, and remote log sending. So is the stage after that: the AS
|
||||
number and country of every client, looked up through GeoJS or in the IPinfo
|
||||
Lite database file, the byte limits, the biased thresholds, lower limits for the
|
||||
AS numbers and countries you list, and the anomaly thresholds, alerts for
|
||||
unusual traffic that refuse nothing. So are the first three parts of the stage
|
||||
after that: the blocklists you name by URL, which it fetches and keeps, with a
|
||||
file of AS numbers' percentages fetched the same way, the DNS blocklists (DNSBL
|
||||
zones), which it asks about each client in the background, and AbuseIPDB, which
|
||||
it asks in the background about each client that has committed an offence.
|
||||
unusual traffic that refuse nothing. So is the stage after that: the blocklists
|
||||
you name by URL, which it fetches and keeps, with a file of AS numbers'
|
||||
percentages fetched the same way, the DNS blocklists (DNSBL zones), which it
|
||||
asks about each client in the background, AbuseIPDB, which it asks in the
|
||||
background about each client that has committed an offence, and the decision
|
||||
list of a CrowdSec engine you run, which it fetches and keeps as a blocklist.
|
||||
`smallwebwaf` passes each request to the app and the app's answer back,
|
||||
unchanged, within its timeouts and size limits, works out each client's address,
|
||||
looks up its AS number and country unless you switch that off, bans a client
|
||||
@@ -39,26 +40,28 @@ you choose, with lower limits for the clients of the AS numbers and countries
|
||||
you list, refuses a client that comes from a country you refuse or from a
|
||||
network you refuse, refuses, limits or only notes a client a blocklist or a
|
||||
DNSBL zone you name lists, or AbuseIPDB scores at or over the score you set,
|
||||
lets the networks you choose through, checks each request against the rule files
|
||||
and bans a client whose request is a clear sign of attack, keeps its bans, each
|
||||
client's counters and history, GeoJS's answers, the last good copy of each list
|
||||
it fetches, the DNSBL zones' verdicts, AbuseIPDB's scores and the AbuseIPDB
|
||||
bans a client your CrowdSec engine's decision list lists until that decision
|
||||
ends, lets the networks you choose through, checks each request against the rule
|
||||
files and bans a client whose request is a clear sign of attack, keeps its bans,
|
||||
each client's counters and history, GeoJS's answers, the last good copy of each
|
||||
list it fetches, the DNSBL zones' verdicts, AbuseIPDB's scores and the AbuseIPDB
|
||||
checks spent today in JSON files across restarts, takes in your edits of those
|
||||
files, such as a ban you make, keep or lift, and of the rule files while it
|
||||
runs, writes a JSON log line for every request, sends its log lines to a syslog
|
||||
server too if you name one, sends an alert to a webhook, to Slack and to ntfy,
|
||||
each if you name one, for each ban it makes or makes permanent, for traffic over
|
||||
an anomaly threshold you set, for a client a blocklist, a DNSBL zone or
|
||||
AbuseIPDB lists, for GeoJS failing, a list it cannot fetch, a DNSBL zone or
|
||||
AbuseIPDB that fails or refuses a query and the day's AbuseIPDB checks used up,
|
||||
for a rule file or state file with an error and for a replacement of the lookup
|
||||
database it cannot read, serves Prometheus metrics to a scraper that holds the
|
||||
metrics token, lets an admin who holds the admin token list, add and lift bans
|
||||
and ask what it knows of a client, and in `observe` mode passes on the requests
|
||||
it would refuse, logging what it would have done with them. It comes as the
|
||||
image the app's own image is built on. The rest of the design comes after that,
|
||||
in the order of the build order in [`SPEC.md`](SPEC.md). The survey of existing
|
||||
tools that led to the design is in [`EVALUATION.md`](EVALUATION.md).
|
||||
an anomaly threshold you set, for a client a blocklist, the CrowdSec decision
|
||||
list, a DNSBL zone or AbuseIPDB lists, for GeoJS failing, a list it cannot
|
||||
fetch, a DNSBL zone or AbuseIPDB that fails or refuses a query and the day's
|
||||
AbuseIPDB checks used up, for a rule file or state file with an error and for a
|
||||
replacement of the lookup database it cannot read, serves Prometheus metrics to
|
||||
a scraper that holds the metrics token, lets an admin who holds the admin token
|
||||
list, add and lift bans and ask what it knows of a client, and in `observe` mode
|
||||
passes on the requests it would refuse, logging what it would have done with
|
||||
them. It comes as the image the app's own image is built on. The rest of the
|
||||
design comes after that, in the order of the build order in
|
||||
[`SPEC.md`](SPEC.md). The survey of existing tools that led to the design is in
|
||||
[`EVALUATION.md`](EVALUATION.md).
|
||||
|
||||
## Getting started
|
||||
|
||||
@@ -163,16 +166,17 @@ in `bin/state` unless `SWWAF_STATE_DIR` is set, and the default rule file of
|
||||
and the requests or bytes counted in it, the client's percentage of that kind
|
||||
of limit and the setting that gave it when a biased threshold lowered the
|
||||
limit, the request that broke it, the client's AS number, AS name and country
|
||||
once they are looked up, the blocklists, DNSBL zones and AbuseIPDB, with its
|
||||
score, that listed the client when the ban was made, the netblock's requests
|
||||
since it was first seen, how many of them the ban has refused, and how many
|
||||
bans the netblock had before, for a broken limit, for a clear sign of attack
|
||||
and by an admin. At most `SWWAF_MAX_BANS` bans `smallwebwaf` made are kept,
|
||||
past, active and permanent; past that, the earliest such ban of the netblock
|
||||
that has gone longest without a request is dropped first. The bans whose cause
|
||||
is `admin`, those you make or keep, are kept besides, and never dropped.
|
||||
`bans.json` shows the bans and their notes, a restart lifts none, and you
|
||||
make, keep or lift a ban by editing it (see "State files" below).
|
||||
once they are looked up, the blocklists, the CrowdSec decision list, DNSBL
|
||||
zones and AbuseIPDB, with its score, that listed the client when the ban was
|
||||
made, the netblock's requests since it was first seen, how many of them the
|
||||
ban has refused, and how many bans the netblock had before, for a broken
|
||||
limit, for a clear sign of attack, by an admin and for CrowdSec's decision. At
|
||||
most `SWWAF_MAX_BANS` bans `smallwebwaf` made are kept, past, active and
|
||||
permanent; past that, the earliest such ban of the netblock that has gone
|
||||
longest without a request is dropped first. The bans whose cause is `admin`,
|
||||
those you make or keep, are kept besides, and never dropped. `bans.json` shows
|
||||
the bans and their notes, a restart lifts none, and you make, keep or lift a
|
||||
ban by editing it (see "State files" below).
|
||||
- Checks each request against the rules of the rule files (see "Rule files"
|
||||
below) after the rate limits, and before its body is read. A `log` rule that
|
||||
matches is noted in the log line; a `block` rule refuses the request with
|
||||
@@ -214,6 +218,14 @@ in `bin/state` unless `SWWAF_STATE_DIR` is set, and the default rule file of
|
||||
limit, the lowest of its percentages applying, as for a biased threshold, and
|
||||
with `log` nothing more is done. Whatever the action, the request's log line
|
||||
names the lists, and each raises an alert.
|
||||
- Checks the client's own address against the decision list of the CrowdSec
|
||||
engine `SWWAF_CROWDSEC_LAPI_URL` names, after the blocklists and before the
|
||||
DNSBL zones (see "CrowdSec" below). A request from a client a decision in
|
||||
force bans is refused with `SWWAF_BAN_RESPONSE` before its body is read, and
|
||||
bans the client's netblock, as any ban covers it, until the decision ends,
|
||||
with the cause `crowdsec`; the client's next requests are refused under that
|
||||
ban. The request's log line names the list, and it raises an alert besides the
|
||||
ban's. A client a blocklist refuses is not checked.
|
||||
- Checks the client's own address against the DNSBL zones `SWWAF_DNSBL_ZONES`
|
||||
names, after the blocklists and before the rate limits (see "DNS blocklists"
|
||||
below), by the verdicts it keeps. A zone is asked about a client in the
|
||||
@@ -226,7 +238,7 @@ in `bin/state` unless `SWWAF_STATE_DIR` is set, and the default rule file of
|
||||
before their bodies are read; they are not counted for the rate limits, and
|
||||
make no ban. With `log`, nothing more is done. Whatever the action, the
|
||||
request's log line names the zones, and each raises an alert. A client a
|
||||
blocklist refuses is not checked.
|
||||
blocklist refuses, or the CrowdSec decision list bans, is not checked.
|
||||
- Checks the client with AbuseIPDB while `SWWAF_ABUSEIPDB_KEY` is set, after the
|
||||
DNSBL zones and before the rate limits (see "AbuseIPDB" below), by the scores
|
||||
it keeps. Only a client whose history counts an offence is checked, so far one
|
||||
@@ -235,38 +247,39 @@ in `bin/state` unless `SWWAF_STATE_DIR` is set, and the default rule file of
|
||||
request waits for AbuseIPDB. A score at or over `SWWAF_ABUSEIPDB_MIN_SCORE` is
|
||||
a hit, and `SWWAF_REPUTATION_ACTION` does with its client what it does with
|
||||
one a DNSBL zone's verdict lists. The request's log line names AbuseIPDB, and
|
||||
it raises an alert. A client a blocklist or a DNSBL zone refuses is not
|
||||
checked.
|
||||
it raises an alert. A client a blocklist or a DNSBL zone refuses, or the
|
||||
CrowdSec decision list bans, is not checked.
|
||||
- Checks the client's own address against the static lists, the three netblock
|
||||
settings below, before anything else, its lookup included. A client in
|
||||
`SWWAF_ALLOW_NETS` skips bans, the country lists, the blocklists, the DNSBL
|
||||
zones, AbuseIPDB, the rate limits, the byte limits and the rule files, and is
|
||||
not looked up; the timeouts and size limits still apply. A client in
|
||||
`SWWAF_DENY_NETS` is refused with `SWWAF_BAN_RESPONSE` before its body is
|
||||
read, and the request is not counted for the rate limits; an address in
|
||||
`SWWAF_ALLOW_NETS` too is let through. A client in
|
||||
`SWWAF_ALLOW_NETS` skips bans, the country lists, the blocklists, the CrowdSec
|
||||
decision list, the DNSBL zones, AbuseIPDB, the rate limits, the byte limits
|
||||
and the rule files, and is not looked up; the timeouts and size limits still
|
||||
apply. A client in `SWWAF_DENY_NETS` is refused with `SWWAF_BAN_RESPONSE`
|
||||
before its body is read, and the request is not counted for the rate limits;
|
||||
an address in `SWWAF_ALLOW_NETS` too is let through. A client in
|
||||
`SWWAF_RATE_LIMIT_EXEMPT_NETS` is neither counted nor refused by the rate
|
||||
limits, and has no bytes counted by the byte limits; the country lists, the
|
||||
rule files and bans still apply to it.
|
||||
- In `observe` mode, with `SWWAF_MODE=observe`, refuses none of the requests
|
||||
that `SWWAF_DENY_NETS`, a ban, the country lists, a blocklist, a DNSBL zone's
|
||||
verdict, AbuseIPDB's score, a rate limit or a rule would refuse: it passes
|
||||
them to the app, and their log lines name what `enforce` mode would have done
|
||||
(see `would_action` in "Request log" below). The checks run, and requests and
|
||||
bytes are counted, as in `enforce` mode, with three differences: neither a
|
||||
broken rate limit or byte limit nor a `ban` rule makes a ban; a broken limit
|
||||
does not set the client's counters back to zero, so each request over a rate
|
||||
limit is logged as one that would be refused, and each whose bytes keep the
|
||||
client over a byte limit as breaking it; and a request under a ban does not
|
||||
make it permanent. As in `enforce` mode, the bytes counted are only those of
|
||||
the requests `enforce` mode would have passed to the app. A ban it would have
|
||||
made, or made permanent, raises the alert `enforce` mode would have raised,
|
||||
marked as what would have happened (see "Alerts" below). The bans in
|
||||
`bans.json` are kept, and refuse requests again when `smallwebwaf` next runs
|
||||
in `enforce` mode, as long as they last. The timeouts and size limits still
|
||||
apply, since they protect `smallwebwaf` and the app themselves, and a request
|
||||
for one of `smallwebwaf`'s own endpoints without its token is still answered
|
||||
`401`. It is for trying a configuration before enforcing it.
|
||||
that `SWWAF_DENY_NETS`, a ban, the country lists, a blocklist, the CrowdSec
|
||||
decision list, a DNSBL zone's verdict, AbuseIPDB's score, a rate limit or a
|
||||
rule would refuse: it passes them to the app, and their log lines name what
|
||||
`enforce` mode would have done (see `would_action` in "Request log" below).
|
||||
The checks run, and requests and bytes are counted, as in `enforce` mode, with
|
||||
three differences: neither a broken rate limit or byte limit, a `ban` rule nor
|
||||
the CrowdSec decision list makes a ban; a broken limit does not set the
|
||||
client's counters back to zero, so each request over a rate limit is logged as
|
||||
one that would be refused, and each whose bytes keep the client over a byte
|
||||
limit as breaking it; and a request under a ban does not make it permanent. As
|
||||
in `enforce` mode, the bytes counted are only those of the requests `enforce`
|
||||
mode would have passed to the app. A ban it would have made, or made
|
||||
permanent, raises the alert `enforce` mode would have raised, marked as what
|
||||
would have happened (see "Alerts" below). The bans in `bans.json` are kept,
|
||||
and refuse requests again when `smallwebwaf` next runs in `enforce` mode, as
|
||||
long as they last. The timeouts and size limits still apply, since they
|
||||
protect `smallwebwaf` and the app themselves, and a request for one of
|
||||
`smallwebwaf`'s own endpoints without its token is still answered `401`. It is
|
||||
for trying a configuration before enforcing it.
|
||||
- Answers `GET /_smallwebwaf/healthz` itself with `200` and `ok`, before any
|
||||
check and without asking the app, for the image's health check.
|
||||
- Answers `GET /_smallwebwaf/metrics` with its metrics (see "Metrics" below) for
|
||||
@@ -286,12 +299,13 @@ in `bin/state` unless `SWWAF_STATE_DIR` is set, and the default rule file of
|
||||
`SWWAF_LOG_REMOTE_URL` names one (see "Sending the log to a syslog server"
|
||||
below).
|
||||
- Sends an alert for each ban it makes or makes permanent, for a count over an
|
||||
anomaly threshold, for a client a blocklist, a DNSBL zone or AbuseIPDB lists,
|
||||
for GeoJS failing, a list it cannot fetch, a DNSBL zone or AbuseIPDB that
|
||||
fails or refuses a query, and the day's AbuseIPDB checks used up, for a rule
|
||||
file or state file with an error, and for a replacement of the lookup database
|
||||
it cannot read, holding back repeats and, past an hourly limit, rolling the
|
||||
rest into one summary, to each destination you name: as a JSON object to the
|
||||
anomaly threshold, for a client a blocklist, the CrowdSec decision list, a
|
||||
DNSBL zone or AbuseIPDB lists, for GeoJS failing, a list it cannot fetch, the
|
||||
CrowdSec decision list among them, a DNSBL zone or AbuseIPDB that fails or
|
||||
refuses a query, and the day's AbuseIPDB checks used up, for a rule file or
|
||||
state file with an error, and for a replacement of the lookup database it
|
||||
cannot read, holding back repeats and, past an hourly limit, rolling the rest
|
||||
into one summary, to each destination you name: as a JSON object to the
|
||||
webhook `SWWAF_ALERT_WEBHOOK_URL` names, as a message to the Slack incoming
|
||||
webhook `SWWAF_ALERT_SLACK_WEBHOOK_URL` names, and as a message to the ntfy
|
||||
topic `SWWAF_ALERT_NTFY_URL` names (see "Alerts" below).
|
||||
@@ -370,9 +384,9 @@ effective settings are logged at start, unless `SWWAF_LOG_LEVEL` is `warn` or
|
||||
- `SWWAF_REQUEST_MAX_BYTES` (default `100M`): the largest request body.
|
||||
- `SWWAF_RESPONSE_MAX_BYTES` (default `5G`): the largest response body.
|
||||
- `SWWAF_ALLOW_NETS` (default empty): netblocks whose clients skip bans, the
|
||||
country lists, the blocklists, the DNSBL zones, AbuseIPDB, the rate limits,
|
||||
the byte limits and the rule files, such as your monitoring or your own
|
||||
networks.
|
||||
country lists, the blocklists, the CrowdSec decision list, the DNSBL zones,
|
||||
AbuseIPDB, the rate limits, the byte limits and the rule files, such as your
|
||||
monitoring or your own networks.
|
||||
- `SWWAF_RATE_LIMIT_EXEMPT_NETS` (default empty): netblocks whose clients the
|
||||
rate limits and the byte limits do not apply to, such as a machine that talks
|
||||
to the app all day.
|
||||
@@ -495,6 +509,17 @@ effective settings are logged at start, unless `SWWAF_LOG_LEVEL` is `warn` or
|
||||
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, whose decision list is fetched (see "CrowdSec" below), as an `http`
|
||||
or `https` URL without a user or a fragment, such as `http://172.17.0.1:8080`.
|
||||
While it is unset, no list is fetched. Set without `SWWAF_CROWDSEC_LAPI_KEY`,
|
||||
it stops the start, and so does a URL whose decision list, the URL with
|
||||
`v1/decisions` added to its path, `SWWAF_BLOCKLIST_URLS` or
|
||||
`SWWAF_ASN_LIMIT_PERCENT_URL` names too.
|
||||
- `SWWAF_CROWDSEC_LAPI_KEY` (default unset): the key the engine gave for
|
||||
`smallwebwaf`, as `cscli bouncers add smallwebwaf` prints it. Set without
|
||||
`SWWAF_CROWDSEC_LAPI_URL`, it stops the start. The settings logged at start
|
||||
show `********` in its place.
|
||||
- `SWWAF_REPUTATION_ACTION` (default `limit:25`): what is done with a client a
|
||||
zone's verdict lists, or whose AbuseIPDB score is a hit, as
|
||||
`SWWAF_BLOCKLIST_ACTION` is for a blocklist: `deny`, `limit:<percent>` or
|
||||
@@ -516,7 +541,8 @@ effective settings are logged at start, unless `SWWAF_LOG_LEVEL` is `warn` or
|
||||
limit or byte limit.
|
||||
- `SWWAF_LIMIT_BAN_REPEAT_WINDOW` (default `24h`): a rate limit or byte limit
|
||||
broken again within this time after a ban ended, other than one for a clear
|
||||
sign of attack, bans for three times as long as that ban.
|
||||
sign of attack or for CrowdSec's decision, bans for three times as long as
|
||||
that ban.
|
||||
- `SWWAF_MAX_BAN_DURATION` (default `7d`): a ban for a broken rate limit or byte
|
||||
limit that would be longer is permanent instead.
|
||||
- `SWWAF_ATTACK_BAN_DURATION` (default `7d`): the ban for a first clear sign of
|
||||
@@ -668,7 +694,8 @@ and `SWWAF_ANOMALY_NET_V6_PREFIX` cannot be off.
|
||||
Several limits are fixed rather than settings. At most 100,000 answers from
|
||||
GeoJS are kept, for 7 days each, at most 20,000 anomaly counters, at most
|
||||
100,000 verdicts of the DNSBL zones, with at most 1,000 queries to them under
|
||||
way at once, and at most 100,000 scores of AbuseIPDB.
|
||||
way at once, and at most 100,000 scores of AbuseIPDB, and the CrowdSec decision
|
||||
list is fetched again a minute after it was last fetched or tried.
|
||||
|
||||
### Settings given as files
|
||||
|
||||
@@ -754,20 +781,22 @@ which every line has.
|
||||
refused because its client is in `SWWAF_DENY_NETS`, in a blocklist while
|
||||
`SWWAF_BLOCKLIST_ACTION` is `deny`, or listed by a DNSBL zone or AbuseIPDB
|
||||
while `SWWAF_REPUTATION_ACTION` is `deny`, `banned` for one refused because a
|
||||
ban covers its client or because it matched a `ban` rule, which bans its
|
||||
client, `country_denied` for one refused for its client's country,
|
||||
`rate_limited` for one that broke a rate limit and banned its client,
|
||||
`rule_blocked` for one a `block` rule refused, `too_large` for a request or
|
||||
response over its size limit, `timed_out` for one that ran out of time,
|
||||
`upstream_error` when the app could not be reached or its answer broke off,
|
||||
and `admin` for one `smallwebwaf` answered at its own endpoint.
|
||||
ban covers its client, or because it matched a `ban` rule or the CrowdSec
|
||||
decision list lists its client, either of which bans its client,
|
||||
`country_denied` for one refused for its client's country, `rate_limited` for
|
||||
one that broke a rate limit and banned its client, `rule_blocked` for one a
|
||||
`block` rule refused, `too_large` for a request or response over its size
|
||||
limit, `timed_out` for one that ran out of time, `upstream_error` when the app
|
||||
could not be reached or its answer broke off, and `admin` for one
|
||||
`smallwebwaf` answered at its own endpoint.
|
||||
- `would_action` is there in `observe` mode for a request that
|
||||
`SWWAF_DENY_NETS`, a ban, the country lists, a blocklist, a DNSBL zone's
|
||||
verdict, AbuseIPDB's score, a rate limit or a rule would have refused in
|
||||
`enforce` mode, and names the action that refusal would have had: `denied`,
|
||||
`banned`, `country_denied`, `rate_limited` or `rule_blocked`. `action` then
|
||||
names what was done: `forward` for a request passed to the app, and another
|
||||
action, such as `too_large`, for one a size or time limit refused.
|
||||
`SWWAF_DENY_NETS`, a ban, the country lists, a blocklist, the CrowdSec
|
||||
decision list, a DNSBL zone's verdict, AbuseIPDB's score, a rate limit or a
|
||||
rule would have refused in `enforce` mode, and names the action that refusal
|
||||
would have had: `denied`, `banned`, `country_denied`, `rate_limited` or
|
||||
`rule_blocked`. `action` then names what was done: `forward` for a request
|
||||
passed to the app, and another action, such as `too_large`, for one a size or
|
||||
time limit refused.
|
||||
- `limit_percent` is there for a request the rate limits count whose client a
|
||||
biased threshold, `SWWAF_BLOCKLIST_ACTION` for a blocklist that lists it, or
|
||||
`SWWAF_REPUTATION_ACTION` for a DNSBL zone whose verdict lists it or an
|
||||
@@ -785,11 +814,12 @@ which every line has.
|
||||
health check, one from a client in `SWWAF_ALLOW_NETS` or
|
||||
`SWWAF_RATE_LIMIT_EXEMPT_NETS`, one for a path that
|
||||
`SWWAF_RATE_LIMIT_EXEMPT_PATHS` exempts, and one that `SWWAF_DENY_NETS`, a
|
||||
ban, the country lists, a blocklist, a DNSBL zone's verdict or AbuseIPDB's
|
||||
score refuse, or would refuse in `observe` mode. Its `minute_bytes`,
|
||||
`hour_bytes` and `day_bytes` give the client's bytes in each window as the
|
||||
byte limits count them, in the same way: for a request whose bytes they count,
|
||||
with its own, once it has ended; for any other, those counted before it.
|
||||
ban, the country lists, a blocklist, the CrowdSec decision list, a DNSBL
|
||||
zone's verdict or AbuseIPDB's score refuse, or would refuse in `observe` mode.
|
||||
Its `minute_bytes`, `hour_bytes` and `day_bytes` give the client's bytes in
|
||||
each window as the byte limits count them, in the same way: for a request
|
||||
whose bytes they count, with its own, once it has ended; for any other, those
|
||||
counted before it.
|
||||
- `rule_ids` is there for a request that matched rules of the rule files, and
|
||||
lists their ids in the order they matched, up to the one that refused it.
|
||||
- `limit_hit` is there for a request that broke a rate limit, or whose bytes
|
||||
@@ -799,18 +829,22 @@ which every line has.
|
||||
several. `offence` is then `limit`. A request whose bytes broke a byte limit
|
||||
is not refused: its `action` is what it would have been otherwise, such as
|
||||
`forward`.
|
||||
- `reputation` is there for a request whose client a blocklist or a DNSBL zone's
|
||||
verdict lists, or whose AbuseIPDB score is a hit, and gives the URLs of the
|
||||
blocklists that list it, in the order `SWWAF_BLOCKLIST_URLS` names them, then
|
||||
the zones whose verdict lists it, in the order `SWWAF_DNSBL_ZONES` names them,
|
||||
then `abuseipdb`, whatever `SWWAF_BLOCKLIST_ACTION` and
|
||||
`SWWAF_REPUTATION_ACTION` say. A zone whose verdict on the client has not come
|
||||
yet, or was given `SWWAF_REPUTATION_CACHE_TTL` ago or more, is not named, nor
|
||||
is AbuseIPDB for such a score. It is left out for a client the blocklists are
|
||||
not checked for: one in `SWWAF_ALLOW_NETS`, and one `SWWAF_DENY_NETS`, a ban
|
||||
or the country lists refuse first, or would in `observe` mode. The zones are
|
||||
not checked either for a client a blocklist refuses, nor AbuseIPDB's score for
|
||||
one a blocklist or a zone refuses, or would in `observe` mode.
|
||||
- `reputation` is there for a request whose client a blocklist, the CrowdSec
|
||||
decision list or a DNSBL zone's verdict lists, or whose AbuseIPDB score is a
|
||||
hit, and gives the URLs of the blocklists that list it, in the order
|
||||
`SWWAF_BLOCKLIST_URLS` names them, then the URL of the CrowdSec decision list
|
||||
when a decision in force bans the client, then the zones whose verdict lists
|
||||
it, in the order `SWWAF_DNSBL_ZONES` names them, then `abuseipdb`, whatever
|
||||
`SWWAF_BLOCKLIST_ACTION` and `SWWAF_REPUTATION_ACTION` say. A zone whose
|
||||
verdict on the client has not come yet, or was given
|
||||
`SWWAF_REPUTATION_CACHE_TTL` ago or more, is not named, nor is AbuseIPDB for
|
||||
such a score. It is left out for a client the blocklists are not checked for:
|
||||
one in `SWWAF_ALLOW_NETS`, and one `SWWAF_DENY_NETS`, a ban or the country
|
||||
lists refuse first, or would in `observe` mode. The CrowdSec decision list is
|
||||
not checked either for a client a blocklist refuses, nor are the zones for one
|
||||
a blocklist refuses or the decision list bans, nor AbuseIPDB's score for one a
|
||||
blocklist or a zone refuses or the decision list bans, or would in `observe`
|
||||
mode.
|
||||
- `ban_expires` is there for a request that made a ban or was refused under one,
|
||||
or in `observe` mode would have been refused under one, and gives when the ban
|
||||
ends, in the same form as `time`, or `permanent`.
|
||||
@@ -874,22 +908,23 @@ gives, as "Alert webhook schema" in [`SPEC.md`](SPEC.md) describes, and to
|
||||
it, as below. An alert is for one of these events, and is sent when
|
||||
`SWWAF_ALERT_EVENTS` names its event:
|
||||
|
||||
- `ban`: a ban `smallwebwaf` makes, for a broken rate limit or byte limit or a
|
||||
clear sign of attack.
|
||||
- `ban`: a ban `smallwebwaf` makes, for a broken rate limit or byte limit, a
|
||||
clear sign of attack, or a client the CrowdSec decision list lists.
|
||||
- `permanent_ban`: a permanent ban it makes, or a ban for a clear sign of attack
|
||||
that a request made permanent.
|
||||
- `anomaly`: a count of requests or bytes over an anomaly threshold, raised by
|
||||
each request that ends with the count over it, in `observe` mode as in
|
||||
`enforce` mode. It refuses and bans nothing.
|
||||
- `reputation_hit`: a request whose client a blocklist or a DNSBL zone's verdict
|
||||
lists, or whose AbuseIPDB score is a hit, one alert for each blocklist and
|
||||
each zone that lists it, and one for AbuseIPDB, whatever
|
||||
`SWWAF_BLOCKLIST_ACTION` and `SWWAF_REPUTATION_ACTION` say, in `observe` mode
|
||||
as in `enforce` mode.
|
||||
- `reputation_hit`: a request whose client a blocklist, the CrowdSec decision
|
||||
list or a DNSBL zone's verdict lists, or whose AbuseIPDB score is a hit, one
|
||||
alert for each blocklist and each zone that lists it, one for the decision
|
||||
list, and one for AbuseIPDB, whatever `SWWAF_BLOCKLIST_ACTION` and
|
||||
`SWWAF_REPUTATION_ACTION` say, in `observe` mode as in `enforce` mode.
|
||||
- `source_failure`: GeoJS failing or refusing `smallwebwaf`, a fetch of a list
|
||||
failing (see "Blocklists" below), a query to a DNSBL zone failing or refused
|
||||
(see "DNS blocklists" below), or a check with AbuseIPDB failing or refused, or
|
||||
the check that uses up the day's AbuseIPDB checks (see "AbuseIPDB" below).
|
||||
failing (see "Blocklists" below), the CrowdSec decision list's among them (see
|
||||
"CrowdSec" below), a query to a DNSBL zone failing or refused (see "DNS
|
||||
blocklists" below), or a check with AbuseIPDB failing or refused, or the check
|
||||
that uses up the day's AbuseIPDB checks (see "AbuseIPDB" below).
|
||||
- `file_error`: a rule file edited while it runs that has an error, an edit of a
|
||||
state file set aside as `<name>.bad`, a state file it could not write while
|
||||
running, or a replacement of the lookup database it could not read, which it
|
||||
@@ -939,7 +974,8 @@ is sent on one line:
|
||||
"earlier_bans": {
|
||||
"limit": 0,
|
||||
"attack": 0,
|
||||
"admin": 0
|
||||
"admin": 0,
|
||||
"crowdsec": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -962,7 +998,8 @@ is sent on one line:
|
||||
- `reason` is a short sentence; for a ban, the ban's `reason` in `bans.json`;
|
||||
for an `anomaly`, what was counted over which threshold, such as
|
||||
`requests per minute of the netblock 203.0.113.0/24 over the threshold of 1000`;
|
||||
for a `reputation_hit`, `listed by a blocklist`, `listed by a DNSBL zone` or
|
||||
for a `reputation_hit`, `listed by a blocklist`,
|
||||
`listed by the CrowdSec decision list`, `listed by a DNSBL zone` or
|
||||
`scored by AbuseIPDB at or over SWWAF_ABUSEIPDB_MIN_SCORE`.
|
||||
- `detail` is what is particular to the event: for a ban, its `cause`, when it
|
||||
ends as `ban_expires`, in the form the request log gives it, and its `notes`,
|
||||
@@ -971,12 +1008,12 @@ is sent on one line:
|
||||
`asn` and the `name` of the named netblock for `watch`, the `window`, `minute`
|
||||
or `hour`, the `kind`, `requests` or `bytes`, the `count`, which is weighted
|
||||
as the rate limits weigh theirs, and the `threshold`; for a `reputation_hit`,
|
||||
the `source`, the URL of the blocklist, the zone or `abuseipdb`, and for
|
||||
AbuseIPDB the `score`; for `source_failure`, the `source`, `geojs`, the URL of
|
||||
the list, the zone or `abuseipdb`, the `error`, and for GeoJS, when it is
|
||||
asked again, `asking_again_in`; for `file_error`, the `file`, which for an
|
||||
edit set aside is the file it was renamed to, and the `error`, which for a
|
||||
file that does not parse names where in it the error is.
|
||||
the `source`, the URL of the blocklist or of the CrowdSec decision list, the
|
||||
zone or `abuseipdb`, and for AbuseIPDB the `score`; for `source_failure`, the
|
||||
`source`, `geojs`, the URL of the list, the zone or `abuseipdb`, the `error`,
|
||||
and for GeoJS, when it is asked again, `asking_again_in`; for `file_error`,
|
||||
the `file`, which for an edit set aside is the file it was renamed to, and the
|
||||
`error`, which for a file that does not parse names where in it the error is.
|
||||
- `suppressed_repeats` is how many repeats the cooldown held back before this
|
||||
alert, and for a `summary`, those no other alert gives (see below).
|
||||
|
||||
@@ -1022,14 +1059,14 @@ and Slack this JSON object, shown indented; it is sent on one line:
|
||||
```
|
||||
|
||||
An alert for the same event as the last one sent, on the same netblock, and for
|
||||
a `reputation_hit` about the same blocklist, zone or AbuseIPDB, or for a
|
||||
`file_error` about the same file, or for a `source_failure` about the same
|
||||
source, or for an `anomaly` in the same scope, with the same netblock, AS number
|
||||
or name, whatever its window and kind, less than `SWWAF_ALERT_COOLDOWN` after
|
||||
it, is a repeat: it is held back and counted, and the next alert sent for them
|
||||
gives that count as `suppressed_repeats`. As each hour of the clock, in UTC,
|
||||
ends, the cooldowns that have run out are dropped, and the repeats they held
|
||||
back, which no alert sent since has given, go in that hour's summary.
|
||||
a `reputation_hit` about the same blocklist, decision list, zone or AbuseIPDB,
|
||||
or for a `file_error` about the same file, or for a `source_failure` about the
|
||||
same source, or for an `anomaly` in the same scope, with the same netblock, AS
|
||||
number or name, whatever its window and kind, less than `SWWAF_ALERT_COOLDOWN`
|
||||
after it, is a repeat: it is held back and counted, and the next alert sent for
|
||||
them gives that count as `suppressed_repeats`. As each hour of the clock, in
|
||||
UTC, ends, the cooldowns that have run out are dropped, and the repeats they
|
||||
held back, which no alert sent since has given, go in that hour's summary.
|
||||
|
||||
Past `SWWAF_ALERT_MAX_PER_HOUR` alerts in an hour, the hour's other alerts are
|
||||
held back and counted by event. An alert held back this way starts no cooldown.
|
||||
@@ -1065,21 +1102,23 @@ with times in UTC.
|
||||
|
||||
- `bans.json`: every ban with its notes, indented to be read. A permanent ban's
|
||||
`expires` is `null`. A ban's `cause` is `limit` for a broken rate limit or
|
||||
byte limit or `attack` for a clear sign of attack, for a ban `smallwebwaf`
|
||||
made, and `admin` for one you made or keep. Its `reason` is a short text: for
|
||||
a ban `smallwebwaf` made, the limit broken, such as
|
||||
`requests per minute over the limit of 1000` or
|
||||
`bytes per hour over the limit of 21474836480`, or the rule that matched, such
|
||||
as `matched the rule env-file`; for yours, what you wrote. Its `lifted` is
|
||||
when you lifted it, and is left out until you do. The `kind` in the notes of a
|
||||
ban for a broken limit is `requests` or `bytes`, what the limit is on. For a
|
||||
limit a biased threshold lowered, the reason and the notes' `limit` give the
|
||||
lowered limit, and the notes' `limit_percent` and `limit_percent_setting` the
|
||||
client's percentage of that kind of limit and the setting that gave it. The
|
||||
notes' `reputation` gives each blocklist, DNSBL zone or AbuseIPDB that listed
|
||||
the client when the ban was made, as its `source`, named and ordered as in the
|
||||
request log's `reputation`, with AbuseIPDB's `score` of the client. It is left
|
||||
out when none did, and the example below shows it.
|
||||
byte limit, `attack` for a clear sign of attack or `crowdsec` for a client the
|
||||
CrowdSec decision list lists, for a ban `smallwebwaf` made, and `admin` for
|
||||
one you made or keep. Its `reason` is a short text: for a ban `smallwebwaf`
|
||||
made, the limit broken, such as `requests per minute over the limit of 1000`
|
||||
or `bytes per hour over the limit of 21474836480`, the rule that matched, such
|
||||
as `matched the rule env-file`, or the scenario that made CrowdSec's decision,
|
||||
such as `CrowdSec's decision for crowdsecurity/ssh-bf`; for yours, what you
|
||||
wrote. Its `lifted` is when you lifted it, and is left out until you do. The
|
||||
`kind` in the notes of a ban for a broken limit is `requests` or `bytes`, what
|
||||
the limit is on. For a limit a biased threshold lowered, the reason and the
|
||||
notes' `limit` give the lowered limit, and the notes' `limit_percent` and
|
||||
`limit_percent_setting` the client's percentage of that kind of limit and the
|
||||
setting that gave it. The notes' `reputation` gives each blocklist, the
|
||||
CrowdSec decision list, each DNSBL zone or AbuseIPDB that listed the client
|
||||
when the ban was made, as its `source`, named and ordered as in the request
|
||||
log's `reputation`, with AbuseIPDB's `score` of the client. It is left out
|
||||
when none did, and the example below shows it.
|
||||
- `clients.json`: each client's two buckets of requests in the minute, the hour
|
||||
and the day, its two buckets of bytes in each, `minute_bytes`, `hour_bytes`
|
||||
and `day_bytes`, and its history: when it was first and last seen, its AS
|
||||
@@ -1091,20 +1130,20 @@ with times in UTC.
|
||||
`grep` shows everything about one.
|
||||
- `lookups.json`: GeoJS's answers, one to a line, each with the client's AS
|
||||
number, AS name and country, when GeoJS gave it and when it was last used.
|
||||
- `reputation.json`: each list fetched from a URL (see "Blocklists" below),
|
||||
indented to be read, under `lists`: its `url`, when it was last `tried`, the
|
||||
fetch failed or not, and its last good copy: when that was `fetched`, and its
|
||||
`lines`, as fetched, comment lines included, each on a line of its own, both
|
||||
left out while no fetch of it has succeeded; and under `verdicts`, each
|
||||
verdict of a DNSBL zone still in use (see "DNS blocklists" below): its `zone`,
|
||||
the `client`'s address, whether the zone `listed` the client, and when the
|
||||
zone gave it, `fetched`; and under `abuseipdb` (see "AbuseIPDB" below), the
|
||||
`day`, in UTC, whose checks it counts, left out before the first, the checks
|
||||
`spent` that day, and under `scores`, each score of AbuseIPDB still in use:
|
||||
the `client`, its IPv4 address as a /32 or its IPv6 group, its `score`, and
|
||||
when AbuseIPDB gave it, `fetched`. As the file is read, the lists the settings
|
||||
no longer name, and the verdicts of the zones they no longer name, are
|
||||
dropped.
|
||||
- `reputation.json`: each list fetched from a URL (see "Blocklists" and
|
||||
"CrowdSec" below), indented to be read, under `lists`: its `url`, when it was
|
||||
last `tried`, the fetch failed or not, and its last good copy: when that was
|
||||
`fetched`, and its `lines`, as fetched, comment lines included, each on a line
|
||||
of its own, both left out while no fetch of it has succeeded; and under
|
||||
`verdicts`, each verdict of a DNSBL zone still in use (see "DNS blocklists"
|
||||
below): its `zone`, the `client`'s address, whether the zone `listed` the
|
||||
client, and when the zone gave it, `fetched`; and under `abuseipdb` (see
|
||||
"AbuseIPDB" below), the `day`, in UTC, whose checks it counts, left out before
|
||||
the first, the checks `spent` that day, and under `scores`, each score of
|
||||
AbuseIPDB still in use: the `client`, its IPv4 address as a /32 or its IPv6
|
||||
group, its `score`, and when AbuseIPDB gave it, `fetched`. As the file is
|
||||
read, the lists the settings no longer name, and the verdicts of the zones
|
||||
they no longer name, are dropped.
|
||||
- `alerts.json`: the state of the alerts (see "Alerts" above), indented to be
|
||||
read: under `cooldowns`, for each event and netblock, with the `source` too
|
||||
for a `reputation_hit`, or event and `file` or `source`, or for an `anomaly`,
|
||||
@@ -1172,7 +1211,8 @@ at its default of `limit:25`, lowered to a quarter:
|
||||
"earlier_bans": {
|
||||
"limit": 0,
|
||||
"attack": 0,
|
||||
"admin": 0
|
||||
"admin": 0,
|
||||
"crowdsec": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1211,11 +1251,11 @@ counter's `netblock`, unless it counts an AS number or the whole service, its
|
||||
window in which it has requests or bytes; a list's `url`, `fetched` or `lines`,
|
||||
which is `[]` for an empty list; a verdict's `zone`, `client`, `listed`, which
|
||||
is `false` for a client the zone does not list, or `fetched`. So does a ban
|
||||
whose `cause` is not `limit`, `attack` or `admin`, alerts waiting for a
|
||||
destination that is not `webhook`, `slack` or `ntfy`, an anomaly counter whose
|
||||
`scope` is not `client`, `net`, `asn`, `total` or `watch`, and a copy of a list
|
||||
with a line that would make its fetch fail. An answer's `asn` or `as_name` left
|
||||
out reads as empty.
|
||||
whose `cause` is not `limit`, `attack`, `admin` or `crowdsec`, alerts waiting
|
||||
for a destination that is not `webhook`, `slack` or `ntfy`, an anomaly counter
|
||||
whose `scope` is not `client`, `net`, `asn`, `total` or `watch`, and a copy of a
|
||||
list with a line that would make its fetch fail. An answer's `asn` or `as_name`
|
||||
left out reads as empty.
|
||||
|
||||
While it runs, `smallwebwaf` watches `SWWAF_STATE_DIR` and takes in your edit of
|
||||
a state file as soon as you save it: what the file then holds replaces what
|
||||
@@ -1240,7 +1280,7 @@ and its `expires`, `null` for a ban that never ends; its `reason` and its
|
||||
written so at the file's next write. A ban whose `cause` is `admin` is never
|
||||
dropped and does not count toward `SWWAF_MAX_BANS`. A ban whose `cause` is
|
||||
`attack` becomes permanent at the first request it refuses; one whose `cause` is
|
||||
`admin` does not. This `bans.json` bans `203.0.113.0/24` for good:
|
||||
`admin` or `crowdsec` does not. This `bans.json` bans `203.0.113.0/24` for good:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -1369,11 +1409,11 @@ scraped, and keeps this one as `exported_instance` unless the scrape sets
|
||||
and `kind`, `requests` for a rate limit or `bytes` for a byte limit,
|
||||
`smallwebwaf_size_and_time_limit_hits_total` by `limit`, the setting whose
|
||||
limit was passed, `smallwebwaf_offences_total` by `kind`, and
|
||||
`smallwebwaf_bans_made_total` by `cause`, `limit`, `attack` or `admin`, the
|
||||
last for the bans you add through `POST /_smallwebwaf/bans`, and those whose
|
||||
`cause` is `admin` that you add to `bans.json` while `smallwebwaf` runs;
|
||||
`smallwebwaf_active_bans` and `smallwebwaf_permanent_bans`, neither of which
|
||||
counts a lifted ban.
|
||||
`smallwebwaf_bans_made_total` by `cause`, `limit`, `attack`, `admin` or
|
||||
`crowdsec`, `admin` for the bans you add through `POST /_smallwebwaf/bans`,
|
||||
and those whose `cause` is `admin` that you add to `bans.json` while
|
||||
`smallwebwaf` runs; `smallwebwaf_active_bans` and
|
||||
`smallwebwaf_permanent_bans`, neither of which counts a lifted ban.
|
||||
- `smallwebwaf_rule_matches_total`: the requests that matched each rule, by
|
||||
`rule_id` and `action`, the rule's own; and `smallwebwaf_rules_loaded`: the
|
||||
rules read from the rule files.
|
||||
@@ -1402,8 +1442,9 @@ scraped, and keeps this one as `exported_instance` unless the scrape sets
|
||||
`smallwebwaf_lookup_database_read_failures_total`: the replacements of it that
|
||||
could not be read.
|
||||
- By `source`, the URL of each list `SWWAF_BLOCKLIST_URLS` or
|
||||
`SWWAF_ASN_LIMIT_PERCENT_URL` names: `smallwebwaf_reputation_hits_total`: the
|
||||
requests whose client the blocklist lists, a series that comes with the first;
|
||||
`SWWAF_ASN_LIMIT_PERCENT_URL` names, and of the CrowdSec decision list:
|
||||
`smallwebwaf_reputation_hits_total`: the requests whose client the blocklist
|
||||
or the decision list lists, a series that comes with the first;
|
||||
`smallwebwaf_reputation_failures_total`: the fetches of the list that failed;
|
||||
and `smallwebwaf_reputation_last_fetch_timestamp_seconds`: when the copy of it
|
||||
in use was fetched, `0` while there is none.
|
||||
@@ -1633,7 +1674,8 @@ For each request `smallwebwaf`:
|
||||
the deny list or currently banned;
|
||||
- looks up its AS number and country, and refuses it if that country is denied,
|
||||
or is not among the only ones allowed;
|
||||
- checks it against the blocklists, and for a cached reputation verdict;
|
||||
- checks it against the blocklists, bans it if the CrowdSec decision list lists
|
||||
it, and checks it for a cached reputation verdict;
|
||||
- picks the client's limit percentage from those;
|
||||
- checks the minute, hour and day request counters against the limits, and bans
|
||||
the client if it breaks one;
|
||||
@@ -1944,6 +1986,58 @@ the metrics nor `reputation.json` hold it. Given as a file, with
|
||||
`SWWAF_ABUSEIPDB_KEY_FILE`, it can be kept out of the app's reach (see "Settings
|
||||
given as files" above).
|
||||
|
||||
## CrowdSec
|
||||
|
||||
While `SWWAF_CROWDSEC_LAPI_URL` names the local API of a CrowdSec engine you
|
||||
run, `smallwebwaf` fetches the engine's decision list, at that URL with
|
||||
`v1/decisions` added to its path, with `SWWAF_CROWDSEC_LAPI_KEY`, a key the
|
||||
engine gave, such as `cscli bouncers add smallwebwaf` makes. This is how a list
|
||||
kept for a whole fleet of hosts can reach `smallwebwaf` without it depending on
|
||||
CrowdSec. It is unset by default, for the reason no blocklist is named, and
|
||||
since it needs an engine of your own.
|
||||
|
||||
The list is fetched again a minute after it was last fetched or tried, the fetch
|
||||
failed or not, and is kept as a blocklist is (see "Blocklists" above): its last
|
||||
good copy, the engine's answer as it came, stays in use while a fetch fails, and
|
||||
`reputation.json` keeps it with the time it was fetched, so that a restart keeps
|
||||
it in use too. A fetch fails when the engine answers other than `200`, such as
|
||||
`403` for a key it does not know, when it does not finish within a minute, when
|
||||
the answer is longer than 16 MiB or is not a JSON list of decisions, or when a
|
||||
decision to ban gives a value that is not an address or a netblock, or a
|
||||
`duration` that does not read. A failure is counted, logged and raised as a
|
||||
`source_failure` alert, held back as a repeat within `SWWAF_ALERT_COOLDOWN`.
|
||||
|
||||
The decisions of the type `ban` on an address or a netblock, the scopes `Ip` and
|
||||
`Range`, are used; any other, such as one to show a captcha or one on a country,
|
||||
is left out. A decision bans until its `duration`, the time it had left as the
|
||||
engine answered, has passed since the fetch, so that one that has ended no
|
||||
longer bans, even while the copy in use still holds it because the engine cannot
|
||||
be reached.
|
||||
|
||||
A client in `SWWAF_ALLOW_NETS` is not checked, nor is one a blocklist refuses.
|
||||
Any other is checked by its own address after the blocklists. A request from a
|
||||
client a decision in force bans is refused with `SWWAF_BAN_RESPONSE`, and bans
|
||||
the client's netblock, as any ban covers it, until that decision ends, the one
|
||||
that ends last if several ban the client. The ban's cause is `crowdsec`, and its
|
||||
reason names the decision's scenario. Only a client that sends a request is
|
||||
banned, so that a long list does not fill `bans.json`. Such a ban counts toward
|
||||
`SWWAF_MAX_BANS`, is never made permanent, and does not make the netblock's next
|
||||
ban for a broken limit longer. The request's log line and the ban's notes name
|
||||
the list by its URL, as they name a blocklist, and the request raises a
|
||||
`reputation_hit` alert besides the ban's.
|
||||
|
||||
A ban you lift while its decision is in force is followed by another at the
|
||||
client's next request. To let a client in, delete its decision in CrowdSec, such
|
||||
as with `cscli decisions delete --ip 203.0.113.9`, and lift the ban once the
|
||||
list has been fetched again, a minute or so later. A ban made for a decision
|
||||
deleted in CrowdSec otherwise lasts until that decision would have ended.
|
||||
|
||||
The key is sent to the engine in the `X-Api-Key` header, and nowhere else. The
|
||||
settings logged at start show `********` in its place, and neither the log, the
|
||||
alerts, the metrics nor `reputation.json` hold it. Given as a file, with
|
||||
`SWWAF_CROWDSEC_LAPI_KEY_FILE`, it can be kept out of the app's reach (see
|
||||
"Settings given as files" above).
|
||||
|
||||
## How the code is laid out
|
||||
|
||||
- `cmd/smallwebwaf`: the binary, which only calls `internal/smallwebwaf`.
|
||||
@@ -1957,15 +2051,16 @@ given as files" above).
|
||||
standard library's `httputil.ReverseProxy` within the timeouts and size
|
||||
limits, and writes the request's log line. Its `check` method is where a
|
||||
request is refused before anything reaches the app: for `SWWAF_DENY_NETS`, for
|
||||
a ban, for the country lists, for a blocklist, for a DNSBL zone's verdict, for
|
||||
AbuseIPDB's score, for a rate limit, which bans the client, for a `block` or
|
||||
`ban` rule, the latter banning the client, and for an announced body over the
|
||||
size limit; in `observe` mode, only for the size limit, with what it would
|
||||
have refused for noted in the log line. A request under `/_smallwebwaf/` that
|
||||
`check` lets through is answered by `answerAdmin` instead of reaching the app.
|
||||
Once the answer to a request passed to the app has ended, `countBytes` counts
|
||||
its bytes for the byte limits, and once any request but the health check has
|
||||
ended, `countAnomalies` counts it for the anomaly thresholds.
|
||||
a ban, for the country lists, for a blocklist, for the CrowdSec decision list,
|
||||
which bans the client, for a DNSBL zone's verdict, for AbuseIPDB's score, for
|
||||
a rate limit, which bans the client, for a `block` or `ban` rule, the latter
|
||||
banning the client, and for an announced body over the size limit; in
|
||||
`observe` mode, only for the size limit, with what it would have refused for
|
||||
noted in the log line. A request under `/_smallwebwaf/` that `check` lets
|
||||
through is answered by `answerAdmin` instead of reaching the app. Once the
|
||||
answer to a request passed to the app has ended, `countBytes` counts its bytes
|
||||
for the byte limits, and once any request but the health check has ended,
|
||||
`countAnomalies` counts it for the anomaly thresholds.
|
||||
- `internal/metrics`: the metrics, counted as the other parts tell it what
|
||||
happened, and served in the Prometheus text format.
|
||||
- `internal/bans`: the ban ledger: each netblock's bans with their notes, how
|
||||
@@ -1978,14 +2073,15 @@ given as files" above).
|
||||
client's history and to the notes of its bans; or in the lookup database,
|
||||
which it reads again when the file is replaced. `internal/lookup/lookuptest`
|
||||
writes lookup databases for the tests.
|
||||
- `internal/reputation`: fetches the blocklists and the file
|
||||
`SWWAF_ASN_LIMIT_PERCENT_URL` names as they are due, keeps the last good copy
|
||||
of each, and tells which blocklists list an address and what percentage the
|
||||
file gives an AS number; asks the DNSBL zones about clients in the background,
|
||||
through the standard library's resolver, keeps their verdicts, and tells which
|
||||
zones' verdicts list an address; and checks clients with AbuseIPDB in the
|
||||
background, keeps their scores and the checks spent today, and tells whether a
|
||||
client's score is a hit.
|
||||
- `internal/reputation`: fetches the blocklists, the file
|
||||
`SWWAF_ASN_LIMIT_PERCENT_URL` names and the CrowdSec decision list as they are
|
||||
due, keeps the last good copy of each, and tells which blocklists list an
|
||||
address, what percentage the file gives an AS number, and which decision of
|
||||
the decision list bans an address; asks the DNSBL zones about clients in the
|
||||
background, through the standard library's resolver, keeps their verdicts, and
|
||||
tells which zones' verdicts list an address; and checks clients with AbuseIPDB
|
||||
in the background, keeps their scores and the checks spent today, and tells
|
||||
whether a client's score is a hit.
|
||||
- `internal/ratelimit`: the table of clients: counts each client's requests and
|
||||
bytes, tells when they take it over a rate limit or a byte limit, and keeps
|
||||
each client's history.
|
||||
|
||||
Reference in New Issue
Block a user