iptables简介

本文主要收集、整理、翻译网上的iptables相关资料,记录于此。

Linux内核有一个过滤网络数据包的过滤框架叫netfilter,而iptables就是基于这个框架的工具,通过设置各种规则来影响数据包的流动,从而实现功能多变的防火墙功能。

iptables组成部分

iptables有三部分组成:tables、chains和targets。

tables包含四张表,分别为:

  1. filter 表,使用最频繁,也是命令中默认选定的表,用来决定数据包是否发到目的地
  2. mangle 表,用来修改包头,比如修改TTL值
  3. nat 表,配置NAT(Network Address Translation)网络,即修改源地址或者是目的地址,从而实现不同网段之间的互通
  4. raw 表,iptables是一个有状态的防火墙,一个数据包可能是在一个新建连接上,也可能是一个已存在的连接上的,该表允许你可以在内核开始跟踪包状态之前使用到包。

有些Linux发行版还有security表,被SELinux用来实现安全策略。

内核设置了几个位置点来读取iptables设置的规则,每个位置点上挂载了很多规则,形成链状结构,内核会按顺序依次执行。这些链包括:

  1. PREROUTING 链,位于数据包到达网络设备。这个链会存在于nat,mangle,raw 三个表中
  2. INPUT 链,位于数据包即将交给处理进程之时。这个链存在于mangle,filter表中
  3. OUTPUT 链,位于某进程返回数据包之时。这个链存在于raw,mangle,nat 和 filter 表中
  4. FORWARD 链,位于数据包被路由转发时。这个链存在于mangle 和 filter 表中
  5. POSTROUTING 链,位于数据包即将离开网络设备之时。这个链存在于nat 和 mangle 表中

下图展示了数据包的流向和各链及表的位置:

targets 决定了数据包的命运,就是在配置上特定规则后,要怎么处理该数据包。分两种类型,一种是终止类型,即数据包的命运已经决定,不需要再配置下一个规则了,另一种是非终止类型,表示还需要继续接下来的规则。终止类型的targets有如下几个:

  1. ACCEPT,该包被接受了
  2. DROP,该丢弃掉
  3. REJECT,丢弃该包同时返回拒绝信息。TCP返回'connect reset',UDP 或 ICMP 返回 'destination host unreachable'

非终止类型有LOG,即打个日志到syslog中。

我们还可以创建自己的命名的链,将一定条件下的包发送到我们自定义的规则链里。

简单实例

目前的内核中有两套netfilter,分别对应ipv4和ipv6,所以iptables是针对ipv4的,命令ip6tables是针对ipv6的,多数时候他俩的用法相同。接下来我们都是以iptables作为例子。

iptables -t filter -A INPUT -s xx.xx.xx.xx -j REJECT

上面命令解释为:指定filter表,以append方法,在INPUT链中加一条规则,源IP地址为xx.xx.xx.xx的数据包,拒绝掉。非常有用的命令,当然如果不想回复拒绝给来源机器,可以将REJECT改成DROP。

正如前面提到的,filter表是默认的,所以可以不写。同时还能指定IP范围,即CIDR notation写法:

iptables -A INPUT -s xx.xx.xx.0/24 -j REJECT

还可以接收数据包,但是阻止返回给某IP机器:

iptables -A OUTPUT -d xx.xx.xx.xx -j DROP

查询某个表里的所有规则,其中-n表示显示IP,而不需要反查出域名,-L就是--list列表规则的意思,-t nat就是指定nat表:

iptables -nL --line-numbers -t nat

删除规则有两种方式,一种是将之前添加规则时的-A换成-D,另一种是写规则的序号:

iptables -D INPUT 2

特别注意:如果有多个需要删除,应该从后往前删,因为每删一个,该规则后面的都会往前挪。

清空规则 使用:

iptables -F INPUT

上面说到的是添加(append)规则,还可以插入和替换规则:

iptables -I INPUT 1 -s xx.xx.xx.xx -j ACCEPT
iptables -R INPUT 1 -s xx.xx.xx.xx -j ACCEPT

扩展模块

通过-p可以指定某种协议,tcp/udp/icmp(注意如果是ipv6,你需要写成ipv6-icmp),例如:

iptables -A INPUT -p tcp -j DROP

接下来,我们要使用扩展模块。比如我们已经匹配了所有的tcp协议包,在这个基础上我们还要求阻止目标端口为22的包,即阻止SSH连接:

iptables -A INPUT -p tcp -m tcp --dport 22 -s xx.xx.xx.0/24 -j DROP

-m tcp就是加载tcp模块,--dport就是传给这个模块的参数。

如果你想阻止SSH和VNC(Virtual Network Console远程控制台),但不想写两遍,VNC使用的是5901端口,可以使用multiport模块:

iptables -A INPUT -p tcp -m multiport --dports 22,5901 -s xx.xx.xx.0/24 -j DROP

连接跟踪模块

我们希望iptables不要动一个已经存在的连接上的数据包,使用conntrack模块就能达到目的。所有的被跟踪的连接有如下一些状态:

  1. NEW:一个网络连接的最开始的包
  2. ESTABLISHED:一个已经建立好网络连接中的包,也就是有接收过返回数据
  3. RELATED:相关连接上的包,比如FTP数据传输,会建立一个数据连接和一个控制连接,它俩就是相关连接
  4. INVALID:表示数据包没有一个合理的状态,可能是内存不足导致,也可能是ICMP的一些特殊类型导致
  5. UNTRACKED:在raw表中设置了某些网络连接为NOTRACK,这些连接上的包就会被豁免
  6. DNAT:这是一个虚拟状态,表示包已经被nat表中的规则修改了目的地址
  7. SNAT:这是一个虚拟状态,表示包已经被nat表中的规则修改了来源地址

通常这样的规则应该放在第一条:

iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

通常INVALID状态的包应该被丢弃掉:

iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

修改默认规则

通常各个规则都有一个默认规则,就是ACCEPT,这允许我们正常使用网络,但是我们也可以修改这个默认规则,比如:

iptables -P INPUT DROP

注意:我们应该在这条规则之前接受建立连接和相关连接的包,否则我们连网络连接都建立不了,网络相关的程序根本无法运行。

选择网络接口设备

通常我们应该允许回环接口设备(loopback interface),它通常叫做lo,通常应该作为第一个规则,所以命令为:

iptables -A INPUT -i lo -j ACCEPT

-i是输入设备,-o就为输出设备,比如你想阻止WIFI上的广告,广告来自某一个IP段,WIFI设备接口名为wlan0,你可以添加这样一条规则:

iptables -A OUTPUT -o wlan0 -d xx.xx.xx.0/29 -j DROP

取反操作

有时候你是用的排队法,比如除了某些端口,其他的都DROP掉,你可以使用取反操作:

iptables -A INPUT -p tcp -m multiport ! --dports 22,80,443 -j DROP

注意:同样你需要接受建立连接和相关连接,否则任何基于TCP的应用你都用不了。

阻止不合理的TCP包

Christmas tree packet是一种设置了FIN,URG和PSH位的特殊包,因为不同的操作系统实现的TCP栈对它的处理不同,所以它被用来做操作系统指纹检查,简单地说就是检测对方是什么操作系统。同时这种包的处理比普通正常包更耗时,所以也会被用来做DoS攻击。

我们可以通过tcp模块中的--tcp-flags来检查包是否合理,它的参数包括两个,一个是mask,表示过滤出哪些需要检查的标志位,另一个是compared-flags,表示需要比较的标志位:

iptables -A INPUT -p tcp -m tcp --tcp-flags ALL FIN,PSH,URG -j DROP

还有很多其他类型的不合理包,比如一个包含SYN和FIN位的包就是不合理的,可以通过如下命令DROP掉:

iptables -A INPUT -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -j DROP

另一种不合理的包,比如一个新建连接不应该仅仅以SYN开头,你可以检查FIN,FST,ACK和SYN,但使用conntrack 和 取反操作会更容易:

iptables -A INPUT -p tcp -m conntrack --ctstate NEW -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j DROP

limit模块

如果你想控制某种包的频率,比如ICMP包,可以这么干:

iptables -A INPUT -p icmp -m limit --limit 1/sec --limit-burst 1 -j ACCEPT

注意:当频率满足设定的限制时,包会被ACCEPT,而当超过限制时,并不会DROP掉,而是执行链中的其他规则,所以如果要它起作用,通常你还需要设置INPUT的默认规则为DROP。

recent模块

limit模块不能处理每个IP的限制,而recent的不同之处就是它是Per-IP的,可以限定同一个IP的攻击者。假设同一个IP的攻击者想建立多条连接,来加大它的攻击,可以这么来限制:

iptables -A INPUT -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSHLIMIT --rsource
iptables -A INPUT -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSHLIMIT --update --seconds 180 --hitcount 5 --name SSH --rsource -j DROP

我们创建了一个命名为SSHLIMIT的限制模块,第一行将源IP添加到recent维护的列表中,如果IP已经在列表中,就更新。第二行便是检查是否超过每180秒5个的限制,如果超过了就DROP。所以只允许每3分钟建立4个新的SSH连接。

有的内核recent模块支持--mask参数,这样你可以将某一个区间的IP作为一个IP来限制:

iptables ... -m recent ... --mask 255.255.255.0

由于recent模块是基于名称提供的,可以做很多更有趣的事情。比如当超过限制后,把之前建立的连接也全部DROP掉,从而彻底阻止攻击者的全部连接。

owner模块

可以基于用户来设置规则,即Per-user。比如,限制自己的孩子连接某个网站,假如孩子的用户名为bobby:

iptables -A OUTPUT -d xx.xx.xx.xx -m owner --uid-owner bobby -j DROP

注意:owner模块只能在OUTPUT和POSTROUTING两个链中使用。

自定义链

有时候你有很复杂的一套规则,但是对所有的包都进行这套规则是低效且没有必要的,这个时候你可以创建一个自定义链,找出公共约束运用这个自定义链。比如原本你是这么写的:

iptables -A INPUT -p tcp -m tcp --dport 22 -s 18.130.0.0/16 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 22 -s 18.11.0.0/16 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 22 -j DROP

很显然你只需要对22端口的tcp协议才需要执行这些复杂的规则,所以你可以这样:

iptables -N ssh-rules

iptables -A ssh-rules -s 18.130.0.0/16 -j ACCEPT
iptables -A ssh-rules -s 18.11.0.0/16 -j ACCEPT
iptables -A ssh-rules -j DROP

iptables -A INPUT -p tcp -m tcp --dport 22 -j ssh-rules

注意:自定义链没有默认规则,所以你需要在末尾设置一个默认规则。假如你在自定义链之后还有其他规则,这里你就不要用DROP,而是用RETURN来返回原链继续执行其他规则。

使用自定义链也会让结构清晰,有点像函数,能够确保不会影响到其他的规则,假如规则链非常长的话。

如果要删除一个自定义链,你需要先删除使用到这个链的所有规则,然后再执行:

iptables -X ssh-rules

记录日志

正如最开始说的target分类型,而LOG就是一个非终止型的target,即将特定包打到日志文件/var/log/syslog里,有的系统是/var/log/message。例如:

iptables -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN FIN,SYN -j LOG
iptables -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN FIN,SYN -j DROP

LOG还支持一个--log-prefix的参数,用于日志前缀,方便事后grep查找:

iptables -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN FIN,SYN -j LOG --log-prefix=iptables:

iptables保存

iptables是直接设置给内核的,当服务器重启就丢失了。有两个命令用来备份和恢复设置的规则:

iptables-save > iptables.rules
iptables-restore < iptables.rules

同时,在Ubuntu上可以安装软件iptables-persistent,它是一个常驻进程,用来辅助自动化备份和恢复规则。

发表于 01-21 23:04   修改于 01-21 23:06   评论:0   阅读:346  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo