iptables详解

Posted by Dylan Chen on June 30, 2018

iptables是一个Netfilter管理工具,能够实现对内核中网络防火墙的管理。它工作在用户空间中,通过规则来定义防火墙的行为。

Netfilter是由Rusty Russell提出的Linux 2.4内核防火墙框架,该框架简洁而灵活,可以实现数据包过滤、数据包处理、地址伪装、透明代理、动态网络地址转换等安全策略应用中的功能。

tables & chains

iptables使用表来描述不同的功能,如包过滤、网络地址转换。共定义了5个不同的表:

  • raw 用于配置数据包,raw的操作发生在数据包被conntrack跟踪之前。我们可以在这里将一个连接标记为NOTRACK以忽略网络跟踪。
  • filter 用于过滤本机流入流出的数据包,是iptables默认使用的表,也是我们经常操作的表。
  • nat 网络地址转换,用于转换五元组(协议、源地址、源端口、目的地址、目的IP)
  • mangle 主要用于修改数据包中的路由标记。如TTLTOSMARK
  • security 用于强制访问控制网络规则。
$ cat /proc/net/ip_tables_names
mangle
security
raw
filter
nat

每个表都定义了一些内置链(chain),一个链可以看成是一个规则的集合,它决定了这些规则起作用的时间。当一个数据包经过防火墙的时候,数据包会按特定的顺序经过不同表的不同链。iptables默认定义了五个链:

  • PREROUTING
  • INPUT
  • FORWARD
  • OUTPUT
  • POSTROUTING

这些链和Netfilter内核防火墙框架提供的钩子是一一对应的,Netfilter提供了如下五个钩子供内核模块注册使用:

  • NF_IP_PRE_ROUTING 这个钩子会在数据包刚进入本机网络栈,还未到达路由表前被触发。
  • NF_IP_LOCAL_IN 这个钩子会在数据包经过路由表后,目的地址为本机时触发。
  • NF_IP_FORWARD 这个钩子会在数据包经过路由表后,目标地址是其他主机时触发。
  • NF_IP_LOCAL_OUT 这个钩子会在本地产生的数据包进入本机网络栈时被触发。
  • NF_IP_POST_ROUTING 这个钩子会在数据包经过路由后进入网卡前被触发。

不同的表内置的链不尽相同,下表列举了不同的表默认的链。

raw PREROUTING、OUTPUT
filter INPUT、OUTPUT、FORWARD
nat PREROUTING、POSTROUTING、OUTPUT
mangle PREROUTING、OUTPUT、FORWARD、INPUT、POSTROUTING
security INPUT、OUTPUT、FORWARD

总结来说,iptables中table关注的是功能,如包过滤、地址转换。而chain专注的是执行的时间,它们和Netfilter提供的钩子一一对应。

遍历顺序

不同场景下数据包将遍历不同的链:

  • 目的地为本机的包:PREROUTING -> INPUT
  • 进入本机但目的地为其他主机的包:PREROUTING -> FORWARD -> POSTROUTING
  • 由本地发出的包:OUTPUT -> POSTROUTING

下图是数据包详细的流经图:

tables_traverse.jpg

自定义链

除了预定义的链外,iptables 也支持自定义链,和预定义链一样,可以向链中添加一组规则。所不同的是,预定义链并没有注册到 Netfilter 的钩子(HOOK)上,所以他们无法像预定义链一样自动被触发,而是需要通过预定义链上的规则跳转到预定义链上。

例如,docker中自定义的两个链 DOCKERDOCKER-ISOLATION 就是通过 FORWARD 链中规则跳转过去的。

$ iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N DOCKER
-N DOCKER-ISOLATION
-A FORWARD -j DOCKER-ISOLATION
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER ...
...
-A DOCKER-ISOLATION ...
...

网络连接状态

在iptables中,数据包可以和网络连接的四种状态相关联,即NEW、 ESTABLISHED、RELATED以及INVALID。连接的跟踪可以帮助Netfilter框架了解一个连接的状态,从而通过更紧密的规则提供更好的安全策略。所以iptables防火墙也属于有状态的防火墙。

网络连接跟踪是由内核中一个特殊的框架conntrack实现的,并且针对不同的协议(如TCP,UDP),存在对应的conntrack模块。conntrack从数据包中提取特定的信息来跟踪不同连接的不同状态。我们可以通过/proc/net/ip_conntrack查看跟踪的连接状态。

状态 描述
NEW NEW表示当前的数据包是一个特定连接的第一个包,如SYN包。当然也存在不是SYN包同样被认为是NEW的情况,比如一个连接已经timeout,但是实际上并没有被关掉。
ESTABLISHED 当conntrack从两个方向都接收到数据包的时候,连接的状态为ESTABLISHED。例如:一个请求获得了响应,那么连接就从NEW进入ESTABLISHED。
RELATED RELATED状态比较复杂一些,一个连接处于RELATED状态,需要它和一个已经处于ESTABLISHED的连接进行关联,例如一个主连接衍生出的子连接。
INVALID 该状态表示一个包无法被正确识别或者连接不存在任何状态。多种原因可能导致INVALID,例如系统内存耗尽、一个不存在的连接的ICMP错误消息。
UNTRACKED 当一个包在raw表中被标记为NOTRACK时,连接将进入UNTRACKED状态,这时候所有RELATED的连接都不可见。

iptables语法

iptables [-t 表]
	<命令>
	[链]
	[规则号码]
	[匹配条件]
	[-j 匹配后的动作]

例如:

来自 192.168.21.0/24 的所有 icmp 全部 DROP。

$ iptables -t filter -A INPUT -s 192.168.21.0/24 -p icmp -j DROP

匹配后执行的动作被称为 Target,如 ACCEPT,DROP。

命令

查看命令

-L (–list)

查看指定表的所有规则,默认为 filter 表。支持如下参数:

  • -t 指定表名,默认为 filter
  • -n 以数字的方式显示 IP 和 PORT
  • -v 显示详细信息
  • -x 在计数器上显示精确值,不做单位换算
  • –line-numbers 显示规则的行号
$ iptables -L

-S (–list-rules)

输出一个链或所有链中的规则,和 -L 相比,-S 只是简洁地输出了规则列表。

$ iptables -S INPUT
-P INPUT ACCEPT

链管理命令

-P (–policy)

设置默认策略。例如默认丢弃所有目的地址为本机的包:

$ iptables -P INPUT DROP

-F (–flush)

清空链中的规则。例如清空 filter 表中 FORWARD 链中的规则:

$ iptables -t filter -F FORWARD 

-N (–new)

新建一个自定义链。例如Docker中自定义了 DOCKER-ISOLATION 链来实现网络间的隔离:

$ iptables -N DOCKER-ISOLATION

-E (–rename-chain)

重命名链。例如将链 CHAIN-A 重命名为 CHAIN-B:

$ iptables -E CHAIN-A CHAIN-B

-X (–delete-chain)

删除自定义的链。例如删除 CHAIN-A:

$ iptables -X CHAIN-A

-Z (–zero)

清空链中的计数器:

$ iptables -vL FORWARD
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
1220K 8021M DOCKER-ISOLATION  all  --  any    any     anywhere             anywhere
63452 4983K ACCEPT     all  --  any    docker0  anywhere             anywhere             ctstate RELATED,ESTABLISHED
   78  4056 DOCKER     all  --  any    docker0  anywhere             anywhere
64631  389M ACCEPT     all  --  docker0 !docker0  anywhere             anywhere
    0     0 ACCEPT     all  --  docker0 docker0  anywhere             anywhere
$ iptables -Z FORWARD
$ iptables -vL FORWARD
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DOCKER-ISOLATION  all  --  any    any     anywhere             anywhere
    0     0 ACCEPT     all  --  any    br-574e3e12cd8a  anywhere             anywhere             ctstate RELATED,ESTABLISHED
    0     0 DOCKER     all  --  any    br-574e3e12cd8a  anywhere             anywhere
    0     0 ACCEPT     all  --  br-574e3e12cd8a !br-574e3e12cd8a  anywhere             anywhere
    0     0 ACCEPT     all  --  br-574e3e12cd8a br-574e3e12cd8a  anywhere             anywhere
    0     0 ACCEPT     all  --  any    docker0  anywhere             anywhere             ctstate RELATED,ESTABLISHED
    0     0 DOCKER     all  --  any    docker0  anywhere             anywhere
    0     0 ACCEPT     all  --  docker0 !docker0  anywhere             anywhere
    0     0 ACCEPT     all  --  docker0 docker0  anywhere             anywhere

规则管理命令

-A (–append)

追加规则。例如在Filter 表的 INPUT 链中追加一条规则:

$ iptables -A INPUT -p tcp --dport 80 -j ACCEPT

-I (–insert)

插入规则到链中,默认为第一条。例如插入规则作为 INPUT 链的第二条规则。

$ iptables -I INPUT 2 -p tcp --dport 8080 -j ACCEPT

-R (–replace)

替换链中的一条规则。

$ iptables -R INPUT 2 -p tcp --dport 8080 -j DROP

-D (–delete)

删除链中的一条规则。

$ iptables -D INPUT 2

匹配

每个iptables规则都包含一组匹配信息和一个 Target (规则匹配后执行的动作)。只有当数据包匹配这组匹配信息的时候,指定的 Target 才会被执行。

-s (–source)

匹配原地址或网络,仅支持IP。

-d (–destination)

匹配目标地址或网络,仅支持IP。

-p (–protocol)

匹配上层协议(TCP/UDP/ICMP/ALL)

-i (–in-interface)

匹配流入的网络接口,一般用在 INPUT 和 PREROUTING 上,例如 eth0。

-o (–out-interface)

匹配流出的网络接口,一般用在 INPUT 和 PREROUTING 上,例如 eth0。

-f (–fragment)

匹配分片的包的第二片及以后的部分。

--sport (–source-port)

匹配源端口,是对TCP和UDP协议匹配的扩展。可以指定单个端口也可以指定连续的端口范围。

$ iptables -A INPUT -p tcp --dport 80 ACCEPT
$ iptables -A INPUT -p tcp --dport 80:90 ACCEPT

--dport (–destination-port)

匹配目的端口,是对TCP和UDP协议匹配的扩展。可以指定单个端口也可以指定连续的端口范围。

--tcp-flags

匹配 TCP 的标志位(SYN ACK FIN PSH RST URG)。

$ iptables -p tcp --tcp-flags SYN,FIN,ACK SYN -j ACCEPT

扩展匹配

-m 用来指定显示的扩展匹配,例如, -m state 指定按网络连接状态匹配的扩展。

-m state

匹配连接状态:NEWESTABLISHEDINVALIDRELATED,格式是: -m state --state <状态>

iptables -A INPUT -m  state --state RELATED,ESTABLISHED -j ACCEPT

-m mac

匹配源主机的Mac地址,例如,阻断Mac地址为 00:0c:29:a7:bb:99 的源主机发来的包:

$ iptables -A INPUT -m mac --mac-source 00:0c:29:a7:bb:99 -j DROP

-m limit

匹配单位时间连接控制,使用 /second/minute/hour/day等单位为后缀,默认是3/hour。例如限制目的ip为192.168.2.201每秒3个包:

$ iptables -A INPUT -d 192.168.2.201 -m limit --limit 3/s -j ACCEPT
$ iptables -A INPUT -d 192.168.2.201 -j DROP

--limit只是按一定速率去匹配而已,要想限制的话后面要再跟一条DROP

-m iprange

匹配 IP 范围,格式为:-m iprange [!] <--src-range|--dst-range> IPADDR-IPADDR。例如匹配源 IP 范围 192.168.21.10 到 192.168.21.19

$ iptables -A INPUT -p tcp -m iprange --src-range 192.168.21.10-192.168.21.19 -j DROP

-m multiport

匹配多个端口,格式为:-m multiport [!] <--sports|--dports|--ports> port1[,port2,..,portn]

-m ttl

匹配 IP 头里的 TTL (Time To Live,即生存期),格式为:-m ttl <–ttl-eq|–ttl-gt|–ttl-lt> num

$ iptables -A INPUT -p icmp -m ttl --ttl-eq 64 -j DROP

-m time

匹配起始时间与结束时间:

-m time --datestart YYYY[-MM[[-DD[Thh[:mm[:ss]]]]] --datestop YYYY[-MM[-DD[Thh[:mm[:ss]]]]]

匹配时间和日期:

--timestart hh:mm[:ss] --timestop hh:mm[:ss] [!] --monthdays day[,day...]
--timestart hh:mm[:ss] --timestop hh:mm[:ss] [!] --weekdays day[,day...]

例如,每周的周末拒绝 ping :

iptables -A INPUT -p icmp -m time --timestart 00:00:01 --timestop 23:59:59 --weekdays Sat,Sun -j DROP

动作

动作,即 -j (–jump) 指定的 Target 。它告诉一条规则,当数据包匹配该规则的时候该如何处理。

ACCEPT

允许数据包通过。

DROP

丢弃数据包。

LOG

将数据包信息记录到rsyslog中。支持以下参数:

  • --log-level 所有级别debug,info,notice,warning,warn,err,error,crit,alert, emerg,panic
  • --log-prefix 在记录的信息之前加上指定的前缀,前缀最多能有29个英文字符
  • –log-tcp-sequence
  • –log-tcp-options
  • –log-ip-options

需要在/etc/rsyslog.conf添加一条配置kern.=info /var/log/firewall.log,然后重启rsyslog服务。

$ iptables -A INPUT -p icmp -j LOG --log-prefix ICMP_ --log-level info

REJECT

丢弃数据包,同时发生适当的响应报文(如:针对TCP连接的RST包或针对 UDPICMP 端口不可达消息)。可以通过 --reject-with 指定返回什么样的信息。

  • icmp-net-unreachable
  • icmp-host-unreachable
  • icmp-port-unreachable
  • icmp-proto-unreachable
  • mp-net-prohibited
  • mp-host-prohibited
  • port-unreachable 默认选项

DNAT

目标地址转换。注意目标地址转换要在数据包到达网卡之前,所以规则应该添加在 PREROUTING 链上。

$ iptables -t nat -A PREROUTING -d 192.168.2.201 -p tcp --dport 80 -j DNAT --to-destination 192.168.2.202

SNAT

源地址转换。基于源地址的转换一般用在我们的许多内网用户通过一个外网的口上网的时候,这时我们将内网地址转换为一个外网的 IP,我们就可以实现连接公网的功能。

--to-source 用于指定源地址和端口,可以是单独的ip也可以是ip段。例如,将所有192.168.2.0/24 段的 IP 在经过的时候全都转换成 172.16.100.1 这个是外网 IP

$ iptables -t nat -A POSTROUTING -s 192.168.2.0/24 -j SNAT --to-source 172.16.100.1

MASQUERADE

动态地址伪装。自动查找可用的IP地址,当外网接口是通过ppp拨号上网时,拨到的公网地址可能会不通

--to-ports 用于设置外出包能使用的端口,可以省略。

$ iptables -t nat -A POSTROUTING -s 192.168.2.0/24 -j MASQUERADE --to-ports 80-90

REDIRECT

在内部转发包或流到另一端口。例如将外网访问80端口的数据转发到8080端口。

$ iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080

MARK

打防火墙标记。

RETURN

返回父链中继续处理数据包。

规则保存&开启

通过 iptables 设置的规则是即时生效的,但是当机器重启后,配置的规则也将丢失。想要机器重启后规则仍能生效,我们需要将规则保存起来。

在机器启动的时候,iptables 会自动加载 /etc/sysconfig/iptables 文件中的规则。所以我们可以将规则保存到该文件中。

$ iptables-save > /etc/sysconfig/iptables

我们也可以指定自己的文件来导入规则。

$ iptables-restore < /etc/sysconfig/iptables.test

参考:

  • https://blog.csdn.net/liukuan73/article/details/78631540
  • https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture
  • https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html