用自由软件构建防火墙
作者: 夏武 ,徐继哲
一、自由软件介绍
什么是自由软件?自由软件强调的是用户的自由,而不是价格。简单来说,如果一个软件赋予了用户如下4个自由度,那么这个软件就是自由软件:
- 出于任何目的,运行软件的自由。
- 学习软件如何工作,以及为了满足自己的需要修改软件的自由。(显然,这个自由度的前提是能够访问软件的源代码)
- 为了帮助你的邻居和朋友,将你的软件拷贝给他的自由。
- 为了能够让整个社团受益,公开发行改进之后的软件的自由。(显然,这个自由度的前提是能够访问软件的源代码)
自由软件的历史要追溯到Richard Stallman在1983年发起了自由软件运动。该运动以GNU(GNU's Not Unix)工程为核心,开发了大量优秀的自由软件,比如GNU Emacs, GNU GCC, GNU GDB等,最终的目标是要开发一个完整的、自由的操作系统。随着这些自由软件日渐流行,为了保证已经发行的自由软件能够继续自由下去, Richard Stallman创造性地发明了对称版权(copyleft)思想,并在1985年实现了第一个对称版权(copyleft)许可证,作为GNU Emacs的许可证。在1991年,GNU实现了GNU GPL version 2。 在GNU GPL version 2的守护之下,大量的优秀自由软件不断涌现,其中名气最大当数Linux kernel。 下面我们将介绍如何通过自由软件来构建防火墙。
二、国内防火墙厂商的技术现状
谈到防火墙,不得不谈的是国内防火墙厂商的技术现状。总结起来,有下面的一些特点:
1、在与国外产品技术上有差距,在竞争中处于下风
虽然近年来随着网络安全的日益重要,国内防火墙厂商很活跃,推出了不少的新产品。不过,同国外的同类产品相比,在稳定性和性能上还是有差距。在低端的市场上,国内厂商有一定的份额,但是在中高端市场,特别是在金融、电信、电子商务等领域,国外的产品仍是主流。
2、大量应用和参考自由软件技术
为了缩小和国外场上的技术差距,国内防火墙场上大量的应用和参考自由软件技术。从国内各防火墙厂商的招聘研发工程师的要求可见一斑,几乎所有的招聘要求里 面都提到要对Linux下的包过滤框架netfilter需要熟悉。通过充分的发掘象Linux这样的自由软件的潜力,很多厂商迅速构建自己的平台,加快 了开发速度,提高了竞争力。
3、不遵守自由软件协议
从近年来国内安全行业的发展来看,可以说没有自由软件,国内防火墙厂商将发展比较慢。但是,很多厂商在从学习或者利用自由软件的过程中获益后,没有捐赠或者回馈自由软件社区,有时还常常违反GNU GPL协议。这都是不明智之举。从长远的眼光看来,百害而无一益。
三、定制防火墙
在流行定制的现在,普通用户自己定制个人计算机可能大家已经司空见惯了,不过定制防火墙倒是有些新鲜。当需要用到防火墙的时候,你可能会马上想到去市场上买一个某某厂家生产的防火墙,因为定制防火墙可能不象定制个人计算机那么简单。
不过,在自由软件的帮助下,你也可以轻松的定制自己的防火墙,在省下一大笔开销和享受定制乐趣的同时,更好的保护自己或者组织的网络安全。
下面我们将介绍一下利用现有的自由软件定制防火墙的步骤:
3.1、硬件准备
对于定制防火墙需要的硬件,很多你淘汰的计算机都可以派上用场。当然,现在计算机硬件价格变得越来越便宜,在硬件上的选择会更多。下面我们将以X86架构的硬件为例。
具体的要求可以考虑下面几个方面:
- 对于存储设备,可以考虑使用IDE硬盘或者CF Card。存储设备上的分区格式可以采用EXT3或者EXT2格式,并在使用之前分好区。
- 最好有三个网络接口,也就是三块网络接口卡,包括进口,出口,DMZ
当然要定制防火墙,需要一个用来构建的计算机(这台机器最好速度能快些,因为编译的过程比较耗时间)。我们可以在这台机器上装上一个GNU Linux系统或者使用LiveCD。
对硬盘分区的命令如下(假定我们要安装防火墙的硬盘为IDE,在GNU Linux下对应的设备文件是/dev/hdb):
#fdisk /dev/hdb
分完区之后,我们可以格式化分区:
#mkfs.ext3 /dev/hdb1
接着将格式化的分区挂载到/mnt/firewall
#mount -t ext3 /dev/hdb1 /mnt/firewall
3.2、软件准备
定制防火墙需要的自由软件包括:
- netfilter/iptables
- SSH daemon
- DHCP和DNS服务器
- grub
- uClibc和交叉编译工具链
- Linux内核
- busybox
- 构建防火墙规则集的工具,例如Firewall builder
3.2.1 netfilter/iptables firewall
netfilter/iptables IP 信息包过滤系统是一种功能强大的工具,可用于添加、编辑和除去规则,这些规则是在做信息包过滤决定时,防火墙所遵循和组成的规则。这些规则存储在专用的信 息包过滤表中,而这些表集成在 Linux 内核中。在信息包过滤表中,规则被分组放在我们所谓的链(chain)中。
虽然 netfilter/iptables IP 信息包过滤系统被称为单个实体,但它实际上由两个组件 netfilter和 iptables 组成。
netfilter组件也称为 内核空间(kernelspace),是内核的一部分,由一些信息包过滤表组成,这些表包含内核用来控制信息包过滤处理的规则集。
iptables 组件是一种工具,也称为 用户空间(userspace),它使插入、修改和除去信息包过滤表中的规则变得容易。可以从http://www.netfilter.org下载该工具并安装使用它。
通过使用用户空间,可以构建自己的定制规则,这些规则存储在内核空间的信息包过滤表中。这些规则具有目标,它们告诉内核对来自某些源、前往某些目的地或具 有某些协议类型的信息包做些什么。如果某个信息包与规则匹配,那么使用目标ACCEPT允许该信息包通过。还可以使用目标DROP或REJECT来阻塞并 杀死信息包。对于可对信息包执行的其它操作,还有许多其它目标。
根据规则所处理的信息包的类型,可以将规则分组在链中。处理入站信息包的规则被添加到INPUT链中。处理出站信息包的规则被添加到OUTPUT链中。处 理正在转发的信息包的规则被添加到FORWARD链中。这三个链是基本信息包过滤表中内置的缺省主链。另外,还有其它许多可用的链的类型(如 PREROUTING和POSTROUTING ),以及提供用户定义的链。每个链都可以有一个策略,它定义“缺省目标”,也就是要执行的缺省操作,当信息包与链中的任何规则都不匹配时,执行此操作。
建立规则并将链放在适当的位置之后,就可以开始进行真正的信息包过滤工作了。这时内核空间从用户空间接管工作。当信息包到达防火墙时,内核先检查信息包的头信息,尤其是信息包的目的地。我们将这个过程称为路由。
如果信息包源自外界并前往系统,而且防火墙是打开的,那么内核将它传递到内核空间信息包过滤表的INPUT链。如果信息包源自系统内部或系统所连接的内部 网上的其它源,并且此信息包要前往另一个外部系统,那么信息包被传递到OUTPUT链。类似的,源自外部系统并前往外部系统的信息包被传递到 FORWARD链。
接下来,将信息包的头信息与它所传递到的链中的每条规则进行比较,看它是否与某条规则完全匹配。如果信息包与某条规则匹配,那么内核就对该信息包执行由该 规则的目标指定的操作。但是,如果信息包与这条规则不匹配,那么它将与链中的下一条规则进行比较。最后,如果信息包与链中的任何规则都不匹配,那么内核将 参考该链的策略来决定如何处理该信息包。理想的策略应该告诉内核DROP该信息包。 下图说明了这个信息包过滤过程。(下面插入一张报过滤图)
3.2.2 SSH daemon
SSH(secure shell)是一种通用,功能强大的基于软件的网络安全解决方案,计算机每次向网络发送数据时,SSH都会自动对其进行加密。数据到达目的地时,SSH自 动对加密数据进行解密。整个过程都是透明的。它使用了现代的安全加密算法,足以胜任大型公司的任务繁重的应用程序的要求。
为了能远程管理你的防火墙,你需要安装SSH daemon程序。在自由软件里面有很多选择,这里我们选择OpenSSH,可以从 这里 下载。
3.2.3 DHCP and DNS server
为了配置你的防火墙网络,需要安装DHCP和DNS服务器。这里我们可以选择dnsmasq这个自由软件, 它同时具有DHCP和DNS的功能,并且被广泛应用。3.2.4 bootloader
我们定制防火墙需要引导加载程序,功能是将操作系统和防火墙程序启动起来。通常情况下,引导加载程序包括固化在固件(firmware)中的boot代码(可选)和Boot Loader两大部分。
对于PC机,引导加载程序由 BIOS(其本质就是一段固件程序)和位于硬盘 MBR 中的 OS Boot Loader(比如,LILO 和 GRUB 等)一起组成。BIOS 在完成硬件检测和资源分配后,将硬盘 MBR 中的 Boot Loader 读到系统的 RAM 中,然后将控制权交给 OS Boot Loader。Boot Loader 的主要运行任务就是将内核映象从硬盘上读到 RAM 中,然后跳转到内核的入口点去运行,也即开始启动操作系统。
这里我们选择GNU Grub作为我们的bootloader。
3.2.5 uclibc和交叉编译工具链
libc库是大部分应用程序的基础,这里有二种通用的实现:glibc和uclibc。其中uclibc是glic的轻量级实现。这里我们选择uclibc。可以从 这里 下载。
另外从 这里 下 载uclibc对应的交叉编译工具链。后面我们需要这个交叉编译工具链来编译后面的应用程序。如果你下载的是交叉编译工具链的源代码,需要编辑交叉编译工 具链中的Makefile,然后编译。编译过程会下载适合定制防火墙的编译器,生成类似toolchain_ARCHITECTURE,例如 toochain_i386,里面包括头文件,类库和交叉编译器。
3.2.6 Linux kernel
Linux因其健壮性、可靠性、灵活性以及好象无限范围的可定制性而在IT业界变得非常受欢迎。Linux 具有许多内置的能力,使开发人员可以根据自己的需要定制其工具、行为和外观,而无需昂贵的第三方工具。如果Linux系统连接到因特网或LAN、服务器或 连接 LAN 和因特网的代理服务器,所要用到的一种内置能力就是针对网络上Linux系统的防火墙配置。可以在 netfilter/iptables IP 信息包过滤系统(它集成在 2.6.x 版本的 Linux 内核中)的帮助下运用这种能力。
我们这里选择Linux内核版本为2.6.15, 可以从 这里 下载。
3.2.7 busybox
BusyBox 是很多标准Linux工具的一个单个可执行实现。BusyBox包含了一些简单的工具,例如 cat 和 echo,还包含了一些更大、更复杂的工具,例如 grep、find、mount 以及telnet(不过它的选项比传统的版本要少);有些人将 BusyBox称为Linux工具里的瑞士军刀。
可以从 这里 下载busybox。
3.3、编译内核
新的Linux内核做了很多的改进,最明显的就是图形化的配置菜单。你可以执行"make gconfig"(基于GTK+)或者"make xconfig"(基于Qt)来配置内核,选择需要的功能和特性。
编译内核需要考虑是否将内核模块直接编译进内核,因为防火墙的内核实现部分是以内核模块的形式存在的。二者的区别在于,将内核模块不直接编译进内核相比较而言扩展性更好一些。不过,为了简单起鉴,我们将内核模块直接编译进内核。
在配置内核选项的时候,强烈推荐你将netfilter相关的选项和你的网卡驱动程序都选上,并且这些选项都应该是静态编译的。
配置完选项之后就可以编译了,编译会花费几分钟的时间。编译完成之后将在arch/ARCHICTURE/boot(比如 arch/i386/boot)下生成一个叫做bzImage的内核映像文件,这个文件下面的步骤将会用到。
3.4、编译应用程序
编译应用程序非常简单。需要注意的一点是,要用前面下载的交叉编译工具链编译后面的应用程序。我们从busybox开始吧。
首先解压缩下载的busybox源代码包,执行"make menuconfig"进行配置。在进行配置的时候,一个重要的选项是指定交叉工具链中GCC的位置,这对编译机器和目标机器体系结构不同的情况比较有 用。另外,ifconfig, ip, udhcp选项和子选项需要激活。等这些都配置好之后,需要配置Init Utilities部分,把你需要在系统启动阶段启动的服务都打开。这一些都配置好之后就可以编译了。
接着是编译iptables, openssh, dnsmasq, grub,如果遇到的问题的话,这些自由软件都有很好的安装文档。
3.5、制作文件系统
需要安装的软件在前面已经编译好了,该是制作文件系统的时候了。首先,将busybox安装到/mnt/firewall目录下,这是你将会看到一个很亲却和熟悉的环境。 接下来在/mnt/firewall下面创建以下的几个文件夹:
- dev
- etc
- lib
- proc
- usr
- boot
- 将交叉编译工具链的lib目录下的库文件拷贝到lib目录
- 拷贝iptables和dnsmasq的可执行文件到sbin目录
- 在dev目录下创建设备文件
3.6、安装引导程序
我们这里选择grub作为引导程序。将grub里面的stage1、stage2、e2fs_stage_1_5拷贝到boot目录下,然后编辑 menu.list,安装grub的引导程序。到此,定制防火墙的工作基本上完成了。重新启动,定制的成果马上就可以看到了。
3.7、定制基于Netfilter的内核模块
netfilter和iptables已经提供了非常强大的功能,大部分情况下都够用了。如果你需要一些特别的功能的话,完全可以基于netfilter开发自己的特色内核模块。我们以一个例子来说明如何做到这一点。
假如我们要实现禁止来自192.168.0.1的所有访问的功能,下面是实现这个功能的源代码:
#define __KERNEL__
#define MODULE
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/skbuff.h>
#include <linux/udp.h>
static struct nf_hook_ops netfilter_ops;
static unsigned char *ip_address = "\xC0\xA8\x00\x01";
static char *interface = "lo";
struct sk_buff *sock_buff;
struct udphdr *udp_header;
unsigned int main_hook(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff*))
{
if(strcmp(in->name,interface) == 0){
return NF_DROP;
}
sock_buff = *skb;
if(!sock_buff){ return NF_ACCEPT; }
if(!(sock_buff->nh.iph)){
return NF_ACCEPT;
}
if(sock_buff->nh.iph->saddr == *(unsigned int*)ip_address){
return NF_DROP;
}
return NF_ACCEPT;
}
int init_module()
{
netfilter_ops.hook = main_hook;
netfilter_ops.pf = PF_INET;
netfilter_ops.hooknum = NF_IP_PRE_ROUTING;
netfilter_ops.priority = NF_IP_PRI_FIRST;
nf_register_hook(&netfilter_ops);
return 0;
}
void cleanup_module()
{
nf_unregister_hook(&netfilter_ops);
}
在上面的这段代码中,我们以#define和#include语句开头。接着定义了nf_hook_ops和网络序的IP地址 (192.168.0.1), 同时定义了叫"lo"的回环接口。主要的功能是在函数main_hook中实现的。在这个函数中,我们比较了设备名称,如果设备名称是回环接口,返回 NF_DROP,也就是说我们丢弃了这个包。接下来,我们验证了IP地址的有效性,最后比较了IP地址。如果IP地址相同,返回NF_ACCEPT,即丢 弃这个IP包。否则接受这个IP包。
函数init_module是这个内核模块的初始化函数,在这个模块装载进内核空间的时候被执行。函数cleanup_module是退出函数,在这个模块被从内核空间释放掉以后被执行。
代码写好了之后,我们需要编写makefile和Kconfig文件,且假定上面编写代码文件名是fhook.c:
#makefile
在Linux 2.6内核中,我们可以利用内核的makefile编译内核模块。
obj-y := fhook.o
obj-$(CONFIG_FIREWALL_HOOK) += fhook.o
#Kconfig
menu FIREWALL_HOOK
config FIREWALL_HOOK
tristate "firewall hook"
---help---
firewall hook
endmenu
模块编译之后,可以使用如下指令转载内核模块:
#insmod fhook.ko
现在你可以开始试用你写的功能了。
3.8、开发基于WEB的管理界面
如果你觉得基于命令行的防火墙管理不方便的话,我们可以自己定制防火墙的WEB管理界面。下面介绍一下具体的方法。
首先,需要确定使用的网页服务器。常用的网页服务器包括apache,lighttpd等,可以考虑使用apache作为我们的网页服务器(这当然有个人 喜好的问题)。但是,由于apache功能很强大,对于我们的防火墙来说,很多功能都是用不到的,所以我们需要对apache的功能进行裁减。
接下来是选择开发语言的问题。开发语言可能是差异化比较大的,有C、C++、PERL、PHP、PYTHON可供选择。由于我们的防火墙可能会运行在一些 内存比较有限的机器上,并且对速度要求比较高,业务流程也比较简单,所以C和C++是比较适合的语言。当然你也可以用其他的语言,依赖于你的具体情况。
选择了开发语言之后,其实就应该可以开发了。不过,为了提高你的开发效率,最好关注一下可以使用的自由软件类库。比如对于使用C和C++开发的话, libcgi 是一个不错的类库。
准备工作做好之后就可以开发了。我们下面以一个实例来说明开发的具体思路。比如我们想实现一个打开IP数据包转发的功能,下面是我们的C程序源代码:
#include <stdio.h >
#include <time.h >
#include <cgi.h >
int main()
{
char *path = "/proc/sys/net/ipv4/ip_forward";
char cmd[256];
cgi_init();
cgi_process_form();
cgi_init_headers();
sprintf(cmd, "echo 1 > %s", path);
if (!system(cmd))
puts("<html><head><title>打开IP数据包转发成功</title>< /head><bodygt;打开IP数据包转发成功</body></html>");
else puts("<html><head><title>打开IP数据包转发失败</title>< /head><bodygt;打开IP数据包转发失败</body></html>");
cgi_end();
return 0;
}
这里我们使用了libcgi的类库函数,也就是以cgi_开头的函数。我们通过执行命令行指令打开了IP数据包的转发功能,然后根据返回值的不同输出不同的返回信息。 另外,为了使界面定制性更好,可以考虑使用模板引擎,比如
clearsilver
。
五、总结
总的来说,对于常规的网络安全应用,我们完全可以用已有的自由软件定制出符合自己需求的防火墙,而不必购买硬件防火墙设备,这就是自由软件的价值,自由软 件不但功能强大,更重要的是赋予了用户使用软件的自由。在面对网络安全这样的关键应用的时候,自由软件让你能够自己控制自己的命运,而不依赖某个厂商。
回复 Tekkaman 2010年12月15日 星期三 09:31
有机会在