Linux网络防火墙实战:nftables从入门到精通(附常见配置示例)

如果你是一位Linux系统管理员或网络安全工程师,那么对防火墙的掌控能力,几乎等同于你对整个系统安全边界的掌控力。过去十几年,iptables 一直是这个领域的绝对主角,但它的复杂性、规则集膨胀后的性能问题,以及IPv4/IPv6双栈管理的割裂感,让许多老手也感到头疼。于是,一个更现代、更统一、设计也更优雅的替代品——nftables,逐渐从Linux内核的深处走到了舞台中央。

这篇文章不是另一篇罗列nft add table、nft add chain基础命令的入门手册。我们的目标是实战。我们将直接从你每天都会遇到的企业级网络管理场景出发:如何为Web服务器构建一个既安全又不失灵活性的防火墙策略?如何高效地实现端口转发和网络地址转换(NAT)?怎样对特定流量进行统计和监控?当规则越来越多时,如何用更高级的数据结构(如集合、字典)来管理,以提升性能和可维护性?

我们将绕过繁琐的理论堆砌,用具体的命令行示例贯穿始终。你会发现,nftables 的语法虽然初看陌生,但其逻辑的一致性和表达能力,一旦掌握,会让你再也回不去iptables的时代。让我们开始这场从入门到精通的实战之旅。

1. 构建你的第一个企业级防火墙策略

在接触任何具体命令之前,理解 nftables 的核心架构至关重要。它不再像 iptables 那样分为 filter、nat、mangle、raw 等多个独立的表,而是采用了一个更清晰的层级模型:地址簇(Family) → 表(Table) → 链(Chain) → 规则(Rule)。此外,它还引入了集合(Set) 和字典(Map) 这类高级数据结构,用于高效地管理IP地址、端口等列表。

对于大多数服务器环境,inet 这个地址簇是我们的首选,因为它能同时处理IPv4和IPv6流量,实现了真正的双栈统一管理。

1.1 初始化防火墙:清空与基础表链创建

上手第一步,通常是建立一个干净的环境。但请注意,在已运行的生产服务器上执行清空操作会瞬间移除所有防火墙规则,可能导致服务中断。务必在测试环境或明确知晓后果的情况下进行。

# 清空所有现有nftables规则(危险!请在测试环境操作)
nft flush ruleset

接下来,我们为一个典型的Web服务器创建基础框架。假设服务器需要开放SSH(22)、HTTP(80)、HTTPS(443)端口,并屏蔽其他所有不必要的入站访问。

# 创建一个名为‘filter’的表,使用inet地址簇以同时支持IPv4/IPv6
nft add table inet filter

# 创建基础链:input链处理发往本机的数据包,forward链处理转发的数据包,output链处理本机发出的数据包
# 优先级使用‘filter’(对应数值0),这是过滤链的标准优先级
nft add chain inet filter input { type filter hook input priority filter\; policy drop\; }
nft add chain inet filter forward { type filter hook forward priority filter\; policy drop\; }
nft add chain inet filter output { type filter hook output priority filter\; policy accept\; }

这里有几个关键点:

  • policy drop; 为 input 和 forward 链设置了默认策略为“丢弃”。这是一种“默认拒绝”的安全模型,只有明确允许的流量才能通过。
  • policy accept; 为 output 链设置了默认策略为“接受”,通常我们信任本机发出的流量。
  • 反斜杠 \; 是必要的,用于在Bash中转义分号,表示命令的结束。

1.2 添加安全规则:允许必要流量

现在,在默认拒绝的背景下,我们需要“凿开”几个必要的孔。

# 1. 允许本地回环(lo)接口的流量,许多本地服务依赖于此
nft add rule inet filter input iif lo accept

# 2. 允许已建立和相关联的连接通过,这是状态化防火墙的核心,确保应答流量能回来
nft add rule inet filter input ct state established,related accept

# 3. 允许ICMP协议(ping等),便于网络诊断
nft add rule inet filter input ip protocol icmp accept
nft add rule inet filter input ip6 nexthdr icmpv6 accept

# 4. 开放服务端口:SSH, HTTP, HTTPS
nft add rule inet filter input tcp dport { 22, 80, 443 } accept

执行 nft list ruleset 查看当前完整的规则集:

table inet filter {
    chain input {
        type filter hook input priority filter; policy drop;
        iif lo accept
        ct state established,related accept
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept
        tcp dport { 22, 80, 443 } accept
    }
    chain forward {
        type filter hook forward priority filter; policy drop;
    }
    chain output {
        type filter hook output priority filter; policy accept;
    }
}

注意:上述规则顺序很重要。nftables 规则按顺序匹配,第一条匹配的规则生效。我们将 ct state established,related accept 放在开放端口之前,能有效减少后续规则对已连接流量的匹配开销。

2. 高级流量管理:集合、字典与限速

当规则变得复杂时,直接罗列IP和端口会显得笨拙且难以维护。nftables 的命名集合(Named Set) 和字典(Map) 就是为了解决这个问题而生。

2.1 使用命名集合管理IP白名单

假设我们需要允许一个内部的运维IP段(例如 10.10.0.0/24)和几个特定的外部监控IP访问所有端口,而其他IP只能访问80和443。

# 创建一个存储IPv4地址的命名集合
nft add set inet filter trusted_ips { type ipv4_addr\; flags interval\; }

# 向集合中添加元素(IP段和单个IP)
nft add element inet filter trusted_ips { 10.10.0.0/24, 203.0.113.5, 198.51.100.10 }

# 创建规则:来自可信IP集合的流量,直接接受
nft add rule inet filter input ip saddr @trusted_ips accept

# 注意:这条规则需要放在‘tcp dport { 80, 443 } accept’规则之前,因为可信IP享有更高权限

flags interval; 声明允许在集合中使用CIDR表示的IP段,这是一个非常实用的特性。你可以随时更新这个集合,而无需修改任何规则:

# 增加一个新的可信IP
nft add element inet filter trusted_ips { 192.0.2.33 }
# 移除一个IP
nft delete element inet filter trusted_ips { 203.0.113.5 }

2.2 使用字典实现端口动态跳转

字典(Map)是键值对的集合,它能根据数据包的特征(键)动态决定执行的动作(值)。一个经典场景是根据目标端口,将流量引导至不同的内部链进行处理,实现模块化规则管理。

# 首先,创建两个常规链用于处理不同类型端口的流量
nft add chain inet filter tcp_chain
nft add chain inet filter udp_chain

# 在tcp_chain和udp_chain中添加具体的规则(例如日志记录、限速等)
nft add rule inet filter tcp_chain log prefix \"TCP-Traffic: \" level info
nft add rule inet filter udp_chain log prefix \"UDP-Traffic: \" level info

# 创建一个字典,将L4协议映射到对应的处理链
nft add map inet filter service_map { type inet_proto : verdict \; }

# 为字典添加映射关系:tcp协议跳转到tcp_chain,udp协议跳转到udp_chain
nft add element inet filter service_map { tcp : jump tcp_chain, udp : jump udp_chain }

# 在input链中应用字典:根据数据包的传输层协议,选择下一步动作
nft add rule inet filter input meta l4proto vmap @service_map

这种方式的优势在于,扩展性极佳。未来如果需要新增一种协议(如icmp)的特殊处理,只需在字典中添加一个映射,并创建对应的处理链即可,无需修改主链的逻辑。

2.3 连接速率限制(防暴力破解)

保护SSH等服务免受暴力破解攻击是防火墙的常见任务。nftables 可以基于 limit 表达式轻松实现。

# 限制对TCP 22端口的新连接尝试:每分钟最多10次,超过则丢弃
nft add rule inet filter input tcp dport 22 ct state new limit rate 10/minute accept
# 上一条规则只“接受”每分钟前10个新连接,其余新连接会继续向下匹配
# 由于默认策略是drop,因此超限的连接将被丢弃

# 更清晰的写法:使用独立的链和显示丢弃
nft add chain inet filter ssh_bruteforce
nft add rule inet filter input tcp dport 22 ct state new jump ssh_bruteforce
nft add rule inet filter ssh_bruteforce limit rate 10/minute return # 允许的,返回
nft add rule inet filter ssh_bruteforce log prefix \"SSH brute force attempt: \" drop # 超限的,记录并丢弃

3. 网络地址转换(NAT)与端口转发实战

NAT是网关、路由器或提供端口转发服务的服务器的核心功能。nftables 的NAT语法比 iptables 更直观。

3.1 源地址转换(SNAT/Masquerade)

让内网机器通过网关访问互联网,需要使用SNAT(源地址转换)或它的自动版——Masquerade(伪装)。

# 首先,必须为NAT创建一个专用的表。虽然可以在filter表做NAT,但分开更清晰。
nft add table ip nat

# 创建postrouting链。SNAT/Masquerade工作在postrouting钩子上。
# 优先级设为‘srcnat’(100),确保在过滤之后进行地址转换。
nft add chain ip nat postrouting { type nat hook postrouting priority srcnat\; }

# 使用Masquerade:将来自内网网卡eth1的流量的源IP替换为网关的外网IP(自动获取)
nft add rule ip nat postrouting oif eth0 masquerade
# 或者使用SNAT指定一个固定的外网IP
# nft add rule ip nat postrouting oif eth0 snat to 203.0.113.1

3.2 目的地址转换(DNAT)与端口转发

将公网IP的某个端口映射到内网服务器的端口,这是端口转发的标准做法。

# 在nat表中创建prerouting链。DNAT工作在prerouting钩子上。
# 优先级设为‘dstnat’(-100),确保在路由决策之前修改目标地址。
nft add chain ip nat prerouting { type nat hook prerouting priority dstnat\; }

# 示例1:将网关公网IP的TCP 2222端口,转发到内网服务器192.168.1.100的22端口(SSH)
nft add rule ip nat prerouting tcp dport 2222 dnat to 192.168.1.100:22

# 示例2:将公网IP的UDP 53端口(DNS),转发到内网DNS服务器192.168.1.53
nft add rule ip nat prerouting udp dport 53 dnat to 192.168.1.53

# 示例3:范围转发。将公网IP的8000-8010端口范围,转发到内网同一台机器的相同端口范围
nft add rule ip nat prerouting tcp dport 8000-8010 dnat to 192.168.1.200

提示:仅仅配置DNAT规则通常不够。因为转发后的数据包目的地是内网IP,它们可能还需要通过网关的 forward 链(在 filter 表中)。请确保你的 forward 链策略或规则允许这些转发的流量通过。同时,为了内网服务器能正确回包,相应的SNAT/Masquerade规则也通常是必需的。

4. 监控、排错与规则集维护

一个健壮的防火墙配置离不开监控和易于维护的架构。

4.1 流量统计与日志记录

nftables 内建了计数器,可以方便地统计规则匹配的流量。

# 在任意规则末尾加上‘counter’关键字,即可启用统计
nft add rule inet filter input tcp dport 443 counter accept
# 查看规则集时,会显示匹配的数据包数量和字节数
# nft list ruleset -a
# ... tcp dport https counter packets 12345 bytes 987654321 accept # handle 10 ...

对于更复杂的监控,可以使用 log 语句。但生产环境需谨慎使用,避免日志洪水。

# 记录所有被丢弃的数据包的前128字节,并打上标签
nft add rule inet filter input log prefix \"[DROP] \" group 0 level warn limit rate 30/minute drop

4.2 规则集持久化与版本管理

内存中的规则在重启后会消失。持久化配置是必须的。

# 备份当前规则集到文件
nft list ruleset > /etc/nftables/backup.nft

# 系统启动时恢复规则
# 在 systemd 系统上,通常通过 nftables.service 读取 /etc/nftables.conf
# 我们可以将备份文件链接为配置文件
cp /etc/nftables/backup.nft /etc/nftables.conf
systemctl enable --now nftables

更专业的方法是编写模块化的 nft 脚本文件(.nft),而不是直接导出运行时规则。脚本更清晰,易于加入注释和版本控制(如Git)。

# /etc/nftables/main.nft
#!/usr/sbin/nft -f
# Flush all rules
flush ruleset

# Define tables and chains
table inet filter {
    chain input {
        type filter hook input priority filter; policy drop;
        # ... 你的所有规则 ...
    }
    # ... 其他链 ...
}
table ip nat {
    # ... NAT 规则 ...
}

然后使用 nft -f /etc/nftables/main.nft 加载。

4.3 高效排错技巧

当规则不按预期工作时,可以按以下步骤排查:

  1. 检查规则列表和句柄:nft list ruleset -a 显示所有规则和其唯一的 handle。handle 是删除或替换特定规则的关键。

    nft list chain inet filter input -a
    
  2. 使用追踪(Trace):对于复杂规则,内核追踪功能无比强大。

    # 启用特定规则的追踪(假设handle是15)
    nft add rule inet filter input position 15 meta nftrace set 1
    # 在另一个终端监听追踪事件
    nft monitor trace
    

    这会将匹配该规则的数据包的详细决策过程打印出来,显示它经过了哪些链、匹配了哪些规则、最终结果如何,是排错的终极利器。

  3. 规则顺序调整:使用 insert、replace、delete 命令精细管理规则。

    # 在handle为10的规则之前插入一条新规则
    nft insert rule inet filter input position 10 tcp dport 9090 accept
    # 替换handle为12的规则
    nft replace rule inet filter input handle 12 tcp dport 3306 drop
    # 删除handle为8的规则
    nft delete rule inet filter input handle 8
    

在我管理的众多服务器中,从 iptables 迁移到 nftables 最初需要一点适应,但一旦熟悉了其统一的语法和强大的数据结构,管理效率的提升是显而易见的。特别是用命名集合管理动态IP列表,用字典实现策略路由,这些特性让防火墙规则不再是难以维护的“面条代码”。最后记住,任何防火墙变更前,最好先通过 nft --check -f your_script.nft 验证脚本语法,并在测试环境充分验证,这才是保障生产环境稳定性的不二法门。

更多推荐