Linux防火墙:Netfilter的深度解析
想象一下,Linux 系统的网络世界是一座繁华的城市,数据包就像是川流不息的车辆。但是,这座城市需要交通规则和守护者来维持秩序,避免混乱和危险。Netfilter 就像是这座网络城市的超级警察和交通指挥中心,它能够精准地识别哪些 “车辆” 可以通行,哪些需要拦截。现在,就让我们深入了解一下这位网络世界的 “守护者”——Netfilter 的工作原理和应用吧!
一、Netfilter概述
Linux 2.4.x引入的子系统,Netfilter是Linux 2.4.x引入的一个子系统,它作为一个通用的、抽象的框架,提供一整套的hook函数的管理机制,使得诸如数据包过滤、网络地址转换(NAT)和基于协议类型的连接跟踪成为了可能, 在 Linux 系统中发挥着至关重要的作用。它是一个强大而灵活的网络包过滤框架,为系统管理员提供了对网络数据包的精细控制。
Netfilter 通过在内核中插入钩子点,实现对网络数据包的过滤、修改和转发。它在 IP 报文处理流程中插入了 5 个挂载点,分别是 NF_IP_PRE_ROUTING、NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_LOCAL_OUT 和 NF_IP_POST_ROUTING。在这些挂载点上,可以注册处理数据包的回调函数。当数据包进入 Linux 内核经过挂载点的时候,会执行回调函数来处理数据包。
回调函数有多种返回值,例如 NF_ACCEPT 表示继续正常的报文处理;NF_DROP 将报文丢弃;NF_STOLEN 表示由钩子函数处理了该报文,不要再继续传送;NF_QUEUE 将报文入队,通常交由用户程序处理;NF_REPEAT 表示再次调用该钩子函数。
Netfilter 支持多种协议栈,目前 Linux 2.6 版内核的 Netfilter 支持 IPv4、IPv6 以及 DECnet 等协议栈。它主要通过表、链实现规则,其中规则是对特定报文的处理说明,包括匹配字段和 action;链是一组规则的集合;表是链中相同功能的规则集合。
Netfilter 可以实现多种功能,如在 Linux 内核中利用 Netfilter 解析数据包,自适应多层 VLan 和 PPPoe 网络环境;对特定 DNS 域名的请求数据包进行过滤;解析 IP 层数据头部;对特定端口的数据包进行过滤;解析 HTTP 请求和返回数据包,对特定 Host、URI、文件下载的数据包进行过滤;在以太网环境中,对数据包进行处理;在网桥环境中,对经过网桥的数据包进行解析。
二、Netfilter工作原理
2.1什么是Netfilter
Netfilter是Linux内核中的一个数据包处理模块,它可以提供数据包的过滤、转发、地址转换NAT功能。Iptables是一个工具,可以用来在Netfilter中增加、修改、删除数据包处理规则,Netfilter是位于网卡和内核协议栈之间的一堵墙,是一种免费的软件防火墙。
Netfilter中有三个主要的概念:规则、表、链,等级依次递增
- 规则是对特定报文的处理说明,包括匹配字段和action。
- 链是一组规则的集合。
- 表是链中相同功能的规则集合。
这幅示意图中,IP包一进一出,有几个关键的检查点,它们正是Netfilter设置防火墙的地方。Netfilter通过向内核协议栈中不同的位置注册钩子函数来对数据包进行过滤或者修改操作,这些位置称为挂载点,主要有 5 个:PRE_ROUTING、LOCAL_IN、FORWARD、LOCAL_OUT 和 POST_ROUTING,如下图所示:
* PRE_ROUTING:IP包进入IP层后,还没有对数据包进行路由判定前;
- LOCAL_IN:进入主机,对IP包进行路由判定后,如果IP 包是发送给本地的,在进入传输层之前对IP包进行过滤;
- LOCAL_OUT:IP包通过传输层进入用户空间,交给用户进程处理。而处理完成后,用户进程会通过本机发出返回的 IP 包,在没有对输出的IP包进行路由判定前进行过滤;
- FORWARD:IP包进行路由判定后,如果IP包不是发送给本地的,在转发IP包出去前进行过滤;
- POST_ROUTING:对于输出的IP包,在对IP包进行路由判定后进行过滤;
在图中可以看出决定IP包走向就是路由,按照路由的判定可以分为两条路线:
- 第一个路由通过查找输入数据包 IP头部的IP地址,判断是否为本机的IP地址是否一致,如果与本机的 IP地址一致,说明数据是发送给本机的,否则说明数据包是发送给其他主机,只是经过本机中转;
- 第二个路由判定根据输出数据包 IP头部的IP地址 从路由表中查找对应的路由信息,然后根据路由信息获取下一主机的 IP地址,然后进行数据传输;
通过向挂载点注册钩子函数,就能够对处于不同阶段的数据包进行过滤或者修改操作。由于钩子函数能够注册多个,因此挂载点通过链表链接,所以挂载点又被称为链,因此LOCAL_IN挂载点又称为INPUT链、LOCAL_OUT 挂载点又称为 OUTPUT链、FORWARD挂载点又称为 PORWARD链、PRE_ROUTING挂载点又称为 PREROUTING链、POST_ROUTING挂载点又称为 POSTOUTING链。
2.2什么是iptables
iptables是建立在 Netfilter 之上的数据包过滤器,通过向 Netfilter 的挂载点上注册钩子函数来实现对数据包过滤的,从iptables这个名字上可以看出一定具有表的概念,iptables通过把这些规则表挂载在 Netfilter 的不同链上,对进出内核协议栈的数据包进行过滤或者修改操作。
iptables包括四种表:
- Filter表用于过滤数据包,是iptables的默认表,因此如果你配置规则时没有指定表,那么就默认使用Filter表,Filter表可以作用于INPUT链、OUTPUT链、PORWARD链;
- NAT表用于对数据包的网络地址转换(IP、端口),分别可以挂载到PREROUTING链、POSTOUTING链、OUTPUT链;
- Mangle主要用来修改IP数据包头,比如修改TTL值,同时也用于给数据包添加一些标记,从而便于后续其它模块对数据包进行处理,可以作用在所有链上;
- Raw表用于判定数据包是否被状态跟踪处理,可以作用于PREROUTING链、OUTPUT链;
数据包从网络中进入到内核协议栈的过程中,要执行的 iptables 规则,如果在执行某条 iptables 规则失败后,会直接把数据包丢弃,不会继续执行下面的规则。
表的实现
表的基本数据结构是ipt_table(位于include/linux/netfilter_ipv4/iptables.h,Line413)。
1 struct ipt_table
{
struct list_head list; // 一个双向链表
char name[IPT_TABLE_MAXNAMELEN]; // 被用户空间使用的表函数的名字
struct ipt_replace *table; // 表初始化的模板,定义了一个初始化用的表,该表的所默认的HOOK 所包含的规则等信息
// 用户通过系统调用进行表的替换时也要用
unsigned int valid_hooks; // 表所监听的HOOK,实质是一个位图
rwlock_t lock; // 整个表的读/写自旋锁
struct ipt_table_info *private; // 表所存储的数据信息,也就是实际的数据区,
// 仅在处理ipt_table 的代码内部使用
struct module *me; // 如果是模块,那么取THIS_MODULE,否则取NULL
};
其中:
unsigned int valid_hooks;:这个位图有两个作用:一个是检查Netfilter中哪些Hook对应着合法的entries;二是用来为ipt_match以及ipt_target数据结构中的checkentry()函数核算可能的Hook。
struct module *me;:当取值为THIS_MODULE时,可以阻止用户rmmod一个仍然被某个规则指向的模块的尝试。
struct ipt_replace *table;:该数据结构是被用户空间来替换一个表的,其定义位于include/linux/netfilter_ipv4/ip_tables.h,Line230。
struct ipt_replace
{
char name[IPT_TABLE_MAXNAMELEN];
unsigned int valid_hooks;
unsigned int num_entries; // 规则表入口的数量
unsigned int size; // 新的规则表的总大小
/* Hook entry points. */
// 表所监听HOOK 的规则入口, 是对于entries[ ]的偏移
unsigned int hook_entry[NF_IP_NUMHOOKS];
unsigned int underflow[NF_IP_NUMHOOKS]; // 规则表的最大下界
// 旧的计数器数目,即当前的旧entries 的数目
unsigned int num_counters;
struct ipt_counters *counters; // 旧的计数器
struct ipt_entry entries[0]; // 规则表入口
};
上文所提到的filter、nat、mangle表分表是ipt_table这个数据结构的三个实例:packet_filter(位于net/ipv4/netfilter/iptable_filter.c,Line84)、Nat(位于net/ipv4/netfilter/ip_nat_rule.c,Line104)以及packet_mangler(位于net/ipv4/netfilter/iptable_mangler.c,Line117);
ipt_table_info(位于net/ipv4/netfilter/iptables.c,Line86),是实际描述规则表的数据结构。
struct ipt_table_info
{
unsigned int size;
unsigned int number; // 表项的数目
unsigned int initial_entries; // 初始表项数目
// 所监听HOOK 的规则入口
unsigned int hook_entry[NF_IP_NUMHOOKS];
unsigned int underflow[NF_IP_NUMHOOKS]; // 规则表的最大下界
// 规则表入口,即真正的规则存储结构ipt_entry 组成块的起始地址,
//对多CPU,每个CPU 对应一个
char entries[0] ____cacheline_aligned;
};
添加iptables规则
使用 iptables 命令添加规则,iptables可以分为四部分:
iptables -t表 -A链 匹配规则 动作
匹配条件
匹配条件分为基本匹配条件与扩展匹配条件,基本匹配条件包括源IP地址和目标IP地址等,扩展匹配条件包括源端口和目标端口等;
处理动作
处理动作是指当匹配条件成功后要进行的一系列操作过程,动作也可以分为 基本动作 和 扩展动作,常用的动作如下:
- ACCEPT:允许数据包通过;
- DROP:直接丢弃数据包,不给任何回应信息;
- REJECT:拒绝数据包通过,必要时会给数据发送端一个响应的信息,客户端刚请求就会收到拒绝的信息;
- SNAT:源IP地址转换;
- DNAT:目标IP地址转换;
- REDIRECT:在本机做端口映射;
-t <表>:指定要操纵的表;
-A <链>:向规则链中添加条目;
-D <链>:从规则链中删除条目;
-I <链>:向规则链中插入条目;
-R <链>:替换规则链中的条目;
-L:显示规则链中已有的条目;
-F:清楚规则链中已有的条目;
-Z:清空规则链中的数据包计算器和字节计数器;
-N:创建新的用户自定义规则链;
-P:定义规则链中的默认目标;
-h:显示帮助信息;
-p:指定要匹配的数据包协议类型;
-s:指定要匹配的数据包源ip地址;
-j <动作>:指定要进行的动作行为;
-i <网络接口>:指定数据包进入本机的网络接口;
-o <网络接口>:指定数据包要离开本机所使用的网络接口。
--dport <端口>:匹配目标端口号。
--sport <端口>:匹配来源端口号。
一条完整的规则由三个数据结构共同实现,分别是:
一个ipt_entry结构,存储规则的整体信息;
0或多个ipt_entry_match结构,存放各种match,每个结构都可以存放任意的数据,这样也就拥有了良好的可扩展性;
1个ipt_entry_target结构,存放规则的target,类似的,每个结构也可以存放任意的数据;
下面将依次对这三个数据结构进行分析:
存储规则整体的结构ipt_entry,其形式是一个链表(位于include/linux/netfilter_ipv4/ip_tables.h,Line122),如下所示:
struct ipt_entry
{
struct ipt_ip ip;
unsigned int nfcache;
u_int16_t target_offset;
u_int16_t next_offset;
unsigned int comefrom;
struct ipt_counters counters;
unsigned char elems[0];
};
其成员含义如下:
struct ipt_ip ip;:这是对其将要进行匹配动作的IP 数据报报头的描述,其定义于include/linux/netfilter_ipv4/ip_tables.h,Line122,其成员包括源/目的IP 及其掩码,出入端口及其掩码,协议族、标志/取反flag 等信息。
unsigned int nfcache;:HOOK 函数返回的cache 标识,用以说明经过这个规则后数据报的状态,其可能值有三个,定义于include/linux/netfilter.h,Line23:
#define NFC_ALTERED 0x8000 //已改变
#define NFC_UNKNOWN 0x4000 //不确定
另一个可能值是0,即没有改变。
u_int16_t target_offset;:指出了target 的数据结构ipt_entry_target的起始位置,即从ipt_entry 的起始地址到matc存储结束的位置 u_int16_t next_offset;:指出了整条规则的大小,也就是下一条规则的起始地址,即ipt_entry 的起始地址到match 偏移再到target 存储结束的位置
unsigned int comefrom;:所谓的“back pointer”,据引用此变量的代码(主要是net/ipv4/netfilter/ip_tables.c 中)来看,它应该是指向数据报所经历的上一个规则地址,由此实现对数据报行为的跟踪
struct ipt_counters counters;:说明了匹配这个规则的数据报的计数以及字节计数(定义于include/linux/netfilter_ipv4/ip_tables.h,Line100)
unsigned char elems[0];:表示扩展的match 开始的具体位置(因为它是大小不确定的),当然,如果不存在扩展的match 那么就是target 的开始位置
扩展match的存储结构ipt_entry_match,位于include/linux/netfilter_ipv4/ip_tables.h,Line48。
struct ipt_entry_match
{
union {
struct {
u_int16_t match_size;
char name[IPT_FUNCTION_MAXNAMELEN];
} user;
struct {
u_int16_t match_size;
struct ipt_match *match;
} kernel;
u_int16_t match_size; //总长度
} u;
unsigned char data[0];
};
其中描述match 大小的`u_int16_t match_size;`,从涉及这个变量的源码看来,在使用的时候需要注意使用一个宏IPT_ALIGN(位于include/linux/netfilter_ipv4/ip_tables.h,Line445)来进行4 的对齐处理(0x3 & 0xfffffffc),这应该是由于match、target 扩展后大小的不确定性决定的。
在结构中,用户空间与内核空间为不同的实现,内核空间中的描述拥有更多的信息。在用户空间中存放的仅仅是match 的名称,而在内核空间中存放的则是一个指向ipt_match 结构的指针。
结构ipt_match 位于include/linux/netfilter_ipv4/ip_tables.h,Line342:
struct ipt_match
{
void *matchinfo, // 指向规则中match 数据的指针,
// 具体是什么数据结构依情况而定
int offset, // IP 数据报的偏移
const void *hdr, // 指向协议头的指针
u_int16_t datalen, // 实际数据长度,即数据报长度-IP 头长度
int *hotdrop);
int (*checkentry)(const char *tablename, // 可用的表
const struct ipt_ip *ip,
void (*matchinfo,
unsigned int matchinfosize,
unsigned int hook_mask); // 对应HOOK 的位图
void (*destroy)(void *matchinfo,
unsigned int matchinfosize);
struct module *me;
};
其中几个重要成员:
`int (*match)(……);`:指向用该match 进行匹配时的匹配函数的指针,match 相关的核心实现。返回0 时hotdrop 置1,立即丢弃数据报;返回非0 表示匹配成功。
`int (*checkentry)(……);`:当试图插入新的match 表项时调用这个指针所指向的函数,对新的match 表项进行有效性检查,即检查参数是否合法;如果返回false,规则就不会被接受(譬如,一个TCP
的match 只会TCP 包,而不会接受其它)。
`void (*destroy)(……);`:当试图删除一个使用这个match 的表项时,即模块释放时,调用这个指针所指向的函数。我们可以在checkentry()中动态地分配资源,并在destroy 中将其释放。
target的存储结构ipt_entry_target,位于include/linux/netfilter_ipv4/ip_tables.h,Line71,这个结构与ipt_entry_match结构类似,同时其中描述内核target结构ipt_target(位于include/linux/netfilter_ipv4/iptables.h,Line375)也与ipt_match类似,只不过其中的target()函数返回值不是0/1,而是verdict。
而target的实际使用中,是用一个结构ipt_standard_target专门来描述,这才是实际的target描述数据结构(位于include/linux/netfilter_ipv4/iptables.h,Line94),它实际上就是一个ipt_entry_target + verdict。
其中成员verdict这个变量时一个很巧妙的设计,也是一个非常重要的东西,其值的正负有着不同的意义。它的值包括IPT_CONTINUE、IPT_RETURN以及前文所述的NF_DROP等值,那么它的作用是什么呢?原因如下:
o 由于iptables 是在用户空间中执行的,也就是说Netfilter/IPTables这个框架需要用户态与内核态之间的数据交换以及识别。而在具体的程序中,verdict 作为`struct ipt_standard_target`的一个成员,也是对于`struct ipt_entry_target`中的target()函数的返回值。这个返回值标识的是target()所对应的执行动作,包括系统的默认动作以及外部提交的自定义动作。
o 但是,在用户空间中提交的数据往往是类似于“ACCPET”之类的字符串,在程序处理时则是以`#define NF_ACCEPT 1`的形式来进行的;而实际上,以上那些执行动作是以链表的数据结构进行存储的,在内核空间中表现为偏移。
o 于是,verdict 实际上描述了两个本质相同但实现不同的值:一个是用户空间中的执行动作,另一个则是内核空间中在链表中的偏移——而这就出现了冲突。
o 解决这种冲突的方法就是:用正值表示内核中的偏移,而用负值来表示数据报的那些默认动作,而外部提交的自定义动作则也是用正值来表示。这样,在实际使用这个verdict 时,我们就可以通过判断值的正负来进行相应的处理了。
o 位于net/ipv4/netfilter/ip_tables.h 中的函数ipt_do_table()中有一个典型的verdict 使用(Line335,其中v 是一个verdict 的实例):
if (v !=IPT_RETURN) {
verdict = (unsigned)(-v) - 1;
break;
}
其中的IPT_RETURN 定义为:
#define IPT_RETURN (-NF_MAX_VERDICT – 1)
而宏NF_MAX_VERDICT 实际上就是:
#define NF_MAX_VERDICT NF_REPEAT
这样,实际上IPT_RETURN 的值就是-NF_REPEAT-1,也就是对应REPEAT,这就是对执行动
match的定位如下:
- 起始地址为:当前规则(起始)地址 + sizeof(struct ipt_entry);
- 结束地址为:当前规则(起始)地址 + ipt_entry->target_offset;
- 每一个match的大小为:ipt_entry_match->u.match_size;
target的定位则为:
- 起始地址为match的结束地址,即:当前规则(起始)地址 + ipt_entry->target_offset;
- 结束地址为下一条规则的起始地址,即:当前规则(起始)地址 + ipt_entry->next_offset;
- 每一个target的大小为:ipt_entry_target->u.target_size;
这些对于理解match以及target相关函数的实现是很有必要明确的。
同时,include/linux/netfilter_ipv4/ip_tables.h中提供了三个“helper functions”,可用于使对于entry、target和match的操作变得方便,分别为:
- 函数ipt_get_target(),作用是取得target的起始地址,也就是上面所说的当前规则(起始)地址 + ipt_entry->target_offset;
- 宏IPT_MATCH_ITERATE(),作用是遍历规则的所有match,并执行同一个(参数中)给定的函数。其参数为一个ipt_entry_match结构和一个函数,以及函数需要的参数。当返回值为0时,表示遍历以及函数执行顺利完成;返回非0值时则意味着出现问题已终止;
- 宏IPT_ENTRY_ITERATE(),作用是遍历一个表中的所有规则,并执行同一个给定的函数。其参数是一个ipt_entry结构、整个规则表的大小,以及一个函数和其所需的参数。其返回值与宏IPT_MATCH_ITERATE()类似;
那么,如何保证传入的ipt_entry结构是整个规则表的第一个结构呢?据源码看来,实际调用这个宏的时候传入的第一个参数都是某个ipt_table_info结构的实例所指向的entries成员,这样就保证了对整个规则表的完整遍历。
规则的使用
当一个特定的Hook被激活后,数据报就进入Netfilter/iptables系统进行遍历,首先检查‘struct ipt_ip ip’,然后数据报将依次遍历各个match,也就是’struct ipt_entry_match’,并执行相应的match函数,即ipt_match结构中的*match所指向的函数。当match函数匹配不成功时返回0,或者hotdrop被置为1时,遍历将会停止。(这部分有点乱,先放这,等看源码好好看看。。)
对match的遍历完成后,会开始检查’struct ipt_entry_target’,其中如果是一个标准的target,那么会检查‘struct ipt_standard_target’中的verdict,如果verdict值是正的而偏移却指向了不正确的位置,那么,ipt_entry中的comefrom成员就有了用武之地——数据报返回所经历的上一个规则。对于非标准的target呢?就会调用target()函数,然后根据其返回值进行后面的处理。(对于verdict不太明白,先放这。。)
iptables 与 Netfilter 的关系
iptables 作为用户空间的工具,为用户提供了一个直观的界面来编写防火墙规则。用户可以通过命令行操作 iptables,将特定的规则配置下发到内核空间的 Netfilter。而 Netfilter 作为内核中的防护框架,接收来自 iptables 的规则,并在网络数据包流经内核协议栈时,依据这些规则对数据包进行处理。例如,当用户使用 iptables 命令设置了一条过滤规则,iptables 会将这条规则转化为特定的格式,并通知 Netfilter 在相应的位置进行处理。这样,两者紧密配合,共同实现了对网络流量的精细控制。
三、Netfilter/iptables-IPv4总体架构
Netfilter主要通过表、链实现规则,可以这么说,Netfilter是表的容器,表是链的容器,链是规则的容器,最终形成对数据报处理规则的实现。
3.1Netfilter的Hook机制
Netfilter的通用框架不依赖于具体的协议,而是为每种网络协议定义一套Hook函数。这些Hook函数在数据报经过协议栈的几个关键点时被调用,在这几个点中,协议栈将数据报以及Hook函数标号作为参数,传递给Netfilter框架。
对于它在网络堆栈中增加的这些Hook,内核的任何模块可以对每种协议的一个或多个Hook进行注册,实现挂接。这样当某个数据报被传递给Netfilter框架时,内核能检测到是否有任何模块对该协议和Hook函数进行了注册。
若注册了,则调用该模块的注册时使用的“回调”函数,这样这些模块就有机会检查、修改、丢弃该数据报及指示Netfilter将该数据报传入用户空间队列。
这样,Hook提供了一种方便的机制:在数据报通过Linux内核的不同位置上截获和操作处理数据报。
3.2iptables基础模块
iptables基础模块实现了三个表来筛选各种数据报,具体的讲,Linux2.4内核提供了这三种数据报的处理功能是相互间独立的模块,都基于Netfilter的Hook函数和各种表、链实现的。这三个表包括:filter表、nat表以及mangle表。具体功能模块:
- 数据报过滤模块
- 连接跟踪模块
- 网络地址转换模块(NAT)
- 数据报修改模块(mangle)
- 其他高级功能模块
3.3Netfilter 的架构
Netfilter 的四表五链架构是其实现强大功能的关键。filter 表主要用于数据包过滤,当数据包经过 filter 表中的链时,会根据预先设置的规则判断是否允许数据包通过。例如,可以设置规则只允许特定 IP 地址或端口的数据包通过。nat 表用于网络地址转换,在网络环境中,当需要将内部网络的 IP 地址转换为外部可访问的地址时,nat 表就发挥了重要作用。比如,在一个企业网络中,通过 nat 表可以将内部多个设备的私有 IP 地址转换为一个公共 IP 地址,实现对外通信。mangle 表用于修改数据包的头部信息,如修改服务类型(TOS)、生存时间(TTL)等。这可以用于实现服务质量调整和策略路由等应用。raw 表则主要用于决定数据包是否被状态跟踪机制处理。优先级的设置确保了数据包在处理过程中按照特定的顺序进行。当数据包进入 Netfilter 框架时,首先会经过 raw 表的处理,如果 raw 表处理完后决定不进行连接跟踪处理,那么数据包将跳过 nat 表和连接跟踪处理,直接进入后续的处理阶段。
⑴防火墙状态机制配置
Netfilter 的防火墙状态机制是其高效处理数据包的重要手段之一。根据连接状态匹配数据包,可以加快转发速度。连接状态主要有 New、Established、Related 和 INVALID 四种。当一个新的连接建立时,数据包处于 New 状态。一旦连接建立成功,后续的数据包就处于 Established 状态。Related 状态表示与已建立连接相关的数据包,比如 FTP 数据连接与控制连接相关。INVALID 状态表示数据包的状态无法确定或无效。这种状态机制可以记录动态网络地址转换(dnat),方便在需要时进行还原。例如,在进行网络地址转换时,Netfilter 可以记录下转换前的地址信息,以便在后续的数据包处理中进行还原。同时,状态机制还可以控制规则的存放和开启,根据不同的连接状态应用不同的规则,提高了防火墙的灵活性和效率。
⑵Netfilter 模块收发和转发数据包流程
Netfilter 模块在收发和转发数据包时,经过多个关键处理阶段。当数据包进入网络层时,首先会经过 Netfilter 的 hook function。这些 hook function 就像一个个检查点,对数据包进行特定的处理。例如,在 NF_IP_PRE_ROUTING 挂载点,数据包刚刚进入网络层,此时可以进行目的地址转换等操作。内置链在数据包处理过程中也起着重要作用。不同的链对应着不同的处理阶段和功能。例如,在转发数据包时,会经过 NF_IP_FORWARD 链的处理,在这里可以进行 FORWARD 包过滤。Netfilter 根据优先级和规则对数据包进行处理,确保数据包按照既定的策略进行转发、过滤或其他操作。在整个处理过程中,Netfilter 充分发挥了其灵活性和可扩展性,为网络安全提供了坚实的保障。
四、Netfilter基本配置
⑴配置 22/ssh 端口访问控制规则
使用 iptables 可以灵活地配置 22/ssh 端口的访问控制规则。例如,可以使用以下命令禁止所有人访问 22 端口:iptables -A INPUT -p tcp –dport 22 -j DROP。若要恢复连接,可以使用iptables -I INPUT -p tcp –dport 22 -j ACCEPT。还可以通过插入指定行号信息,将规则插入到特定位置,如iptables -I INPUT 2 -p tcp –dport 22 -j ACCEPT。删除指定规则可以使用iptables -D INPUT -p tcp –dport 22 -j ACCEPT或根据规则行号删除相应规则,如iptables -D INPUT 2。如果只允许特定 IP(如 10.0.0.1)通过 ssh 连接这台服务器,可以使用iptables -I INPUT -s 10.0.0.1 -p tcp –dport 22 -j ACCEPT。
⑵禁止网段连入
要禁止特定网段连入服务器,可以使用以下命令。例如,禁止 172.16.1.0 网段访问 172.16.1.188,可以使用iptables -A INPUT -s 172.16.1.0/24 -d 172.16.1.188 -j DROP。
⑶控制特定网段访问服务器主机的端口
如果要禁止某个 172.16.1.0 网段访问服务器主机的 22 端口,可以使用iptables -A INPUT -s 172.16.1.0/24 -d 172.16.1.188 -p tcp –dport 22 -j DROP。在入方向控制时,可以使用iptables -I INPUT -i eth0 -p tcp –dport 22 -j ACCEPT;在出方向控制时,可以使用iptables -I OUTPUT -o eth0 -p tcp –sport 22 -j DROP。
⑷设置除特定网段外其他网段禁止连接
可以通过两种方法设置除特定网段外其他网段禁止连接。方法一是修改默认规则,将默认规则改为拒绝,如iptables -A INPUT -s 10.0.0.0/24 -d 172.16.1.8 -j ACCEPT;方法二是使用取反操作,如iptables -A INPUT! -s 10.0.0.0/24 -d 172.16.1.8 -j DROP。
⑸测试匹配列举端口范围
可以使用 iptables 测试匹配列举端口范围。设置连续多端口控制策略可以使用iptables -A INPUT -p tcp –dport 22:80 -j DROP;设置不连续多端口控制策略可以使用iptables -A INPUT -p tcp -m multiport –dport 22,80 -j DROP。
五、企业级防火墙配置
⑴备份服务器的重要性
在企业级防火墙配置中,备份服务器起着至关重要的作用。备份服务器可以确保在防火墙配置出现问题或遭受攻击时,能够快速恢复到之前的稳定状态。例如,飞塔防火墙自动备份技巧中,通过在 CentOS 7 备份服务器上安装工具 sshpass,并进行一系列配置,可以实现对飞塔防火墙的自动备份。创建存储备份文件的文件夹,编写备份脚本firewallbackup.sh,同时创建ip.txt文件记录飞塔防火墙的登陆地址。通过配置 CentOS 7 的计划任务crontab -e,可以实现每天定时自动备份,如每天 11 点和 23 点自动备份一次。这样可以有效防止因防火墙配置丢失或损坏而导致的业务中断,为企业网络安全提供了有力的保障。
⑵开放 SSH 端口
开放 SSH 端口是企业级防火墙配置中的常见需求。使用 iptables 可以灵活地配置 SSH 端口的访问控制规则。例如,可以使用以下命令禁止所有人访问 22 端口:iptables -A INPUT -p tcp –dport 22 -j DROP。若要恢复连接,可以使用iptables -I INPUT -p tcp –dport 22 -j ACCEPT。还可以通过插入指定行号信息,将规则插入到特定位置,如iptables -I INPUT 2 -p tcp –dport 22 -j ACCEPT。删除指定规则可以使用iptables -D INPUT -p tcp –dport 22 -j ACCEPT或根据规则行号删除相应规则,如iptables -D INPUT 2。如果只允许特定 IP(如 10.0.0.1)通过 ssh 连接这台服务器,可以使用iptables -I INPUT -s 10.0.0.1 -p tcp –dport 22 -j ACCEPT。
⑶防止配置出错导致无法连接服务器
在配置企业级防火墙时,需要注意防止配置出错导致无法连接服务器。可以在配置前进行备份,以便在出现问题时能够快速恢复。同时,可以使用其他工具配置和管理 Netfilter 规则,如 firewalld。firewalld 是一个动态防火墙管理器,它可以动态修改单条规则,不需要像 iptables 那样,修改了规则后必须全部刷新才可以生效。firewalld 的配置方法主要有三种:firewall-config、firewall-cmd 和直接编辑 xml 文件。其中 firewall-config 是图形化工具,firewall-cmd 是命令行工具。通过使用 firewalld,可以更加方便地管理防火墙规则,提高配置的准确性和可靠性。
⑷使用其他工具配置和管理 Netfilter 规则
除了 iptables 和 firewalld,还有其他工具可以配置和管理 Netfilter 规则。例如,Juniper 防火墙配置备份中,可以通过多种方式备份和恢复防火墙配置,包括设备重启动、操作系统备份和恢复、配置文件备份和恢复等。通过这些工具,可以更加全面地管理企业级防火墙,提高网络安全水平。
六、Netfilter的应用案例
6.1Netfilter 在不同场景中的应用
Netfilter 在网络安全领域有着广泛的应用。它可以作为强大的防火墙,通过设置规则过滤网络数据包,阻止恶意流量进入系统。例如,可以根据数据包的源 IP 地址、目标 IP 地址、协议类型、端口号等信息进行精细过滤,确保只有合法的数据包能够通过。
在实现负载均衡方面,Netfilter 也发挥着重要作用。正如前面提到的,通过利用 Netfilter 的功能,可以实现将网络流量分配到不同的服务器上,提高系统的可用性和性能。例如,通过 DNAT(目标网络地址转换)可以将请求数据包分离到不同的目的地址,然后配置路由将这些数据包导向不同的服务器,实现负载均衡。
Netfilter 还可以与 VPN 技术集成,保护数据传输。例如,IPSec VPN 技术中,Netfilter 可以在数据包处理过程中进行加密、认证等操作,确保数据在传输过程中的安全性。
6.2具体案例
丢弃网络包:有开发者编写了一个简单的内核模块,通过注册 Netfilter 钩子函数,实现对所有网络包的丢弃,并将这一操作记录到/var/log/messages中。其实现过程如下:首先定义一个nf_hook_ops结构体变量nfho,然后编写钩子函数hook_func,在该函数中打印 “packet dropped” 并返回NF_DROP,表示丢弃数据包。在模块加载时,将钩子函数、协议类型、钩子触发点编号等信息设置到nfho结构体中,并调用nf_register_hook注册钩子函数。当模块卸载时,调用nf_unregister_hook注销钩子函数。
针对 UDP 包过滤:另一个案例是针对 UDP 包进行过滤。同样通过编写内核模块,在 Netfilter 的钩子函数中获取 IP 头部和 UDP 头部,检查协议字段是否为 UDP(协议号为 17),如果是则进行相应处理。在这个案例中,首先定义了nf_hook_ops结构体变量nfho以及udphdr和iphdr结构体指针。在钩子函数中,通过skb_network_header和skb_transport_header函数获取网络头部和传输头部,然后检查协议字段。如果是 UDP 包,则打印 “got udp packet” 并返回NF_DROP,表示丢弃该数据包。在模块加载和卸载时,分别进行注册和注销钩子函数的操作。
禁止 IP 协议:可以使用 iptables 的u32匹配方法来禁止具有特定 IP 选项的数据包。例如,通过检查数据包的 IHL(互联网标题长度)字段,如果大于 20(没有选项的 IPv4 标头长度),则丢弃数据包。可以使用iptables -A INPUT -m u32! –u32 ‘0 & 0x0F000000 >>24 = 5’ -j DROP命令来实现,该命令表示如果数据包的前 32 位值经过处理后不等于 5(没有选项的 IHL 值),则丢弃该数据包。
禁止特定端口及 PING:可以使用 iptables 来禁止特定端口的访问。例如,要禁止所有人访问 22/ssh 端口,可以使用iptables -A INPUT -p tcp –dport 22 -j DROP命令。如果要禁止特定网段访问服务器主机的 22 端口,可以使用iptables -A INPUT -s 172.16.1.0/24 -d 172.16.1.188 -p tcp –dport 22 -j DROP命令。此外,还可以使用 iptables 禁止 ICMP 包,实现其他机器不能 ping 通本机的效果,例如使用iptables -I INPUT -p icmp –icmp-type 8 -j DROP命令,其中–icmp-type 8表示能在本机 ping 通其他机器,而其他机器不能 ping 通本机。
七、Netfilter的特点及优势
①强大的扩展性
Netfilter 最大的优点就在于其扩展性好,可以任意定义新的模块扩展其功能。用户浏览 netfilter.org 就会知道,它里面融合了大量的策略,如 ebtables、arptables、nft 等都是 Netfilter 的扩展之一。Liuux 中使用的仅仅是它的一个很小的部分,大部分的内容作为可插拔的 module 处于待命状态,为用户提供了极大的灵活性来满足不同的网络管理需求。
②易于理解的结构
Netfilter 的结构比较容易理解,即使是新手也能很快掌握其规则配置方法。它的四表五链架构清晰明了,用户可以通过 iptables 等工具直观地配置规则,对网络数据包进行过滤、修改和转发等操作。例如,filter 表用于数据包过滤,nat 表用于网络地址转换,mangle 表用于修改数据包头部信息,raw 表用于决定数据包是否被状态跟踪机制处理。每个表中的链对应着不同的处理阶段和功能,用户可以根据实际需求进行配置。
③丰富的功能和可配置性
Netfilter 允许用户和系统管理员配置规则来过滤、修改和转发网络数据包,实现多种功能。例如,可以通过设置规则控制哪些流量被允许进出系统,实现数据包过滤;支持网络地址转换,使得私有网络中的设备可以共享一个公共 IP 地址访问外部网络;可以跟踪网络连接的状态,动态地管理连接状态。同时,Netfilter 还提供了多个钩子点,在数据包进入系统和离开系统时进行关键处理。常见的钩子点包括 PREROUTING、INPUT、FORWARD、OUTPUT 和 POSTROUTING,用户可以在这些钩子点上注册处理数据包的回调函数,对数据包进行特定的处理。
④高效的工作流程
Netfilter 的工作流程经过多个钩子点处理数据包,确保数据包按照既定的策略进行转发、过滤或其他操作。当数据包进入网络栈时,会经过 Netfilter 的处理点。根据定义的规则,数据包可能会被接受、拒绝或修改。例如,在 NF_IP_PRE_ROUTING 挂载点,数据包刚刚进入网络层,此时可以进行目的地址转换等操作。内置链在数据包处理过程中也起着重要作用,不同的链对应着不同的处理阶段和功能。这种工作流程充分发挥了 Netfilter 的灵活性和可扩展性,为网络安全提供了坚实的保障。
⑤高度的灵活性和控制能力
Netfilter 在 Linux 网络栈中扮演着关键角色,提供了高度的灵活性和控制能力。用户可以根据自己的需求配置防火墙规则、进行网络地址转换、实施流量控制等操作。例如,可以使用 iptables 等工具灵活地配置规则,对特定 IP 地址、端口号、协议类型等进行精细过滤。同时,Netfilter 还支持多种协议栈,目前 Linux 2.6 版内核的 Netfilter 支持 IPv4、IPv6 以及 DECnet 等协议栈,为用户提供了广泛的应用场景。
八、Netfilter的不足之处
Netfilter 虽然在网络安全领域发挥着重要作用,但也存在一些不足之处。
①状态表结构效率不高
Netfilter 的状态表使用链表结构,虽然是 HASH 方式,但链接数非常大时,每个 HASH 表内的参数还是很多的,顺序查找起来就比较慢。例如,专业路由器大都使用类似二叉树结构实现快速查找,而 Linux 内核的 Netfilter 却采用 HASH 表,这在处理大量网络数据包时可能会导致效率下降。
②目标功能单一导致效率低
某些目标功能太单一,导致某些情况下效率不高。如要对某类数据进行流量限制,对超过限值的包记录这样的需求,就需要三条规则,一条是家流量限制匹配 – j ACCEPT,第二条 – j LOG –log-prefix…,第三条才是 – j DROP,同样的规则匹配要匹配三次。如果不要求记录的话会简单一些,用一个子链来实现流量限制,就不用重复匹配条件了。但 netfilter 把日志作为一个目标而不是象 2.2 的 ipchains 那样记录日志是个规则选项,效率总是不高。
③缺乏某些功能
Netfilter 某些功能不太好实现,如 SYN 代理,SYN 代理在 freebsd、openbsd 等内核都自带,但在 linux 中却迟迟未能包括,确实在 netfilter 这个架构下这个功能不太好弄。
④对攻击识别和单个 IP 连接数限制困难
对需要进行统计的攻击识别比较困难,如各种 flood 攻击,无法定义 flood 模式,只能简单的进行流量限制,限制了非法包的同时也限制了合法包。很难对一个网段内单个 IP 的连接数进行限制,这样一台机器染毒疯狂发包时不能把肇事 IP 挑出来,只能整个网段都限制,一损具损;同样如果想对内容进行过滤,如 string 匹配,也是同样情况。