1. 项目概述:为什么是nftables?
如果你在Linux服务器上待过一段时间,肯定和iptables打过交道。它就像一位功勋卓著的老兵,守护了Linux网络二十多年。但老兵也有老兵的局限:规则多了性能下降、语法复杂、IPv4和IPv6还得分开管理。所以,当nftables在Linux内核3.13版本中悄然登场,并在后续版本中逐渐成熟,最终成为主流发行版的默认防火墙后端时,我知道,是时候该换换脑子了。
nftables不是对iptables的简单修补,而是一次彻底的重构。它用一个统一的框架,取代了原先iptables、ip6tables、arptables和ebtables这“四大金刚”。这意味着,无论你处理的是IPv4、IPv6还是ARP包,都只需要学习和使用一套语法和工具——nft命令。内核层面的优化,比如规则集的编译和基于Netfilter的钩子(hook)高效执行,让它在处理大量规则时,性能表现往往更优。更重要的是,它的配置语法更清晰、更富有表达力,像写一个简单的脚本一样去定义防火墙策略,这在管理复杂网络环境时,优势是决定性的。
这篇内容,就是把我从iptables“移民”到nftables的实战经验,以及这几年在生产环境里摸爬滚打攒下的配置心得,系统地整理出来。我的目标很明确:让你看完之后,不仅能理解nftables的核心思想,更能直接上手,用我提供的示例去搭建、调试和管理你自己的防火墙。无论你是刚接触Linux网络安全的运维新人,还是想从iptables平滑过渡的老手,这里都有你需要的“干货”。
2. nftables核心概念与架构解析
要玩转nftables,死记硬背命令是没用的,必须从根上理解它的设计哲学。它把防火墙规则看作是对网络数据包的一次“审判”流程,而这个流程由几个核心部件精密协作完成。
2.1 规则集、表、链与规则:四层结构
你可以把nftables的配置想象成一本书。
- 规则集:就是整本书。它是所有nftables配置的顶层容器,系统启动时从
/etc/nftables.conf(通常)加载的就是它。 - 表:书里的各个章节。表是链的容器,并且与特定的“地址族”绑定。主要的地址族有:
ip:处理IPv4数据包(对应旧的iptables)。ip6:处理IPv6数据包(对应旧的ip6tables)。inet:同时处理IPv4和IPv6数据包。这是最常用、最高效的选择,一套规则管两种协议。arp:处理ARP协议数据包。bridge:处理桥接网络的数据包。netdev:在数据包进入网络协议栈之前进行处理,用于早期过滤。
- 链:章节里的具体段落。链是规则的容器,它被绑定在Netfilter内核框架的特定“钩子点”上。数据包流经网络协议栈时,会触发这些钩子,从而进入对应的链进行处理。主要的钩子点有:
prerouting:数据包进入网卡后,进行路由决策之前。input:数据包目的地是本机。forward:数据包需要被转发到其他机器。output:由本机进程产生的数据包,准备发送出去。postrouting:数据包离开本机之前,路由决策之后。
- 规则:段落里的句子。规则是具体的行动指令,由表达式和语句构成。表达式用于匹配数据包(比如源IP、目标端口),语句则定义匹配后的动作(比如接受、丢弃、跳转)。
一个简单的结构示例在脑海中应该是这样的:一本叫规则集的书,里面有一个叫inet的章节(表),这个章节里有一段叫filter_input的段落(链),段落里写着“如果数据包来自192.168.1.0/24网段且目标端口是22,那么就接受它”这样一条句子(规则)。
2.2 地址族与钩子:理解数据包的生命周期
选择正确的地址族和钩子,是编写有效规则的前提。我强烈建议,除非有特殊需求,否则新建表时优先使用inet地址族。它创建的链能同时看到IPv4和IPv6的数据包,极大地简化了配置。
理解钩子,就是理解数据包在你机器里的“旅行路线”:
- 一个外部数据包到达网卡,首先进入
prerouting钩子。这里通常做网络地址转换或连接跟踪。 - 内核查看数据包目标IP,如果是本机,则送入
input钩子。你的防火墙规则在这里决定是否允许这个包交给本地进程。 - 如果目标IP不是本机,且内核启用了转发功能,则送入
forward钩子。你的防火墙规则在这里决定是否允许转发这个包。 - 本地进程产生的数据包,首先进入
output钩子。 - 无论是转发还是本地发出的包,在离开网卡前,都会经过
postrouting钩子,通常在这里做源地址转换。
你的防火墙链,就需要挂载在这些关键的“检查点”上。
2.3 匹配器、语句与动作:构建规则的基石
一条规则的威力,全在于匹配器和语句的组合。
- 匹配器:使用
nft命令的表达式语法,非常灵活。ip saddr 192.168.1.0/24:匹配IPv4源地址。ip6 daddr fe80::/10:匹配IPv6目标地址。tcp dport { 22, 80, 443 }:匹配TCP目标端口(支持端口集合)。ct state new,established:匹配连接跟踪状态为“新建”或“已建立”。meta iifname “eth0”:匹配输入网络接口名。
- 语句与动作:
accept:接受数据包。drop:丢弃数据包,不发送任何响应。reject:拒绝数据包,并发送拒绝响应(如TCP RST)。log:将数据包信息记录到内核日志。counter:对匹配的数据包进行计数。jump/goto:跳转到另一个用户自定义链。
注意:
drop和reject的区别很重要。drop是“静默丢弃”,对方会一直等待超时,像石沉大海。reject是“明确拒绝”,会立刻告诉对方“不行”,比如对TCP连接发送RST复位包。在对外服务时,根据安全策略谨慎选择。对内网管理流量误用drop,可能会导致排障时非常困惑。
3. 从零开始:nftables安装与基础配置
理论说再多,不如动手敲一行命令。我们从一个干净的环境开始,搭建一个可用的nftables防火墙。
3.1 安装与启停
在主流发行版上,安装nftables通常很简单:
# 对于基于RHEL/CentOS/Fedora的系统 sudo dnf install nftables -y # 或 sudo yum install nftables -y # 对于基于Debian/Ubuntu的系统 sudo apt update && sudo apt install nftables -y安装后,需要设置开机自启并启动服务:
sudo systemctl enable nftables sudo systemctl start nftables关键点来了:nftables服务启动时,会自动从/etc/nftables.conf(或其他发行版指定的默认路径,如/etc/sysconfig/nftables.conf可能指向它)加载规则。所以,我们后续的所有配置,最终都要持久化到这个文件里。
检查服务状态和当前规则集:
sudo systemctl status nftables sudo nft list ruleset # 查看当前内存中的所有规则3.2 你的第一个nftables规则集
让我们清空默认规则,从头创建一个简单的、允许SSH和Ping,拒绝其他所有入站流量的防火墙。
首先,备份现有配置,然后清空规则集:
sudo cp /etc/nftables.conf /etc/nftables.conf.bak sudo nft flush ruleset # 清空内存中所有规则现在,我们通过nft命令交互式地创建规则,最后再保存到配置文件。
创建一个
inet族(同时处理IPv4/IPv6)的表,命名为filter:sudo nft add table inet filter在
filter表中创建三条链:input链:处理发送到本机的数据包。策略设为drop(默认拒绝)。forward链:处理转发的数据包。策略设为drop。output链:处理本机发出的数据包。策略设为accept(默认允许,通常更安全且方便)。
sudo nft add chain inet filter input { type filter hook input priority 0\; policy drop\; } sudo nft add chain inet filter forward { type filter hook forward priority 0\; policy drop\; } sudo nft add chain inet filter output { type filter hook output priority 0\; policy accept\; }这里
priority 0是链的优先级,数字越小优先级越高。对于filter表,0是标准值。为
input链添加规则:- 允许本地回环接口流量:这是系统内部通信必需的。
sudo nft add rule inet filter input iif “lo” accept - 允许已建立和相关联的连接:这是状态化防火墙的核心,允许对外请求的回应包进来。
sudo nft add rule inet filter input ct state established,related accept - 允许ICMP协议(Ping):网络诊断需要。
sudo nft add rule inet filter input ip protocol icmp accept sudo nft add rule inet filter input ip6 nexthdr icmpv6 accept - 允许TCP端口22(SSH):为了我们能远程管理服务器。
sudo nft add rule inet filter input tcp dport 22 accept
实操心得:规则的顺序就是防火墙的检查顺序,从上到下执行,一旦匹配就执行动作并停止。所以,一定要把最常用、匹配范围最广的规则(如
established,related)放在前面,把最具体的规则(如开放某个端口)放在后面,最后才是默认的拒绝策略。这能显著提升防火墙处理性能。- 允许本地回环接口流量:这是系统内部通信必需的。
保存配置: 现在内存中的规则已经生效了。为了永久保存,我们需要将当前规则集导出到配置文件。
sudo nft list ruleset > /etc/nftables.conf现在,你的
/etc/nftables.conf文件里应该已经有了完整的规则定义。下次系统重启或nftables服务重启时,会自动加载这些规则。验证配置:
- 尝试从另一台机器Ping你的服务器,应该能通。
- 尝试SSH到你的服务器,应该能连接。
- 尝试用
nmap扫描一个未开放的端口(如80),应该显示被过滤或关闭。 - 随时使用
sudo nft list ruleset来查看完整规则。
4. 进阶实战:常见场景配置示例
基础框架搭好了,我们来看看在实际工作中,如何用nftables解决具体问题。以下示例均基于上面创建的inet filter表。
4.1 示例一:Web服务器防火墙配置
假设你有一台运行Nginx/Apache的Web服务器,需要开放HTTP/HTTPS,同时允许SSH管理,并做一些基本的安全加固。
# 清空input链原有规则(谨慎操作,最好在已有允许SSH的规则基础上添加) # 假设我们基于之前的配置继续 # 1. 允许HTTP (80) 和 HTTPS (443) sudo nft add rule inet filter input tcp dport { 80, 443 } accept # 2. 限制SSH访问源IP(强烈建议!) # 假设你的管理IP是 203.0.113.10 sudo nft insert rule inet filter input position 3 tcp dport 22 ip saddr 203.0.113.10 accept # 注意:这里用了‘insert’和‘position 3’,是为了把这条更具体的规则插入到‘允许所有22端口’规则之前,并覆盖它。 # 你需要先找到‘tcp dport 22 accept’规则的位置(用‘sudo nft list chain inet filter input’查看行号)。 # 3. 防御SYN Flood攻击(简易版) # 创建一个名为‘syn_flood’的独立链来处理 sudo nft add chain inet filter syn_flood sudo nft add rule inet filter syn_flood limit rate 10/second burst 20 packets return sudo nft add rule inet filter syn_flood drop # 在input链中,将TCP SYN包跳转到这个链处理 sudo nft insert rule inet filter input position 4 tcp flags syn ct state new jump syn_flood # 4. 记录并丢弃非法状态包 sudo nft add rule inet filter input ct state invalid log prefix “Invalid packet: “ group 0 drop配置解析:
- 端口集合:
{ 80, 443 }语法可以一次匹配多个端口,比写两条规则更简洁高效。 - 规则顺序管理:
insert和position参数是精细化管理规则顺序的利器。一定要善用list chain查看现有规则顺序后再插入。 - 限速与防攻击:
limit rate语句是nftables的“神器”之一,可以轻松实现包速率限制。上面的例子限制了每秒最多10个新SYN连接,突发允许20个,超过则丢弃。这能有效缓解简单的SYN洪水攻击。 - 连接跟踪与日志:
ct state invalid匹配那些不符合任何已知连接的数据包(比如没有三次握手的TCP包),记录日志并丢弃,是很好的安全实践。
4.2 示例二:配置网络地址转换
如果你的服务器充当网关或路由器,需要为内网设备提供NAT上网服务,配置如下:
启用内核IP转发:
echo “net.ipv4.ip_forward=1” | sudo tee -a /etc/sysctl.conf echo “net.ipv6.conf.all.forwarding=1” | sudo tee -a /etc/sysctl.conf sudo sysctl -p创建nat表并添加规则:
# 创建nat表 sudo nft add table ip nat # 创建postrouting链(SNAT:源地址转换,内网出外网) sudo nft add chain ip nat postrouting { type nat hook postrouting priority 100\; } # 假设外网网卡是eth0,内网网段是192.168.100.0/24 sudo nft add rule ip nat postrouting ip saddr 192.168.100.0/24 oifname “eth0” masquerademasquerade动作会自动使用外网接口的IP作为源地址进行转换,特别适合动态获取IP(如PPPoE)的环境。如果是固定IP,也可以用snat to 你的公网IP。在filter表中允许转发(基于之前的filter表):
# 允许内网到外网的转发(状态化,已建立和相关的连接自动允许) sudo nft add rule inet filter forward ct state established,related accept # 允许从内网接口(假设是eth1)发起的新连接转发到外网 sudo nft add rule inet filter forward iifname “eth1” oifname “eth0” ct state new accept别忘了将
/etc/nftables.conf中的filter表的forward链默认策略从drop改为accept,或者确保你的允许规则在默认拒绝之前。更安全的做法是保持policy drop,但用明确的规则允许特定流量。
4.3 示例三:利用集合与命名集管理IP和端口
当需要管理大量IP或端口时,一条条写规则是噩梦。nftables的命名集功能完美解决了这个问题。集合可以存储IP、端口、MAC地址等,并且支持动态更新。
场景:我们需要允许一个IP列表访问管理端口(22),并阻止一个黑名单IP列表访问任何服务。
# 1. 创建两个IP地址集合 sudo nft add set inet filter admin_whitelist { type ipv4_addr\; flags interval\; } sudo nft add set inet filter attacker_blacklist { type ipv4_addr\; } # 2. 向集合中添加元素 sudo nft add element inet filter admin_whitelist { 192.168.1.100, 192.168.1.200-192.168.1.210 } sudo nft add element inet filter attacker_blacklist { 10.0.0.5, 10.0.0.77 } # 3. 在规则中引用集合 # 将允许白名单IP访问SSH的规则放在前面(假设input链已有基础规则) sudo nft insert rule inet filter input position 3 ip saddr @admin_whitelist tcp dport 22 accept # 在链的靠前位置(在允许某些服务之前)丢弃黑名单IP sudo nft insert rule inet filter input position 1 ip saddr @attacker_blacklist drop # 4. 动态管理集合(无需重载整个规则集) # 临时添加一个IP到黑名单 sudo nft add element inet filter attacker_blacklist { 10.0.0.99 } # 从黑名单中删除一个IP sudo nft delete element inet filter attacker_blacklist { 10.0.0.77 }优势:
- 管理高效:IP列表变化时,只需更新集合,无需修改和重载规则。
- 性能优异:nftables内部对集合查询进行了高度优化,即使集合很大,匹配速度也很快。
- 灵活:支持单个地址、范围、网络段等多种格式。
4.4 示例四:流量统计与日志记录
监控和审计是安全运维的重要部分。
# 1. 为特定端口的流量添加计数器 # 统计HTTP和HTTPS端口的入站流量 sudo nft add rule inet filter input tcp dport { 80, 443 } counter accept # 之后可以用 ‘sudo nft list ruleset’ 查看计数器数值 # 2. 记录被拒绝的SSH尝试(记录到内核日志,如/var/log/kern.log) sudo nft add rule inet filter input tcp dport 22 log prefix “SSH Reject: “ group 0 level info drop # 这条规则需要放在‘允许SSH’规则之后,默认策略之前。 # 3. 创建一个专门的日志链(更清晰的管理) sudo nft add chain inet filter log_chain sudo nft add rule inet filter log_chain log prefix “Dropped: “ group 0 sudo nft add rule inet filter log_chain drop # 然后在input链末尾跳转到这个链 sudo nft add rule inet filter input jump log_chain使用counter可以很方便地查看流量情况。而log语句对于调试防火墙规则、发现攻击尝试至关重要。prefix参数可以帮助你在系统日志中快速定位防火墙日志。
5. 运维与排障:让防火墙稳定可控
配置好了不是结束,日常管理和问题排查才是真正的开始。
5.1 规则管理最佳实践
- 配置文件是王道:所有修改最终都应反映在
/etc/nftables.conf中。交互式命令用于测试,测试无误后务必sudo nft list ruleset > /etc/nftables.conf保存。 - 使用注释:nftables配置文件支持以
#开头的注释。为复杂的规则或集合添加注释,几个月后你一定会感谢自己。# 允许办公室IP段访问管理服务 add rule inet filter input ip saddr @office_ips tcp dport { 22, 9090 } accept - 版本控制:将
/etc/nftables.conf纳入Git等版本控制系统,每次变更都有记录,可以轻松回滚。 - 测试规则:在应用新规则集前,可以先将其保存到一个临时文件(如
new_rules.nft),然后用sudo nft -f new_rules.nft加载测试。确认无误后再覆盖正式配置。
5.2 常见问题与排查技巧
防火墙配置不当,最容易导致“服务器失联”。这里有几个救命锦囊:
规则不生效?检查顺序和默认策略。
- 使用
sudo nft list ruleset或sudo nft list chain <表> <链>仔细查看规则顺序。记住,数据包从上到下匹配,第一条匹配的规则决定其命运。 - 确认链的默认策略(
policy)。如果你的规则都是accept,但链的默认策略是drop,并且没有规则匹配,数据包就会被丢弃。
- 使用
把自己关在门外了?配置“逃生舱”规则或使用本地控制台。
- 方法一(推荐):在
input链的最前面,添加一条允许来自本地某个“管理VLAN”或特定安全IP的SSH流量的规则。这样即使后面的规则配置错误,你还能从这个受信的入口进入。 - 方法二:在物理服务器上,可以配置一个cron任务,每隔几分钟检查SSH端口是否可达,如果不可达,则自动回滚到一个已知的安全配置。这是一个高风险操作,需谨慎设计。
- 方法三:如果以上都没做,只能通过云服务商的控制台、IPMI、或物理接触服务器来修复了。
- 方法一(推荐):在
使用日志定位问题。
- 在怀疑被错误阻止的规则前插入
log语句。 - 使用
sudo tail -f /var/log/kern.log或sudo journalctl -f实时查看内核日志,观察数据包被匹配和处理的轨迹。
- 在怀疑被错误阻止的规则前插入
连接跟踪相关问题。
- 如果FTP、SIP等复杂协议的服务不正常,可能是需要加载特定的连接跟踪辅助模块。
- 检查
/proc/sys/net/netfilter/nf_conntrack_max,如果服务器并发连接数很高,可能需要调大这个值,防止连接跟踪表满导致新连接被丢弃。
5.3 性能监控与优化
- 查看规则统计:使用
sudo nft list ruleset可以看到每条规则匹配的计数器(如果添加了counter语句)。这有助于了解哪些规则最活跃,优化规则顺序(将匹配频率高的规则上移)。 - 简化规则:尽量使用集合、端口范围、IP网段来合并规则,减少规则总数。
- 谨慎使用
log:在高流量环境下,日志记录会带来巨大性能开销。生产环境中,通常只对可疑或拒绝的流量进行抽样记录,而不是全部记录。
从iptables切换到nftables,初期会有些不习惯,但一旦熟悉了它统一、清晰的语法和强大的功能,你就再也回不去了。它带来的管理效率和性能提升,在复杂的网络环境中尤其明显。我个人的经验是,将旧的iptables脚本用iptables-translate工具转换,可以作为起点,但亲手用nftables的逻辑重新设计和编写规则,才能真正发挥它的威力。最后,永远记住防火墙的金科玉律:默认拒绝,最小权限开放。