![使用iptables轉發,同時妥善保留來源IP](https://rvso.com/image/776289/%E4%BD%BF%E7%94%A8iptables%E8%BD%89%E7%99%BC%EF%BC%8C%E5%90%8C%E6%99%82%E5%A6%A5%E5%96%84%E4%BF%9D%E7%95%99%E4%BE%86%E6%BA%90IP.png)
我有一台運行 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
)。連接到連接埠時server:25
,Docker 容器會看到由下列提供的本機 IP docker 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 時(而不僅僅是在連接到連接埠 2525 時)正確地看到公用 IP 位址。
謝謝
# 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
答案1
只需讓 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。這就是最初(錯誤)的原因iptables重定向是由 OP 進行的。
一個人可以:
透過將屬性設為 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 --到目的地 172.18.0.2:25
為了確保 Docker 自己的防火牆規則不會阻止此類流量,請從 filter/ 開始執行類似的
FORWARD
操作DOCKER-USER
鏈。最初(如果從啟動,您可能還必須先建立
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:為 Docker 容器指派靜態 IP。
這裡還有一個 UL SE Q/A,其中包含我關於與 Docker 互動時出現的問題的答案(它適用於nftables但關於鏈條的一些部分
DOCKER-USER
或br_netfilter
仍然令人感興趣):nftables 白名單 docker