-
Guard v1.0.0-rc.1
Pre-releaseAll checks were successfulrelease / release (push) Successful in 1m28sreleased this
2026-10-25 19:00:46 +00:00 | -2 commits to main since this releaseAdded
-
First version. Guard watches log files (followed through rotation and
truncation) and the systemd journal (by unit and syslog identifier),
matches lines with patterns holding one<IP>, counts each address's
bounty within a window, and bans an address that reaches a watch's
hits in a Housecarl ban list (Housecarl 0.2): of every port, or of the
watch's protocol and ports, for a time that grows for repeat
offenders. IPv6 addresses are counted and banned by the ban list's
prefix (/64). Never banned:never_ban(addresses, prefixes, host
names), loopback, link-local, multicast; Housecarl adds its own. Bans
wait while Housecarl can't be reached, across restarts. -
Presets:
sshd,jarl,housecarl,steward,bind-xfr,
postfix-sasl,dovecot,nginx-auth,housecarl-drops
(Housecarl's log of dropped packets, TCP only by default). -
Commands:
guard run,check,config [--example],presets,
test(a watch's patterns over a log),statusandreload(through
a local socket),bans,ban,unban,version. -
Reloading:
systemctl reload guard,guard reloador SIGHUP reads
guard.yaml again without losing the bounties. A watch keeps its
bounties under the same name and takes its new settings at once; its
source starts again only if it changed; watches gone stop, new ones
start. A file with a mistake is refused. The Housecarl connection,
state, socket and log wait for a restart (guard reloadsays so). -
A Debian package with a sandboxed systemd unit, as the
guarduser in
theadmandsystemd-journalgroups, published with each release to
the Gitea Debian registry (stable, ortestingfor a release
candidate) along with tarballs for Linux amd64 and arm64. -
Weights. A pattern may capture the user name tried, with
<USER>
(one word) or a group nameduser(any form), and a watch'susers
weighs matches by it (users: {root: 5, admin: 3, "*": 1}); a match
pattern may weigh more on its own ({pattern: …, weight: 3}). The
weight is how many hits a match counts as; a weight ofhitsbans on
the first match. The presets capture the user where their
program logs it (sshd, Jarl, Housecarl, Steward, Dovecot, nginx).
guard testshows each match's user and weight. -
Observe mode:
mode: observe(per watch, ordefaults.mode) counts as
usual but bans nobody; Guard logs "would ban (observe mode)" and
guard statuslists the would-be bans. They count as no offence. -
Escalation, off unless a watch asks:
escalate: {after: 3, within: 1d, list: edge, time: 1w}sends an address's third ban within a day
(counting every watch's bans) to another ban list or action, of every
port unless it says which, for its own time.escalate: defaulttakes
defaults.escalate. -
Actions: a watch can ban with your own commands instead of Housecarl
(actions: {nft: {ban: …, unban: …}},ban: {action: nft}), run with
sh -cand the ban only inGUARD_*environment variables (never
written into the command). Guard keeps their bans in its state file and
runs the unban command when each ends, also after a restart; a failed
ban is tried again while it lasts, a failed unban up to 5 times. A
Guard whose watches all use actions needs no Housecarl.guard test-actionruns an action's ban and unban for a test address;guard bans/ban/unban --actionwork on its bans through the running Guard;
guard checkchecks the commands' syntax. docs/actions.md, with an
nftables table and script (contrib/nft/, tried on nftables 1.1.3). -
Restarts lose nothing: Guard keeps where each watch read up to (each
file's place, the journal's cursor, Housecarl's log) and the watches'
highest bounties in its state file, every ten seconds and on stopping,
and goes on from there: a file rotated meanwhile is finished first, a
truncated one read from its start, one more than 10 MB behind followed
from its end; a journal cursor the journal no longer has, from now.
A line older than the watch's window counts no bounty. -
guard import fail2ban FILTER [--file F] [--mode M] [--set K=V]
converts a fail2ban filter (its includes,[Init], substitutions,
mode) into a watch, with notes on what it left out (Python-only
regular expressions, multi-line patterns, lines that aren't failures).
Over fail2ban 1.1.0's own samples it finds 73% of what fail2ban finds,
with no false or wrong address. No fail2ban file is shipped. -
defaults.max_tracked(100000): the most addresses a watch keeps a
bounty for. Full, it forgets the lowest bounties first, so a flood of
one-match addresses never pushes out one close to a ban; logged, and
counted inguard status. -
The state file has a format version; one from a newer Guard is refused
instead of overwritten. -
The logo (
artwork/logo.png, traced toartwork/logo.svg), the
wordmark and the README's banner (artwork/wordmark.svg,
artwork/banner.svg, fromscripts/wordmark.py), and a ROADMAP.md
(several Housecarls for 2.0.0).
Changed
- The presets are anchored at the start of the line (a syslog head
allowed, nothing else), so text a client chose earlier in a line can't
start a match; their sample lines include more planting attempts. housecarlmay be left out when no watch bans into a ban list or
reads Housecarl's log.defaults.banis written as a watch'sbanis:ban: {time: 1h}
instead ofban: 1h(an error that says so), sobanmeans the same
everywhere and can take more defaults later.guard statusshows where each watch's bans go and its mode, the
actions' bans in force, and escalated bans.
Fixed
- Numbers in guard.yaml are exact:
hits: 0.5was silently taken for
the default 5 andhits: 2.7for 2; a fraction, a quoted number, 0
or less (andfactorbelow 1, a time of nothing) are now errors, and
the same holds for every new number. <IP>in a pattern not anchored after the address took
::ffff:1.2.3.4as::ffff:1; it now takes the whole address.- The package's
guarduser is now in theadmandsystemd-journal
groups itself, as the documentation said, not only through its unit's
SupplementaryGroups.sudo -u guard guard testandguard check
could read neither/var/log's files (permission denied) nor the
journal. Upgrading adds the groups. - Shorter, plainer command output. Tables name a journal source by its
first two units or identifiers (journal postfix/smtpd, postfix/submission/smtpd +1), sopresets,checkandstatus
fit a normal terminal.guard testprints the first 20 matching
lines (not 200) and the first 20 addresses it would ban (--allfor
every one), single addresses without/32, and counts the matches
fromnever_banaddresses. When Housecarl doesn't answer,guard check
says so once ("Housecarl at URL doesn't answer: connection refused")
instead of once per ban list with the request's URL, and only asks for
log:readwhen Housecarl refused the log.guard statustells "not
running" from "no permission".guard banssays when there are none;
guard unbansays "1 ban lifted".
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-