您的位置:首页 > 其它

关于netfilter总结及包过滤防火墙实现

2010-04-22 08:57 218 查看

一.Netfilter简介

从Linux Kernel 2.4开始,一个新的网络包过滤框架替代了原来的pchains/ipfwadm系统,那就是netfilter和iptables。作为内核网络协议堆的一个扩展子集,netfilter可以在内核空间非常高效的进行包过滤,网络地址转换(NAT)和包重组等功能。同时,新的Netfilter/Iptables框架设计采取了更优的软件设计策略,如模块化设计,使其具有良好的扩展性,让全世界的网络程序员可以针对各自的应用的开发各种不同的应用模块。

二.Netfilter框架分析

1. Netfilter提供了一个框架,将对网络代码的直接干涉降到最低,并允许用规定的接口将其他包处理代码以模块的形式添加到内核中,具有极强的灵活性。
2. Netfilter事实上是内核网络协议栈中(如 IPv4, IPv6 和 DECnet)中的一些HOOK集合,通俗的说,netfilter的架构就是在整个网络流程的若干位置放置了一些监测点(HOOK),而在每个检测点上登记了一些处理函数进行处理(如包过滤,NAT等,甚至可以是用户自定义的功能)。
3. 一个理想的IPv4的遍历图如下:
IP层的五个HOOK点的位置如下图所示(copy from <packet filter howto>) :

--->[1]--->[ROUTE]--->[3]--->[4]--->
| ^
| |
| [ROUTE]
v |
[2] [5]
| ^
| |
v |
[local process]

[1]:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验 和等检测), 源地址转换在此点进行;
[2]:NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行;
[3]:NF_IP_FORWARD:要转发的包通过此检测点,FORWORD包过滤在此点进行;
[4]:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的目的地址转换功能(包括地址伪装)在此点进行;
[5]:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行。
在IP层代码中,有一些带有NF_HOOK宏的语句,如IP的转发函数中有:
<-ipforward.c ip_forward()->
NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, dev2, ip_forward_finish);
其中NF_HOOK宏的定义提炼如下:
<-/include/linux/netfilter.h->
#ifdef CONFIG_NETFILTER
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn)
(list_empty(&nf_hooks[(pf)][(hook)])
? (okfn)(skb)
: nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
#else /* !CONFIG_NETFILTER */
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
#endif /*CONFIG_NETFILTER*/
如果在编译内核时没有配置netfilter时,就相当于调用最后一个参数,此例中即执行ip_forward_finish函数;否则进入HOOK点,执行通过nf_register_hook()登记的功能
(这句话表达的可能比较含糊,实际是进入nf_hook_slow()函数,再由它执行登记的函数)。
NF_HOOK宏的参数分别为:
<1>.pf:协议族名,netfilter架构同样可以用于IP层之外,因此这个变量还可以有诸如 PF_INET6,PF_DECnet等名字。
<2>.hook:HOOK点的名字,对于IP层,就是取上面的五个值;
<3>.skb:套接字缓冲区的结构;
<4>.indev:进来的设备,以struct net_device结构表示;
<5>.outdev:出去的设备,以struct net_device结构表示;
(后面可以看到,以上五个参数将传到用nf_register_hook登记的处理函数中。)
<6>.okfn:是个函数指针,当所有的该HOOK点的所有登记函数调用完后,转而走此流程。
这些点是已经在内核中定义好的,除非你是这部分内核代码的维护者,否则无权增加或修改,而在此检测点进行的处理,则可由用户指定。像packet filter,NAT,connection track这些功能,也是以这种方式提供的。正如netfilter的当初的设计目标--提供一个完善灵活的框架,为扩展功能提供方便。
如果我们想加入自己的代码,便要用nf_register_hook函数,其函数原型为:
int nf_register_hook(struct nf_hook_ops *reg)
我们考察一下struct nf_hook_ops结构:
struct nf_hook_ops
{
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
int pf;
int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
我们的工作便是生成一个struct nf_hook_ops结构的实例,并用nf_register_hook将其HOOK上。其中list项我们总要初始化为{NULL,NULL};由于一般在IP层工作,pf总是PF_INET;hooknum就是我们选择的HOOK点;一个HOOK点可能挂多个处理函数,谁先谁后,便要看优先级,即priority的指定了。netfilter_ipv4.h中用一个枚举类型指定了内置的处理函数的优先级:
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_LAST = INT_MAX,
};
hook是提供的处理函数,也就是我们的主要工作,其原型为:
unsigned int nf_hookfn(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *));
它的五个参数将由NFHOOK宏传进去。
了解了这些,基本上便可以可以写一个lkm出来了。
4. 所有的包过滤、NAT等功能都是基于此结构。
5. Netfilter基础
从上面关于IPv4的netfilter的例子讨论,可以看到钩子函数是如何被激活的。
内核模块可以对一个或多个这样的钩子函数进行注册挂接,并且在数据报经过这些钩子函数时被调用,从而模块可以修改这些数据报,并向netfilter返回如下值:
NF_ACCEPT 继续正常传输数据报
NF_DROP 丢弃该数据报,不再传输
NF_STOLEN 模块接管该数据报,不要继续传输该数据报
NF_QUEUE 对该数据报进行排队(通常用于将数据报给用户空间的进程进行处理)
NF_REPEAT 再次调用该钩子函数

总结:
Netfilter的展示框架简化了Netfilter的结构,看之一目了然,这也是最基层的构架方式,就像建房子的地基一样。

三.Netfilter体系结构

1. Netfilter主要通过表、链实现规则,可以这么说,Netfilter是表的容器,表是链的容器,链是规则的容器,最终形成对数据报处理规则的实现。
详细地说,Netfilter/IPTables的体系结构可以分为三大部分:
<1>. Netfilter的HOOK机制:
HOOK提供了一种方便的机制:在数据报通过linux内核的不同位置上截获和操作处理数据报
<2>. IPTables基础模块:
实现了三个表(filter表,nat表,mangle表)来筛选各种数据报。表是通过规则链实现的
<3>. 具体功能模块:
数据报过滤模块、连接跟踪模块、网络地址转换模块、
数据报修改模块 、其他高级功能模块

2. Netfilter/IPTables总体架构如图:

总结:
Netfilter的体系结构说明了从功能到实现的流程,以及各个层次的关系,就像建好的房子内地基与墙以及房梁加上房梁柱之间的系统关联一样。

四.向规则链中插入规则的模式

inux2.4提供了一个简洁强大的工具"iptables"来插入/删除/修改规则链中的规则。使它作为Netfilter配置的窗口。这里并不对iptalbes进行详细的介绍,而只是讨论它的主要的一些特性:
该命令实现对所有的ip表进行处理,当前包括filter,nat及mangle三个表格,及以后扩展的表模块。
该命令支持插件来支持新的匹配参数和目标动作。因此对netfilter的任何扩展都非常的简单。仅仅需要编写一个完成实际目标动作处理的模块和iptalbes插件(动态连接库)来添加所需要的一切。
它有两个实现:iptables(IPV4)及ip6tables。两者都基于相同的库和基本上相同的代码。
基本的iptables命令
一个iptables命令基本上包含如下五部分:
希望工作在哪个表上
希望使用该表的哪个链
进行的操作(插入,添加,删除,修改)
对特定规则的目标动作
匹配数据报条件
基本的语法为:
iptables -t table -Operation chain -j target match(es)
例如希望添加一个规则,允许所有从任何地方到本地smtp端口的连接:
iptables -t filter -A INPUT -j ACCEPT -p tcp --dport smtp
当然,还有其他的对规则进行操作的命令如:清空链表,设置链缺省策略,添加一个用户自定义的链....
基本操作:
-A 在链尾添加一条规则
-I 插入规则
-D 删除规则
-R 替代一条规则
-L 列出规则
基本目标动作,适用于所有的链
ACCEPT 接收该数据报
DROP 丢弃该数据报
QUEUE 排队该数据报到用户空间
RETURN 返回到前面调用的链
foobar 用户自定义链
基本匹配条件,适用于所有的链
-p 指定协议(tcp/icmp/udp/...)
-s 源地址(ip address/masklen)
-d 目的地址(ip address/masklen)
-i 数据报输入接口
-o 数据报输出接口
出了基本的操作,匹配和目标还具有各种扩展。
总结:
Iptables作为Netfilter配置的窗口(通过socket与内核通信),有其独特的意义,可以省去直接对话netfilter,作为相得益彰的配套工具使用,还是不错的

五.为什么需要Netfilter?

其实这个问题也可以变为ipchains有什么缺点导致被抛弃?下面只是其中的几个原因:
因为基于2.2内核的ipchains没有提供传递数据包到用户空间的框架,所以任何需要对数据包进行处理的代码都必须运行在内核空间,而内核编程却非常复杂,而且只能用C语言实现,并且容易出现错误并对内核稳定性造成威胁。
透明代理实现非常复杂,必须查看每个数据包来判断是否有专门处理该地址的socket。网络栈代码中在11个文件中共出现了34个"#ifdef"条件编译。
创建一个不依赖于接口地址的数据报过滤规则是不可能实现的。我们必须利用本地接口地址来判断数据报是本地发出、还是发给本地的或是转发的。转发链只有输出接口的信息,因此管理员必需考虑数据报的源。
伪装和数据包过滤都在同一个模块内实现,导致防火墙代码过于复杂。
IPchains代码即不模块化又不易于扩展(例如对mac地址的过滤)
总结:
之所以说明这个问题是由于这样可以锐化Netfilter的特点,便于熟悉

六.用Netfilter/Iptables设计安全防火墙

在Netfilter的框架结构上,我们可以利用iptables应用程序来和内核通信,制定一系列规则,对数据包进行处理。iptables 内建了3个表,filter表,nat表和mangle表,分别用于实现包过滤,网络地址转换和包重构的功能。本文中,主要针对filter表进行操作。最常用的,对于防火墙来说,就是利用在filter表中指定一系列规则(rules)以实现对数据包进行过滤的操作。在filter表中只允许对数据包进行接受,丢弃的操作,而无法对数据包进行更改。
在iptables中规定,链(chains)是规则(rules)的集合,filter表包含3个chain,分别为INPUT,FORWARD,OUTPUT。通过前面的分析,可以确定该3条规则链将分别在:(1)数据包在送入本地进程之前、(2)数据报在内核通过路由算法即将被转发之前,和(3)本地进程向网络发送数据包前三个关键HOOK位置发挥作用。
为了防止外部入侵,更多时候,我们只要在INPUT链中进行过滤即可。现给出一个简单的例子:
首先,设定缺省的策略Policy为DROP,这样,当内核遍历INPUT CHAINS的时候,如果没有满足任何规则,则将数据包按照缺省策略操作。
# iptables -P INPUT DROP
接下来,针对已经建立连接的数据包,我们允许其畅通无阻。
# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
然后逐步开放对应的服务端口,例如,需要提供HTTP服务,开放80端口,命令如下:
# iptables –A INPUT –p tcp –dport 80 –j ACCEPT

也许,有时候,我们并不希望简单的丢弃我们不需要的数据包,因为,当远方主机发送SYN数据包过来的时候,我们如果只是简单的丢弃的话,对方主机还是会持续发送SYN数据包过来,直到多次超时失败才放弃。所以我们可以用REJECT target来拒绝数据包的同时,发送给对方主机一个RST包,终止对方连接,用下面的命令取代上面的命令即可。
iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset
小结:Netfilter/Iptables的包过滤架构是Linux内核开发人员通过对ipfw/ipfwadm/ipchains等早期的包过滤程序的开发经验和全世界用户反馈的分析,重新设计,改造而形成的相对成熟的Linux内核包过滤框架。同时,由于公开源代码,全世界的网络开发人员针对各自的需要和网络出现的新情况,在义务的开发着对应的模块,这极大的丰富了iptables功能。采用 netfilter/iptables设计的网络防火墙,完全可以媲美专业的网络防火墙,更重要的是,他的成本几乎为零。
总结:
可以根据相应防火墙的需求进行相应的配置

七.Netfilter可以实现的功能:

Netfilter防火墙除了可以实现 数据报过滤模块、连接跟踪模块、网络地址转换模块、数据报修改模块(实现见Netfilter-iptables框架简介)外,还可以实现:
Reject:丢弃包并通知包的发送者,同时返回给发送者一个可配置的icmp错误信息,由ipt_Reject.o完成
Mirror:互换源和目的地址以后并重新发送,由ipt_mirror.o完成
Log: 将匹配的数据包传递给系统的syslog()进行记录,由ipt_log.0完成
Ulog:Userspace logging,将数据报排队转发到用户空间中,将匹配的数据适用用户空间的log进程进行记录,由ip_ULOG.o完成。这是Netfilter的一个关键技术,可以使用户进程可以进行复杂的数据报操作,从而减轻内核空间中的复杂度。
Queuing:这是上面的Ulog技术的基础,由ip_queue.o完成,提供可靠地异步包处理以及性能两号的libipq库赖进行用户控件数据包操作的开发。
等等,,,,

以下(数据报过滤模块)作为包过滤防火墙的一个实现:

典型的防火墙设置有两个网卡:一个流入,一个流出。iptables读取流入和流出数据包的报头,将它们与规则集(Ruleset)相比较,将可接受的数据包从一个网卡转发至另一个网卡,对被拒绝的数据包,可以丢弃或按照所定义的方式来处理。通过向防火墙提供有关对来自某个源地址、到某个目的地或具有特定协议类型的信息包要做些什么的指令,规则控制信息包的过滤。通过使用iptables系统提供的特殊命令iptables建立这些规则,并将其添加到内核空间特定信息包过滤表内的链中。

1. 数据包过滤模块
(对于防火墙的设置,有两种策略:一种是全部通讯口都允许使用,只是阻止一些我们知道的不安全的或者容易被利用的口;另外一种,则是先屏蔽所有的通讯口,而只是允许我们需要使用的通讯端口。这里使用的是第二种原则,如果你需要开启其他端口,请先参考计算机通讯口说明,然后自己添加。)
下面为常用的过滤规则:
代码:
#对新配置防火墙的前提配置:
#删除原来 iptables 里面已经有的规则(默认表为filter)
iptables -F
iptables –X

#抛弃所有不符合三种链规则的数据包(没有找到相对应的规则后,默认的处理方法)
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

#设置:本地进程 lo 的 INPUT 和 OUTPUT 链接 ; eth1的 INPUT链
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -i eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -i eth1 -m state --state NEW,INVALID -j LOG
iptables -A OUTPUT -o lo -j ACCEPT

#内网可以访问外网服务的配置
# DNS
iptables -A OUTPUT -o eth1 -p TCP --sport 1024:65535 --dport 53 -j ACCEPT
iptables -A OUTPUT -o eth1 -p UDP --sport 1024:65535 --dport 53 -j ACCEPT
#HTTP
iptables -A OUTPUT -o eth1 -p TCP --sport 1024:65535 --dport 80 -j ACCEPT
#HTTPS
iptables -A OUTPUT -o eth1 -p TCP --sport 1024:65535 --dport 443 -j ACCEPT
#Email 接受 和发送
iptables -A OUTPUT -o eth1 -p TCP --sport 1024:65535 --dport 110 -j ACCEPT
iptables -A OUTPUT -o eth1 -p TCP --sport 1024:65535 --dport 25 -j ACCEPT
# FTP 数据和控制
iptables -A OUTPUT -o eth1 -p TCP --sport 1024:65535 --dport 20 -j ACCEPT
iptables -A OUTPUT -o eth1 -p TCP --sport 1024:65535 --dport 21 -j ACCEPT
#DHCP
iptables -A OUTPUT -o eth1 -p TCP --sport 1024:65535 --dport 68 -j ACCEPT
iptables -A OUTPUT -o eth1 -p UDP --sport 1024:65535 --dport 68 -j ACCEPT
#POP3S Email安全接收
iptables -A OUTPUT -o eth1 -p TCP --sport 1024:65535 --dport 995 -j ACCEPT
#时间同步服务器 NTP
iptables -A OUTPUT -o eth1 -p TCP --sport 1024:65535 --dport 123 -j ACCEPT
#拒绝 eth1 其他剩下的
iptables -A OUTPUT -o eth1 --match state --state NEW,INVALID -j LOG

#外网访问内网服务器的配置:
#web 服务器:
Iptables –A INPUT –p tcp –-dport 80 –j ACCEPT
Iptables –A OUTPUT –p tcp -–sport 80 –j ACCEPT
#ssh:
Iptables –A INPUT –p tcp –-dport 22 –j ACCEPT
Iptables –A OUTPUT –p tcp -–sport 22 –j ACCEPT
#邮件服务:
Iptables –A INPUT –p tcp –-dport 110 –j ACCEPT
Iptables –A INPUT –p tcp –-dport 25 –j ACCEPT
Iptables –A OUTPUT –p tcp -–sport 110 –j ACCEPT
Iptables –A OUTPUT –p tcp –-sport 25 –j ACCEPT
#ftp:
Iptables –A INPUT –p tcp –-dport 21 –j ACCEPT
Iptables –A INPUT –p tcp –-dport 20 –j ACCEPT
Iptables –A OUTPUT –p tcp –-sport 21 –j ACCEPT
Iptables –A OUTPUT –p tcp –-sport 20 –j ACCEPT
#允许ping:
Iptables –A INPUT –p icmp –j ACCEPT
Iptables –A OUTPUT –p icmp –j ACCEPT
最后是有关于iptables存储的命令:
代码:
iptables-save > /etc/iptables.up.rule # 存在你想存的地方

代码:
iptables-restore < /etc/iptables.up.rules #调用
因为iptables 在每次机器重新启动以后,需要再次输入或者调用,为了方便操作,使用
代码:
sudo gedit /etc/network/interfaces


代码:
auto ath0
iface ath0 inet dhcp
后面加上
代码:
pre-up iptables-restore < /etc/iptables.up.rules #启动自动调用已存储的iptables

代码:
post-down iptables-save > /etc/iptables.up.rule #关机时,把当前iptables 储存

2. 网络地址转换

SNAT

你想要进行Source NAT,改变连接的源地址。这在POSTROUIING链中完成,就在它将送出去的最后一刻。这是一个重要的细节,所有Linux本机上的其他任何东西(路由、包过滤)都会看见那个尚未改变的包。也意味着'-o'(送出接口)选项可用了。
用指定'-j SNAT'来进行Source NAT,'--to-source'选项指定一个或一段IP地址,(加上)一个或一段可选的端口号(只能用于UDP和TCP协议)。
# 改变源地址为1.2.3.4
# iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 1.2.3.4

# 改变源地址为1.2.3.4、1.2.3.5或者1.2.3.6
# iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 1.2.3.4-1.2.3.6

# 改变源地址为1.2.3.4,端口1-1023
# iptables -t nat -A POSTROUTING -p tcp -o eth0 -j SNAT --to 1.2.3.4:1-1023

伪装
Source NAT的一个特例被称作伪装。它只能被用于动态分配IP地址的情况。例如标准拨号服务(静态IP地址请用SNAT)。
你无需为IP伪装明确指定源地址。它会使用包送出的那个接口(地址)作为源地址。不过更重要的是,如果那个线路关闭了的话,连接(无论如何都会丢失了)会被忘掉,意味着启用新的IP后返回的包就会有点问题了(指那些响应掉线前发出的包的包)。
# 伪装所有由ppp0送出的东西
# iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE

DNAT

用于PREROUTING链,包刚刚进入的时候。意味着本机上的任何东西看见的都是“真正”的目的地(译者注:即已修改过的目的地址)。也意味着'-i'(进入接口)可用了。
用指定'-j DNAT'来进行Destination NAT,'--to-destination'选项指定定一个或一段IP地址,(加上)一个或一段可选的端口号(只能用于UDP和TCP协议)。
# 改变目标地址为5.6.7.8
# iptables -t nat -A PREROUTING -i eth0 -j DNAT --to 5.6.7.8
# 改变目标地址为5.6.7.8、5.6.7.9或5.6.7.10
# iptables -t nat -A PREROUTING -i eth0 -j DNAT --to 5.6.7.8-5.6.7.10
# 改变Web传送的目标地址为5.6.7.8,8080端口
# iptables -t nat -A PREROUTING -p tcp --dport 80 -i eth0 -j DNAT --to 5.6.7.8:8080

重定向
Destination NAT的一个特例被称为重定向。它相当于对进入接口进行DNAT的简单方便的一种形式。
# 发送进入的80端口的Web传输到我们的Squid(透明)代理
# iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 3128
注意Squid需要被配置为透明代理。

多重映射,重叠和冲突<
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: