與 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
考慮到所有這些,你現在可以檢視說明此配置的示例。