1d5af2248b
之前把 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 项全绿。
110 lines
4.1 KiB
Plaintext
110 lines
4.1 KiB
Plaintext
#!/usr/sbin/nft -f
|
||
# dsh_firewall — 代理节点 v4+v6 统一防火墙(白名单 + REJECT 兜底)
|
||
# 适用于 dmit / aliuk / alius 等手工栈迁移后的代理节点
|
||
# 不碰 ip6 filter / rules.v6(inet 表独立于 iptables-nft 转换层)
|
||
# 用法:nft -f dsh-firewall.nft(幂等,重复执行安全)
|
||
|
||
# 先删旧表(幂等重建)
|
||
table inet dsh_firewall
|
||
delete table inet dsh_firewall
|
||
|
||
table inet dsh_firewall {
|
||
# ── 入站白名单 ──
|
||
chain input {
|
||
type filter hook input priority filter; policy accept;
|
||
|
||
# 本地回环
|
||
iifname "lo" accept
|
||
# NetBird 接口。官方要求主机防火墙放行 wt0:
|
||
# "NetBird automatically adds rules to allow traffic on the wt0 interface."
|
||
# 以前是手工 nft insert 的,每次重跑本脚本都会被 delete table 洗掉 —— 必须进模板。
|
||
iifname "wt0" accept
|
||
|
||
# 容器网桥:只放 DNS 和宿主机对外服务的 80/443(实测容器会连 10.89.1.1:443)
|
||
iifname "podman*" meta l4proto { tcp, udp } th dport 53 accept
|
||
iifname "podman*" tcp dport { 80, 443 } ct state new accept
|
||
iifname "docker0" meta l4proto { tcp, udp } th dport 53 accept
|
||
iifname "docker0" tcp dport { 80, 443 } ct state new accept
|
||
iifname "br-*" meta l4proto { tcp, udp } th dport 53 accept
|
||
iifname "br-*" tcp dport { 80, 443 } ct state new accept
|
||
|
||
# 已建立/相关连接
|
||
ct state established,related accept
|
||
# 乱序/孤儿包静默丢弃,不要回 RST/ICMP(避免反射)
|
||
ct state invalid drop
|
||
|
||
# SSH
|
||
tcp dport 22 ct state new accept
|
||
|
||
# HTTP(Caddy 反代 + ACME HTTP-01)
|
||
tcp dport 80 ct state new accept
|
||
|
||
# HTTPS(Xray REALITY + Caddy)
|
||
tcp dport 443 ct state new accept
|
||
|
||
# HTTP/3(QUIC)
|
||
udp dport 443 accept
|
||
|
||
# Hysteria2
|
||
udp dport 8443 accept
|
||
|
||
# hy2 端口跳跃
|
||
udp dport 20000-30000 accept
|
||
|
||
# ICMP v4(ping,不限速——后续可加 rate limit)
|
||
ip protocol icmp accept
|
||
|
||
# ICMPv6(按 RFC 4890 §4.4.1 必须放行的 type,用数字最保险)
|
||
# 1=dest-unreachable 2=packet-too-big 3=time-exceeded 4=parameter-problem
|
||
# 128/129=echo 133/134=RS/RA 135/136=NS/NA 130/131/132=MLD 143=MLDv2 141/142=IND
|
||
icmpv6 type { 1, 2, 3, 4, 128, 129, 133, 134, 135, 136, 130, 131, 132, 143, 141, 142 } accept
|
||
|
||
# DHCPv6(v6 地址分配必需)
|
||
udp sport 547 udp dport 546 ip6 saddr fe80::/10 accept
|
||
udp dport 546 ip6 saddr fe80::/10 accept
|
||
|
||
# 兜底:拒绝一切未匹配流量
|
||
# v4 用 icmp-host-prohibited,v6 用 icmp6-adm-prohibited
|
||
ip protocol tcp reject with tcp reset
|
||
ip protocol udp reject with icmp port-unreachable
|
||
ip6 nexthdr tcp reject with tcp reset
|
||
ip6 nexthdr udp reject with icmpv6 port-unreachable
|
||
reject
|
||
}
|
||
|
||
# ── 转发:代理节点不需要转发 ──
|
||
# forward 链:作用域白名单 + 默认丢弃。
|
||
#
|
||
# 为什么不能留 `policy accept`:基线的 iptables 就是 `-o podman+ ACCEPT` + 链尾
|
||
# `REJECT`,即"只放容器转发、其余丢弃"。改成 accept 等于把主机变成转发器。
|
||
#
|
||
# 为什么必须显式列出接口:
|
||
# · Podman 发布端口的入站包在 nat prerouting 被 DNAT 后走 forward,而 netavark
|
||
# 的 NETAVARK_FORWARD 只放行 saddr(出站)和 daddr+established(回程),
|
||
# 入站 NEW 包并不匹配——基线靠 `oifname "podman*" accept` 放行,这里同理。
|
||
# · NetBird(wt0)和 ZeroTier(zt*)会自己往 ip filter FORWARD 写
|
||
# `iifname "wt0" accept` 之类的规则;本表默认丢弃是终局裁决,必须同步放行。
|
||
chain forward {
|
||
type filter hook forward priority filter; policy drop;
|
||
|
||
ct state established,related accept
|
||
|
||
iifname "wt0" accept
|
||
oifname "wt0" accept
|
||
iifname "zt*" accept
|
||
oifname "zt*" accept
|
||
|
||
iifname "podman*" accept
|
||
oifname "podman*" accept
|
||
iifname "docker0" accept
|
||
oifname "docker0" accept
|
||
iifname "br-*" accept
|
||
oifname "br-*" accept
|
||
}
|
||
|
||
# ── 出站:全放行(代理节点需要自由出站) ──
|
||
chain output {
|
||
type filter hook output priority filter; policy accept;
|
||
}
|
||
}
|