Linux内核下包过滤框架——iptables&netfilter
iptables & netfilter
1、简介
netfilter/iptables(下文中简称为iptables)组成Linux内核下的包过滤防火墙,完成封包过滤、封包重定向和网络地址转换(NAT)等功能。
iptabels其实不是真正的防火墙,netfilter才是防火墙真正的安全框架(framework),netfilter位于内核空间。iptables其实是一个命令行工具,位于用户空间,我们用这个工具操作真正的框架。
Netfilter是Linux操作系统核心层内部的一个数据包处理模块,它具有如下功能:
- 网络地址转换(Network Address Translate)
- 数据包内容修改
- 数据包过滤的防火墙功能
2、iptables基础
iptatble是按规则来办事的,当数据包与规则匹配时,iptables就根据规则所定义的方法来处理这些数据包,如放行(accept)、拒绝(reject)和丢弃(drop)等。配置防火墙的主要工作就是添加、修改和删除这些规则。
netfilter才是真正的防火墙,它是内核的一部分,所以,如果我们想要防火墙能够达到”防火”的目的,则需要在内核中设置关卡,所有进出的报文都要通过这些关卡,经过检查后,符合放行条件的才能放行,符合阻拦条件的则需要被阻止,于是,就出现了input关卡和output关卡,而这些关卡在iptables中不被称为”关卡”,而被称为”链”。
iptatbles中不仅有input
、output
关卡,还有prerouting
、forward
、postrouting
,当我们启用了防火墙功能时,报文需要经过如下关卡,也就是说,根据实际情况的不同,报文经过”链”可能不同。如果报文需要转发,那么报文则不会经过input链发往用户空间,而是直接在内核空间中经过forward链和postrouting链转发出去的。
2.1、链
防火墙的作用就在于对经过的报文匹配”规则”,然后执行对应的”动作”,所以,当报文经过这些关卡的时候,则必须匹配这个关卡上的规则,但是,这个关卡上可能不止有一条规则,而是有很多条规则,当我们把这些规则串到一个链条上的时候,就形成了”链”。
这样来说,把他们称为”链”更为合适,每个经过这个”关卡”的报文,都要将这条”链”上的所有规则匹配一遍,如果有符合条件的规则,则执行规则对应的动作。
2.2、表
具有相同功能规则的集合叫做“表”,iptables定义来4种表,每种表对应了不同的功能,即我们定义的规则都逃脱不了这4种功能的范围。
- filter表:过滤功能,防火墙;内核模块:iptable_filter
- nat表:网络地址转换功能;内核模块:iptable_nat
- mangle表:拆解报文,修改,并重新封装功能;内核模块:iptable_mangle
- raw表:关闭nat表上启用的连接追踪机制;内核模块:iptatble_raw
需要注意的是,某些“链”注定 不会包含“某类规则”,表链的对应关系如下:
综合上面描述,数据包经过防火墙的流程总结为下:
如果想要linux主机支持转发,则需要开启内核的IP_FORWARD功能,可以临时修改对应文件/proc/sys/net/ipv4/ip_forward。
在写Iptables规则的时候,要时刻牢记这张路由次序图,灵活配置规则。
2.3、规则
根据指定的匹配条件来尝试匹配每个流经此处的报文,一旦匹配成功,则由规则后面指定的处理动作进行处理。
匹配条件分为
基本匹配条件:源IP、目的IP等
扩展匹配条件:源端口、目的端口等
处理动作(在iptatbles中被称为target):
- ACCEPT:允许数据包通过
- DROP:丢弃数据包,不给任何回应信息
- REJECT:拒绝数据包通过,必要时发送一个响应信息
- SNAT:源地址转换,解决内网用户用同一个公网地址上网的问题
- MASOUERADE:SNAT的一种特殊形式,适用于动态的、临时会变的ip上
- DNAT:目的地址转换
- REDIRECT:在本机做端口映射
- LOG:在/var/log/messages文件中记录日志信息,然后将数据包传递给下一条规则。
3、使用
3.1、规则查询
#查看对应表所有规则,省略表名默认为filter表
iptables -t 表名 -L
#指定链的规则,-v:详细信息 -n:直接显示IP地址 --line-numbers:显示规则序号 -x:显示计数器的精确值
iptables --line-numbers -t 表名 -nvxL 链名
3.2、规则管理
添加规则
命令语法:iptables -t 表名 -A 链名 匹配条件 -j 动作
示例:iptables -t filter -A INPUT -s 192.168.1.146 -j DROP
-I:在对应链开头添加规则
-A:在末尾添加规则
规则是按照顺序执行的,添加时候可以指定规则序号,这样就可以在任意位置插入规则了.
命令语法:iptables -t 表名 -I 链名 规则序号 匹配条件 -j 动作
示例:iptables -t filter -I INPUT 5 -s 192.168.1.146 -j REJECT
---设置默认动作---
命令语法:iptables -t 表名 -P 链名 动作
示例:iptables -t filter -P FORWARD ACCEPT
删除规则
命令语法:iptables -t 表名 -D 链名 规则序号
示例:iptables -t filter -D INPUT 3
---按照具体匹配条件与动作删除规则--
命令语法:iptables -t 表名 -D 链名 匹配条件 -j 动作
示例:iptables -t filter -D INPUT -s 192.168.1.146 -j DROP
-F:清空对应链的规则,如果省略链名表示删除表中所有规则
修改规则
---规则原本的匹配条件不可省略!如果省略,源地址会变成0.0.0.0/0,此时如果是拒绝动作,那么ssh就断掉了---
命令语法:iptables -t 表名 -R 链名 规则序号 规则原本的匹配条件 -j 动作
示例:iptables -t filter -R INPUT 3 -s 192.168.1.146 -j ACCEPT
保存规则
---保存在/etc/sysconfig/iptables文件中
service iptables save 或
iptables-save > /etc/sysconfig/iptables
---从文件中重新载入规则
iptatbles-restore < /etc/sysconfig/iptables
3.3、基本匹配条件
当规则中同时存在多个匹配条件时,多个条件之间默认存在“与”的关系.
-
-s: 指定源ip,多个ip用逗号隔开,也可以指定一个网段
iptables -t filter -I INPUT -s 192.168.1.111,192.168.1.118 -j DROP iptables -t filter -I INPUT -s 192.168.1.0/24 -j ACCEPT iptables -t filter -I INPUT ! -s 192.168.1.0/24 -j ACCEPT
-
-d: 匹配目标地址,用法和-s一样
-
-p: 匹配报文协议类型,可以匹配的协议类型tcp、udp、udplite、icmp、esp、ah、sctp等
-
-i: 匹配报文从哪个网卡接口流入本机,由于匹配条件只是用于匹配报文流入的网卡,所以在OUTPUT链与POSTROUTING链中不能使用此选项
-
-o: 匹配报文从哪个网卡接口流出本机
3.3、扩展匹配条件
-
tcp扩展模块
常用的扩展匹配条件如下:
-p tcp -m tcp –-sport 用于匹配tcp协议报文的源端口,可以使用冒号指定一个连续的端口范围
-p tcp -m tcp –-dport 用于匹配tcp协议报文的目标端口,可以使用冒号指定一个连续的端口范围
--tc-flages 用于匹配tcp头部字段标志位,第一部分为需要匹配哪些标志位,第二部分为哪些标志位为1
iptables -t filter -I INPUT -p tcp -m tcp --dport 22 --tcp-flags SYN,ACK,FIN,RST,URG,PSH SYN -j REJECT iptables -t filter -I INPUT -p tcp -m tcp --dport 22 --tcp-flags SYN,ACK,FIN,RST,URG,PSH SYN -j REJECT
--syn 相当于使用”–tcp-flags SYN,RST,ACK,FIN SYN
-
udp模块
当使用扩展匹配条件时,如果未指定扩展模块,iptables会默认调用与”-p”对应的协议名称相同的模块,所以,当使用”-p udp”时,可以省略”-m udp”.udp只有sport和dport两个匹配条件
-
mutiport扩展模块
常用的扩展匹配条件如下:
-p tcp -m multiport –-sports 用于匹配报文的源端口,可以指定离散的多个端口号,端口之间用”逗号”隔开
-p udp -m multiport –-dports 用于匹配报文的目标端口,可以指定离散的多个端口号,端口之间用”逗号”隔开
-
iprange模块
包含的扩展匹配条件如下
-m iprange –-src-range:指定连续的源地址范围,用
-
指定一个连续的范围-m iprange -–dst-range:指定连续的目标地址范围
-
string模块
使用string扩展模块,可以指定要匹配的字符串,如果报文中包含对应的字符串,则符合匹配条件,常用扩展匹配条件如下
–-algo:指定对应的匹配算法,可用算法为bm、kmp,此选项为必需选项。
–-string:指定需要匹配的字符串
-
time模块
常用扩展匹配条件如下
–-timestart:用于指定时间范围的开始时间,不可取反
–-timestop:用于指定时间范围的结束时间,不可取反
–-weekdays:用于指定”星期几”,可取反
–-monthdays:用于指定”几号”,可取反
–-datestart:用于指定日期范围的开始日期,不可取反
–-datestop:用于指定日期范围的结束时间,不可取反
iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --timestart 09:00:00 --timestop 19:00:00 -j REJECT iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --weekdays 6,7 -j REJECT iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --monthdays 22,23 -j REJECT iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --weekdays 5 --monthdays 22,23,24,25,26,27,28 -j REJECT iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --datestart 2017-12-24 --datestop 2017-12-27 -j REJECT
-
connlimit模块
使用connlimit扩展模块,可以限制每个IP地址同时链接到server端的链接数量,注意:我们不用指定IP,其默认就是针对”每个客户端IP”,即对单IP的并发连接数限制
–-connlimit-above:单独使用此选项时,表示限制每个IP的链接数量。
–-connlimit-mask:此选项不能单独使用,在使用–connlimit-above选项时,配合此选项,则可以针对”某类IP段内的一定数量的IP”进行连接数量的限制
iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 2 -j REJECT
-
limit模块
如果我想要限制单位时间内流入的包的数量,就能用limit模块,我们可以以秒为单位进行限制,也可以以分钟、小时、天作为单位进行限制
常用的扩展匹配条件如下
–-limit-burst:类比”令牌桶”算法,此选项用于指定令牌桶中令牌的最大数量
–-limit:类比”令牌桶”算法,此选项用于指定令牌桶中生成新令牌的频率,可用时间单位有second、minute 、hour、day。
iptables -t filter -I INPUT -p icmp -m limit --limit-burst 3 --limit 10/minute -j ACCEPT
-
icmp模块
–-icmp-type:匹配icmp报文的具体类型,可以使用type/code和description两种方式表示
-
state模块
问题: 怎样判断这些报文是为了回应我们之前发出的报文,还是主动向我们发送的报文呢?
state模块可以让iptables实现”连接追踪”机制,”连接”其中的报文可以分为5种状态,报文状态可以为NEW、ESTABLISHED、RELATED、INVALID、UNTRACKED.
--state
3.4、黑白名单机制
前文中一直在强调一个概念:报文在经过iptables的链时,会匹配链中的规则,遇到匹配的规则时,就执行对应的动作,如果链中的规则都无法匹配到当前报文,则使用链的默认策略(默认动作),链的默认策略通常设置为ACCEPT或者DROP。
所以,当链的默认策略为ACCEPT时,链中的规则对应的动作应该为DROP或者REJECT,表示只有匹配到规则的报文才会被拒绝,没有被规则匹配到的报文都会被默认接受,这就是”黑名单”机制。
同理,当链的默认策略为DROP时,链中的规则对应的动作应该为ACCEPT,表示只有匹配到规则的报文才会被放行,没有被规则匹配到的报文都会被默认拒绝,这就是”白名单”机制。
在对应的链中没有设置任何规则时,这样使用默认策略为DROP是非常不明智的,因为管理员也会把自己拒之门外,即使对应的链中存在放行规则,当我们不小心使用”iptables -F”清空规则时,放行规则被删除,则所有数据包都无法进入,这个时候就相当于给管理员挖了个坑,所以,我们如果想要使用”白名单”的机制,最好将链的默认策略保持为”ACCEPT”,然后将”拒绝所有请求”这条规则放在链的尾部,将”放行规则”放在前面,这样做,既能实现”白名单”机制,又能保证在规则被清空时,管理员还有机会连接到主机
iptables -P INPUT ACCEPT
iptabels -I INPUT -p tcp --dport 22 -j ACCEPT
iptabels -I INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -j ACCEPT
3.5、自定义链
当默认链中的规则非常多时,不方便我们管理。
想象一下,如果INPUT链中存放了200条规则,这200条规则有针对httpd服务的,有针对sshd服务的,有针对私网IP的,有针对公网IP的,假如,我们突然想要修改针对httpd服务的相关规则,难道我们还要从头看一遍这200条规则,找出哪些规则是针对httpd的吗?这显然不合理。
但是需要注意的是,自定义链并不能直接使用,而是需要被默认链引用才能够使用.
--创建自定义链
iptables -t filter -N IN_WEB
--引用自定义链
iptables -t filter -I INPUT -p tcp --dport 80 -j IN_WEB
--重命名自定义链
iptables -E IN_WEB WEB
--删除自定义链
删除自定义链需要满足两个条件
1、自定义链没有被引用
2、自定义链中没有任何规则
iptables -D INPUT 1
iptables -F WEB
iptables -X WEB
3.6、网络防火墙
防火墙从逻辑上讲,可以分为主机防火墙与网络防火墙。
主机防火墙:针对于单个主机进行防护。
网络防火墙:往往处于网络入口或边缘,针对于网络入口进行防护,服务于防火墙背后的本地局域网。
iptables都是作为主机防火墙的角色出现的,如果想要使用iptables充当网络防火墙,iptables所在的主机则需要处于网络入口处,网络防火墙主要职责就是“过滤并转发”,所以规则只能定义在FORWARD链中.
上图中把C主机的网关指向B主机网卡1的IP,A主机网关设为B网卡2的地址.
A ping 10.1.0.1 是ping不通的,A主机通过路由表得知,发往10.1.0.0/16网段的报文的网关为B主机,当报文达到B主机时,B主机发现A的目标为10.1.0.1,而自己的IP是10.1.0.3,这时,B主机则需要将这个报文转发给10.1.0.1(也就是C主机),但是,Linux主机在默认情况下,并不会转发报文,如果想要让Linux主机能够转发报文,需要额外的设置,这就是为什么10.1.0.1没有回应的原因.
A ping 10.1.0.3可以ping通: 当报文到达B时,B主机发现自己既是192.168.1.146又是10.1.0.3,所以,B主机就直接回应了A主机,并没有将报文转发给谁,所以A主机得到了10.1.0.3的回应。
临时生效:把/proc/sys/net/ipv4/ip_forward文件内容设置为1表示支持转发报文.
永久生效:/etc/sysctl.conf添加net.ipv4.ip_forward=1
此时就可以ping通了
#如果想要iptables作为网络防火墙,iptables所在主机开启核心转发功能,以便能够转发报文。
#使用如下命令查看当前主机是否已经开启了核心转发,0表示为开启,1表示已开启
cat /proc/sys/net/ipv4/ip_forward
#使用如下两种方法均可临时开启核心转发,立即生效,但是重启网络配置后会失效。
方法一:echo 1 > /proc/sys/net/ipv4/ip_forward
方法二:sysctl -w net.ipv4.ip_forward=1
#使用如下方法开启核心转发功能,重启网络服务后永久生效。
配置/etc/sysctl.conf文件(centos7中配置/usr/lib/sysctl.d/00-system.conf文件),在配置文件中将 net.ipv4.ip_forward设置为1
#由于iptables此时的角色为"网络防火墙",所以需要在filter表中的FORWARD链中设置规则。
#可以使用"白名单机制",先添加一条默认拒绝的规则,然后再为需要放行的报文设置规则。
#配置规则时需要考虑"方向问题",针对请求报文与回应报文,考虑报文的源地址与目标地址,源端口与目标端口等。
#示例为允许网络内主机访问网络外主机的web服务与sshd服务。
iptables -A FORWARD -j REJECT
iptables -I FORWARD -s 10.1.0.0/16 -p tcp --dport 80 -j ACCEPT
iptables -I FORWARD -d 10.1.0.0/16 -p tcp --sport 80 -j ACCEPT
iptables -I FORWARD -s 10.1.0.0/16 -p tcp --dport 22 -j ACCEPT
iptables -I FORWARD -d 10.1.0.0/16 -p tcp --sport 22 -j ACCEPT
#可以使用state扩展模块,对上述规则进行优化,使用如下配置可以省略许多"回应报文放行规则"。
iptables -A FORWARD -j REJECT
iptables -I FORWARD -s 10.1.0.0/16 -p tcp --dport 80 -j ACCEPT
iptables -I FORWARD -s 10.1.0.0/16 -p tcp --dport 22 -j ACCEPT
iptables -I FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
3.7、动作总结
网关配置和上面一样.
-
SNAT(服务在外网,内网主动访问外网)
#主机B iptables -t nat -A POSTROUTING -s 10.1.0.0/16 -j SNAT --to-source 192.168.1.146
此时内网主机ping A主机就能ping通, iptables会自动维护NAT表,并将响应报文的目标地址转换回来.
-
DNAT(服务在内网,外网主动访问内网)
#主机B iptables -t nat -F iptables -t nat -I PREROUTING -d 192.168.1.146 -p tcp --dport 3389 -j DNAT --to-destination 10.1.0.6:3389 iptables -t nat -A POSTROUTING -s 10.1.0.0/16 -j SNAT --to-source 192.168.1.146
表示报文目标地址为B公网IP,目标端口为3389的报文符合匹配条件,做DNAT转换映射到10.1.0.6:3389上.
注:理论上只配置DNAT规则即可,但是如果在测试时无法正常DNAT,可以尝试配置对应的SNAT,此处按照配置SNAT的流程进行。
-
MASQUERADE
MASQUERADE会动态的将源地址转换为可用的IP地址,其实与SNAT实现的功能完全一致,都是修改源地址,只不过SNAT需要指明将报文的源地址改为哪个IP,而MASQUERADE则不用指定明确的IP,会动态的将报文的源地址修改为指定网卡上可用的IP地址.
iptables -t nat -I POSTROUTING -s 10.1.0.0/16 -o eth0 -j MASQUERADE
表示通过外网网卡出去的报文在经过POSTROUTING链时,会自动将报文的源地址修改为外网网卡上可用的IP地址,这时,即使外网网卡中的公网IP地址发生了改变,也能够正常的、动态的将内部主机的报文的源IP映射为对应的公网IP,可以把MASQUERADE理解为动态的、自动化的SNAT,如果没有动态SNAT的需求,没有必要使用MASQUERADE,因为SNAT更加高效。
-
REDIRECT
使用REDIRECT动作可以在本机上进行端口映射
#将本机80端口映射到本机8080端口 iptables -i nat -A PREROUTING -o tcp --dport 80 -j REDIRECT --to-ports 8080
4、iptables实现负载均衡
在Linux中使用iptables完成tcp的负载均衡有两种模式
-
随机(random)
iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 80 -m statistic --mode random --probability 0.33 -j DNAT --to-destination 10.0.0.2:1234 iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 80 -m statistic --mode random --probability 0.5 -j DNAT --to-destination 10.0.0.2:1234 iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 80 -m statistic --mode random -j DNAT --to-destination 10.0.0.2:1234
rules说明:
第一条规则有0.33概率被命中第二条也为0.33,命中概率为(1-0.33)*0.5 = 0.33
第三条为1-0.33-0.33约等于0.33
假设有n个server,想要将流量平均分到n个server,概率为p=1/(n-i+1)。
注意:因为iptables中,规则是按顺序匹配的,由上至下依次匹配,因此设计iptables规则时,要严格对规则进行排序。因此上述三条规则的顺序也不可以调换,不然就无法实现LB均分了。
-
轮询(nth)
iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 80 -m statistic --mode nth --every 3 --packet 0 -j DNAT --to-destination 10.0.0.2:1234 iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 80 -m statistic --mode nth --every 2 --packet 0 -j DNAT --to-destination 10.0.0.2:1234 iptables -A PREROUTING -t nat -p tcp -d 192.168.1.1 --dport 80 -m statistic --mode nth -j DNAT --to-destination 10.0.0.2:1234
轮询算法中有两个参数:
--every:每n个包匹配一次规则
--packet:从第p个包开始
5、总结
-
规则顺序很重要
如果报文已经被前面的规则匹配到,IPTABLES则会对报文执行对应的动作,通常是ACCEPT或者REJECT,报文被放行或拒绝以后,即使后面的规则也能匹配到刚才放行或拒绝的报文,也没有机会再对报文执行相应的动作了(前面规则的动作为LOG时除外),所以,针对相同服务的规则,更严格的规则应该放在前面。
-
当规则中有多个匹配条件时,条件之间是“与”的关系
-
在不考虑1的情况下,应该将更容易被匹配的规则放在前面
比如,你写了两条规则,一条针对sshd服务,一条针对web服务。假设,一天之内,有20000个请求访问web服务,有200个请求访问sshd服务,那么,应该将针对web服务的规则放在前面,针对sshd的规则放在后面,因为访问web服务的请求频率更高。如果将sshd的规则放在前面,当报文是访问web服务时,sshd的规则也要白白的验证一遍,由于访问web服务的频率更高,白白耗费的资源就更多。
-
当IPTABLES所在主机作为网络防火墙时,在配置规则时,应着重考虑方向性,双向都要考虑,从外到内,从内到外。
-
在配置IPTABLES白名单时,往往会将链的默认策略设置为ACCEPT,通过在链的最后设置REJECT规则实现白名单机制,而不是将链的默认策略设置为DROP,如果将链的默认策略设置为DROP,当链中的规则被清空时,管理员的请求也将会被DROP掉。