系列目录
- VPN原理及实现1:VPN概念及要点
- VPN原理及实现2:一般理论
- VPN原理及实现3:隧道的一种实现
- VPN原理及实现4:虚拟网卡构建VPN
- VPN原理及实现5:TCP还是UDP
- VPN原理及实现6:TCP封装的隧道对于拥塞控制的意义
- Linux VPN技术概论 1
- Linux VPN技术概论 2
声明:
- 对于IPSec,不谈其Tunnel以及Transport模式
- 对于OpenVPN,不剖析代码
- 不涉及Linux内核源码剖析
- 不深入谈PPTP以及L2TP
- 本文不是HOWTO也不是技术文档
- 本文的性质是科普
第一部分.VPN要解决的问题以及方案
一.问题及方案
基于主机的第三层vpn的要旨就是“透明/安全的接入”,其中透明的含义就是配置要简单,尽量让用户感觉不到vpn的存在,因此这种vpn的实现其实只要解决两个问题即可:
1. 如何拿到第N层数据,然后放回第M层;
2. 加密/解密第三层ip数据报;
针对第一个问题,实际上我们依赖的是这样一个事实,即OSI的分层网络模型,这样才可以使第M层将第N层的PDU当成有效载荷,解析时只需要根据下层的协议号就可以定位上一层的协议类型。以封装第三层数据的VPN为例,上述第一个问题在linux上有三种方案:
1.使用netfilter
使用netfilter无疑是最直接的方式,对于接收的包,需要在prerouting上挂载一个钩子,对于本地发出的包,则需要在output上挂载一个钩子,这样就可以在钩子函数中进行加密/解密处理了,处理完毕之后再根据策略封装成一个新的ip数据报,重新路由并且发送出去或者接收。
0_1308381239TzgW.gif2.使用虚拟网卡
这是一种不直接但是很自然的方式,因为,我们知道从网卡出来的包一个包含一个IP数据报(暂不考虑其它三层协议,比如ipx)。实现一个虚拟网卡,在其xmit发送函数中处理加密,然后重新封装,重新路由。这里暂时先不考虑在哪里解密,因为网卡发送和接收的逻辑是不一样的,我们可以实现一个虚拟网卡,然而却不能修改ip协议栈,除非使用netfilter。
0_1308381261a58C.gif3.将IP数据报导出到用户态,比如使用packet套结字以及替换send/recv
这是最灵活的方案,然而如果使用packet的话却不行。因为packet无法截获数据报。因此需要在socket层次替换send/recv函数,实现加密/封装以及解密/解封装。这里暂时不考虑send处理,因为对于一般的应用数据,是很难替换send的(Linux没有windows的LSP机制),这种方式,控制接收较方便,而控制发送则很难,和使用虚拟网卡的情形正好相反。
0_1308381261a58C.gif我们发现,2和3是互补的,虚拟网卡很容易拿到发出的未封装的原始IP数据报-通过路由即可,但是拿不到接收的封装过的IP数据报,而替换socket的send/recv则很容易拿到接收的封装过的IP数据报但是拿不到发送的未封装的IP数据报。因此将2和3结合一下,就出现了第四种方式。接下来我们来讨论vpn整体的设计。在架设vpn的时候,首先要明白三件事:
1.确定不安全通路的两个端点。我们就在这两个端点之间建立vpn通路;
2.要考虑到对forward数据包的支持,否则就没有必要使用三层vpn了,使用应用层vpn即可。
3.隧道的概念。隧道其实就是vpn链路两个端点之间的加密通路,其中封装着加密后的原始IP数据报。
确定了vpn链路的端点以后,就可以根据上述的3种方式进行加密/封装,解密/解封装的操作了,也就是说,修建一条隧道。具体的方案如下:
方案1:采用netfilter的方式
这种方案部署起来比较简单,因为端点的两台设备是对称的,netfilter钩子会处理IPSec协议逻辑。成型的项目有Freeswan等,具体细节参见《FreeSWAN 结构框架》。
方案2:虚拟网卡+udp
这种方案在端点两端的加密/封装,解密/解封装是不对称的,其中加密/封装这步操作在虚拟网卡完成,解密在recv逻辑中完成,解封装在send逻辑中完成。成型的项目如cipe等。数据通路实际上就是虚拟网卡的方式和导出到用户态方式的结合
0_1308381261a58C.gif设计要点:
<u style="box-sizing: border-box;">1.VPN隧道终点使用UDP端口区分了不同的IP安全通道,而不需要采用IPSec的方式;
2.为何不用TCP呢?因为会引起重传叠加导致网络不可用,详见CIPE的作者所做:《Why TCP Over TCP Is A Bad Idea》。同样的规则在后续的OpenVPN中依然如此,详见OpenVPN的man手册中的--proto选项。
3.加密在内核态,解密在用户态-使用UDP,之所以这样的不对称法是因为UDP端口是可以从客户端“连接(访问的含义)”的,不同的UDP端口就可以区分出来自不同地点的IP安全通道。这种方案依赖于,安全隧道总是由外部(位置不确定的互联网的任意一处)先发起建立请求。后面的OpenVPN的C/S模型也有赖于这个事实。</u>
二.VPN类型
vpn按照网路通路区分划分为:
1.传输模式VPN
传输模式的VPN只加密数据,不构建隧道,也就是只实现数据安全。如下图所示,图片来自cisco:
0_1308381261a58C.gif2.隧道模式VPN
隧道模式的VPN不但加密数据,但是构建隧道,也就是说实现网络层次PDU的封装。如下图所示,图片来自cisco:
vpn按照认证方式区分可以使用不同的协议承载方式
1.PPPoE
严格来讲,这不是一种VPN,而只是一种接入方式,它只是实现了在以太网上承载ppp包。虽然它不属于vpn的范畴,然而却能表现隧道的思想,所谓隧道无非就是第m层的pdu用一种特定的隧道协议承载第n层的pdu,思想如下图:
0_1308381261a58C.gif2.L2TP
L2TP是一个协议,使用这种协议的vpn用拨号来进行认证。如下图所示,图片来自华为:
0_1308381261a58C.gif3.SSL VPN
这种vpn使用证书,密码等SSL认可的可配置的方式进行认证。它不一定是第四层vpn,也不一定是第七层vpn,而只是强调它使用SSL协议实现认证以及密钥协商等安全策略。本文推崇的vpn也就是使用虚拟网卡实现的ssl vpn。
第二部分.进一步的讨论
一.问题
到此为止,vpn的两个首要工作都得到了解决,使用虚拟网卡,recv,或者netfilter抓住IP数据报,然后直接加密,也就是加密/封装和解封装/解密。但是加密的标准以及加密方式我们还没有涉及,理论上,任何加密算法都是可以对IP数据报进行加密的。
实现VPN有多种方式,标准的方式是使用IPSec协议(ESP,AH等)来实现的,IPSec是一个封装第三层数据报的协议集,也可以理解成一个第四层协议集,和tcp/udp等是完全并列的,tcp,udp的协议号分别为6和17,而esp,ah的协议号分别为50和51。总之IPSec协议集旨在第三层将数据直接进行加密/解密,要支持IPSec无疑需要对协议栈进行修改,在Linux,netfilter可以完成此任务,Freeswan正是这样实现的IPSec。
然而在内核态通过修改协议栈的实现方式很不灵活,配置起来也是比较复杂,如果能在用户态实现数据报的加密/解密就比较好,这是完全可行的,因为IPSec协议集和TCP/UDP一样也是第四层协议,完全可以用tcp或者udp来承载加密后的数据。既然使用虚拟网卡可以方便的拿到IP数据报,那么就可以想办法导出到用户态,然后加密/解密后再发出去。
二.tap虚拟网卡
到此,我们明白**IPSec只是加密/解密的支撑协议,和加密/解密没有关系,**并且这种方式对于配置和实现来讲都不是很灵活,当然,这里先不考虑性能因素。既然IPSec只是支撑协议,那也就是说只要能实现加密/解密,并且能实现将封装后的数据报传输到vpn链路的对端就可以了。可是,又如何将虚拟网卡抓取的ip数据报导出到用户态呢?
前面提到,**使用虚拟网卡抓取IP数据报和使用替换socket的send/recv函数抓取数据报是矛盾且互补的,因此它们的结合对于封装和解封装的实现是不对称的。**
tap网卡的出现带来了福音。tap网卡在实现一个虚拟网卡的同时也实现了一个字符设备,在虚拟网卡的xmit函数中直接将未加密未封装的原始IP数据报放到字符设备的输入缓冲区,然后用户态进程就可以从该字符设备读取到ip数据报,用户态进程只需要将之加密,然后通过socket发出即可,借助协议栈完成原始ip数据报的封装。数据到了对端,用户态进程从socket读取到的就是加过密的原始ip数据报,因为协议栈已经自动解封装了,然后将之解密后写入虚拟网卡字符设备即可,此时,虚拟网卡字符设备模拟中断cpu,从而虚拟网卡调用接收函数。因此,最终我们有了**方案3:**
0_1308381261a58C.gif
采用这个模型的开源项目有很多,它们都基于一个很简单的demo程序:simpletun(http://backreference.org/wp-content/uploads/2010/03/simpletun.tar.bz2)
在simpletun的基础上,衍生了VTun,OpenVPN,Openssh's VPN等项目。
三.安全策略拓展
到此为止,vpn的通路问题解决了,其中最漂亮的方案就是使用tap网卡。可是加密/解密算法,认证算法等还没有涉及。和解决通路问题一样,处理安全策略也有很多的方案:
方案1:使用IPSec协议集以及IKE机制。
这个方案其实就是IPSec的方案,无疑,这种方案对使用tap网卡是有限制的,因此很多情况下都是直接在内核实现。详情参见相关标准。
方案2:编写用户态进程,实现可配置的安全策略
这个方案比较笼统,但是它却是最灵活的。最简单的方式,实现一个通过参数配置加密算法的程序,比如VTun所作的那样,然而VTun对于身份认证和算法及密钥支持比较弱。最终人们想到了SSL协议。
SSL协议可以保证通信的保密性和可靠性,它使用公钥技术,支持安全且灵活的认证机制,常见的有使用X509证书的认证。SSL协议本身就包含密钥协商,因此相比IPSec的IKE要简单的多。正是tap虚拟网卡和SSL的结合,才带来了vpn技术的革命!tap网卡和SSL是缺一不可的,虽然VTun使用了tap网卡,然而它没有使用SSL协议,这就直接阻碍了它的进一步拓展,而cipe则更是一个中间过程,它的安全运算全部在内核态完成,根本无法使用SSL协议。OpenVPN是这个革命的一个产儿。
OpenVPN的基本要素有以下几点:
1.可配置的认证方式
2.可配置的加密算法,密钥协商
3.可插拔的插件机制
4.自动的策略推送机制-包括客户端的虚拟网卡的ip地址
5.集中的管理功能
6.灵活的外接程序
7.强大的日志输出功能
下面是OpenVPN的实现框图:
0_1308381261a58C.gif四.方案对比
1.对比IPsec和虚拟网卡-通路方案选择
1).IPSec的通路方案是在网络层直接修改IP数据报实现封装,然后直接交由路由模块重新路由。IPSec对原始IP数据报的封装是直接在网络层完成的(虽然逻辑上IPSec属于第四层)。
2).虚拟网卡方案利用了这样的一个事实,即可以通过路由的方式将IP数据报导入虚拟网卡,另外从虚拟网卡接收的IP数据报也可以通过路由的方式导出到本机或者其它物理网卡。虚拟网卡对原始IP数据报的封装是利用标准的TCP/IP协议栈完成的(想使用标准协议栈封装数据报,则必须将原始IP数据报放回到协议栈的顶端,即socket层,然后随着数据在协议栈中往下流动,封装过程自然完成)。
2.1).虚拟网卡的副作用:由于虚拟网卡也是一个起始于链路层的网卡,因此可以为之配置网络层地址,即IP地址,出入虚拟网卡的数据和出入物理网卡的数据一样受到所有TCP/IP协议栈语义的约束,因此也就可以通过虚拟网卡很简单地实现一个虚拟的局域网,虽然在物理上虚拟局域网内的设备可能相隔千里。正是这一点使后来的OpenVPN可以很简单的实现client2client。
3).总结:无疑,虚拟网卡的方案没有IPSec的方案更直接,因此效率上也会大打折扣,在这一点上考虑,IPSec的方案要胜出。
2.对比IPSec和SSL-安全策略方案选择
1).IPSec定义了一整套内置的以及可外挂的安全策略协议,包括ESP,AH,IKE等,这些协议的操作全部在网络层完成。
2).SSL是一个安全套结字协议框架,通过Cipher Suit可以灵活配置认证以及加密,摘要算法,这些操作全部在表示层和会话层完成。
3).总结:虽然IPSec是一种自然而然的方式,然而可供选择算法非常有限,并且配置很不灵活,因此SSL相比IPSec的安全协议集无疑会胜出。
3.综合对比-IPSec方案VS虚拟网卡+SSL
在数据通路和安全策略上,VPN更加关注的是安全策略,数据通路对于VPN来讲是完全透明的,而安全策略则需要是高度可定制的,除非在效率要求很高的需求下,虚拟网卡+SSL的方案要略胜一筹。以下只给出IPSec和虚拟网卡+SSL的数据封装框图:
IPSec的方式:
0_1308381261a58C.gif**虚拟网卡+SSL的方式:
0_1308381261a58C.gif五.使用netfilter的引申
netfilter是一个很优秀的框架,它设计的5个HOOK点真的是恰到好处,因此我们可以围绕它做出几乎所有和协议栈相关的东西。包括虚拟网卡的功能也是可以通过netfilter完成的。也就是说,不再需要虚拟网卡了,这样既实现了用户态的安全策略,又不需要虚拟网卡,也就是下面这张图所显示的,仅以forward出口包为例:
0_1308381261a58C.gif作为对比,列出虚拟网卡的方式:
0_1308381261a58C.gif六.Linux的其它可选方案-GRE+ESP/AH
将这节内容放到最后有点过分了,因为这节的内容对于linux上实现VPN而言实在太重要了。
有很多成熟的方案可以实现vpn,特别是建立vpn隧道,其中最方便的vpn通路建立方式当属GRE隧道了,并且GRE也是一个相当成熟的隧道协议。因此使用GRE隧道技术可以方便的铺设隧道。其次,在Linux的2.6内核中,出现了一个新的框架,这就是xfrm-xfrm is an IP framework, which can transform format of the datagrams,i.e. encrypt the packets with some algorithm. xfrm policy and xfrm state are associated through templates TMPL_LIST.This framework is used as a part of IPsec protocol.使用xfrm来实现IPSec要比使用netfilter来得更直接,因为xfrm是内嵌在协议栈内部的,而不是像netfilter一样外挂在外部的。
xfrm通过注册一个struct net_protocol使得ESP,AH在第四层协议中有了名分,毕竟struct net_protocol就是一个协议栈第四层的操作例程集合,正如tcp和udp拥有tcp_protocol以及udp_protocol一样,esp和ah也有esp4_protocol和ah4_protocol(暂不考虑IPv6)。如此xfrm以一种直接的方式在标准的协议栈中处理IPSec数据包,正如下图所示:
可见Linux使用xfrm框架(其中为esp/ah等实现了两个传输层的封装机制,且可以外挂其它机制,比如基于5元素的策略查找等)实现了逻辑上的IPSec安全策略,该框架包含两大部分:
1.将IPSec安全协议集注册在传输层;
2.修改路由模块,加入策略检查逻辑,实现出口数据包的基于策略的捕获,然后将其导入传输层实现安全协议封装。
然而VPN的另一大特性,也就是隧道的实现却另有其人,那就是GRE模块(体现为ip_gre)。本质上,GRE网卡和虚拟网卡的思想是一样的,只不过其使用一个标准协议,即GRE来封装网络层数据报。GRE的关键在于网络的互通,比如它能将ipx封装在IP中,从而实现跨协议通信。
第三部分.网络层VPN技术发展阶段
一.完全内核态的IPSec实现阶段
这是官方的VPN建议,其背后有着强大的协会和组织以及商业机构。代表作有cisco,3com等厂商的核心安全设备,以及开源届的freeswan。Linux2.6内核中内嵌了xfrm框架,该框架以一种直接的方式而不是netfilter这种间接过滤的方式可以方便的实现IPSec。然而使用者却没有因为这种实现的方便性而增多,关键还是在于其配置的不灵活以及协议的封闭。
二.半虚拟网卡阶段
只实现了发送例程而没有实现接收例程的虚拟网卡,导致没有实现接收例程的原因是这个时候还没有虚拟网卡的字符设备,无从接收数据。cipe是这个阶段的代表作。在cipe的实现中,替换socket的send/recv例程并不是必要的,因为原始的协议栈对待从上到下的数据只是封装,对待自下而上的数据是解封装,如果不替换send/recv例程的话,数据到达用户态再发出去时就被封装成了本地出发的数据包了,而我们需要的是只解除隧道封装,而不再封装本地的信息,实际上,对recv的替换倒不是必须的,因为在recv中只处理了解密操作,而在用户态也可以做,不过既然加密是在内核做的,解密也在内核做比较好,其中效率是一大原因。之所以如此复杂,是因为这只是半虚拟网卡。
三.全虚拟网卡阶段
虚拟网卡实现了一个字符设备,完全对称的实现了全虚拟网卡,不但具有发送例程,还有接收例程,这样就可以完美的在两台主机的两个虚拟网卡字符设备之间架设一条“虚拟物理线路”,分别处在两个主机上的两个进程中的一个只需要处理虚拟网卡字符设备和本地socket之间IO数据的处理/转发即可。这种方式将安全策略完全解放到了用户态,为OpenVPN的革命性进展开辟了道路。值得注意的是,此阶段的vpn还是将注意力集中在网络通路上,毕竟安全策略刚刚从内核解放出来。代表作品有VTun。
四.SSL+全虚拟网卡阶段
虚拟网卡加上SSL协议给软件VPN带来了一场革命,不仅仅是这种结合方式,而是这种结合方式之外的扩展功能才是亮点之所在。毕竟VTun也使用了虚拟网卡,其模式和OpenVPN并没有什么太大的不同,然而正是SSL协议给这种结合方式带来了新的活力,就像一剂猛药瞬间激活了最具扩展性的OpenVPN。细解OpenVPN,发现它是如此的简单,实际上它在简单的背后映射了不简单的道理。如果我们细解牛顿三定律,也会发现它不过是伽利略理论的总结,正如OpenVPN不过是VTun加上一些标准化的安全拓展是一样的道理。实际上,所谓的革命本是不存在的,人们普遍将量变引起质变的点当成是革命,而事实上,只要有一个矮人站到巨人的肩上,那他也会比巨人更高大!
五.最终权衡
由于半虚拟网卡只是到达全虚拟网卡的过渡,因此这里不考虑半虚拟网卡,另外全虚拟网卡方案中的VTun也只是到OpenVPN的过渡。而基于IPSec的VPN受到各方面的限制也不予考虑,再者,VPDN的应用场合也有限制,也不予考虑。最终我们只剩下了基于虚拟网卡的SSL VPN,当然正如OpenVPN官方网站所说,OpenVPN相比IPSec所欠缺的就是各个操作系统的原生支持,虽然OpenVPN的设计非常优秀,但是在标准化方面还是比不过IPSec,IPSec的成功,和诸如Cisco等公司的推波助澜是分不开的,我们知道,第一流的公司做标准,第二流的公司做品牌,第三流的公司做产品,因此标准化是很重要的。在Windows上,如想用VPDN,基本直接就能支持,然而想使用OpenVPN,则必须安装客户端。另外效率问题也是OPenVPN的劣势,然而这不是根本的劣势,技术上的问题终究是可以得到解决的,我本人就曾经修改了tun驱动程序,使其吞吐量大幅提高...
第四部分.Linux VPN软件选型
1.IPSec的实现:freeswan
虽然IPSec的侧重点在性能和安全性,然而事实上在Linux中,Freeswan的效率并没有想象的那么高,因为它是netfilter实现的,而netfilter将会过往的每一个数据包进行判定,这会严重影响效率。Freeswan的优点在于其实现的标准性。
Freeswan在2.6之前的Linux内核中使用的更加广泛一些,因为2.6内核有了更好的IPSec方案-Xfrm。另外,由于政治原因,Freeswan的推广受到了很大的限制。
2.cipe
效率很高,然而配置不灵活,且只支持单通道。
3.vtun
效率很高,支持多客户端,每个客户端一个进程处理。配置也很丰富,然而缺乏更高级的安全策略的支持,正如其文档中所说的那样。
4.openvpn
具有革命性的vpn实现,支持多客户端,支持client2client。然而其效率却不是很高。OpenVPN的灵活性及安全性蕴藏着巨大的收益,并且其实现的对称性和简单性吸引了大量的开发者,最终开发出了各个平台的版本,这正体现了OpenVPN的强大。反观VTun,直到现在,它也不支持Windows版本,并且2007年就停止了开发...
5.应用层ssl vpn
这是应用级别的VPN,诸如代理软件,翻墙软件都属于这种范畴,它保护特定的主机,特定的应用,因此对于个人接入拥有很大的优势,它可以得到更多的应用层的用户信息,这是网络层的vpn所无能无力的。然而如果你希望使一个网络安全的接入另一个网络,那只能选择1到4的方案了。
网友评论