Files
abrown38andClaude Opus 5.5 d06bf8d531 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>
2026-10-22 16:29:37 +01:00

3.7 KiB

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.

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) with <IP> once, where the address is:

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

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.