Docker

nftables 白名單泊塢窗

  • November 6, 2021

我的機器上執行了兩個 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-USERand 的自定義鏈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 將呼叫這些掛鉤,無論它們來自iptablesnftables)。

此功能是與 Docker 互動時導致問題的主要原因。如果不了解它,人們會想知道為什麼同一內部橋接 LAN 中的容器不能再相互通信,無論它們是由 Docker 處理還是由 Docker 執行的其他東西(LXC、libvirt/QEMU …)處理。

這是Netfilter 和 General Networking 中的數據包流

Netfilter 和通用網路中的數據包流

因此,可以通過兩種不同的方式遍歷來自 ip/inet 系列中的iptablesnftables的單個鏈:從通常的路由路徑(綠色網路層欄位內的綠色框)以及橋接路徑(藍色鏈路層欄位內的綠色框)。該文件還告訴:

橋接數據包永遠不會進入第 1 層(鏈路層)之上的任何網路程式碼。因此,橋接的 IP 數據包/幀將永遠不會輸入 IP 程式碼。

因此可以保證一個數據包不會遍歷同一條鏈兩次,這是一種解脫。

iptablesnftables之間的互動

由於目標是使用nftables,因此必須知道如何將它們一起使用。

以下是我對此的回答的 Q/A:

總結一下:

  • iptablesnftables可以一起使用
  • nftables可以調整其優先級以在iptablesnftables之間具有確定性的評估順序(對於這種情況:nftablesiptables之後)
  • 無論何時/何地發生這種情況,丟棄的數據包都會被明確丟棄
  • 一個接受的數據包(將由iptables)在同一鉤子的下一個鏈中繼續評估(將是nftables的鏈)。
  • 數據包標記可用於在iptablesnftables之間傳遞消息

以通用方式解決此問題的方法

處理橋樑路徑

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

使用標記連結iptablesnftables

這個想法是使用標記將來自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的前向鏈發生在iptables filter/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 foobetween raw, mangle, or natand BARbetweenPREROUTINGPOSTROUTING視情況而定

  • 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.

警告

現在使用標記來處理這個問題,同時將標記用於其他事情變得更加困難,但小心謹慎並非不可能。例如,通過使用按位運算和帶有這些標記的遮罩。這在iptablesnftables中可用。甚至ip rule在使用標記作為選擇器時也接受遮罩。


重要的額外所需調整

Docker 添加nat規則以使用 iptables 的DNAT目標進行埠轉發。最後,所有暴露/發布的埠都被路由到容器而不是被主機接收。這意味著他們將使用如上所示的iptablesfilter/FORWARD以及(使用 OP 的規則集)nftables inet filter forward鏈,並且不會使用INPUT/ input

還有一些規則會妨礙主機的正確連接。

inet filter input

輸入路徑根本不會用於 Docker 的容器,除了通常用於本地主機訪問但 OP 已經接受的docker-proxyiif 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:全部接受它們或檢查正確的SLAACNDPRS, RA, NS, NA , …), ping … 處理。input``output

引用自:https://unix.stackexchange.com/questions/657545