Guard: watch logs, ban misbehaving addresses in Housecarl
Files and the journal, patterns with one <IP>, bounties within a window, bans in a Housecarl ban list with repeat offenders' bans growing; presets for sshd, jarl, housecarl, steward, bind-xfr, postfix-sasl, dovecot, nginx-auth and housecarl-drops; guard run/check/test/status/bans/ban/unban; a Debian package with a sandboxed unit; an end-to-end test with Housecarl. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
d06bf8d531
47 files changed
+4614
No files matched your search
@@ -0,0 +1,85 @@
|
||||
# Presets and patterns
|
||||
|
||||
A preset is a watch Guard knows: its patterns, and where its program's
|
||||
lines usually are. `guard presets` lists them; `guard presets sshd`
|
||||
shows one's patterns.
|
||||
|
||||
```yaml
|
||||
watch:
|
||||
- name: ssh
|
||||
preset: sshd
|
||||
ban: {protocol: tcp, port: 22}
|
||||
- name: ssh-file # the same, from a syslog file
|
||||
preset: sshd
|
||||
file: /var/log/auth.log
|
||||
```
|
||||
|
||||
A watch's own `file` or `journal` replaces the preset's source, and its
|
||||
`match` and `ignore` lines are added to the preset's.
|
||||
|
||||
| Preset | Source | Matches |
|
||||
|---|---|---|
|
||||
| `sshd` | journal, identifiers `sshd`, `sshd-session` | failed passwords and keys, unknown users, connections closed before login, too many attempts, failed key exchanges, garbage banners |
|
||||
| `jarl`, `housecarl`, `steward` | journal, units `jarl.service`, `housecarl.service`, `steward.service` | their failed logins and refused API tokens (`msg="login failed" ip=…`, text or JSON logs) |
|
||||
| `bind-xfr` | journal, identifier `named` | zone transfers BIND refused (TCP, so the address is real) |
|
||||
| `postfix-sasl` | journal, identifiers `postfix/smtpd`, `postfix/submission/smtpd`, `postfix/smtps/smtpd` | failed SMTP logins |
|
||||
| `dovecot` | journal, identifier `dovecot` | failed IMAP, POP3, submission and ManageSieve logins |
|
||||
| `nginx-auth` | file `/var/log/nginx/error.log` | failed HTTP basic authentication |
|
||||
| `housecarl-drops` | Housecarl's log of dropped packets | every TCP packet a logged rule dropped (on the firewall, for scans) |
|
||||
|
||||
OpenSSH 9.8 and later (Debian 13) logs logins from `sshd-session`, not
|
||||
`sshd`; the preset reads both. Debian 13 has no `/var/log/auth.log`
|
||||
unless rsyslog is installed: the journal is the place.
|
||||
|
||||
There's deliberately no preset for BIND's refused queries: they come
|
||||
over UDP, whose source address anyone can fake, so banning on them would
|
||||
let a stranger get your users' resolvers banned. Use BIND's response
|
||||
rate limiting for floods.
|
||||
|
||||
## Writing patterns
|
||||
|
||||
A pattern is a Go regular expression ([RE2 syntax](https://github.com/google/re2/wiki/Syntax))
|
||||
with `<IP>` once, where the address is:
|
||||
|
||||
```yaml
|
||||
match:
|
||||
- '^<IP> \S+ \S+ \[[^\]]+\] "POST /login[^"]*" 401 '
|
||||
- 'authentication failure; .* rhost=<IP>(?: |$)'
|
||||
```
|
||||
|
||||
`<IP>` matches IPv4 and IPv6 addresses, bracketed (`[2001:db8::9]`),
|
||||
IPv4-mapped (`::ffff:203.0.113.9`, counted as `203.0.113.9`) or with a
|
||||
zone. The text it matches must be a real address: `12:34:56` isn't one.
|
||||
|
||||
Patterns match anywhere in a line unless anchored. A line from a syslog
|
||||
file starts with its date, host and program (`Oct 22 10:00:01 web
|
||||
sshd[123]: `); a journal entry is the message alone. The presets begin
|
||||
with `(?:^|: )` so they work on both.
|
||||
|
||||
**Where the address is matters.** Many log lines carry text a client
|
||||
chose: a user name, a URL, a reverse DNS name. If a pattern lets that
|
||||
text supply the address, a client can get any address banned:
|
||||
|
||||
```
|
||||
Failed password for invalid user x from 198.51.100.1 port 22 from 203.0.113.9 port 4444 ssh2
|
||||
```
|
||||
|
||||
Anchor patterns at the end of the line, or put `<IP>` where the
|
||||
program writes the address after the client's text, so it is the
|
||||
program's address that matches. The presets do; `guard test` shows
|
||||
what yours find. Programs that log the address first (Jarl, Housecarl,
|
||||
Steward log `msg="login failed" ip=… user=…`) are the easiest to match
|
||||
safely.
|
||||
|
||||
## Trying patterns: guard test
|
||||
|
||||
```sh
|
||||
guard test web-login /var/log/nginx/access.log.1
|
||||
guard test ssh # a journal watch: its last 5000 entries
|
||||
zcat /var/log/nginx/access.log.2.gz | guard test web-login -
|
||||
```
|
||||
|
||||
It prints each matching line with its address, then the addresses that
|
||||
would be banned. Nothing is banned. Lines from a file carry no time
|
||||
Guard reads, so every match counts however far apart; the journal's
|
||||
entries have times, and the watch's window is used.
|
||||
Reference in new issue
Block a user