使用 iptables 轉發,同時正確保留源 IP
我有一個執行 Wireguard 的伺服器(因此需要
masquerade
)和一個在埠 2525 上執行的容器。我有以下
iptables
規則:iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 25 -j DNAT --to-destination 172.18.0.1:2525 iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
直接連接時
server:2525
,Docker 容器可以看到我的真實 IP 地址(1.2.3.4
)。連接到 portserver:25
時,Docker 容器看到的是由提供的本地 IPdocker network
:Apr 07 12:45:46 mx postfix/smtpd[87]: lost connection after CONNECT from unknown[172.18.0.1] Apr 07 12:45:46 mx postfix/smtpd[87]: disconnect from unknown[172.18.0.1] commands=0/0
如何確保 Docker 容器在連接到埠 25 時正確看到公共 IP 地址(而不僅僅是在連接到埠 2525 時)。
謝謝
# iptables -L -n -v -t nat Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 52300 3131K DNAT tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:25 to:172.18.0.1:2525 150K 8524K DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 2 120 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 3385 256K MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0 1733K 104M MASQUERADE all -- * !br-b147ffdbc9f3 172.18.0.0/16 0.0.0.0/0 0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:53 0 0 MASQUERADE udp -- * * 172.17.0.2 172.17.0.2 udp dpt:53 0 0 MASQUERADE tcp -- * * 172.18.0.2 172.18.0.2 tcp dpt:25 Chain DOCKER (2 references) pkts bytes target prot opt in out source destination 12 1419 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0 0 0 RETURN all -- br-b147ffdbc9f3 * 0.0.0.0/0 0.0.0.0/0 56 3192 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5354 to:172.17.0.2:53 0 0 DNAT udp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:5354 to:172.17.0.2:53 107 6020 DNAT tcp -- !br-b147ffdbc9f3 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:2525 to:172.18.0.2:25
# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 32:d0:56:15:0a:64 brd ff:ff:ff:ff:ff:ff altname enp0s3 altname ens3 inet 159.223.80.86/20 brd 159.223.95.255 scope global eth0 valid_lft forever preferred_lft forever inet 10.15.0.19/16 brd 10.15.255.255 scope global eth0:1 valid_lft forever preferred_lft forever inet6 2400:6180:0:d0::f57:6001/64 scope global valid_lft forever preferred_lft forever inet6 fe80::30d0:56ff:fe15:a64/64 scope link valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 32:dc:4a:e4:27:be brd ff:ff:ff:ff:ff:ff altname enp0s4 altname ens4 inet 10.130.244.15/16 brd 10.130.255.255 scope global eth1 valid_lft forever preferred_lft forever inet6 fe80::30dc:4aff:fee4:27be/64 scope link valid_lft forever preferred_lft forever 4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000 link/none inet 10.200.200.52/24 scope global wg0 valid_lft forever preferred_lft forever 5: wg1: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000 link/none inet 10.222.111.1/24 scope global wg1 valid_lft forever preferred_lft forever 6: br-b147ffdbc9f3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:46:21:70:c0 brd ff:ff:ff:ff:ff:ff inet 172.18.0.1/16 brd 172.18.255.255 scope global br-b147ffdbc9f3 valid_lft forever preferred_lft forever inet6 fe80::42:46ff:fe21:70c0/64 scope link valid_lft forever preferred_lft forever 7: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:66:22:41:91 brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:66ff:fe22:4191/64 scope link valid_lft forever preferred_lft forever 9: veth31eff9d@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether e6:fb:80:5d:c7:a3 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::e4fb:80ff:fe5d:c7a3/64 scope link valid_lft forever preferred_lft forever 19: veth01269f5@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-b147ffdbc9f3 state UP group default link/ether 36:f4:e7:43:5f:da brd ff:ff:ff:ff:ff:ff link-netnsid 2 inet6 fe80::34f4:e7ff:fe43:5fda/64 scope link valid_lft forever preferred_lft forever
只需讓 Docker 處理重定向,它是動態的,並且在添加、刪除或重新啟動容器時可能會發生變化。但請參閱下面的更新。
此重定向不應指向主機而不是容器的 172.18.0.1。當主機接收到這樣的連接時,它由
docker-proxy
哪個代理處理它到容器,在這個過程中失去了源 IP 地址。
docker-proxy
Docker 已經在規則集的最後一條規則中正確地通過 DNAT + 將該埠(除了主機本身,扮演這個角色的主機)路由到具有 172.18.0.2 地址的正在執行的容器。除了它被配置為使用埠 2525 而不是埠 25。107 6020 DNAT tcp -- !br-b147ffdbc9f3 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:2525 to:172.18.0.2:25
這應該通過 Docker 設置來解決,而不是手動iptables規則,這些規則在容器佈局更改時無法更改。由於埠 25 是特權埠,如果 Docker 正在無根執行,則需要額外的設置,請查看有關公開特權埠的文件。
更新(考慮到 OP 的評論): OP 目前無法使用
-p 25:25
,因為docker-proxy
與本地主機的 SMTP 伺服器發生衝突並競爭偵聽主機上的 25 埠。這就是 OP 進行初始(錯誤)iptables重定向的原因。一個可以:
- 通過將屬性設置為 false
docker-proxy
執行來全域禁用dockerd``userland-proxy
作為參數
--userland-proxy=false
或作為"userland-proxy": false
添加到/etc/docker/daemon.json
.然後,這將允許使用
docker run ... -p 25:25 ...
(如記錄)而不會發生衝突:主機將在到達 localhost 或 $HOSTNAME 時到達自身,遠端系統將到達容器,並且沒有“地址已在使用”將使主機的 SMTP 守護程序或 Docker 的容器失敗開始。
- 或者添加手動重定向(下面的冗長設置幾乎可以自動完成)
每當容器重新啟動時,它的(內部)IP 地址都存在更改的風險。所以必須計算這個。因此,對於
mx
使用命名網路命名的容器mx
和涉及單個 IP 地址的容器,可以如下所述完成。創建一個單獨的預路由鏈(這樣就可以刷新它而無需刷新其他任何東西)並首先呼叫它:
iptables -t nat -N mynat iptables -t nat -I PREROUTING -j mynat
可以通過程式方式檢索容器的 IP 地址(對於
mx
使用單個地址命名的容器的簡單情況):containerip=$(docker container inspect --format '{{.NetworkSettings.IPAddress}}' mx)
(或者可以使用
jq
:)containerip=$(docker container inspect mx | jq '.[].NetworkSettings.IPAddress'
查找網橋介面名稱更加複雜,或者至少我找不到僅使用
docker ... inspect
. 因此,在主機上找到它的 IP 地址並使用它查詢主機ip address
以僅找到設置了該特定 IP 地址的橋接介面(需要該jq
命令。)bridgeip=$(docker network inspect --format '{{(index .IPAM.Config 0).Gateway}}' mx) bridgeinterface=$(ip -json address show to "$bridgeip"/32 | jq -r '.[].ifname')
mynat
每次(重新)啟動容器時刷新並重新填充:iptables -t nat -F mynat iptables -t nat -A mynat ! -i "$bridgeinterface" -p tcp --dport 25 -j DNAT --to-destination "$containerip":25
這將適用於目前情況:
iptables -t nat -I mynat !-i br-b147ffdbc9f3 -p tcp –dport 25 -j DNAT –to-destination 172.18.0.2:25
為了確保 Docker 自己的防火牆規則不會阻止此類流量,請
FORWARD
從DOCKER-USER
鏈中的 filter/ 開始執行類似的操作。最初(如果從引導開始,您可能還必須先創建
DOCKER-USER
):iptables -N myforward iptables -I DOCKER-USER 1 -j myforward
然後以後每次(重新)啟動容器時:
iptables -F myforward iptables -A myforward -p tcp ! -i "$bridgeinterface" -d "$containerip" -p tcp --dport 25 -j ACCEPT
這將適用於目前情況:
iptables -A myforward -p tcp ! -i br-b147ffdbc9f3 -d 172.18.0.2 -p tcp --dport 25 -j ACCEPT
筆記:
- 為了簡化上述規則並避免一些計算,容器及其橋接網路可以使用固定 IP 地址啟動。例如,請參閱此 SO Q/A:Assign static IP to Docker container。
- 這裡還有一個 UL SE Q/A,我的回答是關於與 Docker 互動時的問題(它適用於nftables,但關於
DOCKER-USER
鍊或br_netfilter
橋互動的某些部分仍然很有趣):nftables whitelisting docker