nftables 白名單泊塢窗
我的機器上執行了兩個 docker 容器,其中一個非常嚴格的 nftables 配置處於活動狀態。我想保持這種狀態,但將外部對 docker 容器的訪問列入白名單。
容器打開埠 80 和 6200。docker 服務在禁用 iptables 的情況下啟動。
以下是目前的防火牆配置,包括我的嘗試。
icmp
,ssh
,http
並且https
已經打開。對於 docker,只需要 http 埠 80 和應用程序特定埠 6200。我試圖允許對 docker 的訪問192.168.0.0/16
盡可能嚴格。table inet filter { chain input { type filter hook input priority 0; policy drop; iif lo accept iif eno2 icmp type echo-request accept iif eno2 ip 192.168.0.0/16 tcp dport 22 accept iif eno2 ip 192.168.0.0/16 tcp dport { http, https, 6200 } accept } chain forward { type filter hook forward priority 0; policy drop; } chain output { type filter hook output priority 0; policy drop; } }
我嘗試為 thr 介面添加其他規則
docker0
,但沒有任何成功。我懷疑我必須修改chain forward
?
雖然問題可能看起來很簡單,但它只不過是。擁有 Docker 總是會給系統中處理網路的其他部分帶來一些挑戰。一旦nftables被更廣泛地採用並在未來被 Docker 直接使用,特別是一旦 Docker 停止使用
br_netfilter
,事情可能會變得更簡單。如果您認為在 Docker 中使用nftables仍然值得,我將在下面介紹一種方法,該方法旨在讓 Docker 處理它的部分,並且在更改時不必在其他防火牆規則中複製 Docker 設置,就像使用新的公開埠啟動新容器一樣簡單,完成。
需要解決的問題
iptables
仍然需要目前(2021 年)Docker 仍然使用iptables並且僅使用iptables(它也可以使用firewalld,但只能使用帶有iptables後端的**firewalld。無論如何我都不會考慮這種情況)。因此,目前在使用 Docker 時無法擁有純nftables系統。**iptables可以或不重要的事實。
iptables-legacy``iptables-nft
以下是Docker 和 iptables的一些相關摘錄,對這種情況很有用:
Docker 安裝了兩個
iptables
名為DOCKER-USER
and 的自定義鏈DOCKER
,它確保傳入的數據包總是首先由這兩個鏈檢查。Docker 的所有 iptables 規則都添加到
DOCKER
鏈中。不要手動操作此鏈。如果您需要添加在 Docker 規則之前載入的規則,請將它們添加到DOCKER-USER
鏈中。這些規則在 Docker 自動創建的任何規則之前應用。吹毛求疵:實際上 Docker 會
-A DOCKER-USER -j RETURN
在啟動 docker 之前在其中添加規則,或者更好的是:在所有情況下都可以使用的插入。添加到
FORWARD
鏈中的規則(手動或通過另一個基於 iptables 的防火牆)在這些鏈之後進行評估。Docker 還將
FORWARD
鏈的策略設置為DROP
. 如果您的 Docker 主機還充當路由器,這將導致該路由器不再轉發任何流量。Docker 啟用 IP 轉發,但預設情況下將其設置為防火牆用於其他用途。
可以
iptables
在 Docker 引擎的配置文件中將 key 設置為 false/etc/docker/daemon.json
,但此選項不適合大多數使用者。完全阻止 Docker 創建 iptables 規則是不可能的,事後創建它們非常複雜,超出了這些說明的範圍。 將 iptables 設置為 false 很可能會破壞 Docker 引擎的容器網路。無法避免擁有iptables。
br_netfilter
此外,Docker 還載入核心模組
br_netfilter
以設置此屬性:# sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-iptables = 1
因此,橋接幀(此處 IPv4 類型幀暫時轉換為 IPv4數據包)由iptables 和nftables過濾(即使沒有明確記錄,nftables就像iptables掛鉤到 Netfilter 和 Netfilter 將呼叫這些掛鉤,無論它們來自iptables或nftables)。
此功能是與 Docker 互動時導致問題的主要原因。如果不了解它,人們會想知道為什麼同一內部橋接 LAN 中的容器不能再相互通信,無論它們是由 Docker 處理還是由 Docker 執行的其他東西(LXC、libvirt/QEMU …)處理。
這是Netfilter 和 General Networking 中的數據包流:
因此,可以通過兩種不同的方式遍歷來自 ip/inet 系列中的iptables或nftables的單個鏈:從通常的路由路徑(綠色網路層欄位內的綠色框)以及橋接路徑(藍色鏈路層欄位內的綠色框)。該文件還告訴:
橋接數據包永遠不會進入第 1 層(鏈路層)之上的任何網路程式碼。因此,橋接的 IP 數據包/幀將永遠不會輸入 IP 程式碼。
因此可以保證一個數據包不會遍歷同一條鏈兩次,這是一種解脫。
iptables和nftables之間的互動
由於目標是使用nftables,因此必須知道如何將它們一起使用。
以下是我對此的回答的 Q/A:
總結一下:
- iptables和nftables可以一起使用
- nftables可以調整其優先級以在iptables和nftables之間具有確定性的評估順序(對於這種情況:nftables在iptables之後)
- 無論何時/何地發生這種情況,丟棄的數據包都會被明確丟棄
- 一個接受的數據包(將由iptables)在同一鉤子的下一個鏈中繼續評估(將是nftables的鏈)。
- 數據包標記可用於在iptables和nftables之間傳遞消息
以通用方式解決此問題的方法
處理橋樑路徑
ip/inet 系列中的nftables規則應避免在橋接路徑中執行任何操作。如果沒有 Docker 啟動
br_netfilter
,甚至都不需要考慮這一點。檢測是否在 ip/inet 系列的橋接路徑應該留給iptables來避免讓nftables來處理這個問題並保持通用,無論是否安裝了 Docker。使用iptables執行此操作也比使用ip/inet 系列中的nftables更容易,因為有特定的iptables-m physdev --physdev-is-bridged
測試:
[!] --physdev-is-bridged
如果數據包正在被橋接,因此沒有被路由,則匹配。這僅在 FORWARD 和 POSTROUTING 鏈中有用。
br_netfilter
請注意,如果 Docker 尚未完成此匹配,則此匹配取決於並載入:需要解決由br_netfilter
,引起的問題br_netfilter
!使用標記連結iptables和nftables
這個想法是使用標記將來自iptables的消息傳遞給nftables,以區分情況:
- 規則評估發生在橋接路徑而不是路由路徑中
總是接受這種情況。
- 數據包被 Docker 接受
可以添加進一步的限制,但大多接受這種情況。
- 數據包被 Docker 忽略
使用不必考慮 Docker 存在的普通nftables規則。
- 數據包因任何原因在iptables中被 DROP-ed
這是一個沒有實際意義的案例,nftables不會看到這個數據包,也不需要或可以做任何事情。
iptables
如果在 Docker 啟動之前完成,請創建過濾器鏈
DOCKER-USER
:iptables -N DOCKER-USER
如果之後完成,Docker 將創建它。
添加一條規則,在 Docker 評估之前在
DOCKER
鏈中標記數據包,並使用不同的標記(如前所述在此處插入它們,但對它們進行編號以保持自然順序,這在此處很重要):iptables -I DOCKER-USER 1 -j MARK --set-mark 0xd0cca5e iptables -I DOCKER-USER 2 -m physdev --physdev-is-bridged -j MARK --set-mark 0x10ca1
0x10ca1 和 0xd0cca5e 是任意選擇的值。
附加(在 Docker 執行之前或之後,效果相同,因為 Docker 總是
DOCKER
在之前插入其鏈)一個最終規則,僅當它是暫定的 Docker 評估標記時才重置數據包的標記,並添加一個最終ACCEPT
規則以覆蓋Docker 的預設DROP
策略集在FORWARD
鏈上:這個想法是將與 Docker 無關的數據包的進一步評估推遲到nftables。iptables -A FORWARD -m mark --mark 0xd0cca5e -j MARK --set-mark 0 iptables -A FORWARD -j ACCEPT
表格
將優先級值更改為
inet filter forward
略大於NF_IP_PRI_FILTER
(0) 的值,例如 10 以確保nftables的前向鏈發生在iptablesfilter/FORWARD
之後,以尊重此年表。OP 規則集中的基本鏈線應更改為:chain forward { type filter hook forward priority 0; policy drop;
至:
chain forward { type filter hook forward priority 10; policy drop;
通過檢查數據包上的標記,可以在*nftables中檢測到前面描述的 4 種情況。*添加
counter
表達式以幫助調試。
- 標記 0x10ca1 :橋接路徑
添加網橋路徑直通規則:
nft add rule inet filter forward meta mark 0x10ca1 counter accept
標記 0xd0cca5e:Docker 案例
- 創建一個正常/使用者鏈來處理 Docker 案例並添加一個呼叫它的規則:
nft add chain inet filter dockercase nft add rule inet filter forward meta mark 0xd0cca5e counter jump dockercase
- 添加關於 Docker 的附加限制,但預設接受
例如,限制從eno2介面到達的傳入數據包僅在來自 192.168.0.0/16 內的私有地址時才被接受:
nft add rule inet filter dockercase iif eno2 ip saddr != 192.168.0.0/16 counter drop nft add rule inet filter dockercase counter accept
無標記:與 Docker 無關的一般情況
添加無需考慮 Docker 的存在即可完成的任何操作,包括什麼都沒有並具有預設的刪除策略,否則可能從通常的開始
ct state related,established accept
- (無數據包:丟棄在iptables中,非大小寫)
上面的例子變成:
... chain forward { type filter hook forward priority 10; policy drop; meta mark 0x10ca1 counter accept meta mark 0xd0cca5e counter jump dockercase } chain dockercase { iif eno2 ip saddr != 192.168.0.0/16 counter drop counter accept } ...
實現通用處理
埠 80 和 6200 不必再出現在nftables規則中。如果使用 Docker 命令添加需要公開新埠的新容器,則無需在nftables中執行任何操作:感謝標記,它已經被處理好了。
添加更多鏈
仍然由於
br_netfilter
’s 的影響,如果任何其他基本nftables與該屬性連結hook forward
或包含刪除規則或更有用的是,在不使用圖 7b 下方連結中hook postrouting
描述的技巧的情況下更改規則(nat…),那麼相同類型的排列具有要完成:
- 它的優先級值應該高於iptables的等效鏈優先級
- 這樣的iptables等效鏈(除了
filter/FORWARD
已經完成的DOCKER-USER
)應該收到:iptables -t foo -I BAR -m physdev –physdev-is-bridged -j MARK –set-mark 0x10ca1
with
foo
betweenraw
,mangle
, ornat
andBAR
betweenPREROUTING
或POSTROUTING
視情況而定
- nftables鏈的第一條規則應該是:
meta mark 0x10ca1 accept
- 如果鏈的策略再次出現
drop
,它可能應該再次使用 0xd0cca5e 標記從規則中包含使用者/正常鏈跳轉,如前所述。對於
hook prerouting
,有關--physdev-is-bridged
說明這可能不起作用的文件PREROUTING
:永遠不要在那裡使用預設的丟棄策略。無論如何,對於hook prerouting
某些情況,還不能0xd0cca5e
繼承任何標記filter/FORWARD
,但僅使用iptables也是如此:PREROUTING 無法預見以後會發生什麼。如果你真的想在網橋級別做點什麼,只需在網橋家族中使用nftables,不要依賴從網橋路徑呼叫的 ip/inet 家族的這種特殊情況,因為
br_netfilter
.警告
現在使用標記來處理這個問題,同時將標記用於其他事情變得更加困難,但小心謹慎並非不可能。例如,通過使用按位運算和帶有這些標記的遮罩。這在iptables和nftables中可用。甚至
ip rule
在使用標記作為選擇器時也接受遮罩。重要的額外所需調整
Docker 添加nat規則以使用 iptables 的DNAT目標進行埠轉發。最後,所有暴露/發布的埠都被路由到容器而不是被主機接收。這意味著他們將使用如上所示的iptables鏈
filter/FORWARD
以及(使用 OP 的規則集)nftablesinet filter forward
鏈,並且不會使用INPUT
/input
。還有一些規則會妨礙主機的正確連接。
inet filter input
輸入路徑根本不會用於 Docker 的容器,除了通常用於本地主機訪問但 OP 已經接受的docker-proxy
iif lo accept
情況,因此不必在此答案中進一步處理。這裡不應該出現任何關於 Docker 的內容:對容器埠 80 和 6200 的引用變得無用,應該被刪除。然後,與 Docker 無關,輸入鏈錯過了有狀態規則。沒有它,來自主機輸出的返回流量(DNS 查詢回复、ping回复、下載升級……)將失敗。用這個:
chain input { type filter hook input priority 0; policy drop; ct state related,established accept iif lo accept iif eno2 icmp type echo-request accept iif eno2 ip 192.168.0.0/16 tcp dport 22 accept iif eno2 ip 192.168.0.0/16 tcp dport 443 accept }
輸入路徑仍可能需要Docker 本身(而不是其容器)的附加規則:可能需要規則以允許遠端訪問 Docker API(如果安全考慮允許)或Docker swarm使用的 VxLAN 等各種功能。
inet filter output
同樣,OP 的
inet filter output
鏈的丟棄策略會終止主機連接(無法啟動DNS查詢、ping請求或下載等)。應該有一個policy accept
,或者應該添加來自主機本身的所需傳出流量的例外。該鏈應至少包括以下內容:chain output { type filter hook output priority 0; policy drop; ct state related,established accept oif lo accept udp dport { 53, 123 } accept tcp dport { 53, 80, 443 } accept icmp type echo-request accept }
容器的數據包不是由這些鏈評估的,而是由
forward
鏈評估的,不會受到限制。IPv6
在未正確啟用 ICMPv6 的情況下使用inet系列會阻止任何 IPv6 連接,因為 IPv6 不依賴(幾乎從未防火牆)ARP,而是依賴 ICMPv6 來實現鏈路本地連接。要麼使用
ip
族(並為表使用過濾器以外的其他名稱以避免與 iptables-nft 發生任何衝突)或正確處理 ICMPv6:全部接受它們或檢查正確的SLAAC(NDP:RS, RA, NS, NA , …), ping … 處理。input``output