把服务器锁起来的第一次尝试,往往是把 SSH 也一起锁了
每个站长都听人说过「服务器要开防火墙」。于是很多人 SSH 上去,敲一句 ufw enable,看到提示 Command may disrupt existing ssh connections. Proceed with operation (y|n)? 随手点了 y,然后——当前这个 SSH 会话还活着(因为 ufw 默认不会立刻切断已建立的连接),但下次再连就 Connection timed out 了。更糟的是如果服务器在云上、你又没开 VNC 控制台,那就只能重装了。
防火墙这件事,本质上是「默认拒绝 + 显式放行」的思维。但真正让人踩坑的,是规则生效的顺序、已建立连接和新连接的区别、以及 Docker 会在你背后偷偷改 iptables 这件事。这篇把 ufw 和 nftables 两条路子都讲清楚,并给出一个「顺序正确、可回滚、不会把自己锁在外面」的安全配置流程。
先理解 ufw 和 nftables 是什么关系
很多人以为 ufw、nftables、iptables 是三个并列的防火墙,其实不是。
内核层面只有一套东西:netfilter。它是 Linux 内核里的包过滤框架。我们平时说的「防火墙工具」都只是操作 netfilter 规则的用户态程序:
· iptables:老的用户态工具,操作 ip_tables / ip6_tables 内核模块。规则是「表(table)→ 链(chain)→ 规则(rule)」三层结构,有 filter、nat、mangle 等表。
· nftables:新一代工具,从 Linux 3.13 开始进内核,替代 iptables。规则语法更简洁,支持集合(set)和映射(map),性能更好,而且可以用一个命令原子性地替换整套规则。
· ufw(Uncomplicated Firewall):不是独立防火墙,只是 iptables/nftables 的封装。Ubuntu/Debian 上默认用它,`ufw` 命令背后实际是生成 iptables 规则(新版系统背后是 nftables)。
所以「ufw 和 nftables 冲突」这个说法不太准确,准确的说法是:ufw 管理的是它自己的一套规则,你手动用 nft 加的规则和它各管各的,容易互相覆盖或产生意料之外的放行。社区最稳的做法是二选一:要么全用 ufw,要么全用 nftables,别混着来。
方案一:ufw,适合个人站长的 90% 场景
ufw 最大的价值是「简单不容易错」。它默认的管理哲学是:默认拒绝入站、默认允许出站、默认放行已建立连接。这正好满足绝大多数服务器的需求。
第一步:先放行 SSH,再启用。顺序绝对不能反。永远先放行,再 enable:
# 1. 先装(Debian/Ubuntu 通常自带)
apt install -y ufw
# 2. 关键:先放行 SSH。改成你真实的 SSH 端口,不是 22 就写真实值
ufw allow 22/tcp
# 3. 再放行 Web 端口
ufw allow 80/tcp
ufw allow 443/tcp
# 4. 现在才启用
ufw enable
# 5. 查看状态
ufw status verbose第 5 步的 status verbose 输出里必须看到这么一行,否则说明默认策略被改过:
Default: deny (incoming), allow (outgoing), disabled (routed)第二步:改过 SSH 端口的话,别忘了同步。这是第二高频的「把自己锁在外面」的原因。如果你在 /etc/ssh/sshd_config 里把 Port 改成了 2222,但防火墙里放的还是 22,那你重启 sshd 之后当前会话还活着,下一次重连就进不来了。改端口时两张表都要动:
# sshd_config 里改端口后
ufw allow 2222/tcp
# 先不要删 22,等新端口连通性验证通过了再删
systemctl restart sshd
# 新开一个终端验证 2222 能连上
ufw delete allow 22/tcp第三步:按来源 IP 放行(比如只允许自己访问后台)。
# 只允许某 IP 访问 8080
ufw allow from 203.0.113.7 to any port 8080 proto tcp
# 允许一个网段
ufw allow from 192.168.1.0/24 to any port 3306 proto tcp
# 限制 SSH 暴力破解:同一 IP 60 秒内超过 6 次连接就临时封禁
ufw limit 22/tcpufw limit 是 ufw 内建的一个轻量防爆破机制,比 fail2ban 简单,配置一行就行,代价是它不是持久封禁(fail2ban 可以封更久)。两者可以叠加,不冲突。
第四步:给 Docker 用户的一个大坑警告。如果你服务器上跑了 Docker,ufw 对 Docker 发布的端口几乎不起作用。原因:Docker 启动容器时会往 iptables 的 nat 表 DOCKER 链里插规则,这些规则在 ufw 的规则之前生效。也就是说,你用 docker run -p 3306:3306 暴露的 MySQL,哪怕 ufw deny 3306,外部照样能连进来。
验证方法:在外部机器上直接 nc -zv 你的IP 3306,如果通了但 ufw status 里没有 3306 的允许规则,那就是这个问题。解决方式有三:
# 方式 1(推荐):不要去发布端口,用 Docker 内部网络通信
# 容器之间通过 docker network 互访,不映射到宿主机
# 方式 2:只绑定到本地回环
docker run -p 127.0.0.1:3306:3306 mysql:8
# 方式 3:在 DOCKER-USER 链里加规则(ufw 管不到这段)
iptables -I DOCKER-USER -i eth0 -p tcp --dport 3306 -j DROP最省心的还是方式 1 和 2:能不给宿主机映射端口就不映射。
方案二:nftables,规则更清晰、性能更好
如果你的系统比较新(Debian 10+ / Ubuntu 20.04+ / CentOS 8+),nftables 已经默认可用。它的配置文件是 /etc/nftables.conf,语法自成一派,但只要理解「表 → 链 → 规则」这个骨架就能上手。
一个完整的、可以直接用的服务器防火墙规则集:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
# 已建立连接的集合,用于放行回包
set ssh_ratelimit {
type ipv4_addr
flags dynamic, timeout
timeout 60s
}
chain input {
type filter hook input priority 0; policy drop;
# 1. 放行本地回环
iif lo accept
# 2. 放行已建立/相关的连接(关键:否则回包被丢,表现为"能连上但没响应")
ct state established,related accept
# 3. 丢弃无效包
ct state invalid drop
# 4. 放行 ICMP(至少放行 ping 和 path MTU 发现,否则网络会莫名卡)
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
# 5. SSH:限制每 IP 每分钟最多 6 个新连接
tcp dport 22 ct state new \
add @ssh_ratelimit { ip saddr limit rate 6/minute } accept
# 6. Web 端口
tcp dport { 80, 443 } accept
# 7. 其余全部丢弃(policy drop 已经兜底)
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}几个必须理解的要点:
1. ct state established,related accept 必须放在前面。这一条解决的是「出站请求的回包要不要放行」。如果没有它,你的服务器能发出 DNS 查询、能发出 API 请求,但收不到任何回包——表现是「网络能 ping 通但什么都加载不出来」,极难排查。
2. priority 0 和 policy drop 是 base chain 的属性。hook input 表示这个链挂在「入站」这个钩子上,priority 0 是执行优先级(数字小的先跑),policy drop 表示没有规则匹配时默认丢弃。很多教程漏掉 policy drop,结果规则写了等于没写。
3. 放行 ICMP 不是可选项。完全禁 ping 看起来安全,但会破坏 路径 MTU 发现(PMTUD)。表现是:小页面能打开,大页面(或者大文件上传)卡死。所以至少要放行 icmp 和 icmpv6,其中 icmpv6 在 IPv6 环境下是必需的(邻居发现协议依赖它)。
4. 加载规则前先 nft -c -f /etc/nftables.conf 检查语法。-c 是 check 模式,只解析不生效。这一步能帮你避免「规则写错导致整套配置没加载、防火墙处于全开状态」的尴尬。
# 语法检查(不会真的加载)
nft -c -f /etc/nftables.conf
# 确认无误后加载
nft -f /etc/nftables.conf
# 查看当前生效的完整规则集
nft list ruleset
# 设置开机自启
systemctl enable --now nftables远程配置防火墙的安全方法论:给自己留三张保命牌
不管你用 ufw 还是 nftables,只要是远程 SSH 操作,就必须遵守下面三条。这不是「建议」,是血泪教训。
保命牌一:先放行,后启用,永远不要在同一个命令里做完。ufw 的 enable 之后加一句 sleep 30; ufw disable 作为「自动回滚」,是远程改防火墙的经典技巧:
# 在一条命令里:先配好规则,启用,然后 5 分钟后自动关闭
# 如果这 5 分钟内你连不上了,防火墙会自己退回到关闭状态
ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp \
&& ufw --force enable \
&& (sleep 300 && ufw disable) &这个「定时自救」的模式,比任何备份都实用——因为防火墙把你锁在外面时,你连上去恢复配置的通道都没有了。
保命牌二:改配置前开两个 SSH 会话。一个用来改,另一个保持不动(ssh -o ServerAliveInterval=30 user@host)。如果改坏了,当前连接可能因超时被切,但第二个会话如果一直有心跳,通常能活到你把配置改回去。当然,如果是规则层面的 drop,已建立的连接不受影响(因为 established 是放行的),这正好是最可靠的一张保命牌。
保命牌三:云服务商的安全组是独立的一层,不要只改一台。很多云主机的防火墙有两层:一层是云控制台的「安全组」,一层是系统内的 ufw/nftables。流量要两层都放行才能通。两层都拒的时候,你在系统里怎么调 ufw 都没用,问题出在云控制台那边。反过来,如果只想临时开放一个端口,先在云安全组里放行试试,往往比改系统防火墙快。
排查顺序建议这样:先从本机 curl 127.0.0.1:端口 → 如果本机能通、外网不通,问题在防火墙或安全组;再从本机 curl 内网IP:端口 → 通了说明监听没问题,纯是入站规则;最后看云控制台安全组。这个「由内到外」的顺序能省掉大量瞎猜。
怎么验证防火墙规则真的生效了
配置完不算完,要能证明它按预期工作。三个验证动作:
1. 在外部机器上扫描你的端口。从一台不同网络的机器(手机 4G、或者另一台 VPS)上扫:
# 扫常见端口
nmap -Pn -p 22,80,443,3306,6379 你的服务器IP
# 快速看某个端口是否可达
nc -zv 你的服务器IP 3306期望结果是:22/80/443 open,3306/6379 filtered(被丢弃表现为 filtered,被拒绝表现为 closed,两者语义不同)。
2. 看计数器有没有在动。nftables 可以给规则加计数:
# 在规则里加 counter
tcp dport { 80, 443 } counter accept
# 查看计数
nft -a list ruleset | grep -A1 counter如果某条 drop 规则的计数一直涨,说明有人(或者某个扫描器)一直在敲那个端口,规则正在干活。
3. 查日志确认丢包原因。给 drop 规则加 log 前缀(注意日志量,别把磁盘写满):
chain input {
...
tcp dport 22 log prefix "FW-DROP-SSH: " drop
}然后在 journalctl -k -f 里就能看到被丢弃的包来自哪个 IP。这对判断「是不是自己在被扫描」很有用。
小结:记住三条主线
防火墙不复杂,复杂的是它和其他系统的交互。三条主线记住,基本就不会翻车:
第一条:默认拒绝入站,显式放行需要的,永远先放行 SSH 再启用。任何时候远程改防火墙,都要给自己留一条能进去的路(定时回滚、keepalive 会话、云控制台 VNC)。
第二条:别忘了「已建立连接」和「ICMP」这两类流量。前者不能放行会导致「能发不能收」的诡异现象,后者完全禁止会破坏路径 MTU 发现,表现为大文件上传卡死。
第三条:Docker 和云安全组是两个独立的、会覆盖你规则的东西。Docker 会在 iptables 里插到 ufw 前面,云安全组在网络层比系统防火墙更靠前。出了「端口明明没放行却能访问」或者「规则明明放行了却不通」的问题,先往这两个方向查。
最后,如果非要在 ufw 和 nftables 之间选:个人站长用 ufw 就够了,一行一条规则,出错概率最低;只有当你要做复杂的按连接数限速、按集合匹配、或者需要精细的规则组织和原子替换时,nftables 的优势才体现出来。工具栏不是越复杂越好,能稳定保护服务器的才是好工具。