红联Linux门户
Linux帮助

iptables和策略路由实现VPN感兴趣流的截获

发布时间:2014-11-21 21:45:08来源:linux网站作者:dog250

感兴趣流是VPN的术语,说的是需要进行保护的流量,也就是说需要进入VPN隧道的流量,然则仔细推敲之后,发现基于IP层加密的VPN这么使用“流”的概念是有问题的,因为对于IP,根本不存在流的概念,实质在于IP协议根本就没有方向。即使这样,本文还是介绍了一种全网互通的感兴趣流的截获技术。

这个拓扑图中有四个网段,其中VPN端点的每一侧都有两个,两个网段中的其中一个的资源是需要加密访问的。虽然图比较简单,但是它却很有代表性,几乎囊括了所有的访问情形,那就是从任意网段加密或者明文访问任意的其它网段。那么怎样定义一个规则从而实现感兴趣流的截获呢?仔细思考这个问题就会发现它实际上并没有想象的那么简单,因为在同一个位置IP层无法区分一个数据包是到加密网段的需要加密的包还是从加密网段发起的到非加密网段的访问的返回包。因此必然需要一定的传输层信息,使用基于五元组的conntrack机制才可以准确区分这二者。


对于IPSec,是通过配置复杂的策略数据库来实现的,具体可以参见FreeSwan的实现框图。而对于OpenVPN,我们可以使用更加灵活的策略路由来实现,当然仅仅使用策略路由是不够的,还要使用conntrack模块,具体来讲就是使用mangle表的规则来对一些数据包打上mark,然后根据这些mark定位策略路由表。整个过程所使用的工具就是经典强大的iproute2以及iptables 。

我使用ip_conntrack的ctstate这个match来区分下面两类数据包:

1.源自任意地点经由此地到加密网段的访问包。

2.源自任意地点经由此地的到达非加密网段访问包的返回包。


ip_conntrack为一个流保存了一组状态,通过状态机来切换这些状态,本文中我们所使用到的有两个状态,分别是NEW和ESTABLISHED。其中NEW状态表示基于五元组进行连接跟踪的一个流的第一个包,而ESTABLISHED状态则标示一个流的反方向的第一个以及后续两个方向的所有包(这里没有考虑INVALID情况,也没有考虑过期)。也就是说,当发起一个请求时,数据包到达VPN端点的时候,其ctstate一定是NEW,而此时可以捕获其目的地址,根据目的地址是否是加密网段而进行走隧道还是走明文的抉择,另外一种情况,如果是访问加密网段的返回包,那么也是要加密走隧道的,而此时该包到达VPN的另一个端点时,其ctstate已经是ESTABLISHED的了,因此可以捕获其源地址,如果源地址是加密网段,ctstate为ESTABLISHED,那么也是要加密的,其余的则全部明文放过。对于两个或者多个VPN端点,都是如此配置即可,因此我们为整个VPN所要做的,仅仅是知道到达哪些网段的流量需要加密即可,然后把上述的文字组织成脚本即可。

本方法使用OpenVPN这种VPN再合适不过了,因为OpenVPN有丰富的接口和事件脚本和外部网络事件联动,同时其推送能力也会最大限度的简化配置。如此一来,你就不必像配置IPSec VPN那样,非常对称的在两端同时进行配置,而只需要在OpenVPN服务器端进行统一的配置即可,所有的客户端的配置都是可以推送下去的。这也是非对称的,C/S模式的OpenVPN的绝佳舞台。


以下的配置在所有的VPN端点上进行,对于OpenVPN服务器端,直接配置,对于OpenVPN客户端,相关信息由OpenVPN服务器的push route指令以及push setenv-fase指令进行推送。

增加一个新的策略路由表:

echo 100 vpn >> /etc/iproute2/rt_tables

配置两条规则打mark:

iptables -t mangle -A PREROUTING -i $连接内部网络的固定入口 -m conntrack --ctstate NEW  -d $dst/$dst_mask  -j MARK --set-mark 100

iptables -t mangle -A PREROUTING -i $连接内部网络固定入口-m conntrack --ctstate ESTABLISHED  -s $src/$src_mask  -j MARK --set-mark 100

配置策略路由策略:

ip rule add fwmark 100 table vpn

在策略路由表中增加路由:

ip  route add $加密网段/$加密网段掩码 via $加密网段VPN端点对应的OpenVPN虚拟IP table vpn

…可添加多条


到此,我们就可以实现任意网段到任意网段的感兴趣流量的截获-走策略路由表,以及不感兴趣流量的放行-走标准主路由表。

通过配置过程可看出这种方案和以往的其它的方案有一个不同点,那就是截获过程和VPN是独立的,通过策略路由表和VPN系统耦合,实际上Linux网络中,使用conntrack mark的并不仅仅只有策略路由。使用Linux网络就这点好,几乎所有配置工具完全遵守KISS原则,做好且只做好一件事,这样配置就可以非常灵活,代价就是需要你自己来构思如何排列组合这些配置来实现你自己的方案。


在本文介绍的截获方式中,隐含使用了ip_conntrack模块,很多人对这个东西不是很喜欢,因为第一怕它会满,第二怕它影响效率。其实这都是杞人忧天。在64位系统你设置ip_conntrack最大数量为655360足矣,即使32位系统超过1G的物理内存,你也可以设置为100000,不要指望你的Linux网关会有绝佳的表现,使用Linux和同类的系统相对PK一下还可以,追求绝对的表现都是浮云,因此第二个担心也不是什么问题,性能?怕影响性能,能影响多少?一切以代码为先的程序员应该相信ip_conntrack的哈希算法或者会调整内核参数使之最优化才好。当然,如果你的预算允许,那么直接上两中指(Cisco),什么问题都解决了。

每当写一篇文章,最后我难免要感慨一番,实际上这种感慨我认为很有必要,看过《古文观止》之类古文集的家伙应该都知道,每一篇的最后都会有呜呼…,嗟夫…,之类的感慨。作为技术研发人员,每看到一个问题,自己就一定要想办法解决它,哪怕方案再拙劣,起码成一家之言,在解决了之后,不要混杂其它的疑问,更不要挑刺,起码自己完成了功能,实现了效果,即便它带来了新的问题或者新的不确定性。如果真的有新的问题和不确定性,那么就引出另一个问题,然后解决之,千万不要将许多问题杂糅在一起,时刻注意你的目的是什么,既然目的是网络的互通性,那么就先不要考虑效率,至于优化,先把互通性搞定再说。


总结:

本文介绍了一种方法,可是实现任意网络之间的任意流量类型的截获,注意,这种截获是双向的,其实现利用了Linux的ip_conntrack的ctstate这个match模块,配置策略路由来完成,体现了Linux网络的强大性。虽然我没有给出完整的测试拓扑以及配置文件,但是由于原理非常简单,操作起来也不会很困难。正好家里还屯着几块老旧的板子,这样就可以自己DIY一台网关了,再加入一些NAT以及防火墙的功能,其效果想必不比买到的TPLink家用路由器要差,反而还会好很多。具体的DIY过程可以参见《Linux network cookbook》这本书,该书讲解的很清晰,知识点也很简单,很适合DIY。


关于DIY:

如果真的想自己DIY一台自己的家用mini设备,可以实现从世界各地接入且策略性的访问加密或者明文资源,那么推荐看一下FreeSCO以及Endian这两个,其中前者更加灵活一些,在这二者的基础上,你也可以自己实现一个和它们都不同的小型网关。