check / check (push) Waiting to run
SPEC.md is the only place that says how smallwebwaf behaves and why; docs/ holds lists linking to it; the README is short and links to both. Passed independent review in round 8. Model: fable-5-1 (writing); opus-5-5 (review, merge)
120 lines
5.4 KiB
Markdown
120 lines
5.4 KiB
Markdown
# The request log's fields
|
|
|
|
The fields of the JSON object `smallwebwaf` writes for each request. "Request
|
|
log" in [`SPEC.md`](../SPEC.md) says when a line is written and how the log is
|
|
sent to a syslog server. A field that does not apply to a request is left out.
|
|
|
|
## Example
|
|
|
|
```
|
|
{"type":"request","time":"2026-10-03T12:00:00.123Z","instance":"fsn1app1/gitea","client_ip":"203.0.113.9","method":"GET","scheme":"https","host":"app.example","path":"/","query":"","protocol":"HTTP/1.1","status":200,"request_bytes":0,"response_bytes":5120,"referer":"","user_agent":"curl/8.9.1","request_id":"7Q2NHZ4KJ3VXW5YB6R3MEFTD2A","peer_ip":"172.18.0.2","forwarded_for":"203.0.113.9","client_group":"203.0.113.9/32","asn":"AS64496","as_name":"Example Net","country":"DE","request_headers":{"accept":"*/*"},"response_content_type":"text/html; charset=utf-8","upstream_status":200,"action":"forward","counts":{"minute":1,"hour":12,"day":40,"minute_bytes":5120,"hour_bytes":61440,"day_bytes":204800},"duration_total":3.217,"duration_checks":0.041,"duration_upstream_connect":0.052,"duration_upstream_first_byte":2.874,"duration_upstream_total":3.104}
|
|
```
|
|
|
|
## Every line
|
|
|
|
Every line has:
|
|
|
|
- `type`: `request` (the process's own messages have `process`);
|
|
- the standard web log fields;
|
|
- `request_id`, `peer_ip` and `client_group`;
|
|
- `asn`, `as_name` and `country`;
|
|
- `action` and `duration_total`.
|
|
|
|
## Standard web log fields
|
|
|
|
- `time`: when the request arrived, RFC 3339 with milliseconds, in UTC;
|
|
- `instance`: `SWWAF_INSTANCE_NAME`;
|
|
- `client_ip`, `method`, `scheme`, `host`, `path`, `query`, `protocol`;
|
|
- `status`: what the client was sent, `0` if nothing;
|
|
- `request_bytes` and `response_bytes`: body bytes each way;
|
|
- `referer` and `user_agent`.
|
|
|
|
## Request detail
|
|
|
|
- `request_id`: the `X-Request-ID` a trusted proxy sent, or a new random one of
|
|
26 letters and digits.
|
|
- `peer_ip`: the TCP peer, normally traefik.
|
|
- `forwarded_for`: the `X-Forwarded-For` header as received.
|
|
- `client_group`: the client as rate limiting counts it, an IPv4 address as a
|
|
/32 or an IPv6 group.
|
|
- `asn`, `as_name` and `country`: the client's, as looked up. Each is empty
|
|
when:
|
|
- lookups are off;
|
|
- the client is not looked up;
|
|
- the source cannot place the client;
|
|
- GeoJS had not answered by the time the request went on.
|
|
- `content_type` and `content_length`: the request's own.
|
|
- `request_headers`: the headers listed in `SWWAF_LOG_REQUEST_HEADERS`, by name
|
|
in lower case.
|
|
- `has_authorization` and `has_cookie`: true when the request had those headers.
|
|
Their values are never logged.
|
|
- `websocket`: true when the app switched the connection to another protocol.
|
|
|
|
## Response detail
|
|
|
|
- `upstream_status`: what the app answered. It differs from `status` when
|
|
`smallwebwaf` answered itself.
|
|
- `response_content_type`, `cache_control` and `location`: the answer's headers.
|
|
- `aborted`: true when the client went away early.
|
|
|
|
## Decision
|
|
|
|
- `action`: what was done:
|
|
- `forward`: passed to the app;
|
|
- `denied`: refused for `SWWAF_DENY_NETS`, or for a reputation source with
|
|
the action `deny`;
|
|
- `banned`: refused under a ban, or for a `ban` rule, a trap path or a
|
|
CrowdSec decision, each of which bans the client;
|
|
- `country_denied`, `rate_limited`, `rule_blocked` and `waf_blocked`:
|
|
refused by that check;
|
|
- `too_large` and `timed_out`: a size or time limit was passed;
|
|
- `upstream_error`: the app could not be reached, or its answer broke off;
|
|
- `admin`: answered at one of `smallwebwaf`'s own endpoints.
|
|
- `would_action`: in `observe` mode, what `enforce` mode would have done with a
|
|
request it would have refused. `action` then names what was done, normally
|
|
`forward`.
|
|
- `limit_percent` and `limit_percent_setting`: the client's percentage of the
|
|
rate limits and the setting that lowered it, when one did. `bytes_percent` and
|
|
`bytes_percent_setting` do the same for the byte limits.
|
|
- `counts`: the client's totals as rate limiting counts them, this request
|
|
included. It is left out when rate limiting did not count the request. It
|
|
holds:
|
|
- `minute`, `hour` and `day`: requests;
|
|
- `minute_bytes`, `hour_bytes` and `day_bytes`: bytes.
|
|
|
|
A count can have a fraction (see "Counting method" in
|
|
[`SPEC.md`](../SPEC.md)).
|
|
|
|
- `limit_hit`: the limit broken:
|
|
- `minute`, `hour` or `day` for a rate limit;
|
|
- `minute_bytes`, `hour_bytes` or `day_bytes` for a byte limit;
|
|
- `error_burst`.
|
|
|
|
A request whose bytes broke a limit was passed on whole, so its `action`
|
|
stays `forward`.
|
|
|
|
- `rule_ids`: the rule file rules that matched, in order, up to the one that
|
|
refused.
|
|
- `waf_score` and `waf_rule_ids`: the Core Rule Set's score and the rules that
|
|
matched, for a request it inspected.
|
|
- `reputation`: the sources the client is on, in this order:
|
|
- blocklist URLs;
|
|
- the CrowdSec decision list's URL;
|
|
- DNS blocklist zones;
|
|
- `abuseipdb`.
|
|
- `offence`: `limit`, for a request that broke a limit.
|
|
- `ban_expires`: when the client's ban ends, for a request that made one or was
|
|
refused under one: a time, or `permanent`.
|
|
|
|
## Timings
|
|
|
|
All in milliseconds:
|
|
|
|
- `duration_total`: from the request's headers being read to its log line;
|
|
- `duration_checks`: to the end of the checks, before forwarding;
|
|
- `duration_waf`: the part of that the Core Rule Set took;
|
|
- `duration_upstream_connect`: from the hand-over to the app until there was a
|
|
connection;
|
|
- `duration_upstream_first_byte`: until the first byte of the answer;
|
|
- `duration_upstream_total`: until the end of the answer.
|