封包過濾與防火牆
在 Linux 上,Docker 會建立 `iptables` 和 `ip6tables` 規則來實作網路隔離、埠發佈和過濾。
由於 Docker 橋接網路的正常運作需要這些規則,因此您不應修改 Docker 建立的規則。
但是,如果您在暴露於網際網路的主機上執行 Docker,您可能需要新增 iptables 策略來防止未經授權的存取容器或主機上執行的其他服務。本頁說明如何達成此目標,以及您需要注意的注意事項。
**注意**
Docker 會為橋接網路建立 `iptables` 規則。
不會為 `ipvlan`、`macvlan` 或 `host` 網路建立 `iptables` 規則。
Docker 和 iptables 鏈
在 `filter` 表中,Docker 將預設策略設定為 `DROP`,並建立以下自定義 `iptables` 鏈
DOCKER-USER
- 使用者定義規則的預留位置,這些規則將在 `DOCKER` 鏈中的規則之前處理。
DOCKER
- 根據執行中容器的埠轉發設定,判斷是否接受不屬於已建立連線一部分的封包的規則。
- `DOCKER-ISOLATION-STAGE-1` 和 `DOCKER-ISOLATION-STAGE-2`
- 用於將 Docker 網路彼此隔離的規則。
在 `FORWARD` 鏈中,Docker 新增了將與已建立連線無關的封包傳遞到這些自定義鏈的規則,以及接受屬於已建立連線一部分的封包的規則。
在 `nat` 表中,Docker 建立鏈 `DOCKER` 並新增規則來實作遮罩和埠映射。
在 Docker 規則之前新增 iptables 策略
這些自定義鏈中的規則所接受或拒絕的封包,將不會被附加到 `FORWARD` 鏈的使用者定義規則看到。因此,若要新增其他規則來過濾這些封包,請使用 `DOCKER-USER` 鏈。
比對請求的原始 IP 和埠
當封包到達 `DOCKER-USER` 鏈時,它們已經通過了目的地網路位址轉譯 (DNAT) 過濾器。這表示您使用的 `iptables` 旗標只能比對容器的內部 IP 位址和埠。
如果您想根據網路請求中的原始 IP 和埠來比對流量,您必須使用 `conntrack` iptables 擴充功能。例如
$ sudo iptables -I DOCKER-USER -p tcp -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$ sudo iptables -I DOCKER-USER -p tcp -m conntrack --ctorigdst 198.51.100.2 --ctorigdstport 80 -j ACCEPT
**重要**
使用 `conntrack` 擴充功能可能會導致效能降低。
埠發佈和映射
根據預設,對於 IPv4 和 IPv6,守護程式會封鎖對尚未發佈之埠的存取。已發佈的容器埠會映射到主機 IP 位址。為此,它使用 iptables 執行網路位址轉譯 (NAT)、埠位址轉譯 (PAT) 和遮罩。
例如,`docker run -p 8080:80 [...]` 會在 Docker 主機上任何位址的埠 8080 與容器的埠 80 之間建立映射。來自容器的輸出連線將使用 Docker 主機的 IP 位址進行遮罩。
限制對容器的外部連線
根據預設,允許所有外部來源 IP 連線到已發佈到 Docker 主機位址的埠。
若要僅允許特定 IP 或網路存取容器,請在 `DOCKER-USER` 過濾器鏈的頂端插入否定規則。例如,下列規則會捨棄來自除 `192.0.2.2` 以外所有 IP 位址的封包
$ iptables -I DOCKER-USER -i ext_if ! -s 192.0.2.2 -j DROP
您需要將 `ext_if` 變更為與主機的實際外部介面相對應。您可以改為允許來自來源子網路的連線。下列規則僅允許來自子網路 `192.0.2.0/24` 的存取
$ iptables -I DOCKER-USER -i ext_if ! -s 192.0.2.0/24 -j DROP
最後,您可以使用 --src-range
指定要接受的 IP 位址範圍(使用 --src-range
或 --dst-range
時,記得也要加上 -m iprange
)
$ iptables -I DOCKER-USER -m iprange -i ext_if ! --src-range 192.0.2.1-192.0.2.3 -j DROP
您可以將 -s
或 --src-range
與 -d
或 --dst-range
結合使用,以同時控制來源和目的地。例如,如果 Docker 主機具有 2001:db8:1111::2
和 2001:db8:2222::2
位址,您可以針對 2001:db8:1111::2
制定特定規則,並將 2001:db8:2222::2
保持開放。
iptables
很複雜。在 Netfilter.org HOWTO 有更多資訊。
直接路由
連接埠映射可確保已發佈的連接埠可在主機的網路位址上存取,這些位址可能可供任何外部用戶端路由。通常不會在主機網路中為存在於主機內的容器位址設定路由。
但是,尤其是在使用 IPv6 時,您可能更希望避免使用 NAT,而是安排外部路由到容器位址。
要從 Docker 主機外部存取橋接網路上的容器,您必須透過 Docker 主機上的位址設定到橋接網路的路由。這可以使用靜態路由、邊界閘道器協定 (BGP) 或任何其他適合您網路的方法來完成。
橋接網路驅動程式具有選項 com.docker.network.bridge.gateway_mode_ipv6=<nat|routed>
和 com.docker.network.bridge.gateway_mode_ipv4=<nat|routed>
。
預設值為 nat
,會為每個已發佈的容器連接埠設定 NAT 和偽裝規則。使用 routed
模式時,不會設定 NAT 或偽裝規則,但仍會設定 iptables
,以便只能存取已發佈的容器連接埠。
在 routed
模式中,不會使用 -p
或 --publish
連接埠映射中的主機連接埠,並且主機位址僅用於決定是否將映射套用至 IPv4 或 IPv6。因此,當映射僅套用至 routed
模式時,僅允許使用 0.0.0.0
或 ::1
位址,且不得提供主機連接埠。
如果在網路中設定了路由,則可以從任何遠端位址存取 nat
或 routed
模式中映射的容器連接埠,除非 Docker 主機的防火牆有額外的限制。
範例
建立適用於 IPv6 直接路由的網路,並為 IPv4 啟用 NAT
$ docker network create --ipv6 --subnet 2001:db8::/64 -o com.docker.network.bridge.gateway_mode_ipv6=routed mynet
建立具有已發佈連接埠的容器
$ docker run --network=mynet -p 8080:80 myimage
然後
- 只有容器連接埠 80 會開啟,適用於 IPv4 和 IPv6。如果有路由到容器的位址,並且主機的防火牆未封鎖存取,則可以從任何地方存取它。
- 對於 IPv6,使用
routed
模式,連接埠 80 將在容器的 IP 位址上開啟。連接埠 8080 不會在主機的 IP 位址上開啟,傳出的封包將使用容器的 IP 位址。 - 對於 IPv4,使用預設的
nat
模式,可以透過主機 IP 位址上的連接埠 8080 以及直接存取容器的連接埠 80。源自容器的連線將使用主機的 IP 位址進行偽裝。
在 docker inspect
中,此連接埠映射將如下所示。請注意,IPv6 沒有 HostPort
,因為它使用 routed
模式
$ docker container inspect <id> --format "{{json .NetworkSettings.Ports}}"
{"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"8080"},{"HostIp":"::","HostPort":""}]}
或者,要使映射僅限 IPv6,停用 IPv4 對容器連接埠 80 的存取,請使用未指定的 IPv6 位址 [::]
,並且不要包含主機連接埠號碼
$ docker run --network mynet -p '[::]::80'
設定容器的預設繫結位址
根據預設,當容器的連接埠未指定任何特定主機位址時,Docker 精靈會將已發佈的容器連接埠繫結到所有主機位址 (0.0.0.0
和 [::]
)。
例如,以下指令將連接埠 8080 發佈到主機上的所有網路介面卡,包括 IPv4 和 IPv6 位址,可能讓外部世界可以存取它們。
docker run -p 8080:80 nginx
您可以變更已發佈容器連接埠的預設繫結位址,讓它們預設只能由 Docker 主機存取。為此,您可以將精靈設定為使用迴路位址 (127.0.0.1
)。
警告
位於同一個 L2 區段中的主機(例如,連接到同一個網路交換器的olalar)可以連接到發佈到本機主機的連接埠。如需詳細資訊,請參閱 moby/moby#45610。
$ docker network create mybridge \ -o "com.docker.network.bridge.host_binding_ipv4=127.0.0.1"
**注意**
- 將預設繫結位址設定為
::
表示未指定主機位址的連接埠繫結將適用於主機上的任何 IPv6 位址。但是,0.0.0.0
表示任何 IPv4 或 IPv6 位址。- 變更預設繫結位址對 Swarm 服務沒有任何影響。Swarm 服務始終在
0.0.0.0
網路介面上公開。預設橋接器
要設定預設橋接網路的預設繫結,請在
daemon.json
設定檔中設定"ip"
索引鍵{ "ip": "127.0.0.1" }
這會將預設橋接網路上的已發佈容器連接埠的預設繫結位址變更為
127.0.0.1
。重新啟動精靈以使此變更生效。或者,您可以在啟動精靈時使用dockerd --ip
旗標。路由器上的 Docker
Docker 將
FORWARD
鏈的策略設定為DROP
。這將防止您的 Docker 主機充當路由器。如果您希望您的系統充當路由器,您必須將明確的
ACCEPT
規則新增到DOCKER-USER
鏈。例如$ iptables -I DOCKER-USER -i src_if -o dst_if -j ACCEPT
防止 Docker 操作 iptables
可以將 精靈設定 中的
iptables
或ip6tables
索引鍵設定為false
,但不建議大多數使用者使用此選項。它可能會破壞 Docker 引擎的容器網路。所有容器的所有連接埠都可以從網路存取,並且不會從 Docker 主機 IP 位址映射任何連接埠。
無法完全阻止 Docker 建立
iptables
規則,事後建立規則非常複雜,超出這些說明的範圍。與 firewalld 整合
如果您在將
iptables
選項設定為true
的情況下執行 Docker,並且您的系統上啟用了 firewalld