第2章、用用户空间命令iptables实现包过滤
2.1
相关的TCP/IP知识
2.1.1建立TCP连接(通过3次握手实现)
假如服务器A和客户机B通信。
(1)B->;A。当B要和A通信时,B首先向A发一个SYN标记的包,告诉A请求建立连接。只有当A收到B发来的SYN包,才可以建立连接,除此之外别无它法。因此,如果你的防火墙丢弃所有的发往外网接口的SYN包,那么你将不能让外部任何主机主动建立连接。
(2)B<-A。接着,A收到后会发一个对SYN包的确认包(SYN/ACK)回去,表示对第一个SYN包的确认,并继续握手操作。
(3)B->;A。B收到SYN/ACK包后,B发一个确认包(ACK),通知A连接已建立。至此,3次握手完成,一个TCP连接完成。
需要注意的是,当3次握手完成、连接建立以后,TCP连接的每个包都会设置ACK位。这就是为何连接跟踪很重要的原因了,没有连接跟踪,防火墙将无法判断收到的ACK包是否属于一个已经建立的连接。
2.1.2 结束TCP连接(通过4次握手实现)
假如服务器A和客户机B通信。注意,由于TCP连接是双向连接,因此关闭连接需要在两个方向上做。
(1)B->;A。当B要与A结束通信时,B首先向A发一个FIN标记的包,告诉A请求结束连接。由于连接还没有关闭,FIN包总是打上ACK标记。没有ACK标记而仅有FIN标记的包不是合法的包,并且通常被认为是恶意的。
(2)B<-A。A送出ACK包给B,表示成功地中止B->;A传输通道。不过A->;B可能还有数据包需要传送,所以A->;B传输通道仍旧继续畅通,直到传输完毕才会进入下一步。
(3)B<-A。当A完成B<-A的传输后,便送出ACK/FIN包。
(4)B->;A。B送出ACK包给A进行确认。
2.1.3 发送连接复位包结束TCP连接
4次握手不是结束TCP连接的唯一方法。有时,如果主机需要尽快关闭连接(或连接超时,端口或主机不可达),RST包将被发送。注意,由于RST包不是 TCP连接中的必须部分,可以只发送RST包(即不带ACK标记)。但在正常的TCP连接中RST包可以带ACK确认标记。注意,RST包是可以不要收方进行确认的。
2.1.4 无效的TCP标记
至此,已经看到了SYN、ACK、FIN、和RST标记。另外,还有PSH和URG标记。
最常见的非法组合是SYN/FIN包。注意,由于SYN包是用来初始化连接的,它不可能和FIN以及RST标记一起出现,这也是一个恶意攻击。
当网络中出现别的一些组合(如SYN/FIN/PSH、SYN/FIN/RST、SYN/FIN/RST/PSH),很明显网络肯定受到了攻击。
另外,已知的非法包还有FIN(无ACK标记)和“NULL”包。如同早先讨论的,由于ACK/FIN包的出现是为了结束一个TCP连接,那么正常的 FIN包总是带有ACK标记的。“NULL”包就是没有任何TCP标记的包(URG、ACK、PSH、RST、SYN、FIN都为0)。
在正常的网络活动下,到目前为止TCP协议栈不可能产生带有上面提到的任何一个标记组合的TCP包。当你发现这些不正常的包时,肯定有人对你的网络不怀好意。
2.1.5 ICMP类型
ICMP是网间控制消息协议,用来在主机/路由器之间传递控制信息的协议。ICMP包可以包含诊断信息(ping 、 tracerouter)、错误信息(网络/主机/端口不可达),信息(时间戳timestamp,地址掩码address mask等)、或控制信息(source quench、redirect等)。
使用包过滤可以拒绝指定的ICMP类型。
此外,下面类型的ICMP信息建议丢弃。
Redirect(5)、Alternate Host Address(6)、Router Advertisement(9)能用来转发通信。
Echo(8)、Timestamp(13)、Address Mask Request(17)能用来分别判断主机是否启动、本地时间和地址掩码。它们是和返回的信息类别有关的。其本身是不能被利用的,但它们泄漏出的信息对攻击者是有用的,所以建议丢弃这些类型的ICMP。
关于ICMP类型的更详细的表述参见RFC792。
2.2 iptables语法
2.2.1 Iptables的优点
→ iptables允许建立状态(stateful)防火墙,就是在内存中保存穿过防火墙的每条连接。这种模式对于有效地配置FTP和DNS以及其它网络服务是必要的。
→iptables能够过滤TCP标志任意组合报文,还能够过滤MAC地址。
→系统日志比ipchains更容易配置,扩展性也更好。
→对于网络地址转换(Network Address Translation)和透明代理的支持,Netfilter更为强大和易于使用。
→iptables能够阻止某些DoS攻击,例如SYS洪泛攻击。
2.2.2 Iptables的规则要素
一条iptables规则基本上应该包含5个要素:
→指定表(table)
→指定操作命令(command)
→指定链(chains)
→指定规则匹配器(matcher)
→指定目标动作(target)
(1)表。Iptables从其使用的3个表而得名,分别是filter、nat、mangle。对于包过滤防火墙只使用filter表。表filter是默认的表,无需显示说明。
(2)操作命令。包括添加、删除、更新等。
(3)链。对于包过滤防火墙可操作filter表中的INPUT链、OUTPUT链和FORWARD链。也可以操作由用户自己定义的自定义链。
(4)规则匹配器。可以指定各种规则匹配,如IP地址、端口、包类型等。
目标动作。当规则匹配一个包时,真正要执行的任务用目标标识。最常用的内置目标分别是:
→ACCEPT表示允许包通过
→DROP表示被丢弃
此外,包过滤防火墙还可以使用扩展的目标:
→REJECT表示拒绝包,丢弃包的同时给发送者发送没有接受的通知。
→LOG表示包的有关信息被记录日志。
→TOS表示改写包的ToS的值。
要使用上述的扩展目标,必须在内核中激活相应选项或者装载了相应的内核模块。
2.2.3 Iptables工具的调用语法
Iptables的语法非常复杂,要查看该工具的完整语法,应该查看其手册页。
Iptables的语法通常可以简化为下面的形式:
Iptables[-t table]CMD[chain][rule-matcher][-j target]
其中:tables为表名,CMD为操作命令,chain为链名,rule-matcher为规则匹配器,target为目标动作。
2.2.4 制定永久性规则
iptables软件包提供了两个命令分别用于保存和恢复规则集。可以使用下在的命令转储在内存中的内核规则集。其中/etc/sysconfig/iptables是iptables守护进程调用的默认规则集文件:
#/sbin/iptables-save >; /etc/sysconfig/iptables
要恢复原来的规则库,需使用命令:
#/sbin/iptables-restore < /etc/sysconfig/iptables
为了使得用iptables命令配置的规则在下次启动机器时还能被使用,有两种主法。
(1)使用iptables的启动脚本实现。Iptables的启动脚本/etc/rc.d/init.d/iptables每次启动时都使用/etc/sysconfig/iptables所提供的规则进行规则恢复,并可以使用如下的命令保存规则:
#service iptables save
(2) 在自定义脚本中用iptables命令直接创建规则集。可以直接用iptables命令编写一个规则脚本,并在启动时执行这个脚本。
例如:若规则脚本的文件名为/etc/fw/rules,则可以在启动脚本/etc/rd.d/init.d/rc.local中加入下面的代码:
if[-x /etc/fw/rules];then /etc/fw/rules;fi;
这样,机器每次启动时即可执行该规则脚本。
如果使用此种方式,建议使用ntsysv命令关闭系统的iptables守护进程。
2.3 iptables命令使用举例
2.3.1 链的基本操作
(1)清除所有的规则。通常在开始配置包过滤防火墙之初清除所有的规则,重新开始配置,以免原有的规则影响新的设定。使用如下命令进行:
1)
清除预设表filter中所有规则链中的规则。
#iptables -F
2)清除预设表filter中使用者自定义链中的规则。
#iptables -X
3)将指定链所有规则的包字节记数器清零。
#iptables -Z
(2)设置链的默认策略。一般地,配置链的默认策略有两种方法。
1)
首先允许所有的包,然后再禁止有危险的包通过防火墙。即“没有被拒绝的都允许”。这种方法对于用户而言比较灵活方便,但对系统而言,容易引起严重的安全问题。
为此,应该使用如下的初始化命令:
#iptables -P INPUT ACCEPT
#iptables -P OUTPUT ACCEPT
#iptables -P FORWARD ACCEPT
2)
首先禁止所有的包,然后再根据需要的服务允许特定的包通过防火墙。即“没有明确允许的都被拒绝”。这种方法最安全,但不太方便。为了使得系统有足够的安全性,一般采用此种策略进行iptables防火墙的配置。
为此,应该使用如下的初始化命令:
#iptables -P INPUT DROP
#iptables -P OUTPUT DROP
#iptables -P FORWAED DROP
3)列出表/链中的所有规则。包过滤防火墙只使用filter表,此表为默认的表,因此可以使用下面的命令列出filter表中所有规则:
#iptables -L
使用上面的命令时,iptables将逆向解析IP地址,这将发费很多时间,从而造成信息出来的非常慢。为了解决这个问题,可以使用下面的带有-n参数的命令(-n参数用于显示数字化的地址和端口):
#iptables -L -n
4)向链中添加规则。下面的语句用于开放网络接口:
#iptables -A INPUT -i lo -j ACCEPT
#iptables -A OUTPUT -o lo -j ACCEPT
#iptables -A
INPUT -i
eth0 -j ACCEPT
#iptables -A OUTPUT -o eth0 -j ACCEPT
#iptables -A FORWARD -i eth0 -j ACCEPT
#iptables -A FPRWAED -o eth0 -j ACCEPT
5)使用用户自定义链。下面是一个用户自定义链的创建、修改和调用的简单命令序列:
#iptables -N custom
#iptables -A custom -s 0/0 -d 0/0 -p icmp -j DROP
#iptables -A INPUT -s 0/0 -d 0/0 -j custom
首先使用带-N参数的iptables命令新建了一个名为custom的用户逢定义链。然后使用带-A参数的命令添加了一条用户自定义的堵截规则,该规则丢弃全部的ICMP包。最后向默认的INPUT链加入一条规则,使所有的包都由custom自定义链处理。结果全部的ICMP包都将被丢弃。
2.3.2 设置基本的规则匹配
下面举例说明iptables的基本规则匹配(忽略目标动作):
(1)指定协议匹配
1)匹配指定的协议
#iptables -A INPUT -p tcp
2)匹配指定协议之外的所有协议
#iptables -A INPUT -p !tcp
(2)指定地址匹配
1)指定匹配的主机
#iptables -A INPUT -s 192.168.1.1
2)指定匹配的网络
#iptables -A INPUT -s 192.168.1.0/24
3)匹配指定主机之外的地址。
#iptables -A FORWARD -s ! 192.168.0.1
4)匹配指定网络之外的网络
#iptables -A FORWARD -s ! 192.168.0.0/24
(3)指定网络接口匹配。
1)指定单一的网络接口匹配。
#iptables -A INPUT -i eth0
#iptables -A FORWARD -o eth0
2)指定同类型的网络接口匹配
#iptables -A FORWARD -o ppp+
(4)指定端口匹配
1)指定单一的端口匹配。
#iptables -A INPUT -p tcp-sport www
#iptables -A INPUT -p tcp-sport 80
#iptables -A INPUT -p tcp-sport 53
#iptables -A INPUT -p udp-dport 53
2)匹配指定端口之外的端口。
#iptables -A INPUT -p tcp-dport ! 22
3)匹配指定端口的范围。
#iptables -A INPUT -p tcp-soprt 22:80
4)匹配ICMP端口和ICMP类型。
#iptables -A INPUT -p icmp --icmp-type 8
5)指定ip碎片
在TCP/IP通信过程中,每一个网络接口都有一个最大的传输单元(MTU),这个参数定义了可以通过的数据包的最大尺寸。如果一个数据包大于这个参数值时,系统会将其划分成更小的数个数据包(称之为ip碎片)来传输,而接收方则对这些ip碎片再进行重组以还原整个包。
但是在进行包过滤的时候,ip碎片会导致这样一个问题:当系统将大数据包划分成ip碎片传送时,第一个碎片含有完整的包头信息(IP+TCP、UDP和 ICMP),但是后续的碎片只有包头的部分信息(如源地址、目的地址)。因此,检查后面的ip碎片的头部(就像有TCP、UDP和ICMP一样)是不可能的。假如有这样一条规则:
#iptables -A FORWARD -p tcp -s 192.168.1.0/24 -d 192.168.2.100 --dport 80 -j ACCEPT
并且这时的FORWARD的策略(policy)为DROP时,系统只会让第一个ip碎片通过,而丢掉其余的ip碎片,因为第一个碎片含有完整的包头信息,可以满足该规则的条件,而余下的碎片因为包头信息不完整而无法满足规则定义的条件,因而无法通过。
可以通过-fragment/-f选项来指定第二个及其以后的ip碎片,以上面的例子为例,我们可以再加上这样一条规则来解决这个问题:
#iptables -A FORWARD -f -s 192.168.1.0/24 -d 192.168.2.100 -j ACCEPT
但是需要注意的是,现在已经有许多进行ip碎片攻击的实例(例如向Win98 NT4SP5,6 Win2K发送大量的ip碎片进行DoS攻击),因此允许ip碎片通过是有安全隐患的,对于这一点可以采用iptables的匹配扩展来进行限制。
2.3.3 设置扩展的规则匹配
要获得匹配的简要说明,可以使用如下的命令:
#iptables -m name_of_match -help
下面举例说明iptables的扩展规则匹配(忽略目标动作):
(1)多端口匹配扩展。
1)匹配多个源端口。
#iptables -A INPUT -p tcp -m multiport-source-port 22,53,80,110
2)匹配多个目的端口
#iptables -A INPUT -p tcp -m multiport-destionation-port 22,53,80,110
3)匹配多个端口(无论源源端口还是目的端口)。
#iptables -A INPUT -p tcp -m multiport -port 22,53,80,110
(2)指定TCP匹配扩展
通过使用-tcp-flags选项可以根据tcp包的标志位进行过滤,该选项后接两个参数:第一个参数为要检查的标志位,可以是SYN、ACK、FIN、URG、PSH的组合,可以用ALL指定所有标志位:第二个参数是标志位值为1的标志。
#iptables -A INPUT -p tcp-tcp-flags SYN,FIN,ACK SYN
表示SYN、ACK、FIN的标志都要被检查,但是只有设置了SYN的才匹配。
#iptables -A INPUT -p tcp-tcp-flags ALL SYN,ACK
表示ALL(SYN,ACK,FIN,RST,URG,PSH)的标志都要被检查,但是只有设置了SYN和ACK的才匹配。
选项-syn是以上的一种特殊情况,相当于“--tcp-flags SYN,RST,ACK SYN“的简写。
#iptables -p tc-syn
(3)limit速率匹配扩展。
1)指定单位时间内允许通过的数据包的个数。
单位时间可以是/second、/minute、/hour、/day或使用第一个字母。例如:
#iptables -A INPUT -m limit-limit 300/hour
表示限制每小时允许通过300个数据包。
2)指定触发事件的阀值。
使用-limit-burst指定触发事件的阀值(默认是5),用来比对瞬间大量数据包的数量。
#iptables -A INPUT -m limit-limit-burst 10
上面的例子是用来比对一次同时涌入的封包是否超过10个,超过此上限的包将被直接丢弃。
3)同时指定速率限制和触发阀值。
#iptables -A INPUT -p icmp -m limit-limit 3/m-limit-burst 3
假设均匀通过,平均每分钟3个,那么触发阀值burst保持为3个,如果每分钟通过的包的数目小于3,那么触发阀值burst将在每个周期(若每分钟允许通过3个,则周期数为20秒)后加1,但最大值为3。每分钟要通过的包的数量如果超过3,那么触发阀值burst将减掉超出的数值,例如第二分钟有4个包,那么触发阀值burst变成2,同时4个包都可以通过,第三分钟有6个包,则只能通过5个,触发阀值burst将变成0。之后,每分钟如果包数小于等于3,则触发阀值burst将加1,如果每分钟包数大于3,触发阀值burst将逐渐减小最终维持为0。
即每分钟所允许的最大包数量为限制速率(本例为3)加上当前的触发阀值burst数。在任何情况下,都可以保证3个包通过,触发阀值burst相当于是允许额外的包数量。
(4)基于状态的匹配扩展(连接跟踪)。
每个网络连接包括以下信息:源地址、目的地址、源端口、目的湍口,称为套接字对(socket pairs);协议类型、连接状态(TCP协议)和超时时间等。防火墙把这些信息称为状态(stateful),能够检测每个连接状态的防火墙叫作状态包过滤防火墙。它除了能够完成简单包过滤防火墙的包过滤工作外,还在自己的内存中维护一个跟踪连接状态的表,比简单包过滤防火墙具有更大的安全性。 iptables与ipchains的不同之处在于iptables可以使用连接状态信息,创建基于包的状态的规则匹配。
基于状态匹配创建规则的命令格式如下:
iptables -m state-state [!]state [,state,state,state]
其中,state表是一个用逗号分割的列表,用来指定的连接状态可以是如下的4种:
→NEW:该包想要开始一个连接(重新连接或将连接重定向)。
→RELATED:该包是属于某个已经建立连接所建立的新连接。例如:FTP的数据传输连接和控制连接之间就是RELATED关系。
→ESTABLISHED:该包属于某个已经建立的连接。
→INVALID:该包不匹配于任何连接,通常这些包会被DROP。
例如:
1)在INPUT链添加一条规则,匹配已经建立的连接或由已经建立的连接所建立的新连接。即匹配所有的TCP回应包。
#iptables -A INPUT -m state-state RELATED,ESTABLISHED
2)在INPUTP链添加一条规则,匹配所有从非eth0接口来的连接请求包。
#iptables -A INPUT -m state-state NEW -i ! eth0
又如,对于ftp连接可以使用下面的连接跟踪:1)被动ftp连接模式。
#iptables -A INPUT -p tcp-sport 1024: --dport 1024: -m state-state ESTABLES -j ACCEPT
#iptables -A OUTPUT -p tcp-sport 1024: --dport 1024: -m state-state ESTABLISHED,RELATED -j ACCEPT
2)主动ftp连接模式。
#iptables -A INPUT -p tcp-sport 20 -m state-state ESTABLISHED,RELATED -j ACCEPT -m state-state ESTABLISHED -j ACCEPT
(5)TOS匹配扩展。
所有数据包的首部都有一个称为服务类型的专用字段。该字段用于当数据包经过路由器时区分优先顺序。
使用ToS字段,可以使用某种类型的数据包优先于其它的数据包。有标记的数据包在路由器中进行不同的处理。设置ToS字段的工作是在网络层进行的。有关ToS的具体工作过程请参专考RFC1349。
在数据包中设置ToS字段的主要原因是:对不同类型的数据包加以区分,从而在流量较高的网络上减少网络拥塞。
数据包的ToS字段用一个字节表示。此字节的高3位舍弃不用,最低位使用固定值0,因此,有效位共有4个。有效位分别用来控制数据包的最小延时,最大吞吐量,最大可靠度和最小成本。其中,
→最小延时:用于减少数据包从路由到达主机的时间。
→最大吞吐量:用于提高数据传输量。适用于FTP数据端口,HTTP大文件传输和大量的POP3传输。
→最大可靠度:用来试图减少数据重传。
→最小成本:通常只在商业产品中使用。
要创建与ToS字段匹配的规则,可以使用下面的语法:
iptables [-t table]CMD[chain]-m tos tos_value [-j target]
其中tos_value可以是0x02、0x04、0x06、0x08、0x10。
例如:
#iptables -A INPUT -p tcp -m tos 0x08
表示匹配在ToS字段中设置了最大吞吐量的包。
2.3.4 设置目标扩展
要获得目标的简要说明,可以使用如下的命令:
#iptables -j name_of_TARGET-help
目标扩展由内核模块组成,而且iptables的一个可选扩展提供了新的命令行选项。在几个扩展是包含在默认netfilter发布中的。
(1)设置包的ToS值。为了设置数据包的ToS值,需要使用ToS目标扩展。设置ToS的值,只能对FORWARD、OUTPUT和PREROUTING链进行。语法如下:
iptables [-t table]CMD[chain][rule-matcher]-j TOS-set-tos tos_value
其中tos_value可以是0x02、0x04、0x06、0x08、0x10。
下面是使用ToS目标扩展设置ToS值的一些例子:
#iptables -A OUTPUT -p tcp -m tcp-dport 21 -j TOS-set-tos 0x10
#iptables -A OUTPUT -p tcp -m tcp-dport 22 -j TOS-set-tos 0x10
#iptables -A OUTPUT -p tcp -m tcp-dport 110 -j TOS-set-tos 0x08
(2)日志记录。iptables使用新的目标扩展LOG来实现日志记录。LOG目标用syslogd记录此包并继续传播。LOG目标扩展还可以使用如下的一些参数:
→ --log-level
跟踪一个级别名称或数字。合适的名字是'debug'、'info'、'notice'、'warning'、'err'、'crit'、'alert' 和'emerg',相当于数字7到0。参考syslog.conf的手册可获取这些级别的说明。默认是'warning'。
→ --log-prefix
跟踪一个最多29个字符的字符串,它被写入到log信息的开始处,这样可以方便地使用grep对日志进行过滤。
另外,在使用LOG目标扩展的同时,还可以使用limit match,这样可以使记录有用日志的同时确保日志记录不会变得冗长。
下面是使用LOG目标扩展的一些例子:
#iptables -A FORWARD -m tcp -p tcp -j LOG
#iptables -A FORWARD -m icmp -p icmp-sport echo-reply -j LOG
#iptables -A INPUT -m limit-limit 3/m-limit-burst 3 -j LOG-log-prefix "INPUT packet died:"