與 Docker 的 Iptables

問題

為 Docker 容器配置 iptables 規則有點棘手。首先,你會認為經典防火牆規則應該可以解決問題。

例如,假設你已經配置了 nginx-proxy 容器+多個服務容器,以通過 HTTPS 公開一些個人 Web 服務。然後,這樣的規則應該只為 IP XXX.XXX.XXX.XXX 提供對 Web 服務的訪問。

$ iptables -A INPUT -i eth0 -p tcp -s XXX.XXX.XXX.XXX -j ACCEPT
$ iptables -P INPUT DROP

它不起作用,你的容器仍可供所有人使用。

實際上,Docker 容器不是主機服務。它們依賴於主機中的虛擬網路,主機充當此網路的閘道器。對於閘道器,路由流量不是由 INPUT 表處理,而是由 FORWARD 表處理,這使得規則在無效之前釋出。

但並非全部。事實上,當 Docker 守護程序開始發揮其關於容器網路連線的魔力時,會建立許多 iptables 規則。特別是,建立 DOCKER 表以通過將來自 FORWARD 表的流量轉發到此新表來處理有關容器的規則。

$ iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy DROP)
target     prot opt source               destination
DOCKER-ISOLATION  all  --  anywhere             anywhere
DOCKER     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere
DOCKER     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain DOCKER (2 references)
target     prot opt source               destination
ACCEPT     tcp  --  anywhere             172.18.0.4           tcp dpt:https
ACCEPT     tcp  --  anywhere             172.18.0.4           tcp dpt:http

Chain DOCKER-ISOLATION (1 references)
target     prot opt source               destination
DROP       all  --  anywhere             anywhere
DROP       all  --  anywhere             anywhere
RETURN     all  --  anywhere             anywhere

解決方案

如果你檢視官方文件( https://docs.docker.com/v1.5/articles/networking/) ,則會給出第一個解決方案,以限制 Docker 容器對一個特定 IP 的訪問。

$ iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

實際上,在 DOCKER 表的頂部新增規則是個好主意。它不會干擾 Docker 自動配置的規則,而且很簡單。但兩個主要缺點是:

  • 首先,如果你需要從兩個 IP 而不是一個 IP 訪問該怎麼辦?這裡只能接受一個 src IP,其他的將被刪除而沒有任何方法可以防止這種情況發生。
  • 第二,如果你的碼頭工人需要訪問網際網路怎麼辦?實際上沒有請求會成功,因為只有伺服器 8.8.8.8 可以響應它們。
  • 最後,如果你想新增其他邏輯怎麼辦?例如,允許任何使用者訪問你在 HTTP 協議上提供服務的 Web 伺服器,但將其他所有內容限制為特定 IP。

對於第一次觀察,我們可以使用 ipset 。我們不允許在上面的規則中允許一個 IP,而是允許來自預定義 ipset 的所有 IP。作為獎勵,可以更新 ipset 而無需重新定義 iptable 規則。

$ iptables -I DOCKER -i ext_if -m set ! --match-set my-ipset src -j DROP

對於第二個觀察,這是防火牆的規範問題:如果允許你通過防火牆聯絡伺服器,則防火牆應授權伺服器響應你的請求。這可以通過授權與已建立的連線相關的分組來完成。對於 docker 邏輯,它給出:

$ iptables -I DOCKER -i ext_if -m state --state ESTABLISHED,RELATED -j ACCEPT

最後一個觀察集中在一點:iptables 規則是必不可少的。實際上,在 DROP 規則之前,必須將接受某些連線(包括與 ESTABLISHED 連線有關的連線)的附加邏輯放在 DOCKER 表的頂部,否則所有剩餘連線都不匹配 ipset。

因為我們使用 iptable 的 -I 選項,它在表的頂部插入規則,所以必須按相反的順序插入以前的 iptables 規則:

// Drop rule for non matching IPs
$ iptables -I DOCKER -i ext_if -m set ! --match-set my-ipset src -j DROP
// Then Accept rules for established connections
$ iptables -I DOCKER -i ext_if -m state --state ESTABLISHED,RELATED -j ACCEPT 
$ iptables -I DOCKER -i ext_if ... ACCEPT // Then 3rd custom accept rule
$ iptables -I DOCKER -i ext_if ... ACCEPT // Then 2nd custom accept rule
$ iptables -I DOCKER -i ext_if ... ACCEPT // Then 1st custom accept rule

考慮到所有這些,你現在可以檢視說明此配置的示例。