Linux 防火墙分为 应用层防火墙 和 包过滤防火墙,iptables 属于包过滤防火墙,底层使用的是 Linux 内核模块 netfilter,性能佳稳定性好。

原理简析

iptables 基本结构:

iptables -> 规则表 -> 规则链 -> 具体规则

可以看出 iptables 通过参数 t 指定要操作的规则表,使用命令(如添加、删除、替换、清空、查看等)控制规则链,再指定规则的条件,最后使用参数 j 控制数据包的行为。

iptables 用的最多的是 filter 表和 nat 表。filter 表用于数据过滤,nat 表用于网络地址转换。

filter 表有 INPUT、OUTPUT 和 FORWARD 等规则链。INPUT 链控制数据包的流入,PUTPUT 链控制数据包的流出,FORWARD 链控制数据的转发。

nat 表有 PREROUTING、POSTROUTING 和 OUTPUT 规则链,PREROUTING 链用来修改数据包的目标端口和地址,POSTROUTING 链用来修改数据的源端口和地址,OUTPUT 链作用类似 PREROUTING 链,不过是用来修改本机发出的数据的 目标端口和地址。

这几个规则链的关系如下:

当外面的数据包进入本机,首先会经过 PREROUTING 链,对修改后的目标地址进行路由判断,如果发现其目标就是本机,那就走 A 经过 INPUT 链的筛选,符合条件就放行至本应用处理;如果发现目标地址是其他主机,那就走 B 使用 FORWARD 链进行转发,最后通过 POSTROOUTING 链出去。POSTROUTING 链很重要的工作是修改来源地址为本机,并记录这个修改关系。数据回传的时候本机通过这个修改关系转发数据给源主机。

当本机要发出数据包,先经过路由判断进入 nat 表的 OUTPUT 链,用来修改目标主机,然后经过 filter 表的 OUTPUT 链的筛选,符合条件就放行到 POSTROUTING 链,修改源地址就包并发出。

用来修改目标地址的 PREROUTING 链必须放在最前面,放在路由后面将无法选择 A 还是 B。POSTROUTING 链必须放在最后,等到所有链判断完成后再修改源地址,如果放在前面,倘若数据包都出不来,提前修改源地址纯属浪费力气。所以这这个链的位置是有讲究的,并不是随意放置。

语法命令

iptables 语法格式:

iptables [-t 表名] 命令选项 [链名] [条件匹配] [-j 目标动作或跳转]

命令选项

iptables 常用的命令选项:

  • -A:–append,在规则链的尾部插入规则
  • -I: –insert,默认在规则链的头部插入规则,可以指定位置
  • -D:删除规则链中指定规则
  • -F:清空指定链的规则。不指定则清空所有链
  • -L:–list,查看指定链的规则。不指定则显示所有链
  • -v:查看规则时显示详细信息
  • -n:不将 ip 地址反解析为域名

条件匹配

条件匹配分为基本匹配和扩展匹配,扩展匹配又可以分为隐式扩展匹配和显式扩展匹配。基本匹配命令:

  • -p:指定协议规则,tcp、ip 和 icmp
  • -s:指定数据包的源地址
  • -d:指定数据包的目标地址
  • -i :指定入站流量的网卡
  • -o:指定出站流量的网卡

示例

放行 80 端口

1
iptables -t filter -I INPUT --dport 80 -j ACCEPT

iptables 默认操作 filter 表,因此可以省略;INPUT 表示流量流入,方向是客户机到本机,所以客户机 ip 是源 ip,放行的本地端口就是目标端口;可以增加参数 i 指定网卡,增加参数 s 指定客户机 ip。

允许来自 10.0.0.1 流量通过 80 端口

1
iptables -A INPUT -s 10.0.0.1 -i eth0 -p tcp --dport 80 -j ACCEPT

上面使用了参数 p,表示指定协议为 tcp。

查看规则

只要没有语法问题,设置了规则后 iptables 默认没有任何提示。当我们想要查看规则,可以使用命令 L 查看。结合参数 vn 就可以显示更为现实的规则信息并只显示 ip。

1
iptables --line-numbers -vnL

上面使用参数 –line-numbers 可以显示每条规则的编号,便于定位。

一般网上使用命令 iptables -F 来清空 iptables,认为 iptables 太复杂,直接清空不要防火墙了。虽然 -F 参数有清空的作用,但是情况前需要格外注意默认的规则。当我们使用 -vnL 显示规则时,在第一行会看到如下类似信息:

1
Chain INPUT (policy REJECT 285 packets, 48260 bytes)

上面表示 input 规则链的默认 policy 是 reject,这种情况下你胆敢 -F 清空 iptables,你就惊奇发现:怎么动不了了?

使用 -P 参数更改默认 policy 为 accept,再清空。或者直接卸载 iptables。。。

1
2
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT

高级模块

再看看比较高级的模块。如果想要限制连接数量,使用 –connlimit,例如只允许最多 5 个用户使用 ssh 连接主机:

1
iptables -A INPUT -p tcp --dport 22 -m connlimit --connlimit-above 5 -j DROP

使用 –limit 来限制连接频率,例如限制 ping 速度。

1
iptables -A INPUT -p icmp -m limit --limit 10/s --limit-burst 12 -j ACCEPT

不过我测试 limit 好像限制不了 icmp,听说就是这样?

除了 limit,还可以使用 recent 模块。recent 涉及到地址列表,首先使用 –set 参数将连接到本机的 源ip 加入到地址列表里,将地址列表取名为 restricted-ping。

1
iptables -A INPUT -m --set --name restricted-ping

因为目的还没有达到,就不用 -j 指定操作。然后使用参数 name 指定上面的地址列表,使用 –seconds 参数指定时间,使用 –hitcount 指定次数,来实现频率限制。例如在半个小时内只允许 10 个用户连接 ssh。

1
2
iptables -A INPUT -p tcp --dport 22 -m recet --rcheck --seconds 1800 --hitcount 10
--name restricted-ping -j ACCEPT