我正在尝试在位于 NAT 路由器后面的网络上设置 PXE 启动(需要 TFTP)。
我的问题与网络上的许多其他问题类似,但我找到的所有答案都适用于带有 iptables 的 CentOS 7。我需要使用带有防火墙和 nft 作为后端的 CentOS 8 来执行此操作。
无法对 TFTP 流量进行 NAT,因为尽管 TFTP 助手创建了期望,但 iptables 并未将返回连接转发到客户端 https://unix.stackexchange.com/questions/579508/iptables-rules-to-forward-tftp-via-nat
这是我的简化的网络图:
Outside NAT Inside NAT
10.0.10.10 10.0.10.11->192.168.1.1 192.168.1.2
TFTP server --------> NAT ---------> PXE/TFTP client
TFTP 无法正常工作。使用 tcpdump,我看到 RRQ 消息成功从 192.168.1.2 传输到 10.0.10.10。响应到达路由器,但未正确进行 NAT 以到达客户端。
我尝试了 sysctl net.netfilter.nf_contrack_helper 的两个设置(更改设置后重新启动):
# sysctl -a | grep conntrack_helper
net.netfilter.nf_conntrack_helper = 0
当 nf_contrack_helper=0 时:
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
Initial RRQ:
14:02:27.842563 IP (tos 0x0, ttl 64, id 64642, offset 0, flags [DF], proto UDP (17), length 54)
192.168.1.2.36799 > 10.0.10.10.69: [udp sum ok] 26 RRQ "grub2/grubx64.efi" octet
Initial RRQ after NAT:
14:02:27.842619 IP (tos 0x0, ttl 63, id 64642, offset 0, flags [DF], proto UDP (17), length 54)
10.0.10.11.36799 > 10.0.10.10.69: [udp sum ok] 26 RRQ "grub2/grubx64.efi" octet
Response from TFTP server to NAT router:
14:02:27.857924 IP (tos 0x0, ttl 63, id 60000, offset 0, flags [none], proto UDP (17), length 544)
10.0.10.10.60702 > 10.0.10.11.36799: [udp sum ok] UDP, length 516
(repeated several times until timeout)
当 nf_contrack_helper=1 时,传出的数据包根本就不会被 NAT:
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
Initial RRQ:
14:02:27.842563 IP (tos 0x0, ttl 64, id 64642, offset 0, flags [DF], proto UDP (17), length 54)
192.168.1.2.36799 > 10.0.10.10.69: [udp sum ok] 26 RRQ "grub2/grubx64.efi" octet
(repeated several times until timeout)
nf_*_tftp 帮助程序都已加载(无论 nf_contrack_helper 设置如何):
# lsmod | grep tftp
nf_nat_tftp 16384 0
nf_conntrack_tftp 16384 3 nf_nat_tftp
nf_nat 36864 3 nf_nat_ipv6,nf_nat_ipv4,nf_nat_tftp
nf_conntrack 155648 10 nf_conntrack_ipv6,nf_conntrack_ipv4,nf_nat,nf_conntrack_tftp,nft_ct,nf_nat_ipv6,nf_nat_ipv4,nf_nat_tftp,nft_masq,nft_masq_ipv4
上面链接的一篇文章建议使用 iptables 进行以下操作(这很有意义):
iptables -A PREROUTING -t raw -p udp --dport 69 -s 192.168.11.0/24 -d 172.16.0.0/16 -j CT --helper tftp
我如何使用带有 nft 后端的firewalld 做同等的事情。
更新:
Firewalld 配置相当复杂,因此我仅添加相关区域:
外部区域:
<?xml version="1.0" encoding="utf-8"?>
<zone>
<source address="10.0.10.0/24"/>
<service name="tftp-client"/>
<service name="ssh"/>
<masquerade/>
</zone>
内部区域:
<?xml version="1.0" encoding="utf-8"?>
<zone>
<source address="192.168.1.0/24"/>
<service name="dhcp"/>
<service name="ssh"/>
<service name="dns"/>
<service name="tftp"/>
<masquerade/>
</zone>
注意:内部区域的 Masquerade 是一个错误。我删除了它,但行为没有改变。
区域漂移已被禁用。
更新 2:
要回答评论者的请求:
DHCP 配置
DHCP 服务器与 NAT 路由器(网络图中的 192.168.1.1)在同一系统上运行。它是标准的 ISC DHCP,分发 IP 地址(作为固定地址;不涉及地址池)、掩码、网关、DNS 服务器等,以及 PXE Boot 下一个服务器和文件名选项。
这一切显然是有效的。tcpdump 显示客户端向服务器发送了正确的 RRQ 数据包。
响应返回到 NAT 路由器,但不会被发送到 NAT 后面的一侧。
关于 TFTP 工作原理及其如何突破 NAT 的详细信息
如果您了解 TFTP 协议,那么就会很清楚发生了什么;我只是不知道如何使用firewalld/nft/CentOS 8 来处理它。
从根本上讲,问题在于 TFTP 协议以非标准方式使用 UDP 端口。在 DNS 等基于 UDP 的“标准”协议中,响应来自服务器侦听的同一端口。
Request: client:54321 -> server:53
Response: server:53 -> client:54321
(其中 54321 可以是客户端选择的任何随机临时端口号)。
NAT 匹配这些 IP 地址和端口来识别哪个响应属于哪个请求。
TFTP 的做法不同;响应不是来自端口 69,而是来自其他随机端口。
Request (RRQ): client54321 -> server:69
Response (Data): server:12345 -> client:54321
其中 54321 再次是客户端选择的随机临时端口,而 12345 是服务器选择的随机临时端口。
因此,标准 NAT 行为将找不到与原始服务器:12345 匹配的连接,并丢弃数据包。
解决这个问题的方法涉及使用一个帮助程序——了解这个怪癖的 nf_nat_tftp 内核模块。
我只是还没能弄清楚如何使用 CentOS 8、nftables 和firewalld 来实现这一点。
对于我来说,使用 nftables 的答案是完全可以接受的,只要它不违反任何防火墙规则。
答案1
不起作用的原因
它出现防火墙可能适合处理防火墙本地服务,而不是路由服务。
所以远程文件传输协议当使用 OP 中的区域文件配置了firewalld(在 CentOS 8 上)时,设置将在最后添加这些 nft 规则(这里仅显示规则,而不是整个规则集):
table inet firewalld {
chain filter_IN_external_allow {
udp dport 69 ct helper set "helper-tftp-udp"
}
chain filter_IN_internal_allow {
udp dport 69 ct helper set "helper-tftp-udp"
udp dport 69 ct state { new, untracked } accept
}
}
这些规则永远不会匹配,因此毫无用处:它们在输入路径中,而不是在前向路径中。
在运行防火墙的情况下,将这些(盲目复制的)规则添加到正确的位置:在转发路径中,将使 TFTP 正常工作:
nft insert rule inet firewalld filter_FWDI_internal_allow udp dport 69 ct helper set "helper-tftp-udp"
nft add rule inet firewalld filter_FWDI_internal_allow index 0 udp dport 69 ct state '{ new, untracked }' accept
所以最后所谓的直接选择仍然是一个选择,所以一切都存储在防火墙的配置。可惜文档有点误导:
警告:直接规则行为因 FirewallBackend 的值而异。请参阅防火墙d.direct(5)。
如果不仔细阅读,你可能会认为FirewallBackend=nftables
它会通过接受而表现不同nftables规则,但事实并非如此:
# firewall-cmd --version
0.8.0
# firewall-cmd --direct --add-rule inet firewalld filter_FWDI_internal_allow 0 'udp dport 69 ct helper set "helper-tftp-udp" ct state new accept'
Error: INVALID_IPV: invalid argument: inet (choose from 'ipv4', 'ipv6', 'eb')
无需进行更多测试,此“功能”已记录在那里:
https://bugzilla.redhat.com/show_bug.cgi?id=1692964
还有:
https://github.com/firewalld/firewalld/issues/555
直接规则仍然使用iptables与nftables后端。注意事项与规则评估的顺序有关。
在另一个表中处理此问题
我不再认为这样做有什么意义防火墙命令,这将添加iptables规则nftables规则。添加一个独立的表会变得更清晰。它只会在知识产权系列,因为还将添加针对特定 IPv4 网络的过滤器(互联网也可以。
handletftp.nft
(待加载nft -f handletftp.nft
):
table ip handletftp
delete table ip handletftp
table ip handletftp {
ct helper helper-tftp {
type "tftp" protocol udp
}
chain sethelper {
type filter hook forward priority 0; policy accept;
ip saddr 192.168.1.0/24 ip daddr 10.0.10.10 udp dport 69 ct helper set "helper-tftp"
}
}
由于表不同,并且规则集永远不会被刷新,而是特定表被(原子地)删除并重新创建,因此这不会影响防火墙也不防火墙会影响它。
优先级并不重要:这个链是在之前还是之后遍历防火墙的锁链不会改变包裹的命运(仍然在防火墙)。无论顺序如何,如果数据包被防火墙它还将激活该流程的助手。
如果您选择使用nftables服务加载此表,您必须编辑它(例如systemctl edit --full nftables
:),因为除了加载一些可能不充分的默认规则外,它还会刷新全部停止或重新加载的规则,扰乱防火墙。
现在,TFTP 传输将工作并激活特定的帮助程序,可以conntrack
在传输期间通过运行两个命令进行检查:
# conntrack -E & conntrack -E expect
[1] 3635
[NEW] 300 proto=17 src=10.0.10.10 dst=10.0.10.11 sport=0 dport=56597 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=192.168.1.2 master-dst=10.0.10.10 sport=56597 dport=69 class=0 helper=tftp
[NEW] udp 17 29 src=192.168.1.2 dst=10.0.10.10 sport=56597 dport=69 [UNREPLIED] src=10.0.10.10 dst=10.0.10.11 sport=69 dport=56597 helper=tftp
[DESTROY] 299 proto=17 src=10.0.10.10 dst=10.0.10.11 sport=0 dport=56597 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=192.168.1.2 master-dst=10.0.10.10 sport=56597 dport=69 class=0 helper=tftp
[NEW] udp 17 30 src=10.0.10.10 dst=10.0.10.11 sport=42032 dport=56597 [UNREPLIED] src=192.168.1.2 dst=10.0.10.10 sport=56597 dport=42032
[UPDATE] udp 17 30 src=10.0.10.10 dst=10.0.10.11 sport=42032 dport=56597 src=192.168.1.2 dst=10.0.10.10 sport=56597 dport=42032
上面例子中的第 3 个 NEW 条目实际上被标记为 RELATED(这是 tftp 助手的全部作用:期望某种类型的数据包被视为相关),它将被防火墙接受。