feat: IPv6 bans, timeout-less set migration, typed errors, clap/thiserror 2

- Detect an existing ipset without timeout support and migrate it in place
  (temporary set + copy members + ipset swap + destroy) without unblocking.
- IPv6: "<set>6" (family inet6) + ip6tables match-set rule; ban/unban pick
  the set by address family. IPv4 keeps working if IPv6 is unavailable.
- Remove unwrap()/panics: MartilloMaldito::new/with_config and every public
  method return Result<_, MartilloError>; ignore multiport/range rules when
  parsing secured ports; handle an empty DOCKER-USER chain.
- Ipset::save_all / save-ipsets for ipset-persistent (/etc/iptables/ipsets).
- structopt -> clap 4, thiserror 1 -> 2. CLI exits 1 on errors.
- Kernel tests skip without CAP_NET_ADMIN; run them with `unshare -rn`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
midefosandClaude Opus 5.5 committed 2026-10-30 01:36:54 +01:00
1 parent 7342386955
commit 13ca4518b4
8 files changed
+1352 -590

No files matched your search

+50 -3
View File
@@ -6,6 +6,7 @@
## Features
- Ban/unban IPv4 and IPv6 addresses with `ipset` (optional timeout).
- Retrieve all banned IPs.
- Query and manage secured ports.
- Allow or deny access by IP for specific ports.
@@ -16,9 +17,26 @@
## Requirements
- Rust 1.65 or higher.
- `iptables` installed and configured on the system.
- Proper permissions to manage `iptables` rules (e.g., running as root).
- Rust 1.80 or higher.
- `iptables`, `ip6tables` and `ipset` installed on the system.
- Proper permissions to manage `iptables` rules (e.g., running as root, or `CAP_NET_ADMIN`).
---
## Banning with ipset
Bans are stored in two `hash:ip` sets with timeout support: `banned` (IPv4) and
`banned6` (IPv6), each matched by a `-m set --match-set <set> src -j DROP` rule at the
top of `INPUT` (or `DOCKER-USER` with `--docker`) in `iptables`/`ip6tables`. Sets and
rules are created on first use.
If a set already exists **without** timeout support (older versions created it like
that, and `ipset add ... timeout N` then fails), it is migrated automatically without
unblocking anyone: a temporary set with timeout is created, the members are copied,
`ipset swap` puts it in place and the old one is destroyed.
If IPv6 is not available (no `ip6tables`, no IPv6 in the kernel), IPv4 bans keep
working and banning an IPv6 address returns an error.
---
@@ -64,6 +82,25 @@ martillo_maldito get-banned-ips [--docker | -d]
**Options:**
- `--docker`, `-d`: Includes rules in Docker environments.
#### Ban / unban an IP
```bash
martillo_maldito ban-ip --ip <ip> [--timeout <seconds>] [--docker | -d]
martillo_maldito unban-ip --ip <ip> [--docker | -d]
```
IPv6 addresses go to the `banned6` set. Without `--timeout` the ban is permanent.
Every command prints `true`/`false` (or JSON) on stdout; on failure the error is
printed on stderr and the exit code is 1.
#### Save the ipsets
```bash
martillo_maldito save-ipsets [--file /etc/iptables/ipsets]
```
Writes every set in the format restored at boot by `ipset-persistent`.
#### 2. Retrieve all secured ports
```bash
@@ -168,6 +205,16 @@ Saves all current `iptables` rules for future restoration.
---
## Tests
```bash
cargo test # kernel tests are skipped without CAP_NET_ADMIN
unshare -rn cargo test # run them too, isolated, without sudo
cargo build --release && unshare -rn env SELF_ISOLATED=1 ./tests/integration_netns.sh
```
---
## Disclaimer
This repository is designed solely for experimental or educational purposes. It is not intended for use in production environments or critical projects. Use at your own risk.