eli
|
1d5af2248b
|
fix(firewall): forward 链改回"作用域白名单 + 默认丢弃"
之前把 forward 链清空成 policy accept 是错的——那不是标准,是相对基线的
**安全退化**。基线(init-iptables.sh)本来就是:
-A FORWARD -o podman+ -j ACCEPT
-A FORWARD -j REJECT
配合 net.ipv4.ip_forward=1,清空 forward 等于让主机变成转发器。
现在按基线语义实现:
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname/oifname "wt0" accept # NetBird 覆盖网
iifname/oifname "zt*" accept # ZeroTier 覆盖网
iifname/oifname "podman*" accept # Podman
iifname/oifname "docker0" accept
iifname/oifname "br-*" accept
}
两处必须显式列出接口的原因(都实测过):
1. **容器发布端口的入站在 forward 里没有任何现成规则可匹配**。实测
NETAVARK_FORWARD 只有 `saddr 10.89.0.0/24 accept`(出站)和
`daddr 10.89.0.0/24 established accept`(回程),**没有发布端口的入站
accept**。基线是靠 `-o podman+ ACCEPT` 放行的,这里同理用 oifname 匹配。
2. **NetBird/ZeroTier 会自己往 `ip filter FORWARD` 写规则**。实测 oraclejp
的 `ip filter FORWARD` 里有 netbird 加的 `iifname "wt0" accept`。本表的
`policy drop` 是终局裁决,不同步放行就会把 VPN 转发打死。
验证(oraclezyus-arm):SSH/80/443/容器 9000 全通、容器 DNS 与出站正常、
容器到宿主机 443 正常。测试 77 → 79 项全绿。
|
2026-11-11 12:24:44 +08:00 |
|
eli
|
38c2f1fd14
|
fix(firewall): 加 wt0 进模板、删掉无用的 NTP 规则
两项都是在 oraclezyus-arm 上单机验证得出的:
1. **加 `iifname "wt0" accept`**。NetBird 官方要求主机防火墙放行 wt0
("NetBird automatically adds rules to allow traffic on the wt0 interface")。
oraclejp/oraclezyus-arm 上原本是手工 `nft insert` 的,而本脚本每次
`delete table` + 重建,会把它洗掉——必须进模板。
2. **删 `udp sport 123`**。systemd-timesyncd 是纯客户端,实测轮询间隔 34 分钟、
conntrack UDP 超时 120 秒,每次轮询都是新建条目,回包走 established 即可。
实测去掉后 `NTPSynchronized` 仍为 yes。旧表里那条规则的计数器也是 0 命中。
顺带堵掉"源端口 123 可穿透所有 UDP 端口"这个口子。
测试 77 项全绿。
|
2026-11-11 00:11:25 +08:00 |
|
eli
|
a8b22b594c
|
fix(firewall): 按旧 iptables 实现精确复刻容器放行,并补回遗漏的 NTP
用户质疑"iifname podman* accept 到底合不合理、为何旧实现只放 53"——质疑成立,
我之前把整个网桥全放行(还有 docker0/br-*),是修自己弄坏的 DNS 时顺手放开的,
没有论证过。
实测查清了容器到底需要什么:
· 容器 DNS 走 aardvark-dns(网桥网关 :53) → 必须放
· oraclejp 上有容器正在连宿主机 10.89.1.1:443 → 也必须放
· 其余端口无一使用 → 不放
改为按端口放行(podman*/docker0/br-* 各 53 + 80/443),不再整网卡 accept。
容器要访问宿主机其它服务,显式用 --extra-tcp/udp 加。
同时补回旧实现有、新实现漏掉的 `udp sport 123`(NTP)。
关于"高端口 9993-65535":实测全队没有服务监听在该区间的公网地址上——
· xray 的 *:58xxx / *:54xxx 是 UNCONN 通配**出站**源端口,不是监听
· zerotier 的 23517/28019 等只绑内网地址
· 只有 tcp/9993(zerotier)和 udp/51820(WireGuard)需要入站,已由 extra 参数覆盖
所以旧的宽范围是在裸奔,不保留。
(注:我先前汇报过 tcp 58409/38034/43149 是监听,那是我 awk 把本地+对端地址
拼接产生的假端口,已更正。)
验证:5 台容器 DNS 各 3/3 成功、出站 301、9000 入站 200、zerotier 9993 通。
测试 77 项全绿。
|
2026-11-10 18:53:05 +08:00 |
|
eli
|
57b21d5b99
|
refactor(firewall): forward 链彻底不做过滤(回到 netavark 本来就能工作的状态)
之前连续两次修错方向,根因是没搞清 nftables 的多表裁决语义:
· netavark 自己会在 `ip filter` 的 NETAVARK_FORWARD 链里放行容器流量
(实测:`ip saddr 10.89.0.0/24 accept`,Docker 同理有 DOCKER-FORWARD);
· 但 nftables 的 `accept` **不是终局裁决**,包会继续流到下一张表的基础链;
· 本表在 forward 上写 reject → 把运行时已经放行的流量打死。
第一次修:加 `ct status dnat accept`(只解决外部访问发布端口,解决不了容器出站,
因为 SNAT 在 postrouting,ct status 不是 dnat)。
第二次修:再加一堆 `iifname podman*/docker0/br-*` accept。
两次都是"补规则",越补越脆弱,还踩到 nft 1.0.2 不支持通配接口名集合的坑。
正确做法是**这一条都别加**:宿主机防火墙的职责是保护本机服务(input),
转发流量本来就该由容器运行时和云安全组负责,本表不掺和。
这是社区标准结论(Red Hat KB 5885821、netavark#977:把 FORWARD 设 DROP 就会
让 Podman/Docker 连不上)。
改动:`chain forward` 只留 `type filter hook forward priority filter; policy accept;`,
删除之前的 dnat/网桥规则。黄金基线与回归测试同步改为"forward 链内不许出现 reject/drop"。
验证:容器内 DNS 解析正常、出站 301、5 台 9000/api/health 全 200、forward 链 0 条拒绝规则。
测试 77 项全绿。
|
2026-11-10 13:06:25 +08:00 |
|
eli
|
bff47139d7
|
fix(firewall): 补容器 DNS/出站放行、修 --local --apply 的静默失败
三处,都是 iptables→nftables 迁移时丢的语义或本地分支与远端分支不一致:
1. forward 链只放行了 `ct status dnat`(外部连容器发布端口),漏了容器**主动出站**。
SNAT 发生在 postrouting(forward 之后),这类包的 ct status 不是 dnat,于是容器
连外网全被 reject。实测容器内 curl 1.1.1.1 直接 Couldn't connect。
补 `iifname podman*/docker0/br-* accept`。
2. input 链没放行容器网桥,容器内的 DNS(aardvark-dns 监听在网桥网关 :53)和
host-gateway 访问全部被拦。实测容器内域名解析全失败。
同样补网桥放行,另加 `ct state invalid drop`(乱序/孤儿包不该回 RST 给反射用)。
3. `--local --apply` 分支没有 set -e,且 `rm -f; exit $?` 把退出码冲成 0 ——
nft 加载失败也会照样把坏规则持久化并返回成功,下次开机整份加载失败等于裸奔。
改成内部 set -euo pipefail、失败即退、`rc=$?` 保留退出码、落盘用 install -m 644。
另外踩到一个 nft 1.0.2 的坑:`iifname { "podman*", ... }` 会报
"Byteorder mismatch: expected big endian, got host endian",必须拆成多条独立规则。
正是新加的 nft -c 预检在 gcoregon 上拦住了它,否则会把坏规则推上去。
验证:容器内 DNS 解析成功、出站 301、5 台 9000/api/health 全部 200。
测试 77 项全绿。
|
2026-11-10 11:48:48 +08:00 |
|
eli
|
328a41fda3
|
fix(firewall): forward 链放行 DNAT,否则容器发布端口全被挡成 refused
从 iptables 版迁移到 nftables 版时丢了"容器转发先放行"的语义:
· init-iptables.sh 把 reject 追加在 FORWARD 链**尾部**,Docker/Podman 会把自己的链
插在最前面,容器流量天然先被接受;
· nftables 版写成 `ct state established,related accept` + `reject`,而 Podman 的
端口发布靠 NAT 的 DNAT 改写目标地址,改写后照样要过 forward 链 → 撞 reject。
症状很坑:宿主机自己 curl 127.0.0.1 正常(走 lo),外部一律 **connection refused**。
不是超时——本表用 reject 而不是 drop,所以看着像"端口根本没开",很容易往错方向查。
暴露方式:昨天把规则推到 5 台 Oracle,把这些机器上 Podman 发布的 9000(Arcane agent
的 API)打掉了,表现是 Arcane 面板里这批 VPS 全部离线。
修法:在 forward 链的 reject 之前加 `ct status dnat accept`——只放行被本机 NAT 改写过的
包,其余转发仍然拒绝。黄金基线 dsh-firewall.nft 同步更新。
补了两条回归测试:forward 链必须有该规则,且必须排在 reject 之前。
测试 75 → 77 项。
|
2026-11-10 11:24:37 +08:00 |
|
eli
|
5420ea95e3
|
feat(firewall): 主机防火墙收进本仓库,全机队共用
init-firewall.sh 之前落在 oci-toolkit 里,但它是"给任何一台代理节点上防火墙",
跟 OCI 没关系 —— GCP、阿里云、搬瓦工、Oracle 都要用同一份。挪到 vpsetup,
和 setup_*/migrate_* 放一起,按机器角色选 profile:
proxy(默认) / hy2 / web / minimal
端口全走开关(--ssh-port/--hy2-port/--hy2-hop/--no-hy2-hop/--no-http3/
--extra-tcp/--extra-udp/--enable-frps/--enable-xmpp)。默认 profile 生成的
规则与现网 dsh-firewall.nft 逐条一致,老机器重跑是纯 no-op。
测试 tests/run_firewall_tests.sh 75 项,钉死:默认与现网一致、四个 profile 的
端口集合、任何组合都保留链尾 REJECT 兜底、非法输入 exit 2 且不产出规则。
OCI 那边同时删掉,避免两份分叉。
|
2026-11-08 00:48:17 +08:00 |
|