centurysys/awall_nft
GitHub: centurysys/awall_nft
awall_nft 是一个轻量级生成器,它将 awall 风格的 JSON 配置转换为 nftables 规则,为嵌入式 Linux 系统提供安全且易于管理的防火墙解决方案。
Stars: 0 | Forks: 0
# "DNAT" as is, but the user wants a translation to Simplified Chinese. Perhaps I can translate the meaning but keep the acronym in English.
`awall-nft` 是一个轻量级生成器,它将 Alpine Wall (awall) 风格 JSON 配置的一个小型、实用子集转换为原生 nftables 规则。
它专为嵌入式 Linux 网关和类似路由器的产品设计,在这些场景中,手动编写 iptables/nftables 规则容易出错,而更大型的防火墙管理器可能对目标系统来说过于庞大或过于动态。
该项目有意不是完整的 awall 重新实现。它专注于对当前产品用例有用的那个子集:
- 区域
- 基础策略
- 过滤规则
- DNAT
- SNAT 伪装
- TCP MSS 钳制
- 服务定义
- SSH 风格的连接限制
- 转发路径的显式 nftables flowtable 提示
- 运行时接口的动态 flowtable 同步
生成的规则集可以在应用前使用 `nft -c -f` 进行检查。
## 动机
原始系统使用 awall JSON 作为声明式防火墙配置层。awall 随后生成 iptables 规则。
这一直运行良好,但 iptables 正逐渐成为遗留基础设施,而 awall 目前不生成原生 nftables 规则。同时,直接编写 nftables 规则很容易出错:
- 选择错误的钩子或优先级
- 混淆 `input`、`forward` 和 `output`
- 忘记 NAT/过滤交互
- 允许来自意外接口的流量
- 忘记 IPv4/IPv6 差异
- 在面向互联网的接口上创建冗余的日志丢弃规则
`awall-nft` 保留了小型声明式配置模型,但直接生成原生 nftables 规则。
## 设计目标
- 保持配置模型小而易懂。
- 将 JSON 配置视为唯一的事实来源。
- 生成原生 nftables 规则,而非 iptables 规则。
- 应用前进行验证。
- 默认采用安全行为:
- `input`、`forward` 和 `output` 链使用 `policy drop`。
- 仅允许显式配置的区域接口。
- 不隐式信任意外接口。
- 避免面向互联网的丢弃产生冗余的默认日志。
- 保持实现轻量,适合嵌入式 Linux。
- 将防火墙权限规则与 flowtable 优化提示分开。
## 当前状态
当前实现可以:
- 使用 `sunny` 解析 awall 风格的 JSON 文件
- 读取 `optional/main.json` 及其导入列表
- 合并 `base`、`zone`、`filter`、`dnat` 和 `snat` JSON 文件
- 解析 awall 服务定义
- 验证合并后的配置
- 解析服务名称,如 `ssh`、`dns` 和 `rdp`
- 规范化通配符式的接口名称,如 `br+` 和 `wg+`
- 允许具有空接口列表的可选区域
- 生成 nftables 规则
- 为配置的区域间方向生成显式 nftables flowtable 规则
- 将初始 flowtable 设备限制为现有的精确 `ethN` 接口
- 运行 `nft -c -f` 检查生成的规则集
- 可选地使用 `nft -f` 应用已检查的规则集
- 以人类可读的形式显示规范化的配置
生成的 nftables 规则集已通过 `nft -c` 成功检查。
## 配置流程
预期的 awall 风格布局如下:
```
/etc/awall/
optional/
main.json
private/
base.json
zone.json
filter.json
dnat.json
snat.json
```
典型的 `optional/main.json` 如下所示:
```
{
"description": "Main firewall",
"import": [
"base",
"zone",
"filter",
"dnat",
"snat"
]
}
```
## 支持的 awall 子集
### 区域
示例:
```
{
"zone": {
"LAN": {
"iface": ["ppp100", "br+"]
},
"WAN": {
"iface": ["ppp0", "ppp1", "wlan0", "wwan0"]
},
"Closed": {
"iface": ["eth0", "eth1"]
}
}
}
```
以 `+` 结尾的接口名称将作为 nftables 前缀匹配发出:
```
br+ -> iifname "br*"
wg+ -> iifname "wg*"
```
### 策略
示例:
```
{
"policy": [
{ "in": "_fw", "out": "WAN", "action": "accept" },
{ "in": "LAN", "action": "accept" },
{ "out": "LAN", "action": "accept" },
{ "in": "WAN", "action": "drop" },
{ "in": "Closed", "action": "accept" },
{ "out": "Closed", "action": "accept" }
]
}
```
生成的 nftables 过滤链使用 `policy drop`,然后显式地为配置的区域发出规则。
这避免了意外接受来自意外接口(如 USB 网卡或动态创建的接口)的流量。
策略顺序很重要,符合 awall 的规则排序模型。宽泛的单边策略按其出现的顺序展开,更具体的 Guest/DMZ 策略应放在宽泛的遗留策略之前,如果它们旨在限制这些区域。
示例:
```
{
"policy": [
{ "in": "Guest", "out": "WAN", "action": "accept" },
{ "in": "Guest", "out": "LAN", "action": "drop" },
{ "in": "Guest", "out": "Closed", "action": "drop" },
{ "in": "DMZ", "out": "WAN", "action": "accept" },
{ "in": "DMZ", "out": "LAN", "action": "drop" },
{ "in": "DMZ", "out": "Closed", "action": "drop" },
{ "in": "_fw", "out": "WAN", "action": "accept" },
{ "in": "LAN", "action": "accept" },
{ "out": "LAN", "action": "accept" },
{ "in": "WAN", "action": "drop" },
{ "in": "Closed", "action": "accept" },
{ "out": "Closed", "action": "accept" }
]
}
```
在此示例中,`Guest -> LAN` 在后续宽泛的 `out=LAN accept` 策略匹配之前被丢弃。`show forward` 可用于检查有效的转发策略顺序。
### 过滤规则
示例:
```
{
"filter": [
{
"in": "WAN",
"out": "_fw",
"service": "ssh",
"action": "accept",
"conn-limit": {
"count": 3,
"interval": 20
}
}
]
}
```
服务名称使用 `services.json` 解析。
### Similar to 'API Reference' -> 'API 参考', where "API" is kept in English and "Reference" is translated. So for "DNAT", I might translate it to "DNAT" or add a Chinese term. But "DNAT" is a proper acronym, so I should keep it in English. I think for "DNAT", I can output "DNAT" in the translation, but since it's a heading, perhaps "DNAT" alone might be fine, or I can add a Chinese description.
示例:
```
{
"dnat": [
{
"in": "Closed",
"service": {
"proto": "tcp",
"port": 8022
},
"to-addr": "192.168.253.5",
"to-port": 22
},
{
"in": "Closed",
"service": "rdp",
"to-addr": "192.168.253.201",
"to-port": 3389
}
]
}
```
同时支持内联服务定义和命名服务。
DNAT 规则还会为转换后的目标生成匹配的转发链接受规则。这些接受规则在宽泛的策略派生规则(如 `in=WAN drop` 和 `out=LAN accept`)之前发出。
生成的转发规则匹配 `ct status dnat` 加上转换后的目标地址和服务端口,因此只有 DNAT 规则显式转换的数据包才会被接受。这允许发布的服务继续工作,同时仍然可以在宽泛的输出侧策略匹配之前丢弃常规的 WAN 到 LAN 转发。
生成的转发规则示例:
```
iifname @if_wan ct status dnat ip daddr 10.0.3.10 tcp dport 22 accept
```
### SNAT / 伪装
当前支持的 SNAT 形式是实用的伪装形式:
```
{
"snat": [
{ "out": ["WAN", "Closed"] }
]
}
```
这作为 nftables `masquerade` 规则为这些区域中的接口发出。
当前不支持:
- `to-addr` SNAT
- `to-port` SNAT
- 服务范围的 SNAT
- `action: exclude`
### TCP MSS 钳制
示例:
```
{
"clamp-mss": [
{ "out": ["WAN", "Closed"] }
]
}
```
这使用以下方式在 nftables postrouting mangle 钩子中发出:
```
tcp flags syn tcp option maxseg size set rt mtu
```
### 服务
服务定义格式同时支持对象和数组形式:
```
{
"service": {
"ssh": { "proto": "tcp", "port": 22 },
"dns": [
{ "proto": "udp", "port": 53 },
{ "proto": "tcp", "port": 53 }
],
"ipsec": [
{ "proto": "esp" },
{ "proto": "udp", "port": [500, 4500] }
]
}
}
```
`port` 可以是单个数字或数组。
当前支持的协议包括:
- `tcp`
- `udp`
- `icmp`
- `icmpv6`
- `esp`
- `gre`
- `ospf`
- `igmp`
### Flowtable 提示
`flowtable` 是 `awall-nft` 特有的扩展。它不是标准 awall 权限模型的一部分。
它本身不允许流量。常规的 `policy`、`filter`、`dnat` 和 `snat` 处理仍然决定接受哪些数据包。`flowtable` 部分仅标记可以通过 nftables flowtable 加速的显式转发方向,这些方向在通过常规规则后生效。
示例:
```
{
"flowtable": [
{ "in": "LAN", "out": "Closed" },
{ "in": "Closed", "out": "LAN" },
{ "in": "Closed", "out": "Closed" },
{ "in": "Guest", "out": "WAN" }
]
}
```
方向用区域名称表示,而非原始接口名称。例如,`{ "in": "LAN", "out": "Closed" }` 表示从 `LAN` 区域中的接口进入,并通过 `Closed` 区域中的接口离开的转发流量可以添加到 flowtable。
重要属性:
- `flowtable` 是优化提示,不是防火墙权限规则。
- `awall-nft` 不从 `policy` 规则推断 flowtable 资格。
- `in` 和 `out` 是必需的。
- `_fw` 被拒绝,因为 flowtable 仅适用于转发流量。
- 宽泛的单边策略(如 `{ "in": "LAN", "action": "accept" }`)不会自动创建 flowtable 规则。
- 面向 WAN 的方向仅在理解并期望结果的快速路径时才添加。
- 当这些转发策略被明确允许时,`Guest -> WAN` 和 `DMZ -> WAN` 是合理的候选。
在常规规则集生成期间,静态生成器有意保守:仅现有的精确以太网接口(如 `eth0` 和 `eth1`)被添加到初始 nftables flowtable 设备列表。前缀式或动态接口(如 `ppp+`、`wg+`、`br+`、`wlan0`、`wwan0` 或基于 WireGuard 的封闭网络接口如 `wlisc`)由 `flowtable-sync` 处理。
这避免了在配置的接口尚不存在时 `nft -c` 失败。`flowtable-sync` 读取相同的 JSON 配置,根据当前存在的接口解析配置的 flowtable 区域方向,使用所需的设备集重新创建 `ft_forward` flowtable 对象,并重建 `flowtable_forward` 链。它不重建整个防火墙规则集。
典型的生成输出如下所示:
```
flowtable ft_forward {
hook ingress priority 0;
devices = { eth0, eth1 };
}
set flowtable_pairs {
type ifname . ifname;
elements = { "eth0" . "eth1", "eth1" . "eth0" };
}
chain flowtable_forward {
iifname . oifname @flowtable_pairs ct state established meta l4proto { tcp, udp } counter flow add @ft_forward
# awall_nft flowtable-sync may replace this chain
}
```
生成的 `forward` 链在 `ct state established,related accept` 规则之前跳转到 `flowtable_forward`。flowtable 规则本身仅匹配 `ct state established`,因此新数据包仍通过常规策略路径,然后同一数据流的后续数据包才能添加到 flowtable。
### 连接限制
awall 可能生成类似以下的 iptables `recent` 规则:
```
-m recent --update --seconds 20 --hitcount 3 --rsource -j logdrop
-m recent --set --rsource -j ACCEPT
```
`awall-nft` 生成原生的基于 nftables meter 的规则来替代。它有意不记录丢弃。
对于:
```
"conn-limit": {
"count": 3,
"interval": 20
}
```
生成的规则使用每个源地址的 meter,并将速率转换为每分钟的值:
```
ceil(count * 60 / interval)
```
因此 `20` 秒内的 `3` 次变为 `9/minute`。
这并非 iptables `recent` 的精确行为克隆,但适用于抑制 SSH 扫描突发而不淹没日志。
## 生成的 nftables 结构
生成器当前发出:
```
table inet awall_nft
flowtable ft_forward # emitted when flowtable devices exist
chain input
chain flowtable_forward
chain forward
chain output
chain postrouting_mangle
table ip awall_nft_nat
chain prerouting
chain postrouting
```
过滤链使用 `policy drop`。`flowtable_forward` 链作为 flowtable 规则的稳定插入点发出。`flowtable-sync` 可以刷新该链并重新创建 `ft_forward` flowtable 对象,而无需重写整个规则集。
NAT 表目前仅支持 IPv4,因为当前用例是 IPv4 DNAT/SNAT 伪装。
### 命名的 nftables 集合
`awall-nft` 为精确的接口组发出命名的 nftables 集合。这使得生成的规则集更易读,因为规则引用语义组(如 `@if_lan`、`@if_wan`、`@if_dmz` 和 `@if_closed`),而不是在每个规则中重复匿名接口列表。
示例:
```
set if_lan {
type ifname;
elements = { "br0", "eth0", "ppp100" };
}
set if_wan {
type ifname;
elements = { "eth1", "eth2", "ppp0", "ppp10", "wlan0", "wwan0" };
}
chain forward {
iifname @if_wan drop
iifname @if_lan accept
oifname @if_lan accept
}
```
`forward_known_iif` 也对精确接口使用命名集合,flowtable 方向通过命名的 `flowtable_pairs` 集合(`iifname . oifname` 对)发出。前缀接口匹配(如 `wg+`)仍作为显式通配符规则发出,例如 `iifname "wg*" accept`,因为它们不是精确接口集元素。
IPv4 NAT 规则有意继续使用匿名集合。nftables 命名集合的作用域限定于其表,`ip awall_nft_nat` 表无法引用在 `inet awall_nft` 表中定义的集合。
## 命令行界面 (CLI)
### 生成规则集
```
awall_nft generate \
--main /etc/awall/optional/main.json \
--private-dir /etc/awall/private \
--services /usr/share/awall/mandatory/services.json \
-o /tmp/awall_nft.nft
```
默认值为:
```
--main /etc/awall/optional/main.json
--private-dir /etc/awall/private
--services /usr/share/awall/mandatory/services.json
```
因此通常可以缩短为:
```
awall_nft generate -o /tmp/awall_nft.nft
```
### 检查生成的规则集
```
awall_nft check /tmp/awall_nft.nft
```
这将运行:
```
nft -c -f /tmp/awall_nft.nft
```
### 生成并检查
```
awall_nft build-check -o /tmp/awall_nft.nft
```
### 应用规则集
```
awall_nft apply /tmp/awall_nft.nft
```
默认情况下,`apply` 会先检查规则集。要跳过检查:
```
awall_nft apply --no-check /tmp/awall_nft.nft
```
正常用法不建议跳过检查。
### 显示规范化的配置
```
awall_nft show all
```
可用的主题:
```
all
zones
policies
forward
filters
dnat
snat
clamp-mss
flowtable
```
当宽泛策略与显式 Guest/DMZ 策略混合时,`show forward` 很有用。它打印影响 FORWARD 规则生成的策略顺序,例如:
```
policy[0] Guest -> WAN accept
policy[1] Guest -> LAN drop
policy[4] LAN -> * accept
policy[5] * -> LAN accept
```
`*` 表示原始 awall 风格的策略中省略了相应的一侧。
### 同步 flowtable 设备
```
awall_nft flowtable-sync
```
此命令用于运行时创建的接口,如 PPP、WireGuard、网桥以及产品特定的封闭网络接口(如 `wlisc`)。
它执行以下有限更新:
1. 读取并规范化当前的 awall 风格 JSON 配置
2. 将显式 `flowtable` 区域方向解析到当前存在的接口
3. 刷新 `chain inet awall_nft flowtable_forward`
4. 使用解析后的设备重新创建 `flowtable inet awall_nft ft_forward`
5. 重新添加匹配的 `flow add @ft_forward` 规则
它不重新生成或重新应用整个防火墙规则集。
典型的钩子示例:
```
# The user said "translate each of the following headings", so I need to provide Chinese text. Perhaps for technical terms, I keep them in English and translate the surrounding words if any. But here, the headings are given as is.
# 3. **/etc/ppp/ip-up.d/awall-nft-flowtable**: This looks like a file path. File paths are often kept in English, especially in technical contexts. The instruction says to keep technical jargon in English, so the path itself should be kept as is. But for translation, I might need to translate any descriptive parts. However, "/etc/ppp/ip-up.d/" is part of the path, and "awall-nft-flowtable" seems like a script or file name. So, I should keep the entire path in English.
#!/bin/sh
/usr/local/sbin/awall_nft flowtable-sync || true
```
对于 WireGuard 或基于 WireGuard 的封闭网络接口,从接口管理器调用相同的命令,或在使用 `wg-quick` 时从 `PostUp` / `PostDown` 调用。
正常输出有意紧凑:
```
flowtable-sync: synced 1 rule(s), skipped 2 rule(s), devices: eth0, eth1, wlisc
```
输入或输出侧解析为不存在接口的规则会被跳过。
## 构建
```
nimble build -d:release
```
交叉编译到 ARM 也是可能的,例如:
```
nimble build -d:release --cpu:arm
arm-linux-gnueabihf-strip awall_nft
```
## 建议的启动时使用
由于生成器轻量级,规则集可以在启动时生成,而不是保存和恢复已生成的防火墙状态。
典型流程如下:
```
awall_nft generate -o /run/awall_nft/ruleset.nft
awall_nft apply /run/awall_nft/ruleset.nft
```
这保持 JSON 配置作为事实来源。
## 与 awall/iptables 的差异
有意为之的差异:
- 原生 nftables 输出,而非 iptables 规则。
- 默认情况下没有 logdrop 链。
- `conn-limit` 使用 nftables meter/limit,而非 iptables `recent`。
- 过滤链使用 `policy drop`。
- 不信任未定义的接口。
- 允许具有空接口列表的区域,并且发射器会跳过它们。
- 规则使用 nftables 集合和前缀接口匹配进行压缩。
不支持或不完整的区域:
- 完整的 awall 兼容性
- 任意的 awall 变量
- 嵌套导入
- 完整的 SNAT 功能集
- ipset/address-set 配置
- 除了 `nft -c` 之外的回滚/应用安全性
## 未来想法
可能的未来扩展:
## 许可证
MIT
标签:awall, DNAT, Homebrew安装, iptables, JSON配置, Linux网关, MacOS取证, NAT配置, nftables, SNAT, 安全策略, 安全默认, 嵌入式系统, 嵌入式防火墙, 提示词设计, 流表, 流量控制, 流量过滤, 网络安全, 网络工具, 自动化配置, 规则生成, 路由器, 连接限制, 逆向工具, 配置转换, 配置验证, 防火墙生成器, 防火墙管理, 防火墙规则, 隐私保护