前言
在上一章提到,安裝 Docker 後,Docker 會自動修改 iptables 和 NAT 規則,這可能導致我們手動配置的網路連線出現衝突或無法正常運作的情況。接下來我們來驗證這一點。
安裝 Docker 後再驗證
現在安裝完 Docker 後,我們進行 Ping 測試,檢查網絡連線是否正常:
1# 從 ns1 ping 位於 ns0 的 veth0 IP2$ sudo ip netns exec ns1 ping -c 2 172.18.0.23###4PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.5^C6--- 172.18.0.2 ping statistics ---72 packets transmitted, 0 received, 100% packet loss, time 1010ms
1# 從 Host ping 位於 ns0 的 veth0 IP2$ ping -c 2 172.18.0.23###4PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.5^C6--- 172.18.0.2 ping statistics ---72 packets transmitted, 0 received, 100% packet loss, time 1012ms
從結果來看,我們被拒於門外,驗證了 Docker 的安裝修改了 iptables。
iptables 是什麼
iptables
是一個用來管理網路封包過濾和 NAT 的工具,基於 Linux 內核的 netfilter
模組運作。簡單來說,它可以控制哪些網路流量被允許或阻擋,並處理地址轉換 (NAT)。其主要概念包括以下幾個部分:
-
表(Tables)
- filter:管理封包的允許或阻擋(預設的處理)。
- nat:進行地址轉換,適用於封包的來源和目的地址變更(如 SNAT、DNAT)。
- mangle:對封包內容進行修改,通常用於特殊處理(如變更 TOS)。
- raw:設定跳過連線追蹤,用於特定封包不進行追蹤。
-
鏈(Chains)
- INPUT:進入本機的封包。
- OUTPUT:從本機發出的封包。
- FORWARD:路由轉發的封包。
- PREROUTING:處理到達網卡前的封包。
- POSTROUTING:處理即將發出的封包。
-
規則(Rules) 每條規則定義匹配條件(如來源 IP、目的端口)及動作(如 ACCEPT、DROP、LOG)。
-
匹配(Match)與目標(Target)
- 匹配條件:依據協議、IP、端口等過濾封包。
- 目標動作:指定如何處理匹配的封包。
它運作的流程如下:
iptables 內建各表格與鏈的相關性 - 鳥哥
mangle 這個表很少用,省略它進一步精簡如下:
iptables 內建各表格與鏈的相關性(簡圖) - 鳥哥
filter table 的變化
還記得上一章中,我們準備了 network-test-1
, network-test-2
兩台機器,我們只在前者安裝 Docker。下面我們將用這兩台機器查詢 iptable 資料,作為對照組,這樣能更清楚地了解 Docker 對網絡環境的影響。
使用以下指令查詢 iptable:
1# 使用 -t flag 可以指定 table, 預設是 filter2sudo iptables -L --line-number
安裝 Docker 前,執行 iptables
結果如下:
1Chain INPUT (policy ACCEPT)2num target prot opt source destination3
4Chain FORWARD (policy ACCEPT)5num target prot opt source destination6
7Chain OUTPUT (policy ACCEPT)8num target prot opt source destination
安裝 Docker 後,執行 iptables
結果如下:
1Chain INPUT (policy ACCEPT)2num target prot opt source destination3
4Chain FORWARD (policy DROP)5num target prot opt source destination61 DOCKER-USER all -- anywhere anywhere72 DOCKER-ISOLATION-STAGE-1 all -- anywhere anywhere83 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED94 DOCKER all -- anywhere anywhere105 ACCEPT all -- anywhere anywhere116 ACCEPT all -- anywhere anywhere12
13Chain OUTPUT (policy ACCEPT)14num target prot opt source destination15
16 collapsed lines
16Chain DOCKER (1 references)17num target prot opt source destination18
19Chain DOCKER-ISOLATION-STAGE-1 (1 references)20num target prot opt source destination211 DOCKER-ISOLATION-STAGE-2 all -- anywhere anywhere222 RETURN all -- anywhere anywhere23
24Chain DOCKER-ISOLATION-STAGE-2 (1 references)25num target prot opt source destination261 DROP all -- anywhere anywhere272 RETURN all -- anywhere anywhere28
29Chain DOCKER-USER (1 references)30num target prot opt source destination311 RETURN all -- anywhere anywhere
iptables -L
指令省略了不少細節,讓我們改用 iptables-save
指令,可以呈現較完整的資訊:
1# iptables-save 預設會將所有 table 匯出,所以要使用 -t flag 過濾 filter table2sudo iptables-save -t filter
安裝 Docker 前,執行 iptables-save
結果如下:
1# Generated by iptables-save v1.8.8 (nf_tables) on Tue Nov 26 11:16:39 20242*filter3:INPUT ACCEPT [0:0]4:FORWARD ACCEPT [0:0]5:OUTPUT ACCEPT [0:0]6COMMIT7# Completed on Tue Nov 26 11:16:39 2024
安裝 Docker 後,執行 iptables-save
結果如下:
1# Generated by iptables-save v1.8.8 (nf_tables) on Tue Nov 26 11:13:50 20242*filter3:INPUT ACCEPT [0:0]4:FORWARD DROP [0:0]5:OUTPUT ACCEPT [0:0]6:DOCKER - [0:0]7:DOCKER-ISOLATION-STAGE-1 - [0:0]8:DOCKER-ISOLATION-STAGE-2 - [0:0]9:DOCKER-USER - [0:0]10-A FORWARD -j DOCKER-USER11-A FORWARD -j DOCKER-ISOLATION-STAGE-112-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT13-A FORWARD -o docker0 -j DOCKER14-A FORWARD -i docker0 ! -o docker0 -j ACCEPT15-A FORWARD -i docker0 -o docker0 -j ACCEPT7 collapsed lines
16-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-217-A DOCKER-ISOLATION-STAGE-1 -j RETURN18-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP19-A DOCKER-ISOLATION-STAGE-2 -j RETURN20-A DOCKER-USER -j RETURN21COMMIT22# Completed on Tue Nov 26 11:13:50 2024
分析
在安裝 Docker 之前和之後,iptables
的主要變化如下:
- 安裝 Docker 後,
FORWARD
鏈的策略由ACCEPT
變為DROP
,強化了對容器之間和外部的網絡流量控制。這樣的變化有效提升了隔離性和網絡安全性,但同時也可能導致某些應用程序的連接受阻,需要根據具體應用進行額外的配置。 - 增加了多個新鏈,如
DOCKER
、DOCKER-ISOLATION-STAGE-1
、DOCKER-USER
等,以管理 Docker 容器的網絡規則,這些新鏈幫助分離和控制容器之間的通信,確保不同應用之間的網絡隔離,減少潛在的安全風險。
接下來我們詳細分析這些變化:
安裝 Docker 前:
- 三個預設鏈(INPUT、FORWARD、OUTPUT)均設為 ACCEPT,表示所有封包均允許通過。
- 無其他自定義規則或鏈,屬於「默認開放」狀態。
- 缺乏細粒度控制,沒有針對性過濾或封包管理。
安裝 Docker 後:
-
FORWARD
鏈的策略從ACCEPT
改為DROP
- 改變:強制封包必須匹配 Docker 規則才能被轉發。
- 原因:
- 加強網路隔離,避免無限制的封包轉發。
- 僅允許來自容器的相關流量進行通信。
-
新增
DOCKER
鏈- 改變:容器的網路規則集中在
DOCKER
鏈管理。 - 原因:
- 簡化管理,動態添加和刪除容器相關規則。
- 確保容器與宿主機或外部網路的正常通信。
- 改變:容器的網路規則集中在
-
新增
DOCKER-ISOLATION-STAGE-1
和STAGE-2
- 改變:為不同的
bridge
網絡隔離流量。 - 原因:
- 保證來自不同 Docker 網橋(如
docker0
)的容器之間默認不互相通信。 - 用於安全隔離,避免未經授權的內部通信。
- 保證來自不同 Docker 網橋(如
- 改變:為不同的
-
新增
DOCKER-USER
鏈- 改變:在
FORWARD
前引入一層用戶自定義規則。 - 原因:
- 提供靈活性,允許用戶配置自定義防火牆規則,且不影響 Docker 自身規則。
- 改變:在
-
新增 Rule
FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
: 確保容器發起的連線能接收外部回應流量。FORWARD -o docker0 -j DOCKER
: 將容器相關的流量傳遞到DOCKER
鏈。FORWARD -i docker0 ! -o docker0 -j ACCEPT
: 允許容器發往外部的流量。FORWARD -i docker0 -o docker0 -j ACCEPT
: 允許同一網橋內部的容器通信。
FORWARD
鏈在修改後,已經變為預設 DROP
,然而我們 ping 來源和目的介面是自建的 docker1
,而不是安裝 Docker 時預設的 docker0
,所以封包在這個階段被丟掉了。
修改 FORWARD 讓封包可以過橋
需要注意的是,將策略修改為 ACCEPT 可能會使所有的轉發流量自動被允許,這在安全性方面可能會帶來風險,特別是在公開網絡中使用時。建議在更改策略後,根據實際需求進一步增加更細緻的防火牆規則來保護網絡安全。
使用以下指令修改預設策略:
1sudo iptables --policy FORWARD ACCEPT
查詢 iptables FORWARD 鏈的策略:
1$ sudo iptables -L2###3[...]4Chain FORWARD (policy ACCEPT)5[...]
再次測試 bridge 是否暢通:
1# 從 ns1 ping 位於 ns0 的 veth0 IP2$ sudo ip netns exec ns1 ping -c 2 172.18.0.23###4PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.564 bytes from 172.18.0.2: icmp_seq=1 ttl=127 time=0.061 ms664 bytes from 172.18.0.2: icmp_seq=2 ttl=127 time=0.050 ms7
8--- 172.18.0.2 ping statistics ---92 packets transmitted, 2 received, 0% packet loss, time 1065ms10rtt min/avg/max/mdev = 0.050/0.055/0.061/0.005 ms
容器之間的連線已經暢通。
不過容器與 Host 的問題依舊:
1# 從 Host ping 位於 ns0 的 veth0 IP2$ ping -c 2 172.18.0.23###4PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.5^C6--- 172.18.0.2 ping statistics ---72 packets transmitted, 0 received, 100% packet loss, time 1004ms
如果是對外網的連線呢?
1# 找到 Host 的 enX0 ip2$ ip -br addr3###4[...]5enX0 UP 172.31.39.53/20 metric 512 fe80::49d:2ff:fe88:529d/646[...]7######8# 從 ns1 ping 位於 Host 的 enX0 ip9$ sudo ip netns exec ns1 ping -c 2 172.31.39.5310###11ping: connect: Network is unreachable12######13# 從 ns1 ping 位於外部的 www.google.com14$ sudo ip netns exec ns1 ping -c 2 www.google.com15###1 collapsed line
16ping: www.google.com: Name or service not known
看起來,容器內對容器外的連線還沒有導通。
觀察封包流向
在 Linux 中,我們可以使用 tcpdump
網路封包分析工具,用來捕捉網路介面的封包,進行觀察。這次我們先用它,觀察容器間的封包流向。
首先,分別準備好四組指令和終端,分別監控 veth0
, veth0-br
, veth1
, veth1-br
四個網路介面:
1# 在 ns0 監控 veth0 的 ICMP 封包2sudo ip netns exec ns0 tcpdump -i veth0 -nn icmp3
4# 在 host 監控 veth0-br 的 ICMP 封包5sudo tcpdump -i veth0-br -nn icmp6
7# 在 host 監控 veth1-br 的 ICMP 封包8sudo tcpdump -i veth1-br -nn icmp9
10# 在 ns1 監控 veth1 的 ICMP 封包11sudo ip netns exec ns1 tcpdump -i veth1 -nn icmp
我們開啟第五個終端,執行以下指令測試:
1# 從 ns1 ping 位於 ns0 的 veth0 IP2sudo ip netns exec ns1 ping -c 1 172.18.0.2
結果如下:
我們透過錄下來的資訊和時間戳記,排序後可以歸納出,封包的走向是這樣的:
小結
本章我們驗證了 Docker 對於 iptables 的影響,並用偷吃步的方式讓容器間的封包可以正常傳遞,但依然沒有解決容器對外的封包傳遞問題。這部分的內容就留到下一章。我們下次見。