CentOS 8 作為帶有 nft 和 firewalld 的 NAT 路由器 - 如何讓它通過 TFTP?
我正在嘗試在隱藏在 NAT 路由器後面的一個網路上設置 PXE 引導(需要 TFTP)。
我的問題與網路上的許多其他問題相似,但我發現的所有答案都適用於帶有 iptables 的 CentOS 7。我需要使用以 firewalld 和 nft 作為後端的 CentOS 8 來執行此操作。
無法對 TFTP 流量進行 NAT,因為 iptables 未將返回連接轉發到客戶端,儘管 TFTP 幫助程序創建了期望 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>
注意:內部區域的化妝舞會是一個錯誤。我刪除了它,但行為沒有改變。
區域漂移被禁用。
更新 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 埠。在“標準”基於 UDP 的協議(例如 DNS)中,響應來自伺服器偵聽的同一埠。
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 的答案對我來說是完全可以接受的,只要它不違反任何防火牆規則。
不工作的原因
看來firewalld可能適合處理防火牆本地服務,而不是路由服務。
因此,當使用 OP 中的區域文件(僅顯示規則,而不是此處的整個規則集)配置了 firewalld(在 CentOS 8 上)時, tftp設置將在最後添加這些 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
所以最後一個所謂的直接選項仍然是一個選項,所以一切都儲存在firewalld的配置中。唉,文件有點誤導:
警告:直接規則行為因 FirewallBackend 的值而異。請參閱firewalld.direct(5)中的警告。
不仔細閱讀會認為通過接受nftables規則
FirewallBackend=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
直接規則仍然使用帶有nftables後端的*iptables 。*CAVEAT 是關於規則評估的順序。
在另一個表中處理這個
我不再認為使用firewall-cmd執行此操作有什麼意義,它將在nftables規則中添加**iptables規則。添加一個獨立的表只會變得更乾淨。它只是在ip系列中,因為還將添加特定 IPv4 網路的過濾器(inet也可以)。
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" } }
由於表不同並且規則集永遠不會被刷新,而是特定表被(原子地)刪除並重新創建,這不會影響firewalld也不會影響它。
優先級無關緊要:在firewalld的鏈之前或之後遍歷該鏈不會改變數據包的命運(仍在firewalld手中)。無論順序如何,如果數據包被firewalld接受,它也將啟動此流的幫助程序。
如果您選擇使用nftables服務載入此表,則必須對其進行編輯(例如:)
systemctl edit --full nftables
,因為除了載入一些可能不合適的預設規則外,它還會在停止或重新載入時刷新所有規則,從而中斷**firewalld。現在,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
上面範例中的第三個 NEW 條目實際上被標記為 RELATED(這是 tftp 助手的全部作用:期望某種類型的數據包將其視為相關),這將被防火牆接受。