Netfilter 自 1998 年开发,2000 年合并到 Kernel v2.4 版本,是 Kernel 提供的一个流量处理框架,用于实现对 IP 数据包的控制和过滤(Manipulation and Filtering)等功能,包括:
Netfilter 在 Kernel 的 L3-subsystem 中设置多个 Hooks(钩子)点。通过在 IP 流量的必经之路上设置多个 Hooks 点,来实现对所有的 IP 数据包进行检测、过滤、拦截或其他处理,包括:
如下图所示,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(链):
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 为实现数据包控制和过滤功能而设计了 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)是用户预定义的,例如:我们常说的防火墙规则。配置防火墙的主要工作就是添加、修改和删除这些规则。
每条 Rule 都由下列 2 个元素组成:
若干个匹配条件(Xmatch):与 IP 数据包进行匹配,具有以下匹配条件类型:
一个执行动作(Action):数据包匹配所有条件后所需要执行的动作。具有以下动作类型:
链(Chains)的本质是一个有序的 Rules 列表。在复杂的网络环境中,用户可以通过配置 Rules 在 Chain 中的顺序来灵活实现多种效果。
也因为 Chain 中 Rules 的次序非常关键,执行 Rules 时,会按照从上往下的顺序进行。所以越严格的 Rule,就越应该放在越靠前,而 Default Rule 则总是在最后生效。
此外,Netfilter 提供了 5 条内建的 Chains,用户也可以新建自定义的 Chains。
表(Table)是面向应用场景的管理方式,每张表被赋予了不同的应用场景,所以也内含了不同的 Chains 和 Rules。
用户在实际使用 Netfilter 时,往往是通过 Table 作为操作入口,然后对 Chains 和 Rules 进行定义。
Netfilter 内建了以下五张表:
每张表所挂载的链如下图所示:
在不同的 Hooks 点上会执行不同的表:
iptables 是运行在 Userspace 的 Netfilter 配置工具,iptables 用于 IPv4,ip6tables 用于 IPv6。更新的 nftables 已经包含在 Linux kernel v3.13 版本中,以后会取代 iptables 成为主要的 Linux 防火墙配置工具。
大多 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
配置文件为:
制定 iptables 表规则思路:
iptables 语法格式:
$ iptables [ -t 表名 ] 管理选项 [ 链名 ] [ 条件匹配 ] [ -j 目标动作或转发 ]
不指定表名时,默认为 filter 表,不指定链名时,默认表示该表的所有链。除非设置了链的缺省策略,否则需要指定条件匹配。
默认查看的是 filter 表的规则,可以指定表名或链名,也可以显示规则编号。
$ iptables -nvL [--line-numbers] [-t 表名] [链名]
添加规则有两种方式,一种是在链最后追加(-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
# 查看 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);
条件匹配分为基本匹配和扩展匹配,扩展匹配又分为显示匹配和隐式匹配。
基本匹配:无需加载扩展模块,匹配规则生效。
-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 选项指明要调用的扩展模块的扩展机制以及需要手动加载扩展模块。
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
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
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
--algo {bm|kmp}:字符匹配查找时使用算法
--string "STRING":要查找的字符串
--hex-string "HEX-STRING":要查找的字符,先编码成16进制格式
--connlimit-upto n:连接数小于或等于 n 时匹配
--connlimit-above n:连接数大于 n 时匹配
limit:报文速率控制。
state:追踪本机上的请求和响应之间的数据报文的状态。
NOTE:
IP 网络有公网与私网的区分,企业内网使用私网 IP,Internet 使用公网 IP。当使用私网 IP 地址访问公网 IP 时就需要使用到 NAT(Network Address Translation,网络地址转换)技术,包括:
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(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(端口映射)通常和 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 [email protected] -p 2222
# 入向规则
$ 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
$ iptables -A FORWARD -o eth0
$ iptables -I INPUT -j DROP -p tcp -s 0.0.0.0/0 -m string --algo kmp --string "cmd.exe"
$ iptables -A INPUT -p tcp --syn -m limit --limit 5/second -j ACCEPT