Files
smallwebwaf/docs/admin-endpoints.md
T
sneak 3fc63a1e40
check / check (push) Waiting to run
Rename discriminator to waf everywhere (closes #312)
The command is now `sww waf`, and the package is `internal/waf`. Types,
functions, files, tests, comments, docs, the Dockerfile, the runit
script, `script/`, compose examples, the log field `waf_line` and the
`X-From-Waf` test header follow. No behaviour changes, and no setting
changes name: rproxy already finds the waf through `SWRP_WAF_BACKEND`.

Done with one `sed -i` (Discriminator and DISCRIMINATOR to WAF,
discriminator to waf) over the files `git grep -i discriminator`
listed, as sneak allowed for this rename, then `git mv` for the
package and files.

Model: opus-5-5
2026-11-11 00:41:32 +00:00

2.6 KiB

Admin endpoints

The endpoints under /_sww/, what each takes and what each answers. sww waf answers them at its own address, on the private network. "Admin endpoints" in SPEC.md says how their tokens work.

  • GET /_sww/healthz: 200 and ok, to anyone.
  • GET /_sww/metrics: the metrics, for SWW_METRICS_TOKEN (see metrics.md).
  • GET /_sww/bans: every ban held, under bans, each as bans.json lists it. Needs SWW_ADMIN_TOKEN, as do the three below.
  • POST /_sww/bans: bans a netblock. The body is a JSON object of netblock, duration and, if you like, reason. netblock is a netblock such as 203.0.113.0/24, or a client's address, which bans its IPv4 netblock (SWW_BAN_SCOPE_V4_PREFIX) or its IPv6 group. duration is a duration such as 1h or 7d, or permanent. A bad body is answered 400, saying what is wrong:
    • one that is not such an object, or has another field;
    • one longer than 4 KiB;
    • one that names an IPv4-mapped netblock, or an address with a zone.
  • DELETE /_sww/bans/<client>: lifts every active ban covering the address <client>. It answers 404 when none is active.
  • GET /_sww/clients/<ip>: what waf knows of the client. Under client, its counters and history as clients.json holds them, or null when clients.json does not hold it. Under bans, every ban covering the address, with its notes.
  • POST /_sww/report: takes rproxy's report, without a token. It answers 204, or 400 to a body that is no report (see "The report" in SPEC.md).

No check runs on these requests. A missing or wrong token is answered 401. An endpoint whose token is unset answers 404, as does any other path.

sww rproxy answers only the health check of these, at its own address. It asks waf about any other request under /_sww/. One that passes the checks is answered 404, so that the app never sees that prefix (see "rproxy" in SPEC.md).

Examples

With SWW_ADMIN_TOKEN set, the token in $TOKEN, and waf reached as waf:8080 on its network:

# Every ban.
curl -H "Authorization: Bearer $TOKEN" http://waf:8080/_sww/bans

# Ban 203.0.113.0/24 for seven days.
curl -H "Authorization: Bearer $TOKEN" \
    --json '{"netblock": "203.0.113.0/24", "duration": "7d", "reason": "probes for logins"}' \
    http://waf:8080/_sww/bans

# Lift the bans on 203.0.113.9.
curl -H "Authorization: Bearer $TOKEN" -X DELETE \
    http://waf:8080/_sww/bans/203.0.113.9

# What waf knows of 203.0.113.9.
curl -H "Authorization: Bearer $TOKEN" \
    http://waf:8080/_sww/clients/203.0.113.9