check / check (push) Waiting to run
docs/settings.md is now the one settings reference; the spec keeps the conventions, the off list and each group's requirements and reasons, and links to it for the per-setting entries. The endpoint list, the ban notes, the state file contents, the rule file format and the alert object likewise live in docs/ and are linked from the spec. The package list from the README's Design section joins "Architecture". The nine docs files are byte-identical to those on the README branch. Package comments that named "Alert webhook schema", "Request log" and "Metrics endpoint" for their field lists now name the docs files. Model: fable-5-1
115 lines
5.4 KiB
Markdown
115 lines
5.4 KiB
Markdown
# Rule files
|
|
|
|
`smallwebwaf` reads every `*.rules` file in `SWWAF_RULES_DIR`, in the order of
|
|
their names, and checks each request against their rules in that order, unless
|
|
the client is in `SWWAF_ALLOW_NETS`. The requirements the files meet are under
|
|
"Rule files" in [`SPEC.md`](../SPEC.md). A file whose name starts with `.`, such
|
|
as an editor's lock file, is not a rule file.
|
|
|
|
## 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 `_`. No two rules share one. 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`, which Go's HTTP server takes out of every request.
|
|
|
|
`path` and `uri` are matched both as sent and once percent-decoded, so that
|
|
an encoded probe does not slip past. A header sent more than once is matched
|
|
with its values joined by `, `. A header not sent is empty text. No body is
|
|
read; reading bodies is the Core Rule Set's job, once `SWWAF_WAF_BODY_LIMIT`
|
|
switches it on.
|
|
|
|
- The action is `log`, `block` or `ban`. `log` only notes the match in the log
|
|
line. `block` refuses the request with `403` and bans no one; the refusal
|
|
counts toward the error burst. `ban` refuses it and bans the client, as a
|
|
clear sign of attack. Matching stops at the first rule that refuses. Keep
|
|
`ban` for requests no real visitor sends. Anchor a path at the site root with
|
|
`^/`: a file of the same name deeper in a site can be ordinary content.
|
|
- The regex is in Go's syntax, RE2. It has no backreferences or lookaround, and
|
|
takes time linear in the text it reads, so no rule can be made to stall the
|
|
proxy. It matches anywhere in the target unless anchored with `^` and `$`.
|
|
`(?i)` at its front makes it ignore case.
|
|
|
|
## Errors
|
|
|
|
These stop the start, with a message naming the file and line:
|
|
|
|
- a line that is not a rule;
|
|
- a bad `header:` name, or a rule for `Host` or `Transfer-Encoding`;
|
|
- a regex that does not compile;
|
|
- an id used twice.
|
|
|
|
So does a `SWWAF_RULES_DIR` that does not exist. An empty directory is not an
|
|
error: the log says that no rules were loaded.
|
|
|
|
While it runs, `smallwebwaf` watches the directory. It reads the files again
|
|
once the directory has had no change for 2 seconds, so that a file still being
|
|
written is read whole. If the files then hold such an error, the rules stay as
|
|
they were. The log and a `file_error` alert name the file and line. Once the
|
|
file is fixed, it is read again.
|
|
|
|
## The default file
|
|
|
|
The image ships one file, `share/rules.d/00-default.rules`, installed as
|
|
`/etc/smallwebwaf/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 ^$
|
|
```
|
|
|
|
It is kept short and limited to patterns that are wrong for every app, with its
|
|
path rules anchored at the site root. In front of gitea, `/.env` is a probe,
|
|
while `/<owner>/<repo>/src/branch/main/.env.example` is a file in a repository
|
|
that any visitor may open. Anything app-specific belongs in a file the app's
|
|
`Dockerfile` adds or the deployer mounts. A request for `/wp-login.php`, for
|
|
example, is a clear sign of attack in front of gitea and an ordinary login in
|
|
front of WordPress. Such a rule goes in a file named to sort after the default:
|
|
|
|
```
|
|
# 50-gitea.rules: WordPress probes, which gitea never serves
|
|
wp-probe path ban (?i)^/(wp-login\.php|xmlrpc\.php|wp-admin/)
|
|
```
|
|
|
|
```dockerfile
|
|
COPY 50-gitea.rules /etc/smallwebwaf/rules.d/50-gitea.rules
|
|
```
|
|
|
|
## Mounting rule files
|
|
|
|
A directory mounted over `/etc/smallwebwaf/rules.d` replaces the default file;
|
|
single files mounted into it add to it. Many editors save a file by replacing
|
|
it, and a container keeps seeing the old one when the file alone is mounted. So
|
|
rules to be edited while `smallwebwaf` runs belong in a mounted directory, with
|
|
a copy of the default file if the defaults are to stay. The lookup database is
|
|
mounted the same way, for the same reason. To run without rules, mount an empty
|
|
directory or set `SWWAF_RULES_ENABLED=false`.
|