防火墙(firewall)是一项十分重要的安全技术,其普及范围之广也不用我多说了(是个操作系统都带上防火墙了)
防火墙概述
防火墙是建立在内外网络边界上的过滤封锁机制,内 部网络被认为是安全和可信赖的,而外部网络(通常 是Internet)被认为是不安全和不可信赖的
防火墙的功能主要有下面几个:
- 限制外部未授权用户对内部的访问
- 限制内部用户 访问不安全的站点
- 防止入侵者进入网络
要实现上述的功能,防火墙首先必须具有 所有网络数据流 的控制能力,即所有网络数据流在流入/流出计算机时首先必须经过防火墙,并且防火墙需要对数据流进行check以保证数据的可靠性和安全性,说到这里,不用多说,防火墙自身必须对入侵免疫(虽然不太可能)。
针对数据流的处理,一般分为 允许通过、拒绝并提示、拒绝不提示 三种处理方式。
防火墙的种类
防火墙从出现到现在演变出了许多不同的类型
- 分组过滤防火墙(Packet Filtering)
作用于网络层和传输层,主要通过识别端口号、协议信息、源地址和目的地址来判读是否允许数据包通过,静态包过滤和动态包过滤(Dynamic Packet filter)防火墙也是分组过滤防火墙的一种.
- 状态检测防火墙(Statue Detection)
状态检测防火墙可以直接对分组中的数据进行处理,并且结合前后分组的数据进行判断,决定数据包是否通过
- 应用代理防火墙(Application Proxy)
也叫应用网关(Application Gateway),作用于应用层,对每种应用服务编制特定的代理程序,实现监控。
防火墙技术
1.静态包过滤防火墙
静态包过滤防火墙的原理十分简单:
过滤发生在传输层/网络层上,针对每个数据包的源地址、目的地址、端口号、协议等信息,采用预先设置好的规则表来一一查询是否匹配。
静态包过滤防火墙属于是初代防火墙了,其优点很明显: 简单、成本低 ,它的缺点同样也很明显: 保护能力弱、维护规则表很费时
2.动态包过滤防火墙
动态包过滤防火墙是为了弥补静态包过滤防火墙的缺陷而诞生的。
典型的动态包过滤防火墙建立在网络层上,先进的工作在传输层上。其相比于静态包过滤防火墙,增加了所谓动态感知的能力,即对外出的数据包进行身份记录,便于让具有相同连接的数据包通过。从这一点可以看出,动态包过滤防火墙需要同时维护规则表和已见连接的表(并且实时更新).
由此可以看出,其实动态包过滤防火墙相比于静态包过滤防火墙有进步,但是也不是那么有用。
而且实际上的动态包过滤防火墙对于加密通信是十分麻烦的,比如在使用ftp协议时,需要动态的协商文件传输的端口号,如果ftp协议底层使用了ssl加密传输,那么动态包过滤防火墙无法准确判断协商文件传输的端口号是哪一个,也就不能动态地开启端口号进行文件传输。
因此,使用动态包过滤防火墙时,必须保证端口协商以明文方式进行
状态检测防火墙其实就是动态包过滤防火墙
3.电路级网关
电路级网关相对于包过滤防火墙,更加先进,因为它工作在会话层中,除了源、目的IP,源、目的端口、协议等常规包过滤防火墙使用的过滤规则外,电路级网关还额外查看tcp协议的 握手信息SYN、ACK 以及序列号的合法性 ,其示例图如下图所示:
从图中可以看出,其实电路级网关还提供了一个额外功能—代理服务器(proxy server)
其实电路级网关的应用是我们大家很常见的,首先是SOCKS代理,其次是SSL
电路级网关相比于包过滤网关更加安全,然而对系统的效率的影响更大。
4.应用级网关
应用级网关(Application Proxy)是运行在防火墙上的一种服务器程序。它是电路级网关的升级版,因为应用级网关工作在 应用层 上,所以其能够更深入地对数据包进行诊断,即 分析数据包的内容
由此可见,应用级网关针对的是每一个特定的应用,所以不同应用的网关在设计细节上存在不同,因此要维护应用级网关是很困难的一件事(你写的网关程序不像电路级网关可以通用。而且应用级网关会影响网络的效率,成为性能瓶颈。
但是麻烦也往往意味着,应用级网关的安全防护功能十分强大。应用级网关是目前最安全的网关了。
5.空气隙防火墙(Air Gap)
空气隙防火墙,模拟人工拷盘的方式,切断了外部网络和内部网络的直接联系,实现 物理隔离,十分的牛逼。
原理如下图所示:
在内外部主机进行数据交互时,都需要先把数据写入硬盘中,随后进行读取,优点很明显,可以隔离内外主机的直接联系,可以方便的check数据,还可以与应用级网关进行联合使用。
缺点同样明显,效率问题就不用多说了,比应用级网关还慢,而且配置环境十分复杂,成本高昂,维护困难.
状态检测防火墙的实现
本次状态检测防火墙主要基于linux netfilter框架进行实现。
TCP处理
对于TCP连接,思路非常清晰,如下图所示:
UDP处理
众所周知,UDP是无状态的,如何以UDP协议作为传输协议的其他应用层协议例如DNS和ICMP呢?
对于UDP协议,一方发出DNS请求后,防火墙会自动将目的地址和端口返回的、到达源地址和端口包作为状态相关的包而允许通过。
对于ICMP报文,如ping包,其状态相关包就是来自目的地址的echo reply包,没有echo包对应的echo reply包会被视作非法。
状态检测表
状态检测表因为存在于内核中,可以使用非线性结构比如hash表、tree来实现。
这里还有注意状态检测表的超时连接处理的问题。首先要明确一点的是,内核的使用空间是有限的,不可能一直保留超时连接,在必要时需要做删除。
但是删除的时机是需要商榷的。不可能每次检测连接是否存在时就更新一次(那样更新频率太大了,会导致防火墙的效率降低),有一种方法是设置定时器,在一定时间检测整个链表,但是每次检测整个链表还是有点费事的。
这里本人采用的策略是,在一条链上添加连接前,检测该链中的超时连接,然后进行删除。这样,如果有很长一段时间链表上未添加新链,此时如果有原有的超时连接进行通信,防火墙也会允许通过。当然了,如果该链上如果添加连接很频繁,那么超时连接就不应该占用资源,活该被删除。
多连接协议的处理
对于像ftp这种多连接协议,控制连接在21号端口,数据连接主动是20号,被动协商得到(上面说过了,因此端口协商过程不能加密),因此状态检测要分析主连接的内容得到端口号,从而自动设置规则表放行。
利用linux的net filter架构进行开发
原理清晰明了。linux的net filter提供了5个hook点
本次开发的程序有2个,一个是用户程序(供用户添加或删除规则使用),另一个是内核模块,即防火墙的实现。
既然有了2个程序,不可避免的需要考虑内核和用户程序的交互方式。这里笔者使用了 字符设备 的方式来进行交互。
使用锁来完成全局变量同步访问
在进行firewall编写时,每个收到/发送的报文都需要在状态检测表中寻找是否存在连接。还有如果规则允许时,你必须添加连接然后放行。显然,状态检测表是一个全局变量。一般情况下,一个CPU只能一次处理一个报文。但是多核处理器的话,有能力同时处理多个报文,你就需要做些改变了。
换句话说,需要有锁来保证状态检测表的一致性
值得一提的是,锁之所以能保证一致性,是因为锁必须具备原子性(即运行锁相关的函数时不能被打断)
1.互斥锁
互斥锁实现的是线程之间的互斥,如果申请资源时,资源已经被占有,互斥锁会使线程进入睡眠,释放CPU。
2.自旋锁
自旋锁也是实现线程之间的互斥,但是与互斥锁之前的区别是,如果资源已经被占用,线程会进入忙等待的状态(不会释放cpu,会不断查看资源是否被释放)。
3.信号量
信号量是互斥锁的封装和更新。
二元信号量其实相当于是互斥锁。多元信号量除了可以实现互斥外,还可以实现同步(即资源的有序访问,其实互斥锁是无法实现有序访问的)。
4.读写锁
不管是自旋锁还是信号量在同一时间只能有一个进程进入临界区。对于有些情况,我们是可以区分读写操作的。因此,我们希望对于读操作的进程可以并发进行。对于写操作只限于一个进程进入临界区。而这种同步机制就是读写锁。读写锁一般具有以下几种性质。
- 同一时间有且仅有一个写进程进入临界区。
- 在没有写进程进入临界区的时候,同时可以有多个读进程进入临界区。
- 读进程和写进程不可以同时进入临界区。
当然也还有其他的锁的相关内容,这里引用一张图:
关于NAT转换的问题处理
NAT转换的问题常见于防火墙放置在路由器上时,内网数据包在流出时,需要替换成路由器在外网的ip,如果此时状态检测防火墙没有设置对应的ip地址通过规则的话,将导致网络不通。
可以看一个实例
这里指的一提的是,NAT转换是基于已经建立连接之上后做的处理。正常情况下,需要在连理连接后修改连接表,但是由于笔者在设计的时候没有考虑nat,又因为笔者偷懒成性,渴望摆烂,最终没有对防火墙进行重构。
reference
https://www.wangan.com/wenda/3410
https://blog.csdn.net/Mars_guest/article/details/79833855