izm1chael/goban
GitHub: izm1chael/goban
一款用 Go 编写的高性能 fail2ban 替代品,通过原生 Netlink 对接内核防火墙实现极低开销的日志监控与 IP 封禁。
Stars: 0 | Forks: 0
# GoBan
[](https://github.com/izm1chael/goban/actions/workflows/ci.yml)
[](LICENSE)
[](https://goreportcard.com/report/github.com/izm1chael/goban)
一个类似 fail2ban 的日志监控与 IP 封禁工具,使用 Go 编写。
GoBan 可以从文件、Docker 容器日志或 systemd journal 中追踪 (tail) 日志流;
根据每条规则的 regex (正则表达式) 匹配对应的行;并通过 `iptables` + `ipset`(针对 IPv6 则使用 `ip6tables`)
在内核层面封禁违规 IP。它附带了一个控制客户端(`goban-client`),用于查看状态、列表以及手动封禁/解封。
与经典的 fail2ban 相比,它在以下三个方面有所不同:
1. **单一二进制文件,静态链接的 Go 程序。** 无需 Python 运行时,镜像大小约 10 MB。
2. **容器原生支持。** 在 Portainer 堆栈中以兄弟容器 (sibling container) 的形式运行,加上 `--cap-add NET_ADMIN --network host` 参数,即可保护宿主机上的所有其他服务。
3. **通过直接 netlink 由 ipset 支持的封禁。** 在成千上万次封禁中提供 O(1) 查找,并带有内核侧的 TTL 过期机制 —— 无需 Go 侧的解封计时器,也无需每次封禁都 fork-and-exec。
## 基准测试
在同一内核、相同工作负载和相同规则下对 GoBan v1.1 与 fail2ban v1.0.2 进行对比(`benchmark/` 目录中包含完整的可复现测试工具 —— 合成的 sshd-fail 生成器、基于 /proc 的采样器,以及每个单元格 60 秒的稳态测试)。
**原生,真实 iptables(生产场景):**
| 行速率 | GoBan CPU | fail2ban CPU | 加速比 | GoBan RSS | fail2ban RSS | RSS 比率 |
|-----------|-----------|--------------|---------|-----------|--------------|-----------|
| 100/sec | **0.08%** | 2.37% | **30x** | 12.7 MB | 51.3 MB | 4.0x |
| 1K/sec | **0.75%** | 25.03% | **33x** | 14.8 MB | 51.1 MB | 3.5x |
| 10K/sec | **7.49%** | 119.88% (饱和) | **16x** | 15.4 MB | 51.4 MB | 3.3x |
**容器,真实 iptables (Docker,`--network host`):**
| 行速率 | GoBan CPU | fail2ban CPU | 加速比 |
|-----------|-----------|--------------|---------|
| 100/sec | 0.08% | 2.37% | 30x |
| 1K/sec | 0.79% | 7.96% | 10x |
| 10K/sec | 7.68% | 61.62% | 8x |
**饱和状态下的吞吐量准确度(实际处理的行数):**
仅看 CPU 和 RSS 数据会产生误导,因为 fail2ban 在跟不上时会静默丢弃日志行。`benchmark/accuracy.sh` 测量了每个守护进程实际看到的生成行比例。
| 行速率 | GoBan 原生 | GoBan 容器 | fail2ban 原生 | fail2ban 容器 |
|-----------|--------------|-----------------|-----------------|--------------------|
| 1K/sec | 96.1% | 98.4% | 98.4% | 98.4% |
| 5K/sec | 97.3% | 98.3% | 98.1% | **86.6%** |
| 10K/sec | 97.2% | 97.7% | **54.2%** | **43.8%** |
GoBan 在所有速率下都保持在约 97-98%(缺失的 2-3% 是采样时仍在处理管线中的最后几行 —— 延长测试后的稳定窗口可以缩小这一差距)。fail2ban“在 10K 速率下达到 119% CPU 饱和”,实际上是在饱和的同时丢弃了近一半的输入。
这在实际应用中的意义是:
- **在 10K/sec 时,GoBan 每秒处理的行数比 fail2ban 原生多约 80%**,而使用的 CPU 却减少了约 16 倍(7.49% vs 119%)。GoBan 仍然保留了单核约 92% 的余量。
- **在饱和点上,GoBan 的效率比 fail2ban 高约 28 倍**(每个 CPU 百分比处理的行数):GoBan 约为 1300 行/%,而 fail2ban 约为 46 行/%。
- **内存:常驻内存集小约 3-4 倍。** GoBan 的 RSS 随负载平缓增长(更多的追踪器状态);fail2ban 固定的约 51 MB 大部分是 Python 运行时开销 —— 这也是空闲状态下的基础占用。
- **对于 GoBan,iptables 的开销几乎为零**,因为自 v1.1 起,此版本中新增的 netlink 直连 ipset 客户端将每次封禁时的 `ipset` 二进制文件的 fork-and-spawn 替换为一次内核系统调用。在 1K bans/sec 的速率下,iptables 模式的 CPU 开销从 7.73 个百分点 (v1) 降至 0.03 个百分点 (v1.1)。
- **Docker 对 GoBan 的开销可以忽略不计** —— 原生与容器环境下的数据在采样噪声范围内重叠。相比之下,在相同输入速率下,fail2ban 容器比 fail2ban 原生丢弃了更多的行。
在你自己的机器上复现:
```
make build
sudo bash benchmark/full-suite.sh 60 # CPU/RSS matrix, ~30 min
sudo bash benchmark/accuracy.sh 30 # line-processing accuracy, ~10 min
```
## 安装 (Debian / Ubuntu / Fedora / RHEL / Arch)
每次发布 (release) 都附带了主要格式的预构建包:
```
# Debian / Ubuntu / Mint / Raspbian / Kali
sudo apt install ./goban_1.0.0_amd64.deb
# Fedora / RHEL / Rocky / Alma / openSUSE
sudo dnf install ./goban-1.0.0-1.x86_64.rpm
# 或者:sudo rpm -i ./goban-1.0.0-1.x86_64.rpm
# Arch / Manjaro / EndeavourOS / SteamOS
sudo pacman -U ./goban-1.0.0-1-x86_64.pkg.tar.zst
```
该软件包会安装:
- `/usr/local/bin/goban-daemon` 和 `/usr/local/bin/goban-client`
- `/etc/goban/goban.yaml`(示例配置;升级时保留)
- `/etc/goban/rules.d/*.yaml`(10 个内置规则文件)
- `/usr/lib/systemd/system/goban.service`(以及可选的 `goban-persist.service`,用于 ipset 重启持久化)
- 用于控制套接字所有权的系统 `goban` 用户/组
`iptables` 和 `ipset` 被声明为依赖项,因此包管理器会自动拉取它们。守护进程 **不会** 自动启动 —— 请先检查配置,然后:
```
sudo systemctl enable --now goban
sudo systemctl status goban
```
**其他发行版:** Alpine、Void、Gentoo 及类似系统 —— GitHub Release 中的静态二进制文件可在任何安装了 `iptables` 和 `ipset` 且内核版本 ≥3.x 的 Linux 上运行。将 `bin/goban-daemon` 放入 `/usr/local/bin/` 并从 `deploy/goban.service` 复制 systemd 单元(如果适用)。在容器中运行守护进程的 Alpine 用户应使用容器镜像仓库中的 `goban:latest` —— 目前 nfpm 生成的 `.apk` 文件与 Alpine 的 `apk-tools` 之间存在已知的兼容性问题,我们尚未解决。
## 从源代码构建
```
git clone https://github.com/izm1chael/goban
cd goban
make build # produces bin/goban-daemon, bin/goban-client (CGO-free)
make test-race # all packages green with race detector
make docker-build # alpine image with iptables + ipset baked in
```
要支持 journald(构建标签 `journald`,需要 `libsystemd-dev` + CGO):
```
sudo apt install libsystemd-dev # Debian/Ubuntu
make build-journald
```
对于本地包构建(`.deb`, `.rpm`, `.pkg.tar.zst`),请安装 nfpm:
```
go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.42.1
make package VERSION=1.0.0
ls dist/ # three packages, ready to install or distribute
```
## 快速入门 (容器)
```
git clone https://github.com/izm1chael/goban
cd goban
make docker-build
docker run --rm --network host \
--cap-add NET_ADMIN --cap-add NET_RAW \
-v /var/log:/var/log:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v $(pwd)/examples/goban.yaml:/etc/goban/goban.yaml:ro \
-v $(pwd)/examples/rules.d:/etc/goban/rules.d:ro \
-v /run/goban:/run/goban \
goban:latest
```
在另一个 shell 中:
```
goban-client status
goban-client rules
goban-client list
```
## 快速入门 (宿主机安装,systemd)
```
make build
sudo install -m 0755 bin/goban-daemon /usr/local/bin/
sudo install -m 0755 bin/goban-client /usr/local/bin/
sudo install -d -m 0755 /etc/goban /etc/goban/rules.d
sudo cp examples/goban.yaml /etc/goban/goban.yaml
sudo cp examples/rules.d/*.yaml /etc/goban/rules.d/
sudo cp deploy/goban.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now goban.service
```
## 概念
- **Source(来源)**:一个日志流。可以是 `file`(任何路径)、`docker`(按名称或标签选择的容器)或 `journal`(systemd journal,仅限 journald 构建)之一。
- **Rule(规则)**:附加到来源的 regex (正则表达式) 过滤器。当同一 IP 在 `findtime` 内触发 regex 的次数超过 `max_retries` 时,规则会请求 banner 封禁该 IP `bantime` 时长。
- **Banner(封禁器)**:内核防火墙后端。内置两种实现:
- `iptables`(默认):两个 ipset(`goban-ban-v4` + `goban-ban-v6`)以及引用它们的 iptables/ip6tables INPUT 规则。热路径操作直接与 netlink 通信,对接 ipset 子系统。
- `nftables`:原生 netlink 连接到 `NFNL_SUBSYS_NFTABLES`。创建一个 `inet goban` 表、两个带超时的集合、一个 input 挂钩链和两条 drop 规则。无外部二进制文件依赖。在 goban.yaml 中设置 `banner.backend: nftables`。
- **Allowlist(白名单)**:永不被封禁的 CIDR。默认列表包含回环地址和所有 RFC1918 范围,启动时 GoBan 会自动将绑定到本地接口的所有地址加入白名单,因此宿主机无法封禁自身。
## 配置
分层加载:内置默认值 → YAML 文件 → 环境变量。以下文件足以保护 SSH:
```
# /etc/goban/goban.yaml
log_level: info
sock_path: /run/goban/goban.sock
defaults:
max_retries: 3
findtime: 10m
bantime: 24h
sources:
- type: file
name: auth-log
path: /var/log/auth.log
rules:
- name: sshd
source: auth-log
regex: 'Failed password for (?:invalid user )?\S+ from (?P\S+) port'
```
对于更丰富的设置,可将 `rules_dir:` 指向 `/etc/goban/rules.d/` 并放入内置的规则文件。它们涵盖 sshd(基本 + 激进模式)、nginx(noscript、http-auth、badbots、常见探测)、apache(auth、noscript、overflows)、WordPress(wp-login + xmlrpc)、Nextcloud(login + bruteforce)、Postfix(SASL + RBL)、Dovecot、vsftpd、Traefik(auth + ratelimit)以及 Portainer。
有用的环境变量覆盖:
```
GOBAN_LOG_LEVEL=debug
GOBAN_REPLAY_ON_START=false
GOBAN_SOCKET_MODE=0660
GOBAN_DEFAULT_FINDTIME=10m
GOBAN_DEFAULT_BANTIME=1h
GOBAN_IPV6=true
GOBAN_ALLOWLIST=10.0.0.0/8,192.168.0.0/16
```
## 热配置重载
编辑 `/etc/goban/rules.d/my-rule.yaml`(或 `goban.yaml` 中的任何内容)并让守护进程在不丢弃任何内容的情况下获取更改:
```
sudo systemctl reload goban # uses SIGHUP under the hood
# 或者
sudo goban-client reload # equivalent, via the control socket
```
守护进程在应用任何内容之前会完整验证新配置。如果出现任何故障(YAML 格式错误、regex 无效、缺少源),正在运行的守护进程将 **保持不变**,并且错误会被记录(或返回给 `goban-client reload`)。
`name`、`regex`、`max_retries`、`findtime`、`bantime` 和白名单均未更改的规则,在重载后会保留其追踪器状态。
语义发生变化的规则将获得全新的追踪器(这是符合操作员预期的行为 —— 因为规则对“什么算作一次触发”的解释发生了变化)。
**仅限重启** 的字段(重载会拒绝更改,必须重启守护进程):
`sock_path`、`socket_mode`、`socket_group`、`ipset_name_v4`、`ipset_name_v6`、
`ipv6`、`dry_run`、`batch_bans`、`state_path`、`audit_log`。
## 持久化触发状态
进行中的触发计数(失败了一两次但尚未达到阈值的 IP)会在守护进程重启后保留。状态每 30 秒转储一次到 `/var/lib/goban/state-.gob`,并在优雅关闭时保存,在启动时重新加载。已被封禁的 IP 会一直保留在内核 ipset 中,因此这只会影响“差一点就被封禁”的攻击者 —— 但它阻止了坚定的攻击者通过等待守护进程重启来重置其触发计数。
可在 YAML 中调节:
```
state_path: /var/lib/goban/state.gob # empty disables persistence
state_save_interval: 30s
```
## 审计追踪
每次通过 `goban-client` 成功的手动封禁或解封,都会向审计文件追加一行 JSON:
```
audit_log: /var/log/goban/audit.log # empty disables auditing
```
示例行:
```
{"time":"2026-05-11T13:42:01Z","action":"ban","ip":"192.0.2.99","rule":"manual","ttl":"5m0s","source":"manual"}
```
失败的尝试不会被故意记录,因此该文件始终是一个准确的“实际应用了什么”的时间线。请使用 logrotate 来管理其大小。
## 累犯 —— 重复违规者
内置规则(`examples/rules.d/recidive.yaml`,由安装包发送到 `/etc/goban/rules.d/`)会监控 GoBan 自己的审计日志,并对在过去 24 小时内被封禁 5 次以上的任何 IP 重新封禁一周。
要启用它,请定义一个指向审计日志的文件源:
```
sources:
- type: file
name: audit
path: /var/log/goban/audit.log
```
(包中附带的示例 `goban.yaml` 已经这样做了。)该规则具有反馈循环安全性 —— 既通过 中显式的 `excludes` 过滤器,又通过针对任何精确命名为 `recidive` 的规则的硬编码自动排除来保障。
在 `/etc/goban/rules.d/recidive.yaml` 中调整阈值:
```
- name: recidive
max_retries: 5 # # of past bans within findtime
findtime: 24h # window over which past bans count
bantime: 168h # 1 week — typically much longer than per-service bans
```
## 部署前测试规则
要在不实际封禁任何人的情况下针对真实日志验证新的 regex,请将该规则加载到正在运行的守护进程中(任何守护进程,例如预发布主机上的),然后通过客户端进行 dry-run (干跑/试运行):
```
goban-client test --rule sshd /var/log/auth.log
```
客户端从守护进程获取规则的 regex + 阈值 + findtime + bantime,将它们应用于日志文件(如果指定 `-` 则从标准输入读取),并打印哪些 IP 以及何时会被封禁:
```
=== test results for rule "sshd" against /var/log/auth.log ===
Lines read: 27412
Lines matched: 312
Unique IPs seen: 18
Simulated bans: 11
Rule settings: max_retries=5, findtime=10m, bantime=1h
IP FIRST_SEEN BANNED_AT EXPIRES
198.51.100.7 12:03:11 12:05:42 2026-05-11T13:05:42Z
198.51.100.42 12:08:20 12:09:55 2026-05-11T13:09:55Z
...
```
这对于在将规则推广到生产环境之前,根据实际攻击模式调整阈值很有用。时间戳使用读取每行时的挂钟时间,而不是日志内部的时间戳 —— 因此旧的 auth.log 将产生“将在 <现在> 封禁”而不是原始事件时间。这适用于判断“此规则是否会触发得太频繁”;它不是一个重放工具。
## 编写规则
规则是 YAML 格式。regex 必须包含一个 `(?P...)` 命名捕获组;该值会通过 `netip.ParseAddr` 解析,并且必须解析为有效的 IP。规则可以选择性地携带其自身的 `allowlist:` CIDR,这些 CIDR 将绕过该特定规则(全局白名单仍然适用)。
```
rules:
- name: my-app-failures
source: my-app-container # name of a defined source
regex: 'login failed.*from (?P[^\s]+)'
max_retries: 5
findtime: 10m
bantime: 6h
allowlist: # optional per-rule allowlist
- 203.0.113.50/32 # VPN endpoint — bypass THIS rule only
- 10.0.0.0/16
```
GoBan 使用 Go 的 RE2 regex 引擎。**它不支持反向引用、前瞻/后顾或像 fail2ban 的 `` 这样的命名宏。** 要移植 fail2ban 过滤器,请将 `` 替换为 `(?P\S+)`(如果字段被引号或括号括起来,则使用更严格的字符类)。
**日期感知规则。** 匹配回填或重放的日志(例如 journald 在启动时追赶到最新)时,请使用规则的 `datepattern` 字段并在 regex 中添加 `(?P
标签:CISA项目, Docker容器, EVTX分析, fail2ban替代, Go语言, ipset, iptables, IPv6, IP封禁, PE 加载器, PowerShell, 入侵防御, 单文件二进制, 原生Netlink, 子域名枚举, 封禁守护进程, 恶意流量拦截, 攻击面发现, 日志审计, 程序破解, 系统安全, 网络安全, 请求拦截, 隐私保护, 静态链接