Linux 防火墙实战:用 nftables 默认拒绝、逐个放行守住公网端口


自托管的服务一旦开始对外提供访问,它就被放进了"公网大市场":任何能连上网的机器都可能来敲你的端口。很多人靠"默认不开、要用才开"的直觉在抗,但真正的底气来自防火墙——一层在网络入口就决定"哪些包能进、哪些包根本进不来"的规则层。Linux 上负责这件事的是内核包过滤框架:老而广泛的是 iptables,新的、现代的是它的继任者 nftables。这篇讲清防火墙解决什么、iptables 与 nftables 的关系、怎么用 nftables 写一套"默认拒绝加放行"的规则、怎么配置与持久化,以及常见误区和它与"服务层认证"的分工边界。

一、防火墙解决什么:在包的层面决定"谁能进门"

防火墙工作在网络栈里最前端的那层整理动作:对每个进出主机的数据包,按规则集决定是"放行(accept)"还是"丢弃(drop/reject)"。它的价值是"大门级的收缩"——不需要每一个服务自己想办法挡攻击者,而是让大多数不合规的流量根本到不了服务就没了。比如你只想对外开 80/443,防火墙就把 22、3306、8080 这些不该外联的端口挡在门外,攻击面瞬间从"一堆端口"缩到"你主动开的少数端口"。

这层"默认不信任、逐个放行"的思维是安全基线:宁可先全拒、再把你真正要对外开的服务安心放进来,也别图方便"全都开着、靠服务自己挡"。它和前面聊的"服务层认证/限流"分层配合:防火墙管"谁根本进不来",服务层管"进来的人怎么用、能不能滥用"。想清楚这个分工,才不会把两者混为一谈。

二、iptables 与 nftables:老规矩 vs 新体系

Linux 内核的包过滤,历史上是 iptables(连同它背后的 old 框架 table/chain/rule 结构)统治了十几年;如今主流发行版(Debian 12、Ubuntu 22.04+、RHEL9+)纷纷切换到它的继任者 nftables。理解两者,关键不再是"记两套命令",而是认清趋势:nftables 用一套统一、更易读的语法表达一条规则(`add rule 表 链 ...`),替代了 iptables 里 --dport/--proto 等不同工具的分散写法,并支持原子替换整组规则。

# 查看当前用的是 iptables 还是 nftables 后端
ls -l /usr/sbin/iptables        # 常见是链接到 iptables-nft,即底层走 nftables
# 以 nftables 为例,查看规则:列出某个表下所有链的规则
nft list ruleset

实际的建议是:新系统优先用 nftables 写规则(它更现代、语法统一);很多发行版也仍然兼容 iptables 命令(经 iptables-nft 兼容层转成 nftables)。所以不用被"我要不要抛弃 iptables"吓到——重点是掌握"规则是什么意思",命令层面新系统用 nftables 更顺着趋势。先建立"这个命令在增/删/查哪条规则"的心智,比背全所有 flag 更重要。

三、nftables 最小落地:默认拒绝 + 只放行该放的

一套健康的防火墙心法就是"默认拒绝、逐个放行"。用 nftables,最直接的做法是建一个 table,在里面放 input/output/forward 等链,并把 input 的默认策略设为 drop(丢弃所有不匹配放行规则的入站流量),然后显式允许回环(lo)、已建立的连接(conntrack ESTABLISHED,RELATED)以及你真正想开放的服务端口。

# 初始化一个名为 filter 的表,并设默认拒绝非必要入站
nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
# 回环与已建立连接放行
nft add rule inet filter input iif lo accept
nft add rule inet filter input ct state established,related accept
# 只放行 SSH 和 Web
nft add rule inet filter input tcp dport 22 accept
nft add rule inet filter input tcp dport 80 accept
nft add rule inet filter input tcp dport 443 accept
# 其余入站流量默认被 drop(上面 chain 的 policy drop 兜底)

配置时的几条铁律。一是"先把规则测好再持久化":用 `nft -c`(check,只检查不应用)或小步动作,避免把 SSH 端口也拒了导致自己锁在门外——这是防火墙配置最常见的翻车点;二是"保留一条可靠的救命通道":熟悉 IPMI/KVM/控制台这类带外入口,万一规则把自己锁了还能进;三是"默认心态是拒绝",新增开放先想清楚"真的需要向整个公网开放这个端口吗",敏感管理端口尽量只对可信来源(VPN 内网)放行。

四、持久化与自动化:别让"重启后规则失效"毁掉你的防线

nftables 规则如果不持久化,重启后往往就没了——那你的防火墙就形同虚设。主流发行版用 `nftables` systemd 服务来加载并持久化规则集:你把规则写成规则文件,再由服务在启动时 `nft -f` 加载。常见做法是把规则文件放在 `/etc/nftables.conf`(Debian/Ubuntu)或用发行版的防火墙管理工具(如 UFW/firewalld 这类高层封装,再底层也用 nftables 落地)来管理。

# 手动"试跑不生效"的方式是 nft -c 检查;要持久化则写入规则文件并经服务加载
sudo nft -f /etc/nftables.conf     # 从文件加载
sudo systemctl enable --now nftables
# 小步操作:改动前先备份当前 ruleset 以便回滚
sudo nft list ruleset > /root/fw-backup.nft

实践上的稳定做法有三条:把规则做成"文件化、可审计"的,而不是随手敲一堆不记录的命令;每次重大改动前先备份可用回滚;把防火墙配置纳入你的配置管理/脚本(前面聊的 Ansible 那类)与监控里,这样它才不是"某天配过一次就忘"的一次性设定。防火墙是你常驻安全理解的长期成员,要靠文件与自动化喂,而不是靠记忆。

五、边界:防火墙挡"进不来的",服务层挡"进来的怎么用"

最后分清边界。防火墙负责"包层面谁能进门":它能挡掉大量不该来的扫荡和无用端口暴露,但它不替代服务本身的鉴权、限流与漏洞修复——一个开了 443 的应用如果本身有漏洞、口令又弱,防火墙照样挡不住恶意请求打进去。所以自托管的健康分工是组合拳:防火墙收敛暴露面,服务层做认证/限流/补丁,再加上监控与备份兜底。别因为"我有防火墙"就放松对应用安全的投入。

同时也要记得"别过度自信":防火墙规则写错、持久化失败、或管理端口误开,都可能成为你长时间不自知的破口。把"默认禁止 + 最小放行 + 规则文件化 + 定期核查"这套立成习惯,它才会真的守得住。把它当成"大门",但一个负责任的门卫,既要有明确的准入名单,也要配合屋里上了锁、装了监控,才能让这个自托管的小世界安稳运转。

结语

Linux 防火墙的价值,是在网络入口用"默认拒绝、逐个放行"的规则收敛暴露面,把绝大多数不合规的流量挡在服务之前。落地要点:新系统用更现代的 nftables(老命令兼容层也能用),以"回环+已建连接+仅需端口放行"为最小方案;先把规则 `nft -c` 检查再持久化,留一条救命通道防止锁死自己;把规则文件化、纳入 systemd 自动加载与配置管理,并定期核查。认清它与服务层认证的分工——防火墙管"谁进不来的",应用层管"进去后怎么用",两层组合才能构成真正稳妥的自托管安全底线。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

GitLab 自托管实战:把代码仓库与 CI/CD 流水线一起握回自己手里

Nginx 反向代理实战:用统一入口收拢散落端口的自托管服务

评 论
更换验证码