封包過濾與防火牆

在 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::22001: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 位址,且不得提供主機連接埠。

如果在網路中設定了路由,則可以從任何遠端位址存取 natrouted 模式中映射的容器連接埠,除非 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

可以將 精靈設定 中的 iptablesip6tables 索引鍵設定為 false,但不建議大多數使用者使用此選項。它可能會破壞 Docker 引擎的容器網路。

所有容器的所有連接埠都可以從網路存取,並且不會從 Docker 主機 IP 位址映射任何連接埠。

無法完全阻止 Docker 建立 iptables 規則,事後建立規則非常複雜,超出這些說明的範圍。

與 firewalld 整合

如果您在將 iptables 選項設定為 true 的情況下執行 Docker,並且您的系統上啟用了 firewalldDocker 和 ufw

簡易防火牆

編輯此頁面

要求變更