Files
smallwebwaf/docs/rules.md
T
clawbot f7e3190654
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. By hand afterwards: the header's spelling, which
the linter requires, and the padding after `waf` in `sww`'s usage
text.

Model: opus-5-5
2026-11-11 02:43:16 +01:00

72 lines
2.8 KiB
Markdown

# Rule files
The format of a rule file, and the default file the image ships. "Rule files" in
[`SPEC.md`](../SPEC.md) says how the files are read, watched and mounted, and
what each action leads to.
## The format
A rule is a line of four fields separated by spaces or tabs: an id, a target, an
action and a regex. The regex runs to the end of the line. Blank lines and lines
starting with `#` are ignored.
```
# id target action regex
env-file path ban (?i)^/\.env(\.[a-z]+)?$
scanner-agent user_agent ban (?i)\b(sqlmap|nikto|nuclei|wpscan)\b
```
- The id is letters, digits, `-` and `_`, unique across all files. The log line,
the metrics, the alerts and a ban's notes name the rule by it.
- The target is what the regex is matched against:
- `path`: the path without the query;
- `uri`: the path and the query together;
- `query`: the query as sent;
- `method`, `host`, `user_agent` and `referer`;
- `header:<Name>`: any one request header but `Host` and
`Transfer-Encoding`.
- The action is `log`, `block` or `ban`.
- The regex is in Go's syntax, RE2. It matches anywhere in the target unless
anchored with `^` and `$`. `(?i)` at its front makes it ignore case.
## The default file
The image ships one file, `share/rules.d/00-default.rules`, installed as
`/etc/sww/rules.d/00-default.rules`:
```
# 00-default.rules: probes no real visitor sends, anchored at the site root
# id target action regex
env-file path ban (?i)^/\.env(\.[a-z]+)?$
vcs-dir path ban (?i)^/\.(git|svn|hg|bzr)(/|$)
secrets-dir path ban (?i)^/\.(aws|ssh|docker|kube)/
secret-file path ban (?i)^/\.(htpasswd|htaccess|npmrc|netrc|pgpass|git-credentials|bash_history|DS_Store)$
editor-dir path ban (?i)^/\.(vscode|idea)/
backup-file path ban (?i)^/[^/]+\.(php(\.[a-z0-9]+|~)|sql(\.[a-z0-9]+)?)$
log-file path ban (?i)^/(debug|error|access)\.log$
compose-file path ban (?i)^/(docker-)?compose\.ya?ml$
php-shell path ban (?i)^/(shell|c99|r57|wso|alfa)\.php$
scanner-agent user_agent ban (?i)\b(sqlmap|nikto|nuclei|masscan|zgrab|wpscan)\b
path-traversal uri block (\.\./){2,}
empty-agent user_agent log ^$
```
## A file of your own
`waf` reads the rule files in its own container. Rules of your own go in a file
named to sort after the default, such as `50-site.rules`:
```
# 50-site.rules: WordPress probes, which this site never serves
wp-probe path ban (?i)^/(wp-login\.php|xmlrpc\.php|wp-admin/)
```
A `Dockerfile` built on the `smallwebwaf` image, for `waf` to run from, adds it:
```dockerfile
COPY 50-site.rules /etc/sww/rules.d/50-site.rules
```
Or mount it there. A `path` rule applies on every site behind that `waf`.