用自由軟件構建防火墻
作者:夏武,徐繼哲
一、自由軟件介紹
什么是自由軟件?自由軟件強調的是用戶(hù)的自由,而不是價(jià)格。簡(jiǎn)單來(lái)說(shuō),如果一個(gè)軟件賦予了用戶(hù)如下4個(gè)自由度,那么這個(gè)軟件就是自由軟件:
- 出于任何目的,運行軟件的自由。
- 學(xué)習軟件如何工作,以及為了滿(mǎn)足自己的需要修改軟件的自由。(顯然,這個(gè)自由度的前提是能夠訪(fǎng)問(wèn)軟件的源代碼)
- 為了幫助你的鄰居和朋友,將你的軟件拷貝給他的自由。
- 為了能夠讓整個(gè)社團受益,公開(kāi)發(fā)行改進(jìn)之后的軟件的自由。(顯然,這個(gè)自由度的前提是能夠訪(fǎng)問(wèn)軟件的源代碼)
自由軟件的歷史要追溯到Richard Stallman在1983年發(fā)起了自由軟件運動(dòng)。該運動(dòng)以GNU(GNU's NotUnix)工程為核心,開(kāi)發(fā)了大量?jì)?yōu)秀的自由軟件,比如GNU Emacs, GNU GCC, GNUGDB等,最終的目標是要開(kāi)發(fā)一個(gè)完整的、自由的操作系統。隨著(zhù)這些自由軟件日漸流行,為了保證已經(jīng)發(fā)行的自由軟件能夠繼續自由下去, RichardStallman創(chuàng )造性地發(fā)明了對稱(chēng)版權(copyleft)思想,并在1985年實(shí)現了第一個(gè)對稱(chēng)版權(copyleft)許可證,作為GNUEmacs的許可證。在1991年,GNU實(shí)現了GNU GPL version 2。 在GNU GPL version2的守護之下,大量的優(yōu)秀自由軟件不斷涌現,其中名氣最大當數Linux kernel。 下面我們將介紹如何通過(guò)自由軟件來(lái)構建防火墻。
二、國內防火墻廠(chǎng)商的技術(shù)現狀
談到防火墻,不得不談的是國內防火墻廠(chǎng)商的技術(shù)現狀??偨Y起來(lái),有下面的一些特點(diǎn):
1、在與國外產(chǎn)品技術(shù)上有差距,在競爭中處于下風(fēng)
雖然近年來(lái)隨著(zhù)網(wǎng)絡(luò )安全的日益重要,國內防火墻廠(chǎng)商很活躍,推出了不少的新產(chǎn)品。不過(guò),同國外的同類(lèi)產(chǎn)品相比,在穩定性和性能上還是有差距。在低端的市場(chǎng)上,國內廠(chǎng)商有一定的份額,但是在中高端市場(chǎng),特別是在金融、電信、電子商務(wù)等領(lǐng)域,國外的產(chǎn)品仍是主流。
2、大量應用和參考自由軟件技術(shù)
為了縮小和國外場(chǎng)上的技術(shù)差距,國內防火墻場(chǎng)上大量的應用和參考自由軟件技術(shù)。從國內各防火墻廠(chǎng)商的招聘研發(fā)工程師的要求可見(jiàn)一斑,幾乎所有的招聘要求里面都提到要對Linux下的包過(guò)濾框架netfilter需要熟悉。通過(guò)充分的發(fā)掘象Linux這樣的自由軟件的潛力,很多廠(chǎng)商迅速構建自己的平臺,加快了開(kāi)發(fā)速度,提高了競爭力。
3、不遵守自由軟件協(xié)議
從近年來(lái)國內安全行業(yè)的發(fā)展來(lái)看,可以說(shuō)沒(méi)有自由軟件,國內防火墻廠(chǎng)商將發(fā)展比較慢。但是,很多廠(chǎng)商在從學(xué)習或者利用自由軟件的過(guò)程中獲益后,沒(méi)有捐贈或者回饋自由軟件社區,有時(shí)還常常違反GNU GPL協(xié)議。這都是不明智之舉。從長(cháng)遠的眼光看來(lái),百害而無(wú)一益。
三、定制防火墻
在流行定制的現在,普通用戶(hù)自己定制個(gè)人計算機可能大家已經(jīng)司空見(jiàn)慣了,不過(guò)定制防火墻倒是有些新鮮。當需要用到防火墻的時(shí)候,你可能會(huì )馬上想到去市場(chǎng)上買(mǎi)一個(gè)某某廠(chǎng)家生產(chǎn)的防火墻,因為定制防火墻可能不象定制個(gè)人計算機那么簡(jiǎn)單。
不過(guò),在自由軟件的幫助下,你也可以輕松的定制自己的防火墻,在省下一大筆開(kāi)銷(xiāo)和享受定制樂(lè )趣的同時(shí),更好的保護自己或者組織的網(wǎng)絡(luò )安全。
下面我們將介紹一下利用現有的自由軟件定制防火墻的步驟:
3.1、硬件準備
對于定制防火墻需要的硬件,很多你淘汰的計算機都可以派上用場(chǎng)。當然,現在計算機硬件價(jià)格變得越來(lái)越便宜,在硬件上的選擇會(huì )更多。下面我們將以X86架構的硬件為例。
具體的要求可以考慮下面幾個(gè)方面:
- 對于存儲設備,可以考慮使用IDE硬盤(pán)或者CF Card。存儲設備上的分區格式可以采用EXT3或者EXT2格式,并在使用之前分好區。
- 最好有三個(gè)網(wǎng)絡(luò )接口,也就是三塊網(wǎng)絡(luò )接口卡,包括進(jìn)口,出口,DMZ
當然要定制防火墻,需要一個(gè)用來(lái)構建的計算機(這臺機器最好速度能快些,因為編譯的過(guò)程比較耗時(shí)間)。我們可以在這臺機器上裝上一個(gè)GNU Linux系統或者使用LiveCD。
對硬盤(pán)分區的命令如下(假定我們要安裝防火墻的硬盤(pán)為IDE,在GNU Linux下對應的設備文件是/dev/hdb):
#fdisk /dev/hdb分完區之后,我們可以格式化分區:
#mkfs.ext3 /dev/hdb1接著(zhù)將格式化的分區掛載到/mnt/firewall
#mount -t ext3 /dev/hdb1 /mnt/firewall3.2、軟件準備
定制防火墻需要的自由軟件包括:
- netfilter/iptables
- SSH daemon
- DHCP和DNS服務(wù)器
- grub
- uClibc和交叉編譯工具鏈
- Linux內核
- busybox
- 構建防火墻規則集的工具,例如Firewall builder
3.2.1 netfilter/iptables firewall
netfilter/iptablesIP信息包過(guò)濾系統是一種功能強大的工具,可用于添加、編輯和除去規則,這些規則是在做信息包過(guò)濾決定時(shí),防火墻所遵循和組成的規則。這些規則存儲在專(zhuān)用的信息包過(guò)濾表中,而這些表集成在 Linux 內核中。在信息包過(guò)濾表中,規則被分組放在我們所謂的鏈(chain)中。
雖然 netfilter/iptables IP 信息包過(guò)濾系統被稱(chēng)為單個(gè)實(shí)體,但它實(shí)際上由兩個(gè)組件 netfilter和 iptables 組成。
netfilter組件也稱(chēng)為 內核空間(kernelspace),是內核的一部分,由一些信息包過(guò)濾表組成,這些表包含內核用來(lái)控制信息包過(guò)濾處理的規則集。
iptables 組件是一種工具,也稱(chēng)為 用戶(hù)空間(userspace),它使插入、修改和除去信息包過(guò)濾表中的規則變得容易??梢詮膆ttp://www.netfilter.org下載該工具并安裝使用它。
通過(guò)使用用戶(hù)空間,可以構建自己的定制規則,這些規則存儲在內核空間的信息包過(guò)濾表中。這些規則具有目標,它們告訴內核對來(lái)自某些源、前往某些目的地或具有某些協(xié)議類(lèi)型的信息包做些什么。如果某個(gè)信息包與規則匹配,那么使用目標ACCEPT允許該信息包通過(guò)。還可以使用目標DROP或REJECT來(lái)阻塞并殺死信息包。對于可對信息包執行的其它操作,還有許多其它目標。
根據規則所處理的信息包的類(lèi)型,可以將規則分組在鏈中。處理入站信息包的規則被添加到INPUT鏈中。處理出站信息包的規則被添加到OUTPUT鏈中。處理正在轉發(fā)的信息包的規則被添加到FORWARD鏈中。這三個(gè)鏈是基本信息包過(guò)濾表中內置的缺省主鏈。另外,還有其它許多可用的鏈的類(lèi)型(如PREROUTING和POSTROUTING),以及提供用戶(hù)定義的鏈。每個(gè)鏈都可以有一個(gè)策略,它定義“缺省目標”,也就是要執行的缺省操作,當信息包與鏈中的任何規則都不匹配時(shí),執行此操作。
建立規則并將鏈放在適當的位置之后,就可以開(kāi)始進(jìn)行真正的信息包過(guò)濾工作了。這時(shí)內核空間從用戶(hù)空間接管工作。當信息包到達防火墻時(shí),內核先檢查信息包的頭信息,尤其是信息包的目的地。我們將這個(gè)過(guò)程稱(chēng)為路由。
如果信息包源自外界并前往系統,而且防火墻是打開(kāi)的,那么內核將它傳遞到內核空間信息包過(guò)濾表的INPUT鏈。如果信息包源自系統內部或系統所連接的內部網(wǎng)上的其它源,并且此信息包要前往另一個(gè)外部系統,那么信息包被傳遞到OUTPUT鏈。類(lèi)似的,源自外部系統并前往外部系統的信息包被傳遞到FORWARD鏈。
接下來(lái),將信息包的頭信息與它所傳遞到的鏈中的每條規則進(jìn)行比較,看它是否與某條規則完全匹配。如果信息包與某條規則匹配,那么內核就對該信息包執行由該規則的目標指定的操作。但是,如果信息包與這條規則不匹配,那么它將與鏈中的下一條規則進(jìn)行比較。最后,如果信息包與鏈中的任何規則都不匹配,那么內核將參考該鏈的策略來(lái)決定如何處理該信息包。理想的策略應該告訴內核DROP該信息包。 下圖說(shuō)明了這個(gè)信息包過(guò)濾過(guò)程。(下面插入一張報過(guò)濾圖)
3.2.2 SSH daemon
SSH(secureshell)是一種通用,功能強大的基于軟件的網(wǎng)絡(luò )安全解決方案,計算機每次向網(wǎng)絡(luò )發(fā)送數據時(shí),SSH都會(huì )自動(dòng)對其進(jìn)行加密。數據到達目的地時(shí),SSH自動(dòng)對加密數據進(jìn)行解密。整個(gè)過(guò)程都是透明的。它使用了現代的安全加密算法,足以勝任大型公司的任務(wù)繁重的應用程序的要求。
為了能遠程管理你的防火墻,你需要安裝SSH daemon程序。在自由軟件里面有很多選擇,這里我們選擇OpenSSH,可以從這里下載。
3.2.3 DHCP and DNS server
為了配置你的防火墻網(wǎng)絡(luò ),需要安裝DHCP和DNS服務(wù)器。這里我們可以選擇dnsmasq這個(gè)自由軟件, 它同時(shí)具有DHCP和DNS的功能,并且被廣泛應用。3.2.4 bootloader
我們定制防火墻需要引導加載程序,功能是將操作系統和防火墻程序啟動(dòng)起來(lái)。通常情況下,引導加載程序包括固化在固件(firmware)中的boot代碼(可選)和Boot Loader兩大部分。
對于PC機,引導加載程序由 BIOS(其本質(zhì)就是一段固件程序)和位于硬盤(pán) MBR 中的 OS Boot Loader(比如,LILO和 GRUB 等)一起組成。BIOS 在完成硬件檢測和資源分配后,將硬盤(pán) MBR 中的 Boot Loader 讀到系統的 RAM中,然后將控制權交給 OS Boot Loader。Boot Loader 的主要運行任務(wù)就是將內核映象從硬盤(pán)上讀到 RAM中,然后跳轉到內核的入口點(diǎn)去運行,也即開(kāi)始啟動(dòng)操作系統。
這里我們選擇GNU Grub作為我們的bootloader。
3.2.5 uclibc和交叉編譯工具鏈
libc庫是大部分應用程序的基礎,這里有二種通用的實(shí)現:glibc和uclibc。其中uclibc是glic的輕量級實(shí)現。這里我們選擇uclibc??梢詮?a >這里下載。
另外從這里下載uclibc對應的交叉編譯工具鏈。后面我們需要這個(gè)交叉編譯工具鏈來(lái)編譯后面的應用程序。如果你下載的是交叉編譯工具鏈的源代碼,需要編輯交叉編譯工具鏈中的Makefile,然后編譯。編譯過(guò)程會(huì )下載適合定制防火墻的編譯器,生成類(lèi)似toolchain_ARCHITECTURE,例如toochain_i386,里面包括頭文件,類(lèi)庫和交叉編譯器。
3.2.6 Linux kernel
Linux因其健壯性、可靠性、靈活性以及好象無(wú)限范圍的可定制性而在IT業(yè)界變得非常受歡迎。Linux具有許多內置的能力,使開(kāi)發(fā)人員可以根據自己的需要定制其工具、行為和外觀(guān),而無(wú)需昂貴的第三方工具。如果Linux系統連接到因特網(wǎng)或LAN、服務(wù)器或連接 LAN 和因特網(wǎng)的代理服務(wù)器,所要用到的一種內置能力就是針對網(wǎng)絡(luò )上Linux系統的防火墻配置??梢栽趎etfilter/iptables IP 信息包過(guò)濾系統(它集成在 2.6.x 版本的 Linux 內核中)的幫助下運用這種能力。
我們這里選擇Linux內核版本為2.6.15, 可以從這里下載。
3.2.7 busybox
BusyBox是很多標準Linux工具的一個(gè)單個(gè)可執行實(shí)現。BusyBox包含了一些簡(jiǎn)單的工具,例如 cat 和echo,還包含了一些更大、更復雜的工具,例如 grep、find、mount 以及telnet(不過(guò)它的選項比傳統的版本要少);有些人將BusyBox稱(chēng)為L(cháng)inux工具里的瑞士軍刀。
可以從 編譯內核需要考慮是否將內核模塊直接編譯進(jìn)內核,因為防火墻的內核實(shí)現部分是以?xún)群四K的形式存在的。二者的區別在于,將內核模塊不直接編譯進(jìn)內核相比較而言擴展性更好一些。不過(guò),為了簡(jiǎn)單起鑒,我們將內核模塊直接編譯進(jìn)內核。 在配置內核選項的時(shí)候,強烈推薦你將netfilter相關(guān)的選項和你的網(wǎng)卡驅動(dòng)程序都選上,并且這些選項都應該是靜態(tài)編譯的。 配置完選項之后就可以編譯了,編譯會(huì )花費幾分鐘的時(shí)間。編譯完成之后將在arch/ARCHICTURE/boot(比如 arch/i386/boot)下生成一個(gè)叫做bzImage的內核映像文件,這個(gè)文件下面的步驟將會(huì )用到。 編譯應用程序非常簡(jiǎn)單。需要注意的一點(diǎn)是,要用前面下載的交叉編譯工具鏈編譯后面的應用程序。我們從busybox開(kāi)始吧。 首先解壓縮下載的busybox源代碼包,執行"makemenuconfig"進(jìn)行配置。在進(jìn)行配置的時(shí)候,一個(gè)重要的選項是指定交叉工具鏈中GCC的位置,這對編譯機器和目標機器體系結構不同的情況比較有用。另外,ifconfig, ip, udhcp選項和子選項需要激活。等這些都配置好之后,需要配置InitUtilities部分,把你需要在系統啟動(dòng)階段啟動(dòng)的服務(wù)都打開(kāi)。這一些都配置好之后就可以編譯了。 接著(zhù)是編譯iptables, openssh, dnsmasq, grub,如果遇到的問(wèn)題的話(huà),這些自由軟件都有很好的安裝文檔。 需要安裝的軟件在前面已經(jīng)編譯好了,該是制作文件系統的時(shí)候了。首先,將busybox安裝到/mnt/firewall目錄下,這是你將會(huì )看到一個(gè)很親卻和熟悉的環(huán)境。接下來(lái)在/mnt/firewall下面創(chuàng )建以下的幾個(gè)文件夾:3.4、編譯應用程序
3.5、制作文件系統
接著(zhù),我們將往新創(chuàng )建的文件夾里面創(chuàng )建和拷貝程序、庫文件。
最后我們用chroot方式創(chuàng )建根用戶(hù)和啟動(dòng)腳本, 其中啟動(dòng)腳本里面的防火墻規則可以通過(guò)Firewall Builder這樣的自由軟件來(lái)定制。
3.6、安裝引導程序
我們這里選擇grub作為引導程序。將grub里面的stage1、stage2、e2fs_stage_1_5拷貝到boot目錄下,然后編輯menu.list,安裝grub的引導程序。到此,定制防火墻的工作基本上完成了。重新啟動(dòng),定制的成果馬上就可以看到了。
3.7、定制基于Netfilter的內核模塊
netfilter和iptables已經(jīng)提供了非常強大的功能,大部分情況下都夠用了。如果你需要一些特別的功能的話(huà),完全可以基于netfilter開(kāi)發(fā)自己的特色內核模塊。我們以一個(gè)例子來(lái)說(shuō)明如何做到這一點(diǎn)。
假如我們要實(shí)現禁止來(lái)自192.168.0.1的所有訪(fǎng)問(wèn)的功能,下面是實(shí)現這個(gè)功能的源代碼:#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語(yǔ)句開(kāi)頭。接著(zhù)定義了nf_hook_ops和網(wǎng)絡(luò )序的IP地址(192.168.0.1),同時(shí)定義了叫"lo"的回環(huán)接口。主要的功能是在函數main_hook中實(shí)現的。在這個(gè)函數中,我們比較了設備名稱(chēng),如果設備名稱(chēng)是回環(huán)接口,返回NF_DROP,也就是說(shuō)我們丟棄了這個(gè)包。接下來(lái),我們驗證了IP地址的有效性,最后比較了IP地址。如果IP地址相同,返回NF_ACCEPT,即丟棄這個(gè)IP包。否則接受這個(gè)IP包。
函數init_module是這個(gè)內核模塊的初始化函數,在這個(gè)模塊裝載進(jìn)內核空間的時(shí)候被執行。函數cleanup_module是退出函數,在這個(gè)模塊被從內核空間釋放掉以后被執行。
代碼寫(xiě)好了之后,我們需要編寫(xiě)makefile和Kconfig文件,且假定上面編寫(xiě)代碼文件名是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
現在你可以開(kāi)始試用你寫(xiě)的功能了。
3.8、開(kāi)發(fā)基于WEB的管理界面
如果你覺(jué)得基于命令行的防火墻管理不方便的話(huà),我們可以自己定制防火墻的WEB管理界面。下面介紹一下具體的方法。
首先,需要確定使用的網(wǎng)頁(yè)服務(wù)器。常用的網(wǎng)頁(yè)服務(wù)器包括apache,lighttpd等,可以考慮使用apache作為我們的網(wǎng)頁(yè)服務(wù)器(這當然有個(gè)人喜好的問(wèn)題)。但是,由于apache功能很強大,對于我們的防火墻來(lái)說(shuō),很多功能都是用不到的,所以我們需要對apache的功能進(jìn)行裁減。
接下來(lái)是選擇開(kāi)發(fā)語(yǔ)言的問(wèn)題。開(kāi)發(fā)語(yǔ)言可能是差異化比較大的,有C、C++、PERL、PHP、PYTHON可供選擇。由于我們的防火墻可能會(huì )運行在一些內存比較有限的機器上,并且對速度要求比較高,業(yè)務(wù)流程也比較簡(jiǎn)單,所以C和C++是比較適合的語(yǔ)言。當然你也可以用其他的語(yǔ)言,依賴(lài)于你的具體情況。
選擇了開(kāi)發(fā)語(yǔ)言之后,其實(shí)就應該可以開(kāi)發(fā)了。不過(guò),為了提高你的開(kāi)發(fā)效率,最好關(guān)注一下可以使用的自由軟件類(lèi)庫。比如對于使用C和C++開(kāi)發(fā)的話(huà),libcgi是一個(gè)不錯的類(lèi)庫。
準備工作做好之后就可以開(kāi)發(fā)了。我們下面以一個(gè)實(shí)例來(lái)說(shuō)明開(kāi)發(fā)的具體思路。比如我們想實(shí)現一個(gè)打開(kāi)IP數據包轉發(fā)的功能,下面是我們的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>打開(kāi)IP數據包轉發(fā)成功</title></head><bodygt;打開(kāi)IP數據包轉發(fā)成功</body></html>");
elseputs("<html><head><title>打開(kāi)IP數據包轉發(fā)失敗</title></head><bodygt;打開(kāi)IP數據包轉發(fā)失敗</body></html>");
cgi_end();
return 0;
}
這里我們使用了libcgi的類(lèi)庫函數,也就是以cgi_開(kāi)頭的函數。我們通過(guò)執行命令行指令打開(kāi)了IP數據包的轉發(fā)功能,然后根據返回值的不同輸出不同的返回信息。另外,為了使界面定制性更好,可以考慮使用模板引擎,比如clearsilver。五、總結
總的來(lái)說(shuō),對于常規的網(wǎng)絡(luò )安全應用,我們完全可以用已有的自由軟件定制出符合自己需求的防火墻,而不必購買(mǎi)硬件防火墻設備,這就是自由軟件的價(jià)值,自由軟件不但功能強大,更重要的是賦予了用戶(hù)使用軟件的自由。在面對網(wǎng)絡(luò )安全這樣的關(guān)鍵應用的時(shí)候,自由軟件讓你能夠自己控制自己的命運,而不依賴(lài)某個(gè)廠(chǎng)商。
form site : http://www.zeuux.org/science/free-software-and-network-security.cn.html

