Pass-through proxy with timeouts, size limits and a request log (closes #13)
check / check (push) Successful in 1m29s

Milestone 1, the repo's first code. smallwebwaf passes each request to the app and the answer back unchanged, streaming bodies and WebSocket upgrades, within four timeouts (client and app, request and response) and two size limits, and writes one JSON line per request to stdout. Every setting has an SWWAF_ name and a default, and an invalid value stops the start. The repo gets the standard layout: script/ entrypoints, make targets that call them, a Dockerfile that runs the checks, and the Gitea workflow.

Disclosure: SPEC.md changed. Go's server reads the request line and headers before smallwebwaf sees the request, so slow headers are closed without an answer, and neither slow nor oversized headers get a log line.
Disclosure: standard library only.

Model: opus-5-5
This commit was merged in pull request #39.
This commit is contained in:
2026-10-03 17:24:34 +02:00
parent fd77e76177
commit d76715b0df
47 changed files with 4917 additions and 74 deletions
+17 -8
View File
@@ -1,8 +1,8 @@
# smallwebwaf SPEC (draft): protective reverse proxy for one app
Status: fourth draft, with the owner's rulings to date applied. Nothing has been
built yet. `EVALUATION.md` beside this file explains why no existing tool was
chosen.
Status: fourth draft, with the owner's rulings to date applied. Milestone 1 of
the build order is built. `EVALUATION.md` beside this file explains why no
existing tool was chosen.
## Purpose
@@ -409,7 +409,8 @@ The settings, by group:
Bodies stream straight through, so a request body reaches the app while the
client is still sending it.
- `SWWAF_CLIENT_REQUEST_TIMEOUT` (default `60s`): how long a client may take
to send its whole request, headers and body.
to send its request line and headers, and then, from the end of the
headers, its body.
- `SWWAF_CLIENT_REQUEST_HEADER_MAX_BYTES` (default `32K`): the largest
request line and headers a client may send. Over it, `smallwebwaf` answers
`431` and closes the connection, and nothing reaches the app.
@@ -436,6 +437,12 @@ The settings, by group:
an app that is too slow. A request that announces a body larger than its
limit is refused before anything reaches the app. Once the response has
started it can only be cut off, and the connection is closed.
- Go's HTTP server, on which `smallwebwaf` is built, reads a request's line
and headers before `smallwebwaf` sees the request. A client that takes
longer than `SWWAF_CLIENT_REQUEST_TIMEOUT` to send them gets no answer:
the server closes its connection. Headers over
`SWWAF_CLIENT_REQUEST_HEADER_MAX_BYTES` are answered `431` by the server
itself. Neither request gets a line in the request log.
- A WebSocket connection leaves these limits behind once it is upgraded: it
stays open until either side closes it.
- Lookup of AS number and country (R7). On by default through GeoJS, which needs
@@ -1012,10 +1019,12 @@ and the running `smallwebwaf` takes the edit in.
## Request log
One JSON object per line on stdout for every request, including refused ones.
stdout is always on. When `SWWAF_LOG_REMOTE_URL` is set the same lines are also
sent to the remote endpoint, so a deployment can stop depending on docker's log
handling while `docker logs` keeps working.
One JSON object per line on stdout for every request, including refused ones,
apart from those Go's HTTP server ends before `smallwebwaf` sees them (see
"Configuration surface", size and time limits). stdout is always on. When
`SWWAF_LOG_REMOTE_URL` is set the same lines are also sent to the remote
endpoint, so a deployment can stop depending on docker's log handling while
`docker logs` keeps working.
- Standard web log fields: `time` (RFC 3339 with milliseconds), `instance`,
`client_ip`, `method`, `scheme`, `host`, `path`, `query`, `protocol`,