我在 lxc 容器中安装了一个 Web 服务器。借助以下 iptable 规则,外界可以通过其 URL 访问此 Web 服务器:
sudo iptables -t nat -A PREROUTING -i eno1 -p tcp --dport 80 -j DNAT --to 10.42.XXX.XXX:80
我的问题是,当我尝试访问我的网络服务器时来自 lxc 容器网络服务器本身,它不起作用。
curl http://my.url.com
curl: (7) Failed to connect to my.url.com port 80: Connection refused
请注意,它在另一台计算机上也能正常运行。
我尝试添加另一条 iptable 规则:
sudo iptables -t nat -A PREROUTING -i lxcbr0 -p tcp --dport 80 -j DNAT --to 10.42.XXX.XXX:80
我得到:
curl http://my.url.com
curl: (7) Failed to connect to my.url.com port 80: Connection timed out
答案1
添加第二条 PREROUTING 规则后,容器会收到镜像到自身的请求,其源 IP 为源(以及目标)。出于各种原因,这种做法行不通:rp_filter 触发、网络堆栈不知道此连接尝试(它将其发送到 my.url.com,而不是自身)等。如果同一 LAN 中有第二个容器执行请求,则仍会存在非对称路由 + NAT 问题,从而导致其无法正常工作。
如果不改变,那就行不通了还源地址。所以最后必须做双重NAT。
用这条规则来完成已经完成的事情:
sudo iptables -t nat -A POSTROUTING -o lxcbr0 -s 10.42.XXX.XXX -j SNAT 10.43.XXX.XXX
它将正常工作,因为唯一的 IP 10.42.XXX.XXX 是即将lxcbr0 而不是从是上一个 PREROUTING DNAT 规则触发的时间。请注意,现在是 43,不再是 42,而是 10.43.XXX.XXX。在日志中仍然很容易知道请求来自哪里,因为那将在本例中保留。任何 IP 都可以,无论是公共的、私有的、现有的还是不存在的,甚至属于 LXC 主机(例如:my.url.com),只要它不在同一个 LAN 并且由 LXC 主机路由。
作为一种变体,可以使用 NETMAP 对整个子网进行 NAT,从而允许 lxcbr0 后面的其他容器执行相同的请求,而不再存在路由问题,并且仍然具有有用的日志。因此,对于 /24,以前的规则将是(OP 的信息不够精确,无法猜测 LAN 网络掩码是什么。对于 /16,将下面的规则替换为 10.42.0.0/16 和 10.43.0.0/16):
sudo iptables -t nat -A POSTROUTING -o lxcbr0 -s 10.42.XXX.0/24 -j NETMAP 10.43.XXX.0/24
还有一个问题:使用 OP 的第二条规则进行测试,容器无法再在互联网上执行 http 请求,因为每个请求都会返回到 10.42.XXX.XXX。因此,最终使用这 2 条规则重写并分解总共 3 条规则可能会更有用:
sudo iptables -t nat -A PREROUTING -d my.url.com -p tcp --dport 80 -j DNAT --to 10.42.XXX.XXX
sudo iptables -t nat -A POSTROUTING -o lxcbr0 -s 10.42.XXX.0/24 -j NETMAP --to 10.43.XXX.0/24
完成此操作后,即可从 LXC 执行相同的请求主持人本身:
sudo iptables -t nat -A OUTPUT -d my.url.com -p tcp --dport 80 -j DNAT --to 10.42.XXX.XXX
更复杂的情况可能需要在 PREROUTING 中标记数据包并在 POSTROUTING 中匹配标记,但这里不需要。