iptables:将SNAT与OpenVPN的networking重新映射相结合

[为长长的序曲道歉; 一半的问题。]

我有一个工作的OpenVPN设置,VPN服务器将路由返回到一个客户端(以下称为“路由器”),然后可以将自己的子网暴露给运行服务器的计算机以及运行VPN客户端的其他计算机。 这是通过使路由器使用iptablesSNAT目标来实现的。 例如,假设VPN服务器和其他不起眼的客户端位于10.0.77.0/24networking上,那么VPN会创build一个覆盖192.168.252.0/24的tun0接口,而私有子网为192.168.33.0/24。 OpenVPN服务器configuration(除其他外)

 client-to-client route 192.168.33.0 255.255.255.0 push "route 192.168.33.0 255.255.255.0" 

当Linux“路由器”机器,192.168.33.10让我们说,连接到VPN它得到了一个路由,所以它的表看起来像

 192.168.33.0 * 255.255.255.0 U 0 0 0 eth1 192.168.252.0 192.168.252.5 255.255.255.0 UG 0 0 0 tun0 192.168.252.5 * 255.255.255.255 UH 0 0 0 tun0 

然后将其configuration为运行

 sysctl -w net.ipv4.ip_forward=1 iptables -t nat -A POSTROUTING -s 192.168.252.0/24 -j SNAT --to-source 192.168.33.10 

它也可以添加一些iptables规则来创build一个防火墙,但是以上就足以让运行OpenVPN服务器(或另一个客户端)的机器连接到,比如192.168.33.11:数据包通过tun0发送到路由器,路由器使用SNAT将源IP设置为自己的192.168.33.10,然后将数据包转发给其兄弟机器192.168.33.11。 回复数据包发送到路由器,然后通过隧道转发回来,一切都很好。 所以例如在192.168.33.11我可以

 nc -l localhost 9999 

并从10.0.77.13(一些其他的VPN客户端),我可以

 nc 192.168.33.11 9999 

并build立联系。 到现在为止还挺好。

请注意, 任何一个networking上的物理路由器设备都不会发生更改 ; 引号(运行VPN客户端的随机机器)中的“路由器”需要使用SNAT,以便其networking中的其他机器能够通过VPN发回应答包。 对于这个问题,我不是“控制”任何一个networking:我只能添加一些机器自己的路由规则和VPN客户端或服务器。

现在的问题是:让我们说两个networking(在公共互联网上)实际上在实际范围内重叠。 所以在这个例子中,私有子网不是192.168.33.11,而是10.0.77.0/24。 让我们假设重新编号的networking根本不是一个选项。 因此,除了使用SNAT来允许路由器转发数据包之外,从OpenVPN服务器的angular度来看,我需要将路由器的专用networking重新映射到不同的IP范围。 假设我select10.0.78.0/24作为虚拟networking范围:

 route 10.0.78.0 255.255.255.0 push "route 10.0.78.0 255.255.255.0" 

并且从OpenVPN服务器机器上,我想要连接到10.0.78.11去通过隧道,并像以前一样用SNAT处理,但是我也希望路由器子网中的目的地址是10.0.77.11。 我如何configurationiptables来做到这一点?

NETMAP目标看起来像是我想要的,但我无法得到它的工作:

 iptables -t nat -A PREROUTING -i tun0 -j NETMAP --to 10.0.77.0/24 iptables -t nat -A POSTROUTING -s 192.168.252.0/24 -j SNAT --to-source 10.0.77.10 

在OpenVPN服务器机器上,我可以在添加NETMAP目标时ping到10.0.78.10(即路由器的重映射地址),所以它正在做一些事情。

 tcpdump -i tun0 

在此ping期间运行在路由器上显示ICMP echo requestICMP echo reply按预期。 但是,如果我尝试ping 10.0.78.11(即在路由器的子网上的同级机器的重映射的地址),我没有得到答复,路由器上的tcpdump显示请求,但没有回复; 10.0.77.11(兄弟机器)上的tcpdump根本没有显示数据包。 也在路由器上运行:

 iptables -t nat -L -v 

显示正在由NETMAP处理的数据包,但没有由SNAT 。 那么NETMAP目标好像取代了SNAT目标呢?

本质上,我想要的是,当路由器从例如192.168.252.5(在隧道上的OpenVPN服务器的地址)发往10.0.78.11的tun0上接收到一个数据包时,它被重写为两种方式:目标地址更改为10.0.77.11 ,并且源地址更改为10.0.77.10(select新的源端口,以便SNAT可以跟踪这是哪个连接)。 然后当10.0.77.11在10.0.77.10向假端口发送一个应答时,路由器应该颠倒过程,在伪0的源地址为10.0.78.11的tun0上发回一个包到192.168.252.5。 NETMAP可以做到这一点,或者可以在iptables任何其他目标做到这一点?

其他的事情,我试图没有成功:configuration路由器机器的代理ARP; 将虚拟networking范围添加到路由表中。 但感觉这样的事情不应该是必要的。

更新:我完全不关心DNS,只需要联系“私有子网”中的less数几台机器,因此使用IP地址是可以接受的。

这是一个很大的混乱,你会诚实地更好地服务于重新编号,而不是创build一个地址冲突的丑陋的NATnetworking。 您需要支持的其他解决scheme(例如,具有多个区域的分割时域DNS,可能具有自动更新)将是困难和杂乱的,并且随后每个必须处理该networking的人将永远诅咒你的名字,并烧毁你和你的团队。

但是,看起来你遇到的问题是NAT一侧的主机(见?我甚至无法正确描述NAT的侧面,因为它是混乱的)没有path回到主机另一方面。 你也必须为返回path添加一个NETMAP规则(从eth1传入的数据包我假设)。

可是等等! 这是在prerouting链完成的。 因此,目标地址将在做出路由决定之前设置,这几乎是它的工作方式…但是您的路由器有两条路由用于“冲突”networking。 因此,它将按照通常的方式(最窄的匹配前缀首先;度量打破关系)优先,导致一些数据包被reflection回到他们来自的networking,而不是通过NAT路由。 他们也会来源。 不幸的是,源地址不用于路由,所以你不能使用源地址。 input接口也不用于路由select,一旦做出路由决定,就不能再重写目标地址。

所以,你发现,这是做不到的。 你有两个select。 最好是将networking地址冲突的一个子网重新编号,因为你是一个好的networking工程师,而不是一个不称职的networking工程师,而且你的networking不是不必要的复杂。 重新编号VPN子网往往是一个简单的任务,最坏的情况下需要使用sed ,但也许你有一个奇怪的情况重编号子网是一个非常艰巨的任务。 好。

如果因为某种原因你不能这样做,你将需要一个额外的路由器去与你的水平分割DNS。 为每个子网设置一个路由器(实际上,这意味着VPN客户端需要一台路由器,而networking中的主机需要一台路由器,但不幸使用了您为VPN预占的前缀)。 select一些其他的地址范围(这将是一个你可以重新编号的子网之一…)是“外国”的。

现在,假设我们使用VPN主机端A和与非VPN主机端B的networking进行呼叫。假设在路由器A上select“外部”前缀为192.0.2.0/24,在路由器B上,这是198.51.100.0/24。 这些将是用于该子网上的主机的假前缀,用于与其他子网上冲突前缀相冲突的主机(不要使用这些前缀在RFC5737中用于文档目的)。

下面的规则很复杂,因为你不能在POSTROUTING链中使用input接口作为谓词,并且源子网是非唯一的,所以我们必须防止使用过滤规则进行欺骗。 这也是令人困惑的,因为NETMAP决定是否改变源或目标地址的基础上,

在路由器A和B中,在路由器之间的点对点链路上添加一个inputstream量的规则,我将称之为ptp0:

 router-a # iptables -t nat -A PREROUTING -i ptp0 -d 198.51.100.0/24 -j NETMAP --to 10.0.77.0/24 router-a # iptables -t nat -A POSTROUTING -d 10.0.77.0/24 -j NETMAP --to 192.0.2.0/24 router-a # iptables -t filter -A FORWARD -i \!ptp0 -s 10.0.77.0/24 -j DROP router-b # iptables -t nat -A PREROUTING -i ptp0 -d 192.0.2.0/24 -j NETMAP --to 10.0.77.0/24 router-b # iptables -t nat -A POSTROUTING -d 10.0.77.0/24 -j NETMAP --to 198.51.100.0/24 router-b # iptables -t filter -A FORWARD -i \!ptp0 -s 10.0.77.0/24 -j DROP 

现在,这个问题的一部分已经解决了,因为你不再有一个路由器,它的路由表中都有前缀。 没有解决scheme,要求你有相同的前缀引用两个不同的networking,在他们在同一个路由表中的两个不同的主机可以工作; 由于路由的工作原理,这本质上是不可能的。

哦,我几乎忘了 – 我相信你的SNAT规则没有得到处理,因为没有任何匹配它,但重要的是要记住,一旦连接被存储在NAT状态表中,它不再被SNAT规则处理,并且不如果我记得,更长的时间会被统计。

如果您实际上不需要将子网分开,则只需使用第2层桥接即可。 这会让你的生活变得更轻松。

所以我做了使用Vagrant创build一个独立的snat-routing-demo的工作 。 作为一个控制,我最初使用简单的SNAT ,VPN客户端直接指向服务的实际IP,而我解释了OpenVPN设置的细节等等。 然后我介绍了一个虚拟子网,并在SNAT之前添加了NETMAP到表中:

 iptables -t nat -A PREROUTING -d <virtual-subnet> -j NETMAP --to <actual-subnet> 

哪些工作。 我以为我已经在我的真实环境中尝试了这个相当简单的规则,但也许我没有,或者其他东西被打破了; 无论如何,我可以得到类似的规则,“真实”的工作。 没有使用商标似乎是必要的,而且

 iptables -t nat -L -v 

现在显示两个规则正在应用。

限制桥接子网中的哪些主机和端口可以从VPN客户端访问的防火墙规则似乎也可以不加修改地使用实际的IP范围,例如

 iptables -A FORWARD -p tcp -d <actual-host> --dport <some-port> -j ACCEPT iptables -A FORWARD -s <actual-subnet> -j ACCEPT iptables -P FORWARD DROP 

我相信我的另一个答案,我可能会误解你的意图,想一想。

如果我正确地理解了你,你有两个站点之间的VPN链接,它应该在两者之间路由stream量。 但是,不幸的是,这两个(以前是独立的)站点最终都使用了相同的RFC1918地址作为子网,对吧? 这是RFC1918最令人头疼的新特性之一。

不过,最好的解决scheme是重新编号其中一个子网。 但是,如果你不能,这个你正在做的映射事情不是一个不好的解决scheme。

然而,在你的规则中有一些错误。

你的SNAT规则没有logging多lessstream量,因为它除了openvpn客户端路由器自己的数据包之外没有任何匹配。 来自VPN链路的报文的源地址在10.0.77.0/24范围内。 因此,数据包通过VPN从另一端出来,但是对它们的回复被发送到本地段,而不是回到VPN,从而丢失。

就像你需要创build一个伪装的networking前缀来区分前向path中的目的地址一样,反向path地址也必须是可微的,我想这也是你使用SNAT的原因。 所以,你应该修改你的规则(并且要更加具体地关于你的NETMAP,所以你不要重写每个通过VPN的子网中每个目标地址的前三个八位字节):

 iptables -t mangle -A PREROUTING -i tun0 -j MARK --set-mark 1 iptables -t nat -A PREROUTING -i tun0 -d 10.0.78.0/24 -j NETMAP --to 10.0.77.0/24 iptables -t nat -A POSTROUTING -s 10.0.77.0/24 -m mark --mark 1 -j SNAT --to-source 10.0.77.10 

mangle规则是必须的,因为您不能在POSTROUTING规则中使用input接口作为谓词,所以您需要一些其他方法来确定数据包来自VPN链接,并且需要源NAT。