我有一台运行 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,因为 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 端口的监听权。这就是初始(错误)的原因iptablesOP 进行了重定向。
你可以选择以下方式:
docker-proxy
通过将dockerd
属性userland-proxy
设置为 false 来全局禁用无论是作为范围
--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 问答,里面有我关于与 Docker 交互时遇到的问题的回答(它适用于nftables
DOCKER-USER
但关于链或桥相互作用的某些部分br_netfilter
仍然很有趣):nftables 将 docker 列入白名单