在Debian 10(Buster)及之后的版本中,nftables已经成为默认的防火墙框架,逐步取代了传统的iptables。如果你正在维护一台Debian服务器,需要把现有的iptables规则迁移到nftables,核心思路就是:先用iptables-nft兼容层跑通现有规则,再逐步用原生nftables语法重写,最终完全脱离iptables。下面我会从安装、规则转换、实战迁移到验证优化,一步步讲清楚整个过程。

为什么Debian要从iptables转向nftables

iptables已经维护了二十多年,它的底层架构基于多个独立的工具(iptables、ip6tables、ebtables、arptables),规则集分散、语法不统一,性能也逐渐跟不上现代内核的需求。nftables是Linux内核从3.13版本开始引入的新一代包过滤框架,它统一了IPv4、IPv6、ARP、桥接等多种协议的过滤规则,语法更简洁,性能更高,而且支持原子性规则更新——也就是说,你可以一次性加载一整组规则,不会出现加载一半导致防火墙短暂失效的情况。Debian从Buster开始默认使用nftables,到Bookworm已经彻底移除了iptables的独立包,只保留了iptables-nft作为兼容层。

第一步:确认系统环境并安装必要工具

在开始迁移之前,先确认你的Debian版本和当前防火墙状态。登录服务器后执行以下命令:

cat /etc/debian_version
nft list ruleset

如果nft list ruleset返回空或者报错,说明nftables还没有启用任何规则。接下来安装nftables和iptables兼容工具:

apt update
apt install nftables iptables

安装完成后,启用nftables服务并设置开机自启:

systemctl enable nftables
systemctl start nftables

注意:如果你之前用的是ufw或者firewalld,建议先禁用它们,避免规则冲突。执行:

systemctl disable ufw
systemctl stop ufw

第二步:导出现有iptables规则

在迁移之前,你需要把当前iptables的规则完整导出来。用iptables-save命令可以把所有规则输出到文件:

iptables-save > /root/iptables-backup.txt

如果你同时用了IPv6规则,也要导出ip6tables:

ip6tables-save > /root/ip6tables-backup.txt

打开备份文件看一下内容,典型的iptables规则长这样:

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p tcp --dport 22 -j ACCEPT
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
-A INPUT -j DROP
COMMIT

这段规则的意思是:默认丢弃所有入站和转发流量,允许本地回环、已建立连接、SSH(22)、HTTP(80)、HTTPS(443),其余全部拒绝。

第三步:将iptables规则转换为nftables语法

nftables的语法结构和iptables完全不同。iptables是基于"表-链-规则"的三层结构,而nftables是"表-链-规则-表达式"的扁平结构,而且表的类型也不一样。nftables主要有这几种表类型:

filter表:用于包过滤(对应iptables的filter表)

nat表:用于网络地址转换(对应iptables的nat表)

route表:用于路由决策

inet表:同时处理IPv4和IPv6(这是nftables的一大优势)

下面是把上面那段iptables规则转换成nftables的完整写法:

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # 允许本地回环
        iif "lo" accept

        # 允许已建立和相关连接
        ct state established,related accept

        # 允许SSH
        tcp dport 22 accept

        # 允许HTTP
        tcp dport 80 accept

        # 允许HTTPS
        tcp dport 443 accept

        # 拒绝其余所有
        drop
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

几个关键转换点要注意:iptables里的-i lo对应nftables的iif "lo";-m conntrack --ctstate对应ct state;-p tcp --dport对应tcp dport;默认策略DROP在nftables里用policy drop来声明。

第四步:处理NAT规则的迁移

如果你的服务器做了端口转发或者 masquerade,这部分规则要放到nat表或者inet表的nat链里。假设你原来有这样的iptables NAT规则:

*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o eth0 -j MASQUERADE
-A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80
COMMIT

转换成nftables写法:

table ip nat {
    chain postrouting {
        type nat hook postrouting priority 100;
        oifname "eth0" masquerade
    }

    chain prerouting {
        type nat hook prerouting priority -100;
        tcp dport 8080 redirect to :80
    }
}

这里有个细节:nftables里用oifname代替了iptables的-o,用redirect to代替了REDIRECT。而且nftables支持在同一条链里混合IPv4和IPv6规则(如果用inet表的话),但如果你只处理IPv4,就用ip类型的表。

第五步:加载和测试新规则

把转换好的规则保存为文件,比如/etc/nftables.conf,然后用nft命令加载:

nft -f /etc/nftables.conf

加载之后立刻验证规则是否生效:

nft list ruleset

这时候你应该能看到完整的规则集。然后做实际测试:从另一台机器尝试SSH连接、访问80和443端口,确认正常;尝试访问一个未开放的端口,确认被拒绝。如果发现SSH连不上了,别慌——用控制台或者带外管理进去,把input链的policy改成accept,先恢复访问再排查问题。

第六步:设置nftables持久化

Debian的nftables包自带了systemd服务,规则文件默认放在/etc/nftables.conf。确认文件存在并且内容正确后,重启服务使其生效:

systemctl restart nftables
systemctl status nftables

如果你之前用iptables-persistent来保存规则,现在不需要了。nftables的持久化就是/etc/nftables.conf这个文件本身,systemd启动时会自动加载。建议做个备份:

cp /etc/nftables.conf /etc/nftables.conf.bak

第七步:清理iptables兼容层

规则迁移完成并稳定运行一段时间后(建议至少观察一周),你可以卸载iptables包,彻底告别兼容层:

apt remove iptables
apt autoremove

卸载后再确认nftables仍然正常工作:

nft list ruleset

迁移过程中的常见坑和解决方案

第一个坑:规则顺序问题。iptables是按链内顺序逐条匹配,nftables也是,但nftables的hook优先级(priority)会影响链的执行顺序。如果你有多条链,注意设置正确的priority值,数字越小优先级越高。

第二个坑:连接跟踪状态。iptables里用-m conntrack --ctstate,nftables里简化为ct state,但要注意nftables的ct模块功能更强,还支持ct helper、ct mark等高级特性,迁移时不要遗漏。

第三个坑:接口名称。iptables用-i eth0,nftables用iifname "eth0"或者oifname "eth0"。如果你的网卡名称是ens33、enp0s3这种新命名方式,一定要用实际名称,不要想当然写eth0。

第四个坑:Docker和容器网络。如果你的服务器跑了Docker,Docker会自己管理iptables规则,直接迁移可能导致容器网络异常。建议在迁移前停止Docker,迁移完成后再启动,或者单独为Docker的规则创建独立的nftables链。

进阶技巧:用nftables的集合和映射简化规则

nftables支持set和map数据结构,这是iptables做不到的。比如你要封禁一批IP地址,不用写几十条规则,可以这样:

table inet filter {
    set blacklist {
        type ipv4_addr
        elements = { 192.168.1.100, 10.0.0.5, 172.16.0.99 }
    }

    chain input {
        type filter hook input priority 0; policy drop;
        ip saddr @blacklist drop
        iif "lo" accept
        ct state established,related accept
        tcp dport { 22, 80, 443 } accept
        drop
    }
}

这种写法不仅规则更短,而且以后要添加或删除封禁IP,只需要修改set里的elements,不用动规则本身。这在管理大量IP黑名单时非常高效。

总结

从iptables迁移到nftables不是一键完成的事情,但也没有想象中复杂。核心流程就是:导出旧规则、理解语法差异、逐条转换、加载测试、持久化、清理旧工具。Debian官方推荐的路径就是通过iptables-nft兼容层过渡,最终完全切换到原生nftables。掌握了这套方法,你不仅能完成迁移,还能利用nftables更强大的语法特性把防火墙规则写得更清晰、更高效、更易维护。对于生产环境,建议在维护窗口操作,提前做好快照和回滚方案,确保万无一失。