netfilter 是Linux内核里网络部分的一个重要框架,内核通过netfilter完成IP报文的一些操作。iptables是netfilter的前端命令行工具,Linux系统管理员可以通过iptables工具告诉netfilter如何处理接收到的,转发的和准备发送出去的IP报文。netfilter通常以五表四链+链里的规则提供IP报文处理的功能,其中最重要的二个功能是报文过滤和地址转换(NAT)。netfilter工作在Linux的TCP/IP协议栈,本质上,netfilter是在Linux的TCP/IP协议栈的各层各个处理点插入的一套回调函数,以让用户可以在指定点对IP报文进行处理。

1、netfilter架构和工作原则简介

  1. Netfilter的工作过程:
  • 用户通过iptables告诉netfilter如何处理进出Linux的报文。
  • netfilter分析所有IP报文的IP头
  • 如果有命中中的规则,则按照规则指定的方法处理该报文
  1. 看起来很简单,但实际处理过程的实现是比较复杂的。netfilter包含一系列的tables,每个table里包含一些默认的规则集合称为chain, 每个chain里包含一个或者多个rules, 所以netfilter是由tables-chains-rules三层结构构成的。这就是我们通常所说的五表四链。默认的table是filter table,它包含了以下3个chains:
  • INPUT: 对目标地址为Linux本身的IP地址的IP地方进行处理的规则放在这这个chain里。
  • FORWARD: 对目标地址为其它主机的IP地址,Linux主机需要转发的IP报文的处理的规则放在这这个chain里。
  • OUTPUT: 对本Linux产生的,要发送给其它主机的IP报文的处理的规则放在这这个chain里。

还有常用的nat tables, mangle tables,这些表格所包含的chains可以参见:netfilter的各个tables简介

  1. 总体来说,IP报文在netfilter里的处理过程如下图所示
  • 先由各个tables里的prerouting chain处理,然后送给input/forward chain处理,发送时先后经过output和postrouting chain处理。

图1

  • 具体的处理路径见下图的标颜色的部分,根据报文是进链路成还是直接进IP层进行处理,不一样分成链中层处理和IP层处理的不同路径。
    在这里插入图片描述
  • 不考虑raw table的情况下的IP层处理流程如下图:
    在这里插入图片描述
    第一步:报文到达TCP/IP协议栈后, 由bridge check检查,决定这个报文转到IP层处理后先由mangle table的PREROUTING chain里的规则进行分析。 这里可以对IP报文进行所有mangle table可以支持的操作(比如:TOS字节的修改,标记报文等) 。
    第二步:报文转到nat table的pre-routing chain 进行分析处理,此外可以做SNAT, DNAT,端口重映射等等。因为DNAT(destination network address translation)修改了接收到的报文的目标地址,所以逻辑上决定着必须在报文进入内核的路由转发决定处理模块前完成,这样路由转发处理模块才可以按照修改后的目标IP地址,将报文按照修改后的目标地址将报文转发到正确的目标主机。
    第三步:内核的路由转发模块,决定后续如何转发报文(转发或者说这个报文是发送给本机的)。这不是netfilter的功能。
    第四步:如果这个IP报文是发送给本机的,报文会进入mangle table的INPUT chain (修改TOS,打标记等)进行处理。然后进入filter table的INPUT chain进行处理(可以accepted, rejected 或者dropped等)。 如果报文被接收,那么就送上层应用程序处理(比如说一个WEB请求),上层应用产生一个响应报文(比如web浏览的响应)报文,这个报文会被送到mangle table的OUPUT chain 然后是nat table 的OUTPUT chain 再是filter table的OUTPUT chain进行处理(这里处理完成后,还有一个recheck route重新路由的过程,见文章开始的二个图)。最后送到mangle table的 POSTROUTING chain 和nat table 的POSTROUTING chain处理后,最后到达网卡进行发送。

这里描述的是报文如何在预置的table的预置chain中的处理流程。在实际网络中,用户可以定制用户自己的chain,然后通过预置chain将报文转到客户定制的用户chain中进行处理。比如:我们可以为SSH登录连接创建命名为SSH-chain的用户自定义chain然后在INPUT chain中告诉内核把接收到的目标端口为22的TCP报文送到SSH-chain进行处理。

2、iptables操作命令说明

iptables对chain的操作支持以下操作命令:

  • 列出指定chain里的规则rules (iptables –L CHAIN).
  • 修改指定chain的处理动作policy(iptables –P CHAIN ACCEPT).
  • 创建一个用户自定义的chain (iptables –N CHAIN).
  • 删除指定chain里的所有规则rules (iptables –F CHAIN).
  • 删除指定的chain (iptables –D CHAIN),只在chain已经为空的情况下可以操作
  • 清空指定chain的计数器 (iptables –Z CHAIN). chain里的每条规则都有一个计数器来统计命中规则的报文的数量,本命令用于重置计数器。

对于上述的–L, –F, –D, –Z 这几个操作,如果没有指定chain的名字,那么默认对table里的所有chain生效,如果没有指定table则默认为filter table。
如果你想要指定table则需要用-t来指定:iptables –t nat …

iptables对规则的操作支持以下操作命令:

  • 向指定chain里添加一条rule,放在最下方------(iptables –A)
  • 向指定chain里插入一条rule,放在最上方------(iptables –I)
  • 替换指定chain里已有的一条rule------------------(iptables –R)
  • 删除指定chain里的指定rule------------------------(iptables –D)

iptables –A 向指定chain里添加一条rule,添加的这条rule是放在rule list的最后一条;iptables –I 向指定chain里插入一条rule,做为规则列表里的第一条规则。当然,你也可以向rule list的指定位置插入一条规则,如iptables –I CHAIN 4 可以在rule list的第4个位置插入一条新的规则。

iptables –D 可以通过指定规则,也可以通过指定规则的位置这二种方式删除指定规则。
添加一条规则的命令格式如下:

iptables –A <CHAIN_NAME><filtering specifications>… -j <TARGET>

以Filtering specifications字段指定的规则去分析IP报文,命中的报文则用TARGET字段指定的动作去进行操作。

2.1 、Filtering Specifications

iptables的规则支持通过指定网卡,指定协议,指定端口等等过滤条件分析识别IP报文,这些过滤条件还可以被联合起来使用,这给过滤规则提供了最好的灵活性和广泛性。

  1. Filtering specifications for Layer2:

-i 和 -o 用于指定网络接口设备,-i是"–in-interface"的缩写,表示接收数据包的网卡,-o是"–out-interface"的缩写,表示发送数据包的网卡。
+是一个通配符,比如-i eth+ 表示所以以eth三个字母开关的网卡设备,使用通配符可以让我们一次性把规则配置给多个网卡.

"!"表示非或者说取反,比如-i ! eth1 表示eth1收到的报文不会用指定规则去分析与命中。

注意OUTPUT和POSTROUTING chains没有输入网络设备接口,所以这二个chains中的规则不能使用 -i 选项。同样INPUT和PREROUTING chains 没有输出网络设备接口,所以这二个chains中的规则不能使用 -o 选项。
  1. Filtering specifications for Layer3:

在IP层,支持以源IP地址(-s, --src, or --source), 以目标IP地址(-d,–dst, or --destination) 。注意这里的源IP地址和目标IP地址,可以是主机IP地址,IP子网或者域名(如 “-s 217.207.125.58”, “-s www.packtpub.com”, or “-s 217.207.125.58/32”).
使用域名来过滤时,如果对于多IP地址的域名而言,使用域名来过滤的效果等同时将添加规则时DNS服务器为这个域名解析出来的所有IP都加入到规则中。

  1. Filtering specifications for Layer4:
    在传输层,-p参数是"–protocol"的缩写,可以用协议名tcp, udp, or icmp来指定协议类型。
  • 如果是ICMP,还可以用"–icmp-type"来进一步指定ICMP报文的类型。

  • 如果是UDP,可以用"–source-port" or “–sport” and “–destination-port” and "–dport"来进一步指定源和目标端口号。

  • 如果是TCP,TCP, 除上上述的源和目标端口外,还可以用"–tcp-flags", “–syn” and “–tcp-option"选项,其中TCP flags可以使用"SYN ACK FIN RST URG PSH ALL NONE”; --syn"用于识别发起TCP连接的报文,等价于:“–tcp-flags SYN,RST,ACK SYN”。“–tcp-option” 用于过滤指定的tcp option字段的值的报文。

      注意L2, L3, L4既链路层,IP层和传输层的Filtering specifications可以在同一条规则里联合使用。
    

netfilter/iptables还有一个专门的项目来开发实现额外的分析规则rules,这个项目被称为"patch-o-matic", 可以在这个项目的官方网站 http://www.netfilter.org/projects/patch-o-matic/index.html 看到详细介绍。

iptables/netfilter还计划了将rules过滤规则拓展到支持OSI 7层模型里的所有层级的项目,叫l7-filter后面有机会我们可以起一个文章来专门讲讲这个l7-filter的功能。

2.2、Target Specifications

在filter table里,最常用的Target目标的动作为DROP和ACCEPT。如果有一个报文命中了上一章中描述的某条rule规则,然后这条规则的目标的动作是DROP,那么内核会简单的丢弃这个报文,然后就不会去匹配其它规则了。如果目标动作是ACCEPT,那么同样这个包就会被接收下来,而不再继续去匹配其它当前table里的当前chain里的其它规则。还有一个目标动作为REJECT,如果目标动作是REJECT的话,内核会丢弃该报文,并发送一人ICMP报文到被丢弃报文的源IP地址,告之发送方这是一个ICMP 'port unreachable’错误,如果你要自定义ICMP错误信息的话,需要用"–reject-with"选项来替代REJECT。
当然,除了上述DROP, REJECT, ACCEPT, REJECT-WITH之外,目标的动作target也可以是转发指定报文到用户自定义的chain去进行进一步处理。如:我们已经用

iptables -N SSH

创建了一个叫SSH的用户自定义的chain,那么我们就可以用以下命令,将所有目标端口为22的TCP报文转发到SSH chain进行进一步处理。

iptables -A INPUT -p tcp --dport 22 -j SSH

还有一个很有用的目标动作target叫LOG,当报文命中某个规则时,就会在内核log里(dmesg 或者syslogd)记录这个报文, LOG target 提供多个可配置的子项:

  • –log-level level: 定义log的级别为 debug, info, notice, warning, err, crit, alert, and emerg(对应数字为7-0)
  • –log-prefix prefix: 提供最大29字节的log前缀字符串,
  • –log-tcp-sequence: Logs TCP的序列号
  • –log-tcp-options: Logs TCP的option字段的值
  • –log-ip-options: Logs IP的option字段的值
  • –log-uid: Logs产生该报文的用户进程的进程号
    目标动作target为LOG的规则,有一点和上面的ACCEPT, DROP, REJECT不太一样,报文命中这个LOG为target的rule后,会继续去匹配同一个table的同一个chain里的其它规则。注意为了防止log泛洪功击,我们只应该对有限的报文进行LOG的目标动作。

以下是一个以用户自定义chain SSH的例子的使用用例:

  1. 之前我们已经用如下二个命令创建了SSH chain, 并将收到的目标端口为22的TCP报文转发给SSH chain处理
iptables -N SSH
iptables -A INPUT -p tcp --dport 22 -j SSH
  1. 现在我们只接收ACCEPT从以下二个网段过来的SSH请求:192.168.0.0/27 and 10.10.15.0/24,记录LOG其它地址发过来的SSH请求,并限制LOG数量为5次每秒,以防止泛洪LOG攻击。所以我们在SSH chain里增加如下二条规则:
iptables -A SSH -s 192.168.0.0/27 -j ACCEPT
iptables -A SSH -s 10.10.15.0/24 -j ACCEPT
  1. 添加记录其它SSH请求的LOG目标动作,然后丢弃不可信主机发来的SSH请求
iptables -A SSH -m limit --limit 5/s -j LOG
iptables -A SSH -j DROP
  1. 以下是验证配置是否成功:
sudo iptables -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
SSH        tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain SSH (1 references)
target     prot opt source               destination         
LOG        all  --  0.0.0.0/0            0.0.0.0/0            LOG flags 0 level 4
ACCEPT     all  --  192.168.3.0/24       0.0.0.0/0           
ACCEPT     all  --  10.0.0.0/24          0.0.0.0/0           
DROP       all  --  0.0.0.0/0            0.0.0.0/0 
  1. 然后可以用-v选项来查看命中次数:
sudo iptables -L -n -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
  760 53836 SSH        tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain SSH (1 references)
 pkts bytes target     prot opt in     out     source               destination         
  376 22344 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0            LOG flags 0 level 4
  659 45536 ACCEPT     all  --  *      *       192.168.3.0/24       0.0.0.0/0           
    0     0 ACCEPT     all  --  *      *       10.0.0.0/24          0.0.0.0/0           
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0   
  1. 然后使用dmesg命令可以看到如下log记录,说明我们从192.168.3.88收到一个SSH连接。
[26915.359507] IN=eth0 OUT= MAC=00:e0:99:62:ec:b6:70:b5:e8:4b:95:a9:08:00 SRC=192.168.3.88 DST=192.168.3.66 LEN=52 TOS=0x10 PREC=0x00 TTL=64 ID=10784 DF PROTO=TCP SPT=41118 DPT=22 WINDOW=501 RES=0x00 ACK URGP=0 

后续我们还会描述nat table和mangle table的targets目标动作的使用,注意本文只描述了最常用 的target, 如果你需要更多的target详细可以自行查询patch-o-matic项目。

2.3、一个基于Linux的基本的防火墙的配置例子

以下我们为Linux系统创建一个最基本的防火墙规则。默认的情况下Linux的默认target目标动作是在所有table的所有chain上都是ACCEPT。

#!/bin/bash
#assign variable $IPT with the iptables command
IPT=/sbin/iptables
#set policies on each chain
$IPT -P INPUT DROP
$IPT -P FORWARD DROP
$IPT -P OUTPUT ACCEPT #default, but set it anyway
#flush all rules in the filter table
$IPT -F
#allow traffic on the loopback interface
$IPT -A INPUT -i lo -j ACCEPT
#allow icmp traffic
$IPT -A INPUT -p icmp -j ACCEPT
#allow incoming DNS traffic
$IPT -A INPUT -p udp --sport 53 -j ACCEPT
#allow established TCP connections
$IPT -A INPUT -p tcp ! --syn -j ACCEPT
  1. 把INPUT和FORWARD chains的总体策略设置成DROP
  2. 把OUTPUT chain设置成ACCEPT
  3. 本主机不打开转发功能,所以FORWARD chain里不添加任何规则。
  4. 不往OUTPUT chain里添加任何规则,因为我们默认我们可以发送任何报文出去
  5. 把filter table里的所有规则都删除
  6. 有些程序在主机内通信也借用了TCP/IP那么它是走lo口的,我们认为lo口上的报文全部是安全的,所以在INPUT chain里把lo口的目标动作target设置成ACCEPT
  7. 允许接收ICMP报文,使本机在网络上可以被其它主机ping通,也说traceroute, mtr, MTU路径发现等基于ICMP协议的功能可以工作
  8. DNS服务工作在53端口,所以我们接收来自源53端口的连接(如果你有DNS服务器的IP地址,那么我们可以用以下命令替换,只允许DNS server的IP地址发起的源端口53的报文,以达到更加安全的目的,假设DNS servers是1.1.1.1 and 1.1.2.1。
$IPT -A INPUT –s 1.1.1.1 -p udp --sport 53 -j ACCEPT 
$IPT -A INPUT –s 1.1.2.1 -p udp --sport 53 -j ACCEPT 
  1. 最后,我们只允许从自己主机主动发起的TCP连接里的TCP报文被接收。任何从外部来的主动发起的TCP连接请求都会被拒绝(deny
    TCP SYN 报文)。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐