在上一章提到,安裝 Docker 後,Docker 會自動修改 iptables 和 NAT 規則,這可能導致我們手動配置的網路連線出現衝突或無法正常運作的情況。接下來我們來驗證這一點。
現在安裝完 Docker 後,我們進行 Ping 測試,檢查網絡連線是否正常:
# 從 ns1 ping 位於 ns0 的 veth0 IP
$ sudo ip netns exec ns1 ping -c 2 172.18.0.2
###
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
^C
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1010ms
# 從 Host ping 位於 ns0 的 veth0 IP
$ ping -c 2 172.18.0.2
###
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
^C
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1012ms
從結果來看,我們被拒於門外,驗證了 Docker 的安裝修改了 iptables。
iptables
是一個用來管理網路封包過濾和 NAT 的工具,基於 Linux 內核的 netfilter
模組運作。簡單來說,它可以控制哪些網路流量被允許或阻擋,並處理地址轉換 (NAT)。其主要概念包括以下幾個部分:
表(Tables)
鏈(Chains)
規則(Rules)
每條規則定義匹配條件(如來源 IP、目的端口)及動作(如 ACCEPT、DROP、LOG)。
匹配(Match)與目標(Target)
它運作的流程如下:
iptables 內建各表格與鏈的相關性 - 鳥哥
mangle 這個表很少用,省略它進一步精簡如下:
iptables 內建各表格與鏈的相關性(簡圖) - 鳥哥
還記得上一章中,我們準備了 network-test-1
, network-test-2
兩台機器,我們只在前者安裝 Docker。下面我們將用這兩台機器查詢 iptable 資料,作為對照組,這樣能更清楚地了解 Docker 對網絡環境的影響。
使用以下指令查詢 iptable:
# 使用 -t flag 可以指定 table, 預設是 filter
sudo iptables -L --line-number
安裝 Docker 前,執行 iptables
結果如下:
Chain INPUT (policy ACCEPT)
num target prot opt source destination
Chain FORWARD (policy ACCEPT)
num target prot opt source destination
Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
安裝 Docker 後,執行 iptables
結果如下:
Chain INPUT (policy ACCEPT)
num target prot opt source destination
Chain FORWARD (policy DROP)
num target prot opt source destination
1 DOCKER-USER all -- anywhere anywhere
2 DOCKER-ISOLATION-STAGE-1 all -- anywhere anywhere
3 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
4 DOCKER all -- anywhere anywhere
5 ACCEPT all -- anywhere anywhere
6 ACCEPT all -- anywhere anywhere
Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
Chain DOCKER (1 references)
num target prot opt source destination
Chain DOCKER-ISOLATION-STAGE-1 (1 references)
num target prot opt source destination
1 DOCKER-ISOLATION-STAGE-2 all -- anywhere anywhere
2 RETURN all -- anywhere anywhere
Chain DOCKER-ISOLATION-STAGE-2 (1 references)
num target prot opt source destination
1 DROP all -- anywhere anywhere
2 RETURN all -- anywhere anywhere
Chain DOCKER-USER (1 references)
num target prot opt source destination
1 RETURN all -- anywhere anywhere
iptables -L
指令省略了不少細節,讓我們改用 iptables-save
指令,可以呈現較完整的資訊:
# iptables-save 預設會將所有 table 匯出,所以要使用 -t flag 過濾 filter table
sudo iptables-save -t filter
安裝 Docker 前,執行 iptables-save
結果如下:
# Generated by iptables-save v1.8.8 (nf_tables) on Tue Nov 26 11:16:39 2024
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
# Completed on Tue Nov 26 11:16:39 2024
安裝 Docker 後,執行 iptables-save
結果如下:
# Generated by iptables-save v1.8.8 (nf_tables) on Tue Nov 26 11:13:50 2024
*filter
:INPUT ACCEPT [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:DOCKER - [0:0]
:DOCKER-ISOLATION-STAGE-1 - [0:0]
:DOCKER-ISOLATION-STAGE-2 - [0:0]
:DOCKER-USER - [0:0]
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
-A DOCKER-ISOLATION-STAGE-1 -j RETURN
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
-A DOCKER-USER -j RETURN
COMMIT
# Completed on Tue Nov 26 11:13:50 2024
在安裝 Docker 之前和之後,iptables
的主要變化如下:
FORWARD
鏈的策略由 ACCEPT
變為 DROP
,強化了對容器之間和外部的網絡流量控制。這樣的變化有效提升了隔離性和網絡安全性,但同時也可能導致某些應用程序的連接受阻,需要根據具體應用進行額外的配置。DOCKER
、DOCKER-ISOLATION-STAGE-1
、DOCKER-USER
等,以管理 Docker 容器的網絡規則,這些新鏈幫助分離和控制容器之間的通信,確保不同應用之間的網絡隔離,減少潛在的安全風險。接下來我們詳細分析這些變化:
安裝 Docker 前:
安裝 Docker 後:
FORWARD
鏈的策略從 ACCEPT
改為 DROP
新增 DOCKER
鏈
DOCKER
鏈管理。新增 DOCKER-ISOLATION-STAGE-1
和 STAGE-2
bridge
網絡隔離流量。docker0
)的容器之間默認不互相通信。新增 DOCKER-USER
鏈
FORWARD
前引入一層用戶自定義規則。新增 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
,所以封包在這個階段被丟掉了。
需要注意的是,將策略修改為 ACCEPT 可能會使所有的轉發流量自動被允許,這在安全性方面可能會帶來風險,特別是在公開網絡中使用時。建議在更改策略後,根據實際需求進一步增加更細緻的防火牆規則來保護網絡安全。
使用以下指令修改預設策略:
sudo iptables --policy FORWARD ACCEPT
查詢 iptables FORWARD 鏈的策略:
$ sudo iptables -L
###
[...]
Chain FORWARD (policy ACCEPT)
[...]
再次測試 bridge 是否暢通:
# 從 ns1 ping 位於 ns0 的 veth0 IP
$ sudo ip netns exec ns1 ping -c 2 172.18.0.2
###
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=127 time=0.061 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=127 time=0.050 ms
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1065ms
rtt min/avg/max/mdev = 0.050/0.055/0.061/0.005 ms
容器之間的連線已經暢通。
不過容器與 Host 的問題依舊:
# 從 Host ping 位於 ns0 的 veth0 IP
$ ping -c 2 172.18.0.2
###
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
^C
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1004ms
如果是對外網的連線呢?
# 找到 Host 的 enX0 ip
$ ip -br addr
###
[...]
enX0 UP 172.31.39.53/20 metric 512 fe80::49d:2ff:fe88:529d/64
[...]
######
# 從 ns1 ping 位於 Host 的 enX0 ip
$ sudo ip netns exec ns1 ping -c 2 172.31.39.53
###
ping: connect: Network is unreachable
######
# 從 ns1 ping 位於外部的 www.google.com
$ sudo ip netns exec ns1 ping -c 2 www.google.com
###
ping: www.google.com: Name or service not known
看起來,容器內對容器外的連線還沒有導通。
在 Linux 中,我們可以使用 tcpdump
網路封包分析工具,用來捕捉網路介面的封包,進行觀察。這次我們先用它,觀察容器間的封包流向。
首先,分別準備好四組指令和終端,分別監控 veth0
, veth0-br
, veth1
, veth1-br
四個網路介面:
# 在 ns0 監控 veth0 的 ICMP 封包
sudo ip netns exec ns0 tcpdump -i veth0 -nn icmp
# 在 host 監控 veth0-br 的 ICMP 封包
sudo tcpdump -i veth0-br -nn icmp
# 在 host 監控 veth1-br 的 ICMP 封包
sudo tcpdump -i veth1-br -nn icmp
# 在 ns1 監控 veth1 的 ICMP 封包
sudo ip netns exec ns1 tcpdump -i veth1 -nn icmp
我們開啟第五個終端,執行以下指令測試:
# 從 ns1 ping 位於 ns0 的 veth0 IP
sudo ip netns exec ns1 ping -c 1 172.18.0.2
結果如下:
我們透過錄下來的資訊和時間戳記,排序後可以歸納出,封包的走向是這樣的:
本章我們驗證了 Docker 對於 iptables 的影響,並用偷吃步的方式讓容器間的封包可以正常傳遞,但依然沒有解決容器對外的封包傳遞問題。這部分的內容就留到下一章。我們下次見。