目录
文章目录
- 目录
- Netfilter 流量处理框架
- Netfilter 的实现原理
- Netfilter 的工作原理
- 规则(Rules)
- 链(Chains)
- 表(Tables)
- 数据包处理流程图
- iptables CLI
- iptables-service
- 指令应用
- 查看规则
- 添加规则
- 删除规则
- 修改规则
- 保存和加载规则
- 常规初始化配置
- 指令选项解析
- 管理选项
- 条件匹配(Xmatch)
- NAT/NAPT
- SNAT
- DNAT
- PAT
- 应用示例
Netfilter 流量处理框架
Netfilter 自 1998 年开发,2000 年合并到 Kernel v2.4 版本,是 Kernel 提供的一个流量处理框架,用于实现对 IP 数据包的控制和过滤(Manipulation and Filtering)等功能,包括:
- 无状态数据包过滤(IPv4、IPv6)。
- 有状态的数据包过滤(IPv4、IPv6)。
- 基于协议类型的连接跟踪(CT)。
- NAT、NAPT(IPv4、IPv6)。
- 灵活可扩展的基础架构。
- 第三方扩展的 API,例如:被 iptables 调用。
Netfilter 的实现原理
Netfilter 在 Kernel 的 L3-subsystem 中设置多个 Hooks(钩子)点。通过在 IP 流量的必经之路上设置多个 Hooks 点,来实现对所有的 IP 数据包进行检测、过滤、拦截或其他处理,包括:
- 通过:不对数据包进行任何修改,退出检测逻辑,继续后面正常的数据包处理流程。
- 修改:例如进行 NAT 编辑,然后继续后面正常的数据包处理流程。
- 丢弃:例如 iptables 安全策略和防火墙功能。
如下图所示,Netfilter 具有 5 个 Hooks 点,每个 Hook 点都注册了一些 Handler 处理函数,当有数据包经过 Hook 点时, 就会调用相应的 Handler。
#define NF_IP_PRE_ROUTING 0 /* After promisc drops, checksum checks. */
#define NF_IP_LOCAL_IN 1 /* If the packet is destined for this box. */
#define NF_IP_FORWARD 2 /* If the packet is destined for another interface. */
#define NF_IP_LOCAL_OUT 3 /* Packets coming from a local process. */
#define NF_IP_POST_ROUTING 4 /* Packets about to hit the wire. */
#define NF_IP_NUMHOOKS 5
5 个 Hooks 点分别对应下述 5 条 Chaim(链):
- PREROUTING:数据包进入路由表之前。
- INPUT:通过路由表后目的地为本机。
- FORWARD:通过路由表后,目的地不为本机。
- OUTPUT:由本机产生,向外发送。
- POSTROUTIONG:发送到网卡接口之前。
Hook Handler 对 IP 数据包进行判断或处理之后,需要返回一个判断结果,指导接下来要对这个包做什么。可能的结果有:
// include/uapi/linux/netfilter.h
#define NF_DROP 0 // 已丢弃这个包。
#define NF_ACCEPT 1 // 接受这个包,继续下一步处理。
#define NF_STOLEN 2 // 当前处理函数已经消费了这个包,后面的处理函数不用处理了。
#define NF_QUEUE 3 // 应当将包放到队列。
#define NF_REPEAT 4 // 当前处理函数应当被再次调用。
Netfilter 的工作原理
Netfilter 为实现数据包控制和过滤功能而设计了 3 个核心概念:
-
规则(Rules):用于定义 IP 数据包的识别和处理规则,每条 Rule 都包含了 “匹配" 和 “动作" 这 2 个元素。其中,动作包括有:修改或跳转。跳转可以用于处理接受该数据包、拒绝该数据包,也可以跳转到其他 Chain 中继续进行匹配,或者从当前 Chain 中返回到调用者 Chain。
-
链(Chains):每条 Chain 由多条 Rules 组成,这些 Rules 会用于与 IP 数据包进行逐一匹配。一旦匹配上,则执行 Rule 对应的动作。如果当 Chain 中的所有 Rules 都执行完后,但仍然没有跳转时,将根据该 Chain 的默认策略执行对应动作。如果 Chain 也没有默认动作,那么就会返回到调用者 Chain。
-
表(Tables):每张 Table 由多条 Chains 组成,是一种 Chains 和 Rules 的业务场景分类管理手段。不同的 Tables 通常会具有特定的应用场景,例如:filter table 用于进行数据包过滤,nat table 用于进行 NAT 操作等。
规则(Rules)
规则(Rules)是用户预定义的,例如:我们常说的防火墙规则。配置防火墙的主要工作就是添加、修改和删除这些规则。
每条 Rule 都由下列 2 个元素组成:
-
若干个匹配条件(Xmatch):与 IP 数据包进行匹配,具有以下匹配条件类型:
- Interface(接口,e.g. eth0、eth1)
- Protocol(协议类型,e.g. ICMP、TCP、UDP)
- Source IP / Destination IP
- Source Port / Destination Port
-
一个执行动作(Action):数据包匹配所有条件后所需要执行的动作。具有以下动作类型:
- ACCEPT:运行通过。
- DEOP:直接丢弃。
- REJECT:拒绝通过。
- SNAT:源地址转换。
- DNAT:目标地址转换。
- MASQUERADE:特殊的 SNAT,适用于动态变更的 IP。
- LOG:记录日志信息。
- QUEUE:将数据包移交到用户空间。
- RETURN:防火墙停止执行当前链中的后续规则,并返回到调用链。
- REDIRECT:端口重定向。
- MARK:做防火墙标记。
链(Chains)
链(Chains)的本质是一个有序的 Rules 列表。在复杂的网络环境中,用户可以通过配置 Rules 在 Chain 中的顺序来灵活实现多种效果。
也因为 Chain 中 Rules 的次序非常关键,执行 Rules 时,会按照从上往下的顺序进行。所以越严格的 Rule,就越应该放在越靠前,而 Default Rule 则总是在最后生效。
此外,Netfilter 提供了 5 条内建的 Chains,用户也可以新建自定义的 Chains。
- INPUT(输入链):发往本机的数据包通过此链,并执行此链上关于 INPUT 的 Rules,例如:DDoS 攻击防御规则。
- OUTPUT(输出链):从本机发出的数据包通过此链,并执行此链上关于 OUTPUT 的 Rules。
- PORWARD(转发链):由本机转发的数据包通过此链,并执行此链上关于 PORWARD 的 Rules。例如:作为 IP 路由器。
- PREROUTING(路由前链):在处理 IP 路由规则前(Pre-Routing)通过此链,并执行此链上关于 Pre-Routing 的 Rules。例如:DNAT。
- POSTOUTING(路由后链):在处理 IP 路由规则后(Post-Routing)通过此链,并执行此链上关于 Post-Routing 的 Rules。例如:SNAT。
表(Tables)
表(Table)是面向应用场景的管理方式,每张表被赋予了不同的应用场景,所以也内含了不同的 Chains 和 Rules。
用户在实际使用 Netfilter 时,往往是通过 Table 作为操作入口,然后对 Chains 和 Rules 进行定义。
Netfilter 内建了以下五张表:
- filter 表(默认):提供数据包的过滤功能,例如:用于防火墙规则。
- nat 表:提供了 NAT、NAPT 功能,例如:用于网关路由器。
- mangle 表:提供了数据包修改功能,例如:更改 IP Header 的 TOS、DSCP、ECN 位。
- raw 表:用来提前标记报文不需要执行一些流程,例如:不需要建立会话。
- conntrack 表:连接跟踪表,跟踪连接会话,用来实现状态防火墙,是 NAT 功能的基础,也可以扩展更多功能。
每张表所挂载的链如下图所示:
在不同的 Hooks 点上会执行不同的表:
数据包处理流程图
iptables CLI
iptables 是运行在 Userspace 的 Netfilter 配置工具,iptables 用于 IPv4,ip6tables 用于 IPv6。更新的 nftables 已经包含在 Linux kernel v3.13 版本中,以后会取代 iptables 成为主要的 Linux 防火墙配置工具。
iptables-service
大多 Linux 发型版将 iptables 被做成了一个服务,启动,则将防火规则生效。反之,则将防火规则撤销。
# 停止 firewalld 服务
$ systemctl stop firewalld
# 禁用 firewalld 服务
$ systemctl mask firewalld
$ yum install -y iptables
$ yum install iptables-services
$ systemctl enable iptables.service
$ systemctl start iptables.service
配置文件为:
- /etc/sysconfig/iptables
- /etc/iptables/iptables.rules
指令应用
制定 iptables 表规则思路:
- 选择一张表(此表决定了数据包的处理方式,e.g. filter、nat);
- 选择一条链(此链决定了数据包的流经位置,e.g. INPUT、OUTPUT);
- 选择合适的规则匹配条件(此条件决定了对数据包做何种条件匹配,e.g. Source IP、ICMP);
- 选择处理数据包的动作(e.g. ACCEPT、DEOP)。
iptables 语法格式:
$ iptables [ -t 表名 ] 管理选项 [ 链名 ] [ 条件匹配 ] [ -j 目标动作或转发 ]
不指定表名时,默认为 filter 表,不指定链名时,默认表示该表的所有链。除非设置了链的缺省策略,否则需要指定条件匹配。
查看规则
默认查看的是 filter 表的规则,可以指定表名或链名,也可以显示规则编号。
$ iptables -nvL [--line-numbers] [-t 表名] [链名]
- –line-numbers:列出规则的编号。
- 五表:raw,nat,filter,mangle,conntrack
- 五链:INPUT、OUTPUT、FORWARD、PREROUTING、POSTROUTING
添加规则
添加规则有两种方式,一种是在链最后追加(-A)规则,另一种是将规则插入(-I)到链上的某个特定位置。
# 添加规则到指定的链中
$ iptables -A INPUT -s 192.168.1.5 -j DROP
# 插入规则到指定的链中,默认为插入到链首
$ iptables -I INPUT -p tcp --dport 17500 -s 10.0.0.85 -j ACCEPT -m comment --comment "Friendly Dropbox"
删除规则
# 通过编号删除链中的规则
$ iptables -D INPUT 8
修改规则
# 用新规则代替已存在的旧规则
$ iptables -R INPUT 2 -s 127.0.0.1 -d 127.0.0.1 -i lo -j ACCEPT
保存和加载规则
通过命令行添加规则,配置文件不会自动改变,所以必须手动保存:
# 备份与保存规则至指定文件
$ cp /etc/sysconfig/iptables /etc/sysconfig/iptables.bak
$ iptables-save > /etc/sysconfig/iptables
修改配置文件后,需要重新加载服务生效:
$ systemctl reload iptables
或者通过指定配置文件由 iptables 直接加载:
# 从指定文件加载规则
$ iptabls-restore < /PATH/FROM/SOME_RULE_FILE
- -n, --noflush:不清除原有规则。
- -t, --test:仅分析生成规则集,但不提交。
常规初始化配置
# 查看 iptables 现有规则
$ iptables -L -n
# 备份现有的规则
$ cp /etc/sysconfig/iptables /etc/sysconfig/iptables.bak
# 先允许所有
$ iptables -P INPUT ACCEPT
# 清空所有默认规则
$ iptables -F
# 清空所有自定义规则
$ iptables -X
# 所有计数器归 0
$ iptables -Z
# 开放本地回环
$ iptables -A INPUT -i lo -j ACCEPT
# 开放已建立的或相关的连接的
$ iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 开放 22 端口(SSH)
$ iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 开放 21 端口(FTP)
$ iptables -A INPUT -p tcp --dport 21 -j ACCEPT
# 开放 80 端口(HTTP)
$ iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 开放 443 端口(HTTPS)
$ iptables -A INPUT -p tcp --dport 443 -j ACCEPT
#开放 ping
$ iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT
# 允许接受本机请求之后的返回数据 RELATED,是为 FTP 设置的
$ iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# 其他入站一律丢弃
$ iptables -P INPUT DROP
# 所有出站一律绿灯
$ iptables -P OUTPUT ACCEPT
# 所有转发一律丢弃
$ iptables -P FORWARD DROP
指令选项解析
管理选项
- 规则显示
-L, --list [chain]:列出规则;
-v, --verbose:详细信息;
-vv 更详细的信息
-n, --numeric:数字格式显示主机地址和端口号;
-x, --exact:显示计数器的精确值,而非圆整后的数据;
--line-numbers:列出规则时,显示其在链上的相应的编号;
-S, --list-rules [chain]:显示指定链的所有规则;
- 规则管理
-A, --append chain rule-specification:追加新规则于指定链的尾部;
-I, --insert chain [rulenum] rule-specification:插入新规则于指定链的指定位置,默认为首部;
-R, --replace chain rulenum rule-specification:使用新的规则替换指定的旧规则;
-D, --delete chain rulenum:根据规则编号删除规则;
-D, --delete chain rule-specification:根据规则本身删除规则;
- 链管理
-N, --new-chain chain:新建一个自定义链;
-X, --delete-chain [chain]:删除自定义的引用计数为 0 的空链;
-F, --flush [chain]:清空指定链上的规则;
-E, --rename-chain old-chain new-chain:重命名链;
-Z, --zero [chain [rulenum]]:置零计数器;
NOTE:每条规则都有两个计数器
1. packets:被本规则匹配到的数据包个数;
2. bytes:被本规则匹配到的数据包大小之和;
-P, --policy chain target:制定链表的策略(ACCEPT | DROP | REJECT);
条件匹配(Xmatch)
条件匹配分为基本匹配和扩展匹配,扩展匹配又分为显示匹配和隐式匹配。
基本匹配:无需加载扩展模块,匹配规则生效。
-p:指定规则协议,e.g. tcp/udp/icmp/all
-s:指定数据包的源地址,IP or Hostname
-d:指定数据包的目的地址
-i:输入接口,网卡设备
-o:输出接口
!:取反
扩展匹配:需要加载扩展模块,匹配规则方可生效。
隐式匹配:使用 -p 选项指明协议时,无需同时使用 -m 选项指明扩展模块以及不需要手动加载扩展模块。
-p tcp
--sport:匹配报文段的源端口;可以给出多个端口,但只能是连续的端口范围
--dport:匹配报文段的目标端口;可以给出多个端口,但只能是连续的端口范围
--tcp-flags mask comp:匹配报文段的 tcp 标志位
-p udp
--sport:匹配数据报端口;可以给出多个端口,但只能是连续的端口范围
--dport:匹配数据报目标端口;可以给出多个端口,但只能是连续的端口范围
--icmp-type
8:echo request,Ping 请求
0:echo reply,接收 Ping 请求之后响应的 Ping 应答
显示匹配:必须使用 -m 选项指明要调用的扩展模块的扩展机制以及需要手动加载扩展模块。
- multiport(多端口):以离散或连续的方式定义多端口匹配条件,最多 15 个。
iptables -I INPUT -d 172.16.100.7 -p tcp -m multiport --dports 22,80 -j ACCEPT
iptables -I OUTPUT -s 172.16.100.7 -p tcp -m multiport --sports 22,80 -j ACCEPT
- iprange(IP 范围):以连续地址块的方式来指明多个 IP 地址匹配条件。
iptables -A INPUT -d 172.16.100.7 -p tcp --dport 23 -m iprange --src-range 172.16.100.1-172.16.100.100 -j ACCEPT
iptables -A OUTPUT -s 172.16.100.7 -p tcp --sport 23 -m iprange --dst-range 172.16.100.1-172.16.100.100 -j ACCEPT
- time:指定时间范围。
iptables -A INPUT -d 172.16.100.7 -p tcp --dport 901 -m time --weekdays Mon,Tus,Wed,Thu,Fri --timestart 08:00:00 --time-stop 18:00:00 -j ACCEPT
iptables -A OUTPUT -s 172.16.100.7 -p tcp --sport 901 -j ACCEPT
- string:对应用层的报文做字符串模式匹配检测。
--algo {bm|kmp}:字符匹配查找时使用算法
--string "STRING":要查找的字符串
--hex-string "HEX-STRING":要查找的字符,先编码成16进制格式
- connlimit:根据每个客户端 IP 作并发连接数量限制。
--connlimit-upto n:连接数小于或等于 n 时匹配
--connlimit-above n:连接数大于 n 时匹配
-
limit:报文速率控制。
-
state:追踪本机上的请求和响应之间的数据报文的状态。
- INVALID:无法识别的连接
- ESTABLISHED:已建立的连接
- NEW:新建立的连接
- RELATED:相关联的连接,当前连接是一个新连接,但依附于某个已存在的连接
- UNTRACKED:未追踪的连接
NOTE:
- 对于进入的状态为 ESTABLISHED 都应该放行。
- 对于出去的状态为 ESTABLISHED 都应该放行。
- 严格检查进入的状态为 NEW 的连接。
- 所有状态为 INVALIED 都应该拒绝。
NAT/NAPT
IP 网络有公网与私网的区分,企业内网使用私网 IP,Internet 使用公网 IP。当使用私网 IP 地址访问公网 IP 时就需要使用到 NAT(Network Address Translation,网络地址转换)技术,包括:
- SNAT(源地址转换):私网访问公网时使用。
- DNAT(目标地址转换):公网访问私网时使用。
- PAT(端口转换):通常配合 DNAT 使用。
SNAT
SNAT(Source Network Transform,源地址转换),实现私网内主机使用同一个公网 IP 进行上网。即:内网 IP 地址向外访问 Internet 时,发起访问的内网 IP 地址转换为指定的对外 IP 地址(可指定具体的服务以及相应的端口或端口范围),这使内网的多部主机可以通过同一个有效的公网 IP 地址访问外部网络。
数据包出站路径为:APP => TCP/IP stack 路由子系统 => filter:OUTPUT => nat:POSTROUTING => 出站。
# SNAT:由内到外的源地址转换
$ iptables -t nat -I POSTROUTING -s 192.168.0.0/24 -o eth1 -j SNAT --to 198.51.100.3
在外网地址非固定时的共享动态 IP 地址上网场景中,需要使用 IP 地址伪装模式。MASQUERADE 是一种动态 SNAT 技术,通常用于连接到动态 IP 地址的网络,例如:家庭网络或移动网络。
MASQUERADE 会将数据包的 srcIP 地址替换为外部网络接口的 IP 地址,而不需要提前指定固定的某一个公共 IP 地址。这样,无论外部网络的 IP 地址如何变化,MASQUERADE 都能够确保数据包可以正确地返回到内部网络。
$ iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
DNAT
DNAT(Destination Network Transform,目的地址转换)与 SNAT 相对,当外部网络访问内部网络时,进来的 IP 数据包会被改变 dstIP 地址。
# DNAT:由外到内的目的地址转换
$ iptables -t nat -I PREROUTING -p tcp -d 198.51.100.3 --dport 80 -j DNAT --to 192.168.0.2
NAT 转发操作需要在 filter 表中 FORWARD 链中允许,并且打开系统的 ip_forwarding 转发功能。
PAT
PAT(端口映射)通常和 DNAT 一起使用,例如:将本机公网 IP 的 2222 端口映射到虚拟机内网 IP 的 22 端口。
$ iptables -t nat -A PREROUTING -d 210.14.67.127 -p tcp --dport 2222 -j DNAT --to-dest 192.168.188.115:22
# 测试。
$ ssh root@210.14.67.127 -p 2222
应用示例
- 放行所有网段的主机访问本机的 httpd 服务:
# 入向规则
$ iptables -t filter -A INPUT -p tcp –dport 80 -m state –state NEW,ESTABLISHED -j ACCEPT
# 出向规则
$ iptables -t filter -A OUTPUT -p tcp –sport 80 -m state –state ESTABLISHED -j ACCEPT
- 端口重定向:
$ iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
如果你在你的计算机上面运行了这个指令,它只会对连到你的机器上的外部 IP 产生效果。从本地端发起的连线不会遵循 nat 表上 PREROUTING 链的设置。如果你想让本地端也遵循规则,你需要将 lo 接口上的数据包输出由 80 端口转向到 8080 端口上面:
$ iptables -t nat -A OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080
- 指定数据包出去的网络接口:该操作只对 OUTPUT,FORWARD,POSTROUTING 三个链起作用。
$ iptables -A FORWARD -o eth0
- 阻止 Windows 蠕虫的攻击:
$ iptables -I INPUT -j DROP -p tcp -s 0.0.0.0/0 -m string --algo kmp --string "cmd.exe"
- 防止 SYN 洪水攻击:
$ iptables -A INPUT -p tcp --syn -m limit --limit 5/second -j ACCEPT