Ban lists: where Guard and fail2ban ban addresses
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>
This commit is contained in:
1 parent
6f56eefae2
commit
500bfc7bdd
44 files changed
+2541
-39
No files matched your search
+148
@@ -0,0 +1,148 @@
|
||||
# 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
|
||||
|
||||
```sh
|
||||
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
|
||||
|
||||
```sh
|
||||
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:
|
||||
|
||||
```sh
|
||||
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:
|
||||
|
||||
```ini
|
||||
[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`).
|
||||
Reference in new issue
Block a user