A program bans an address (every port, or a protocol's ports) for a time; the ban list says what it is kept from: the firewall, some hosts behind it, or everything. Bans drop ahead of every rule, close the address's connections, group IPv6 by /64, are kept in the database across restarts and audited. Never banned: the inside, the firewall's own addresses, the caller, the list's never. bans grant (bans:write:LIST), hc banlist/bans/ban/unban, Traffic -> Bans, a fail2ban action in contrib/, and failed logins and refused tokens logged for Guard and fail2ban. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.0 KiB
Ban lists
A ban list is where a program bans addresses: Guard (Housecarl's
companion, which reads logs), fail2ban with Housecarl's action, or you
with hc ban. The program says only which address, and perhaps a
protocol and ports. The ban list says what that address is kept from.
So a web server that sees password guessing in its own logs can ban the
guesser at the router without knowing anything about the router.
Ban lists
hc banlist add router --scope firewall # the firewall itself
hc banlist add web --scope hosts --host 10.0.10.57 --host 2001:db8:10::57
hc banlist add edge --scope everything --never trusted
| Scope | Banned addresses are kept from | Where the drops are |
|---|---|---|
firewall |
the firewall itself: its SSH, DNS, web UI | input |
hosts |
the list's hosts (addresses or sets), behind the firewall | forward, to those addresses |
everything |
the firewall and everything behind it | input and forward |
A hosts list covers port forwards to its hosts as well: the forward
chain sees a forwarded connection's translated address and port, the
host's own. A web server's Guard bans tcp/22, and that is right even
when the router forwards its port 2222 to it.
Other settings, with their defaults:
| Setting | Default | |
|---|---|---|
never |
none | addresses, prefixes or sets this list never bans |
default_time (--time) |
1h | a ban's length when the program asks for none |
max_time (--max-time) |
7d | the longest ban; longer ones are cut to it. -1 (none): no limit, so bans may be permanent |
max_entries |
10000 | the most bans the list holds; more are refused (list_full) |
group_v6 |
64 | the IPv6 prefix length banned for an address. One customer usually has a whole /64 and can move around in it; 128 bans the one address. IPv4 addresses are banned alone |
disabled |
no | the list's bans aren't in force (they are kept) |
A ban list can't be renamed: the tokens and programs that ban into it know it by its name. Make a new one instead.
Bans
hc ban edge 203.0.113.9 --time 1d --reason "port scan"
hc ban web 203.0.113.9 --tcp ssh --tcp 8443
hc bans # every list's
hc bans web
hc unban web 203.0.113.9 # all of the address's bans in web
hc unban web 17 # one, by ID
A ban is of every port, or of a protocol's ports (numbers or service names, built-in or yours). A port without a protocol is banned for TCP and UDP; a service name takes the service's protocols. Banning an address again renews the ban: its new time counts from now.
Bans are dropped ahead of every rule in their chains, and ahead of the built-in that lets established connections through, so a ban cuts a connection that is already open. Housecarl also closes the banned address's tracked connections the ban covers when it bans it, because connections offloaded to the flowtable no longer pass the rules at all. Replies to connections the inside opened to a banned address are dropped too.
Bans are kept in the database, apart from the configuration (like a
dynamic set's entries): they aren't in the history, can't be undone,
aren't on probation, and don't go through changesets. They end by
themselves; every change, restart and reboot puts the active ones back
with the time they have left. Each ban and unban is in the audit log
(ban.add, ban.remove), with the program's source and reason.
What is never banned
Whatever a list's never says, Housecarl refuses (422 never_ban) to
ban:
- the inside: addresses in networks of inside zones' interfaces, and private addresses outside the external zones' networks;
- the firewall's own addresses, and its own prefixes (settings);
- the address of whoever asks (a program can't lock itself out);
- loopback, link-local, multicast and unspecified addresses.
A ban grouped to a prefix (IPv6) is refused when the prefix overlaps any of these.
Programs and tokens
Give each program a token for its list alone. A bans grant can name
ban lists, as a sets grant names sets:
hc user add guard-web --no-password --grant bans:write:web
hc token create web --user guard-web # as root on the firewall
With it, the program may ban into web and lift its bans, list them,
and read the list itself (GET /banlists/web: its grouping and times),
and nothing else. viewer has bans:read, operator bans:write.
Guard
Guard reads log files and the systemd journal on any machine, and bans
the addresses that misbehave into a ban list (its own documentation).
Run it on the web server with a token for web, and on the router with
one for router (or the local socket).
fail2ban
Housecarl ships an action for fail2ban,
/usr/share/doc/housecarl/contrib/fail2ban/action.d/housecarl.conf
(contrib/ in the source). Copy it to /etc/fail2ban/action.d/, put
the token in /etc/fail2ban/housecarl.token (mode 0600), and use it in a
jail:
[sshd]
enabled = true
action = housecarl[list=web, url="https://router.lan:8070"]
The jail's port and protocol go with each ban: port = ssh bans only
tcp/22, fail2ban's default 0:65535 bans every port. A bantime of
-1 is the list's longest ban. When fail2ban unbans an address Housecarl
already let go (its own time ran out first), nothing happens. Add
curlopts = --cacert /etc/fail2ban/housecarl-ca.pem for a private CA.
Shadow mode
In shadow mode the ban rules count what they would drop
([would drop]) and let it through, like every other rule, and banning
closes no connections.
The ruleset
Each list has four sets, ban_<id>_v4, ban_<id>_v6 (every port) and
ban_<id>_v4_svc, ban_<id>_v6_svc (address, protocol and port), all
with timeouts:
ip saddr @ban_3_v4 counter drop comment "hc:ban3"
ip saddr . meta l4proto . th dport @ban_3_v4_svc counter drop comment "hc:ban3"
ip6 saddr and ffff:ffff:ffff:ffff:: @ban_3_v6 counter drop comment "hc:ban3"
Set names starting with ban_ are therefore Housecarl's own
(set_name_reserved).