本文主要收集、整理、翻译网上的iptables相关资料,记录于此。
Linux内核有一个过滤网络数据包的过滤框架叫netfilter,而iptables就是基于这个框架的工具,通过设置各种规则来影响数据包的流动,从而实现功能多变的防火墙功能。
iptables有三部分组成:tables、chains和targets。
tables包含四张表,分别为:
有些Linux发行版还有security表,被SELinux用来实现安全策略。
内核设置了几个位置点来读取iptables设置的规则,每个位置点上挂载了很多规则,形成链状结构,内核会按顺序依次执行。这些链包括:
下图展示了数据包的流向和各链及表的位置:
targets 决定了数据包的命运,就是在配置上特定规则后,要怎么处理该数据包。分两种类型,一种是终止类型,即数据包的命运已经决定,不需要再配置下一个规则了,另一种是非终止类型,表示还需要继续接下来的规则。终止类型的targets有如下几个:
非终止类型有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模块就能达到目的。所有的被跟踪的连接有如下一些状态:
通常这样的规则应该放在第一条:
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
注意:如果不确定wlan后面接的数字是几,可以写成:wlan+,相当于前缀匹配。
有时候你是用的排除法,比如除了某些端口,其他的都DROP掉,你可以使用取反操作:
iptables -A INPUT -p tcp -m multiport ! --dports 22,80,443 -j DROP
注意:同样你需要接受建立连接和相关连接,否则任何基于TCP的应用你都用不了。
注意:如果是指定端口范围8000至9000,可以写成:8000:9000。
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
如果你想控制某种包的频率,比如ICMP包,可以这么干:
iptables -A INPUT -p icmp -m limit --limit 1/sec --limit-burst 1 -j ACCEPT
注意:当频率满足设定的限制时,包会被ACCEPT,而当超过限制时,并不会DROP掉,而是执行链中的其他规则,所以如果要它起作用,通常你还需要设置INPUT的默认规则为DROP。
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掉,从而彻底阻止攻击者的全部连接。
可以基于用户来设置规则,即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规则:
sudo iptables -L INPUT
sudo iptables -L OUTPUT
sudo iptables -L FORWARD
iptables是直接设置给内核的,当服务器重启就丢失了。有两个命令用来备份和恢复设置的规则:
iptables-save > iptables.rules
iptables-restore < iptables.rules
同时,在Ubuntu上可以安装软件iptables-persistent,它是一个常驻进程,用来辅助自动化备份和恢复规则。