跳转到内容

防火墙联动

启用后:

  • 某设备某端口首次触发告警 → 封禁该设备在该端口上的流量;
  • 输出「PCDN行为结束」→ 解封;
  • 程序正常退出 → 对仍在封禁中的条目兜底解封并清理规则;
  • 启动失败(HTTP 监听 / 抓包初始化失败)→ 先清理再退出。

封禁粒度是「设备 MAC + 本地端口 + 协议」,不同端口、不同协议各自独立封禁与解封。

[firewall]
enabled = true
backend = "nftables" # hook / iptables / nftables
ban_both = true # 同时封禁 IPv4 与 IPv6
ban_active_alerts = false # 启用 / reload 时补发当前已告警端口的封禁
# timeout = "5s" # 单条防火墙命令超时
backend 实现
hook 一条自定义命令模板,{action} 区分 ban / unban,可适配任意防火墙
iptables iptables / ip6tables 的专用链 PCDN_DETECT,在 chain(默认 FORWARD)插入跳转
nftables bridge 族指定钩子(默认 prerouting)上的拼接集合,按「源 MAC . 源端口」丢弃

只解析与当前 backend 同名的子表,其余子表可保留备用。

[firewall]
enabled = true
backend = "hook"
[firewall.hook]
cmd = "my-fw-hook {action} {mac} {ip} {port} {family} {proto}"
# 换成 iptables 时:
# [firewall.iptables]
# chain = "FORWARD" # 端口封禁的跳转插入点
# input_chain = "INPUT" # UPnP 拦截的跳转插入点
# ban_chain = "PCDN_DETECT" # 端口封禁专用链
# upnp_chain = "PCDN_DETECT_UPNP" # UPnP 拦截专用链
# 换成 nftables 时:
# [firewall.nftables]
# table = "pcdn_detect"
# set = "banned" # 实际使用 <set>_<family>_<proto>
# hook = "prerouting"
# input_hook = "input" # UPnP 专用链钩子
# ban_ttl = "30m"

hook 的占位符:{action} {mac} {ip} {port} {family}(v4 / v6){proto}(udp / tcp)。 只配一条模板,使用者自写 sh 包装按 $1 分发动作。

iptables 的 chain / input_chain 只是跳转插入点,规则实际写入专用链。启动时会安全地 清空并重建专用链:既清理上次被 SIGKILL / OOM 杀死后的残留,也不影响用户自己在 FORWARD / INPUT 里的规则。专用链名可自定义,但不能与 chain / input_chain 重名,长度 ≤ 28, 只能含字母、数字、下划线、连字符。

取值 行为
true(默认) 任一地址族触发,同时封禁该设备的 IPv4 与 IPv6;同一「MAC + 端口 + 协议」只封一次
false 按触发到的地址族单独封禁,v4 / v6 各自独立封禁与解封

ban_both = true 时 hook 后端会对 v4、v6 各执行一次命令。若模板里不含 {family} 等区分项,同一条命令会被执行两次——请保证命令幂等,或改用 iptables / nftables。

  • 封禁协议随触发报文:UDP 触发只封 UDP,TCP 触发只封 TCP。
  • [capture] protocol = "udp"(默认)时抓包层只统计 UDP,因此只会封 UDP。
  • [capture] protocol 变化且防火墙已启用时,热重载会一并原地重建防火墙后端,否则新协议的 封禁命令可能因集合不存在而失败。

ban_active_alerts = true(默认关闭)时,启用防火墙或 reload 会把检测层当前已处于告警中的 端口直接纳入封禁,不等下一轮触发,且幂等。适用于「启动时防火墙关闭、运行中 reload 启用」, 或状态文件只恢复了告警会话而没有封禁的场景。

正常退出会清理;被 SIGKILL / OOM 杀死时用户态无法自救,依赖后端自身的兜底:

后端 兜底方式
nftables 启动时先 delete table 再重建,残留元素与规则一并清除
iptables 启动时清空并重建专用链,不影响用户自己链中的规则
hook 由用户命令负责;建议 ban / unban 幂等,并在启动时自行清理

若希望进程死后由内核自动解封,给 nftables 集合元素加超时:

[firewall.nftables]
ban_ttl = "30m"

启用后集合元素带 timeout,Blocker 按 TTL/3 周期重新 add 续期,进程死亡后最迟在 TTL 内 自动过期。注意:

  • 需要内核 nftables 集合支持 timeout(约 4.8+),且重复 add 能刷新超时;
  • 若续期失败,封禁可能在告警结束前提前失效(fail-open);
  • 只对 nftables 有效,iptables / hook 没有内核级超时语义。

在「按端口封禁」之上叠加的设备级封禁:某设备被封禁的不同端口数达到阈值后,直接封禁该设备的 全部 UDP 端口,仅放行常用端口,从而阻断 UDP 方向的 PCDN,同时不影响基本联网。

[firewall]
enabled = true
backend = "iptables"
[full_ban]
mode = "device" # off(默认)/ on / device
threshold = 5 # 被封禁的不同端口数达到该值后触发
allow = [53, 123, 67, 68, 546, 547] # 放行的 UDP 端口;留空用内置常用端口
# [full_ban.hook]
# cmd = ""
字段 默认 说明
mode off on = 所有被检测设备;device = 仅名单中 full_ban = true 的设备
threshold 5 被封禁的不同端口数达到该值触发;同端口的 v4/v6 只计一个,UDP/TCP 都计入
allow 内置 留空 = [53, 123, 67, 68, 546, 547];显式配置时以其为准,不叠加内置值

行为细节:

  • 冗余规则清理:全封禁生效后会移除该设备此前单独下发的 UDP 端口级规则;TCP 端口不在全封禁 范围内,其规则保留。
  • 触发后的新端口:不再单独下发端口级规则,但仍计入端口统计,使全封禁在该设备告警全部结束 前保持有效。
  • 解除:被封禁端口数归零时自动解除;程序退出时对所有仍处于全封禁状态的设备兜底解除。
  • 作用范围:按源 MAC 生效,对 IPv4 与 IPv6 同时起作用(与 ban_both 无关)。
  • 依赖 [firewall] enabled = true,否则不生效(启动时提示警告)。

启动时会检查后端命令是否存在:

  • iptables 后端缺少 iptables → 报错退出;
  • nftables 后端缺少 nft → 报错退出;
  • 例外是 ip6tables:精简固件常见缺失,此时只告警并忽略 IPv6,IPv4 封禁照常工作, 不会阻止启动(即使 ban_both = true 也只封 IPv4)。