Firewalld

CentOS 8 作為帶有 nft 和 firewalld 的 NAT 路由器 - 如何讓它通過 TFTP?

  • November 13, 2020

我正在嘗試在隱藏在 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 助手的全部作用:期望某種類型的數據包將其視為相關),這將被防火牆接受。

引用自:https://serverfault.com/questions/1042106