Pass-through proxy with timeouts, size limits and a request log (closes #13)
check / check (push) Successful in 1m29s
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:
+51
-54
@@ -17,8 +17,8 @@ configured only by environment variables, that provides:
|
||||
- R5: temporary blocks for abusers, permanent bans for repeat offenders
|
||||
- R6: one or more RBLs or IP reputation APIs
|
||||
- R7: AS number lookup
|
||||
- R8: thresholds biased by AS number or country (for example, listed AS
|
||||
numbers get 50 percent of the normal limit)
|
||||
- R8: thresholds biased by AS number or country (for example, listed AS numbers
|
||||
get 50 percent of the normal limit)
|
||||
- R9: WAF-style attack detection and prevention
|
||||
- R10: runs as a plain env-var-configured sidecar between traefik and one app
|
||||
|
||||
@@ -37,19 +37,19 @@ Closest existing options, and why each still falls short:
|
||||
|
||||
- BunkerWeb is the closest single product.
|
||||
- Meets: R1 (rates in requests per second, minute, hour or day), R2
|
||||
(`LIMIT_IGNORE_IP`, also by AS number and reverse DNS), R3 partly (webhook,
|
||||
Slack, Discord, Matrix plugins, fired only on denied requests; ntfy only
|
||||
through the generic webhook, payload format unverified), R5 partly
|
||||
(`BAD_BEHAVIOR_BAN_TIME`, `0` means permanent; no escalation for repeat
|
||||
offenders, the ban length is one fixed value), R6 (DNSBL plugin, external
|
||||
blacklist URLs, optional CrowdSec), R7 partly (AS number used for
|
||||
(`LIMIT_IGNORE_IP`, also by AS number and reverse DNS), R3 partly
|
||||
(webhook, Slack, Discord, Matrix plugins, fired only on denied requests;
|
||||
ntfy only through the generic webhook, payload format unverified), R5
|
||||
partly (`BAD_BEHAVIOR_BAN_TIME`, `0` means permanent; no escalation for
|
||||
repeat offenders, the ban length is one fixed value), R6 (DNSBL plugin,
|
||||
external blacklist URLs, optional CrowdSec), R7 partly (AS number used for
|
||||
blacklist and whitelist decisions), R9 (ModSecurity with the Core Rule
|
||||
Set, or Coraza plugin), env-var settings.
|
||||
- Fails: R8 (AS number and country can only allow or deny, never scale a
|
||||
limit), R4 (no volume or byte threshold alerts in the free edition;
|
||||
reporting is a paid feature), R5 escalation, and R10 in spirit: since
|
||||
1.6 it needs a `bunkerweb` container plus a `bw-scheduler` container and
|
||||
a database, or the all-in-one image that bundles nginx, scheduler, UI and
|
||||
reporting is a paid feature), R5 escalation, and R10 in spirit: since 1.6
|
||||
it needs a `bunkerweb` container plus a `bw-scheduler` container and a
|
||||
database, or the all-in-one image that bundles nginx, scheduler, UI and
|
||||
Redis in one container. It is designed to be the front door for many
|
||||
sites, not a per-app sidecar. AGPL-3.0.
|
||||
- Unverified: whether several rates (minute, hour, day) can be stacked on
|
||||
@@ -61,15 +61,14 @@ Closest existing options, and why each still falls short:
|
||||
(community blocklist, further blocklists, reputation API), R7 (alerts are
|
||||
enriched with AS number and country), R9 (AppSec component with virtual
|
||||
patching and ModSecurity-syntax rules), R2 (allowlists).
|
||||
- Partly: R1 and R8. Detection is by leaky-bucket scenarios over logs, and
|
||||
a scenario can filter on AS number or country, so a stricter bucket for
|
||||
- Partly: R1 and R8. Detection is by leaky-bucket scenarios over logs, and a
|
||||
scenario can filter on AS number or country, so a stricter bucket for
|
||||
listed AS numbers is possible, but each is a hand-written YAML scenario,
|
||||
it reacts after the fact by banning, and it is not an inline limiter that
|
||||
answers 429.
|
||||
- Fails: R10 (needs the security engine container with persistent state,
|
||||
log acquisition from traefik, a bouncer such as the traefik plugin, and
|
||||
YAML for acquisition, profiles, scenarios and notifications), bytes half
|
||||
of R4.
|
||||
- Fails: R10 (needs the security engine container with persistent state, log
|
||||
acquisition from traefik, a bouncer such as the traefik plugin, and YAML
|
||||
for acquisition, profiles, scenarios and notifications), bytes half of R4.
|
||||
- CrowdSec plus traefik's own `rateLimit` middleware is the best combination
|
||||
with no new code. It gives inline limiting (one window per middleware, keyed
|
||||
by IP, with `sourceCriterion` exclusions), bans with escalation, alerts and
|
||||
@@ -89,19 +88,19 @@ Closest existing options, and why each still falls short:
|
||||
central API, or AppSec only. Supports captcha remediation.
|
||||
- Covers: R5, R6, R9, R3 and R7 through the engine (see Verdict).
|
||||
- Misses: the plugin itself does no rate limiting; R8; R4 bytes.
|
||||
- Configuration: traefik static config to load the plugin, dynamic config
|
||||
or labels for the middleware, CrowdSec YAML for everything else.
|
||||
- Configuration: traefik static config to load the plugin, dynamic config or
|
||||
labels for the middleware, CrowdSec YAML for everything else.
|
||||
- Sidecar fit: no. It lives inside traefik, plus a separate engine
|
||||
container.
|
||||
- Maturity: widely used (about 900 stars, listed in the traefik plugin
|
||||
catalog, documented by CrowdSec itself), actively maintained. Traefik
|
||||
plugins run in an interpreter inside traefik, which costs some
|
||||
per-request time.
|
||||
plugins run in an interpreter inside traefik, which costs some per-request
|
||||
time.
|
||||
- CrowdSec generic bouncers (nginx, Caddy, firewall)
|
||||
- Same engine, different enforcement point. The firewall bouncer blocks at
|
||||
nftables level on the host, which is cheap and covers every service on
|
||||
the host at once; worth considering fleet-wide regardless of this
|
||||
project. Not a sidecar, same misses as above.
|
||||
nftables level on the host, which is cheap and covers every service on the
|
||||
host at once; worth considering fleet-wide regardless of this project. Not
|
||||
a sidecar, same misses as above.
|
||||
- BunkerWeb 1.6.14 (`bunkerity/bunkerweb`)
|
||||
- What it is: nginx with Lua plugins, ModSecurity and the Core Rule Set,
|
||||
configured by settings that are passed as env vars to its scheduler
|
||||
@@ -123,23 +122,22 @@ Closest existing options, and why each still falls short:
|
||||
edition is capped at 10 applications.
|
||||
- Configuration: web UI backed by PostgreSQL. No env-var configuration.
|
||||
- Sidecar fit: no. Seven containers (postgres, management, detector,
|
||||
tengine, and three helpers); the proxy container uses host networking.
|
||||
The detection engine is closed source.
|
||||
tengine, and three helpers); the proxy container uses host networking. The
|
||||
detection engine is closed source.
|
||||
- Maturity: very active, vendor-driven.
|
||||
- Coraza (`corazawaf/coraza`) and Coraza-based proxies
|
||||
- What it is: a Go library that implements the ModSecurity rule language
|
||||
and runs the OWASP Core Rule Set. OWASP project, actively maintained, the
|
||||
- What it is: a Go library that implements the ModSecurity rule language and
|
||||
runs the OWASP Core Rule Set. OWASP project, actively maintained, the
|
||||
successor path now that ModSecurity is in maintenance only.
|
||||
- Packagings: `coraza-caddy` (Caddy module), `coraza-spoa` (HAProxy),
|
||||
`coraza-proxy-wasm` (Envoy), a traefik WASM plugin, and
|
||||
`coreruleset/coraza-crs-docker` (Caddy plus Coraza plus the Core Rule
|
||||
Set, with env vars for backend address, engine mode and rule-set
|
||||
tuning).
|
||||
`coreruleset/coraza-crs-docker` (Caddy plus Coraza plus the Core Rule Set,
|
||||
with env vars for backend address, engine mode and rule-set tuning).
|
||||
- Covers: R9 only. `coraza-crs-docker` fits R10 well: one container, env
|
||||
vars, backend address.
|
||||
- Misses: R1 to R8. ModSecurity-language rules can count requests per IP
|
||||
in a persistent collection, but Coraza's support for persistent
|
||||
collections is limited and this is not a practical rate limiter.
|
||||
- Misses: R1 to R8. ModSecurity-language rules can count requests per IP in
|
||||
a persistent collection, but Coraza's support for persistent collections
|
||||
is limited and this is not a practical rate limiter.
|
||||
- Value here: the right library to embed for R9 in a purpose-built sidecar.
|
||||
- ModSecurity Core Rule Set containers (`owasp/modsecurity-crs`)
|
||||
- What it is: official images of Apache or nginx with ModSecurity and the
|
||||
@@ -149,21 +147,21 @@ Closest existing options, and why each still falls short:
|
||||
- Covers: R9, and R10 (single container, env vars, one backend).
|
||||
- Misses: R1 to R8.
|
||||
- Maturity: rule set is very actively maintained; the ModSecurity engine
|
||||
itself is in maintenance under OWASP after Trustwave ended support in
|
||||
2024.
|
||||
itself is in maintenance under OWASP after Trustwave ended support
|
||||
in 2024.
|
||||
- Anubis (`TecharoHQ/anubis`), 1.27 current
|
||||
- What it is: a single-binary reverse proxy that makes browsers solve a
|
||||
proof-of-work challenge before passing them to `TARGET`. Aimed at
|
||||
scrapers, which is likely a large share of unwanted traffic on a public
|
||||
gitea.
|
||||
- Covers: R10 well (one container, env vars for listener, target,
|
||||
difficulty, cookies). Policy rules can match on path, user agent,
|
||||
headers, IP ranges, and with the vendor's hosted data service also AS
|
||||
number and country, and can weigh a request toward a harder challenge.
|
||||
difficulty, cookies). Policy rules can match on path, user agent, headers,
|
||||
IP ranges, and with the vendor's hosted data service also AS number and
|
||||
country, and can weigh a request toward a harder challenge.
|
||||
- Misses: R1, R3, R4, R5, R6, R9. Bot policy needs a YAML file, not env
|
||||
vars. AS number and country matching depend on the vendor's hosted
|
||||
service. Breaks non-browser clients unless paths are exempted; for
|
||||
gitea, git-over-HTTP and API paths must be allowed through by rule.
|
||||
service. Breaks non-browser clients unless paths are exempted; for gitea,
|
||||
git-over-HTTP and API paths must be allowed through by rule.
|
||||
- Maturity: very active, widely deployed on code forges since 2025.
|
||||
- Value here: complementary. It can be chained (traefik, then the sidecar,
|
||||
then Anubis, then the app) if challenge pages are wanted.
|
||||
@@ -187,29 +185,28 @@ Closest existing options, and why each still falls short:
|
||||
- Sidecar fit: no; they live inside traefik.
|
||||
- Traefik built-in middlewares
|
||||
- `rateLimit` (one average-and-burst window per middleware, optional Redis
|
||||
in traefik 3.x), `inFlightReq`, `ipAllowList`. No bans, alerts,
|
||||
reputation or AS number awareness.
|
||||
in traefik 3.x), `inFlightReq`, `ipAllowList`. No bans, alerts, reputation
|
||||
or AS number awareness.
|
||||
- caddy-waf (`fabriziosalmi/caddy-waf`), 0.4.x
|
||||
- What it is: a Caddy module with regex rules and anomaly scoring, per-IP
|
||||
and per-path rate limiting with one configurable window, IP and DNS
|
||||
blacklists, Tor exit list fetch, country and AS number allow or deny
|
||||
from MaxMind databases.
|
||||
blacklists, Tor exit list fetch, country and AS number allow or deny from
|
||||
MaxMind databases.
|
||||
- Misses: R8 (allow or deny only), R3, R4, R5 (no documented ban state or
|
||||
alerting), R1's three windows. Caddyfile configuration. One maintainer,
|
||||
pre-1.0, AGPL-3.0.
|
||||
- open-appsec (Check Point)
|
||||
- Machine-learning WAF agent attached to nginx, Kong, Envoy or similar,
|
||||
with a declarative policy file or the vendor's cloud console. Covers R9
|
||||
only; rate limiting and richer features are in paid tiers. Not a sidecar
|
||||
in the required sense.
|
||||
- Machine-learning WAF agent attached to nginx, Kong, Envoy or similar, with
|
||||
a declarative policy file or the vendor's cloud console. Covers R9 only;
|
||||
rate limiting and richer features are in paid tiers. Not a sidecar in the
|
||||
required sense.
|
||||
- iocaine
|
||||
- Serves generated garbage pages to clients the fronting proxy classifies
|
||||
as scrapers. Not a limiter, WAF or ban tool; out of scope except as a
|
||||
- Serves generated garbage pages to clients the fronting proxy classifies as
|
||||
scrapers. Not a limiter, WAF or ban tool; out of scope except as a
|
||||
curiosity for scraper traffic.
|
||||
- Pangolin
|
||||
- A tunnelled access platform that bundles traefik and optionally
|
||||
CrowdSec. Replaces the ingress rather than adding a sidecar; out of
|
||||
scope.
|
||||
- A tunnelled access platform that bundles traefik and optionally CrowdSec.
|
||||
Replaces the ingress rather than adding a sidecar; out of scope.
|
||||
|
||||
## Requirement by requirement, across the field
|
||||
|
||||
|
||||
Reference in New Issue
Block a user