dzaczek/nftgeo
GitHub: dzaczek/nftgeo
nftgeo 是一个用于 Debian/Ubuntu 上 nftables 的声明式地理防火墙工具,通过简单的规则文件自动生成并维护按国家/地区/IP 过滤的防火墙策略。
Stars: 1 | Forks: 0
# nftgeo
`nftgeo` 是一个用于 Debian 和 Ubuntu 上 `nftables` 的小型声明式地理防火墙。
你只需针对每个端口和方向描述允许哪些国家或地区,该工具就会为你构建并维护
`nftables` 规则:
- 通过简单的 `rules.conf` 实现基于方向的允许/拒绝规则:入站、出站,
以及转发(路由器/网关/VPN)流量,
- 按国家、地区或字面 IPv4/IPv6 地址或子网进行匹配,
- 支持 tcp、udp、sctp、icmp、icmpv6、esp、ah 和 gre,支持单个端口或端口范围,
- 有状态:对你自身请求的回复始终被允许,
- 在你指定的位置拦截 AbuseIPDB 黑名单,其粒度与其他任何规则的方向/协议/端口设置相同,
- 始终允许包含受信任 IP 或主机名的可配置白名单(后者在每次运行时重新解析),
- 每条规则均带有计数器,因此 `nft list` 会显示每条规则的数据包/字节总数,
- 仅下载你的规则实际使用的国家区域,并具备本地缓存,
- 通过 `systemd` timer 每天刷新两次规则,
- 在加载前验证 `nftables` 文件并对其进行原子替换。
## 工作模型
你可以在 `/etc/nftgeo/rules.conf` 中像写句子一样编写规则:
```
# action dir proto port target
allow in tcp 22 europe
allow in tcp 22 203.0.113.5
allow in icmp - any
deny in tcp 22 ru
deny in any - abuse
allow in all 5060 de
allow out udp 53 any
allow fwd-in esp - europe
deny fwd-in any - abuse
```
两个简单的保证:
- 出现在任何(该方向的)`allow` 规则中的端口默认是**关闭的**:
只有列出的区域能够通过,其他所有人都会被丢弃。
- 你未曾提及的端口/方向将**保持原样**。
在受管端口之上,白名单始终具有最高优先级。AbuseIPDB 黑名单是可选的:
它只会在你编写 `deny ... abuse` 规则的地方被丢弃,因此你可以控制其确切的作用范围(见下文)。
### 字段
- `action` - `allow` 或 `deny`。`deny` 会丢弃某个区域的数据,但不会对
其他所有人关闭该端口,并且在 `allow` 之前进行评估。
- `dir` - 以下选项之一:
- `in` - 入站到此主机(根据源国家匹配),
- `out` - 从此主机出站(根据目标国家匹配),
- `fwd-in` - 路由/转发的流量,根据源国家匹配,
- `fwd-out` - 路由/转发的流量,根据目标国家匹配。
- `proto` - 基于端口的:`tcp`、`udp`、`sctp` 或 `all`(tcp 和 udp)。无端口的:
`icmp`(仅限 IPv4)、`icmpv6`(仅限 IPv6)、`esp`、`ah`、`gre` 或 `any`
(所有协议和端口)。
- `port` - 单个端口(`22`)或范围(`5060-5070`);对于无端口的协议(包括 `any`),请使用 `-`。
- `target` - 源/目标地址的匹配依据:可以混合使用以逗号分隔的
国家代码(`pl`)、地区名称(`europe`)、字面 IPv4/IPv6 地址
(`203.0.113.5`、`2001:db8::1`)、带掩码的 IPv4/IPv6
(`10.0.0.0/8`、`2001:db8::/32`)、配置文件中名为 `GROUP_` 的列表
(通过其小写名称使用,例如 `office`),或者保留字 `any`(所有地址)和
`abuse`(AbuseIPDB 黑名单,仅限 `deny`)。可混合使用的目标可以组合在一条规则中
(`allow in tcp 80 198.51.100.0/24,de,office`);`any` 和 `abuse` 独立使用。
在 `config` 中以 `GROUP_` 变量的形式定义可重用的地址组;
一个组本身可以混合 IP、子网、国家代码和地区名称:
```
GROUP_OFFICE="203.0.113.5 198.51.100.0/24 2001:db8:1::/48"
GROUP_PARTNERS="de fr 192.0.2.0/24"
```
组不能引用其他组。
生成的每条规则都带有 `counter`,因此 `nft list table inet nftgeo`
会报告每条规则的数据包和字节总数。
`in`/`out` 构建 `input`/`output` 链;`fwd-in`/`fwd-out` 构建
`forward` 链,当主机是路由器、网关或在网络间传递流量的 VPN
节点时使用它们。
### 回复与“仅作为响应的入站”
每条链首先接受 `established,related` 连接,因此对你发起的请求的
回复始终是被允许的。这意味着,入站流量只有在作为请求的响应时才会被允许,
*除非*有 `allow in` 规则为新连接显式开放了端口。要运行一个可能向外发送
并接收响应的客户端,只需编写 `allow out` 规则;返回的数据包会自动流通。
### 内置区域
`europe`、`north_america`、`caribbean`、`south_america`、`middle_east`、`asia`、
`africa`、`oceania`。覆盖其中任何一个,或者在 `config` 中添加你自己的 `REGION_`。
## 示例
[`examples/`](examples/) 目录包含可直接适配的 `rules.d` 片段,适用于
常见服务 - SSH、公共 Web 服务器、邮件、Prometheus exporter、
WireGuard 节点、网关、出站控制和全局滥用黑名单。每个
文件都解释了每一行;将你需要的文件复制到 `/etc/nftgeo/rules.d/` 中并
编辑国家/IP。参见 [`examples/README.md`](examples/README.md)。
## 环境要求
- Debian 或 Ubuntu
- `systemd`、`nftables`、`curl`
- root 权限
- 如果你想使用 AbuseIPDB 黑名单,则需要 AbuseIPDB API key
如果没有 AbuseIPDB key,脚本仍然可以工作;AbuseIPDB 集合将保持为空。
## 安装
```
cd /root/nftgeo
sudo ./install.sh
```
安装程序会:
- 安装 `curl`、`nftables` 和 `ca-certificates`,
- 将引擎复制到 `/usr/local/sbin/nftgeo-update`,并将操作员 CLI 复制到
`/usr/local/sbin/nftgeo`,
- 创建 `/etc/nftgeo/config` 和 `/etc/nftgeo/rules.conf`,以及用于
存放插入文件的空 `rules.d/` 和 `groups.d/` 目录,
- 安装 `nftgeo.service` 和 `nftgeo.timer`,
- 在开机时启用该服务,并启用每天运行两次的 timer。
## 配置
所有配置都位于 `/etc/nftgeo` 中:
```
/etc/nftgeo/
config # settings: AbuseIPDB key, WHITELIST, WHITELIST_HOSTS, ZONE_CACHE_HOURS, regions, groups
rules.conf # rules (optional if you only use rules.d)
rules.d/*.conf # rule fragments, included in sorted filename order
groups.d/*.conf # GROUP_*/REGION_* definitions, sourced after config
```
首先读取 `rules.conf`,然后按照 `LC_ALL=C` 排序的文件名顺序读取每个
`rules.d/*.conf` - 使用数字前缀(`10-ssh.conf`、`20-web.conf`、
`90-default.conf`)使顺序一目了然。同样地,`groups.d/*.conf` 会在
`config` 之后按排序顺序被加载,因此后面的文件可以覆盖
先前文件(或 `config`)设置的变量。
关于文件优先级:在一条链内,引擎始终按固定顺序评估 -
`whitelist -> deny -> allow -> deny-by-default` - 无论规则来自
哪个文件(`deny ... abuse` 规则是 `deny` 步骤的一部分)。因此,`deny` 始终
优先于 `allow`,而两条 `allow` 规则永远不会冲突;文件顺序仅影响
在 `nft list` 中规则的查看方式,而不影响过滤决策。
编辑任何文件后,请应用更改:
```
sudo systemctl start nftgeo.service
```
### AbuseIPDB
```
ABUSEIPDB_API_KEY="your-api-key"
ABUSEIPDB_CONFIDENCE_MINIMUM="90"
ABUSEIPDB_LIMIT="10000"
ABUSEIPDB_DAYS="30"
ABUSEIPDB_RETENTION_DAYS="30"
```
- `ABUSEIPDB_API_KEY` - AbuseIPDB API key。
- `ABUSEIPDB_CONFIDENCE_MINIMUM` - 最低滥用置信度分数。
- `ABUSEIPDB_LIMIT` - 最多下载的条目数。
- `ABUSEIPDB_DAYS` - 要考虑的 AbuseIPDB 历史记录天数。
- `ABUSEIPDB_RETENTION_DAYS` - 在最后一次在成功的 API 响应中被看到后,
要在本地保留 AbuseIPDB 地址的天数。
例如,将其设置为 `30` 或 `60`,可以将较旧的下载地址带入
后续的防火墙集合中,并自动丢弃超过该期限的条目。
保留的 AbuseIPDB 状态存储在 `/var/lib/nftgeo/abuseipdb.tsv` 中。
当 AbuseIPDB 下载失败时,`nftgeo` 会使用保留的状态,而不是
加载空的 `abuse` 集合。
黑名单通过 `rules.conf` 中保留的 `abuse` 目标应用,
并且仅当至少有一条规则使用它时才会下载。你可以像对待
任何其他规则一样决定其作用范围:
```
deny in any - abuse # block every protocol/port inbound from abuse IPs
deny in tcp 22 abuse # block abuse IPs only on inbound SSH
deny in all 1-65535 abuse # block abuse IPs on every tcp+udp port inbound
deny out tcp 443 abuse # block outbound HTTPS to abuse IPs
```
`abuse` 仅限 `deny` 且独立使用(它不能与国家或
IP 组合在同一条规则中)。它会在白名单之后被丢弃,因此白名单中的
地址永远不会被它拦截。
#### 额外的黑名单源
`abuse` 集合不限于 AbuseIPDB。将 `ABUSE_FEEDS` 指向任何
纯文本 IP/CIDR 黑名单,它们就会被合并到相同的 `abuse4`/`abuse6`
集合中,因此每条 `deny ... abuse` 规则也会覆盖它们:
```
ABUSE_FEEDS="https://iplists.firehol.org/files/firehol_level1.netset
https://www.spamhaus.org/drop/drop.txt"
```
以空格或换行符分隔的 URL,仅在规则指定 `abuse` 时获取。
注释标记(`#`、`;`)以及地址之后的任何文本都会被剥离,这涵盖了
常见的源格式(FireHOL、Spamhaus DROP、blocklist.de、GreenSnow)。
每个源最后一次正常的副本会被缓存在 `/var/lib/nftgeo/feeds` 下,如果
后续下载失败则会重用该缓存,因此源中断绝不会缩小拦截列表。源在
有无 AbuseIPDB key 的情况下均可工作。
请注意源的质量:保守的列表(FireHOL level1、Spamhaus DROP)很少有
误报,而范围更广的列表(blocklist.de、GreenSnow)会拦截更多内容,但可能
会误伤合法流量。`WHITELIST` 始终优先于 `abuse`,因此请将你
自己的网络保留在那里。
### 白名单(始终允许的 IP)
`WHITELIST` 确保受信任的地址无论如何都能保持连接。白名单 IP
可以访问任何端口,即使它们位于允许的地理区域之外或出现在
AbuseIPDB 黑名单上:
```
WHITELIST="203.0.113.10 198.51.100.0/24 2001:db8::/48"
```
以空格分隔,支持 IPv4 和 IPv6,支持单个地址或 CIDR 范围。这是
保护你自己的管理员 IP 免遭意外锁定的推荐方法。
`WHITELIST` 仅接受字面地址。要按名称将某项加入白名单 -
通常是动态 DNS 管理端点,例如 WireGuard 设备 - 请改为将其列在
`WHITELIST_HOSTS` 中:
```
WHITELIST_HOSTS="wireguard.example.ch vpn.example.net"
WHITELIST_HOSTS_RETENTION_DAYS="7"
```
每次运行时都会解析每个主机名(通过 `getent`,遵循 `/etc/hosts` 和
`resolv.conf`),并将生成的 IP 合并到白名单中。由于
`systemd` timer 会运行更新,这些名称会被定期重新解析;如果
地址发生更改,下一次成功的查找将替换旧的地址。主机最后一次成功解析的地址会保留在
`/var/lib/nftgeo/whitelist_hosts.tsv` 中,保留时间为 `WHITELIST_HOSTS_RETENTION_DAYS`,因此
短暂的 DNS 故障不会切断你的访问,而停止解析的地址将在该期限后失效。IPv4 映射的 IPv6 结果(`::ffff:...`)将被忽略。
### 区域
区域是扩展为国家代码列表的宏。以下区域是
内置的,无需配置:`europe`、`north_america`、`caribbean`、
`south_america`、`middle_east`、`asia`、`africa`、`oceania`。
通过在 `config` 中定义 `REGION_` 变量来覆盖
内置区域或添加你自己的区域;在 `rules.conf` 中使用的名称是
`REGION_` 之后的小写部分:
```
REGION_EUROPE="ad al at ... ua va xk" # e.g. a trimmed Europe without ru/by
REGION_NORDICS="dk fi is no se" # custom region, usable as "nordics"
```
### 区域缓存
```
ZONE_CACHE_HOURS="20"
```
下载的国家区域在被刷新前会被重用这么多小时。
## 手动运行
```
sudo /usr/local/sbin/nftgeo-update
```
运行过程通过锁(`/var/lib/nftgeo/.lock`)串行化,因此手动运行和
计划运行不能重叠;如果在 `LOCK_WAIT` 秒(默认为 60)内无法获取锁的运行
将在不触及规则集的情况下退出。
## 操作员 CLI
`nftgeo` 命令封装了常见的日常检查:
```
nftgeo check 203.0.113.7 # what does the firewall do to this address?
nftgeo status # version, last run, set sizes, drop counters, next run
nftgeo validate # check the current config renders/loads, without applying
nftgeo plan # show how the rendered ruleset differs from what is loaded
nftgeo apply # rebuild and load now (same as the update engine)
```
`nftgeo validate` 和 `nftgeo plan` 允许你在应用之前检查编辑内容:
如果配置无效,`validate` 将以零状态退出,而 `plan` 会打印策略
差异(诸如 abuse/geo 地址之类的集合内容会被省略,因此只会显示你的规则
更改)。两者都需要 root 权限,且均不会触及生效的规则集。
`nftgeo check ` 会报告该地址是否在白名单中、是否在滥用
列表中,或是否在任何地理集合中,打印匹配它的规则,并给出明确的判定
(允许 / 丢弃 / 位置)。`nftgeo status` 是从
生效的表、日志和源缓存中提取的单屏摘要。
## 状态检查
如果你想要直接使用,以下是 `nftgeo status` 背后的原始命令:
```
nftgeo-update --version # installed version
systemctl status nftgeo.timer
systemctl list-timers --all nftgeo.timer
systemctl status nftgeo.service
journalctl -u nftgeo.service -n 100 --no-pager
```
每次运行都会在 `Loaded` 行中记录其版本,例如
`nftgeo 1.0.0 loaded inet/nftgeo: rules=3 ...`。
带有每条规则计数器的活动规则,以及缓存的数据:
```
nft list table inet nftgeo
nft list chain inet nftgeo input # or output / forward
ls /var/lib/nftgeo/zones
ls /var/lib/nftgeo/abuseipdb.tsv
```
每次运行还会将其加载的集合持久化存储在 `/var/lib/nftgeo` 下
(`whitelist4.set`、`whitelist6.set`、`abuse4.set`、`abuse6.set`),以及保留的
`abuseipdb.tsv`。
## 规则是如何构建的
对于上面的 `rules.conf` 示例,生成的链如下所示
(为简洁起见,省略了 IPv6 行):
```
chain input {
type filter hook input priority -100; policy accept;
ct state established,related counter accept # replies always allowed
ip saddr @whitelist4 counter accept # whitelist wins first
tcp dport 22 ip saddr @g_ru4 counter drop # explicit deny
ip saddr @abuse4 counter drop # deny in any - abuse
tcp dport 22 ip saddr @g_europe4 counter accept # geo allow (source)
meta l4proto icmp counter accept # icmp "any"
tcp dport 22 counter drop # deny-by-default
}
chain forward {
type filter hook forward priority -100; policy accept;
ct state established,related counter accept
ip saddr @whitelist4 counter accept
ip daddr @whitelist4 counter accept # both sides for routed traffic
ip saddr @abuse4 counter drop # deny fwd-in any - abuse
meta l4proto esp ip saddr @g_europe4 counter accept # geo allow (source)
meta l4proto esp counter drop # deny-by-default
}
```
`allow in all 5060 de` 和 `allow out udp 53 any` 规则添加了其余内容(一个 udp
output 链和 5060 的 tcp+udp 条目);为了简洁起见,上面省略了它们。
请注意每条规则的 `counter`,以及默认拒绝(deny-by-default)为每个协议
和端口发出一条规则(因此重叠的单个端口和范围永远不会发生冲突)。
系统只会生成你实际使用方向的链。该表会进行原子替换:
生成的文件会在一次 `nft -f` 加载中重新创建该表,因此不存在
规则缺失的时刻。链策略为 `accept`,因此仅受你管理的端口会受到影响;其他一切保持原样。
该工具具备故障安全机制:如果 `allow` 规则中使用的国家解析后没有
地址(其区域无法下载且没有缓存副本),更新将
中止并保留之前的规则集,而不是冒险用空的 allow 集合关闭端口。仅在 `deny` 规则中使用的国家将被跳过 -
空的 deny 集合不会丢弃任何内容 - 因此一个无法解析的代码不能冻结
整个更新。
## 访问安全
在对用于 SSH 的端口进行地理围栏之前,请确保你当前的 IP 位于
允许的地理范围内,或者将其添加到 `WHITELIST` 中。如果你可以通过 VPS
提供商的紧急控制台进行访问,请将其保留为恢复计划。
推荐的 SSH 配置:
```
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
AuthenticationMethods publickey
```
## 更新计划
timer 会在以下时间运行更新:
- 系统启动 2 分钟后,
- 每天 2 次(03:00 和 15:00),
- 最多 30 分钟的随机延迟。
上游 `ipdeny.com` 的国家区域每天重新生成一次,因此
比每天更频繁的刷新不会返回更新的数据。每天两次的计划
缩短了过期时间窗口(以便在 IP 分配在国家之间迁移时
能更快被获取),这意味着单次运行失败不会导致你落后
整整一天。`ZONE_CACHE_HOURS` 设置可防止第二次日常运行
重新下载第一次运行已经获取的内容。要更改频率,请编辑
`/etc/systemd/system/nftgeo.timer` 中的 `OnCalendar=` 并运行
`systemctl daemon-reload`。
## 卸载
```
cd /root/nftgeo
sudo ./uninstall.sh
```
移除生效的 `nftables` 表、systemd 单元、脚本以及
`/etc/nftables.d/nftgeo.nft`。保留 `/etc/nftgeo` 和
`/var/lib/nftgeo` 不变。
## 版本控制
版本发布遵循[语义化版本控制](https://semver.org/)并标记为
`vMAJOR.MINOR.PATCH`。`nftgeo-update --version` 会报告已安装的版本,并且
每次运行都会将其记录在日志中。请参阅 [CHANGELOG.md](CHANGELOG.md) 了解版本之间的
更改。
## 数据源
- AbuseIPDB 黑名单 API:`https://api.abuseipdb.com/api/v2/blacklist`
- 国家 IP 前缀:`https://www.ipdeny.com`
## 注意事项
`nftgeo` 仅触及 `rules.conf` 中列出的端口。它不会为
整个系统设置默认的 `DROP` 策略,也不会关闭其他端口。
标签:Cutter, Debian, nftables, 地理封锁, 网络防火墙, 运维工具