真正封闭端口靠的是drop动作配合集合匹配,而非集合本身;需用nft add rule inet filter input tcp dport {22,80,443} drop等单行命令实现,并确保规则位于accept规则之后、默认策略之前。

用 nftables 批量关闭未使用端口,核心不是“关端口”本身,而是精准控制入站连接——关键在于用集合(Set)配合 drop 动作,在一条规则里统一拦截多个端口,同时确保它在规则链中位置正确、不被覆盖。
先明确:集合本身不封端口,它只负责批量匹配
Anonymous Set(匿名集合)或 Named Set(命名集合)只是把多个端口号组织在一起,比如 { 22, 80, 443, 3306 }。它没有行为,必须搭配动作才有意义:
-
accept→ 放行这些端口 -
drop→ 丢弃这些端口的入站新连接(这才是“关闭”的实质) -
reject→ 主动拒绝并返回错误(更友好但略耗资源)
所以真正起封锁作用的是 drop,集合只是让这条规则写得简洁、可维护。
推荐写法:单行封多个 TCP/UDP 端口(IPv4+IPv6 通用)
使用 inet 地址簇表,一语双关覆盖双栈:
- 封掉常见服务端口(SSH、HTTP、HTTPS、MySQL):
nft add rule inet filter input tcp dport { 22, 80, 443, 3306 } drop - 封掉 UDP 服务(DNS、NTP、SNMP):
nft add rule inet filter input udp dport { 53, 123, 161 } drop - 只封特定来源的端口(如屏蔽某扫描IP访问管理端口):
nft add rule inet filter input ip saddr 192.168.1.200 tcp dport { 22, 23, 3389 } drop
必须注意:顺序决定是否真生效
即使命令执行成功,规则也可能形同虚设。请检查以下三点:
- 这条
drop规则,必须放在所有accept规则之后(比如允许 established 连接、允许内网 IP 的规则) - 但又必须放在默认策略(
policy)之前——如果链默认是accept,而你没加任何规则,那所有流量都放行 - 避免被宽松规则覆盖:例如前面有一条
ip saddr 0.0.0.0/0 accept,后面再加drop就完全无效
典型安全链结构建议:
- 允许已建立连接:
ct state established,related accept - 允许本机回环:
iifname "lo" accept - 按需放行(如 SSH 来自可信网段):
ip saddr 10.10.0.0/16 tcp dport 22 accept -
最后封禁黑名单端口:
tcp dport { 22, 80, 443 } drop - (可选)链末尾设默认策略:
nft add chain inet filter input { policy drop; }
进阶技巧:让封端口可验证、可追踪、可动态调整
光封住不够,还要知道它是否起效、谁触发了、能不能临时放开:
- 加
counter查命中次数:nft add rule inet filter input tcp dport { 22, 80 } counter drop
后续运行nft list chain inet filter input,看 counter 值是否增长 - 配合
log记录被拦截详情(需启用nf_log_netdev模块):nft add rule inet filter input tcp dport { 22, 80 } log prefix "BLOCKED_PORT: " drop - 改用命名集合,便于后期增删端口而不改规则:
nft add set inet filter blocked_ports { type inet_service; }nft add element inet filter blocked_ports { 22, 80, 443 }nft add rule inet filter input tcp dport @blocked_ports drop
之后只需执行nft add element inet filter blocked_ports { 8080 }或nft delete element inet filter blocked_ports { 22 }

















