在 Linux 中配置 ufw:服务器防火墙与 nftables 基础
配置 ufw 只需三步:设置默认策略(sudo ufw default deny incoming 和 allow outgoing),允许 SSH 及所需端口(sudo ufw allow 22/tcp、sudo ufw allow 443/tcp),然后用 sudo ufw enable 启用防火墙。用 sudo ufw status numbered 检查规则;在底层,ufw 通过 iptables 或 nftables 管理 netfilter。
Linux 防火墙决定哪些网络数据包可以进入服务器,哪些应被丢弃。内核通过 netfilter 子系统完成过滤,而管理它的工具包括底层的 iptables 和 nftables,以及上层的 ufw(Uncomplicated Firewall,简易防火墙)。对于大多数 Ubuntu 和 Debian 服务器,配置 ufw 只需五分钟:拒绝所有入站连接,开放 SSH 和服务端口,然后启用。下面是分步操作指南、常见错误,以及理解底层机制所需的 nftables 基础。
Linux 防火墙的工作原理
数据包过滤在内核中完成,即 netfilter。它提供若干挂载点(hook):发往本机的入站流量、本机发出的出站流量以及转发(forward)流量。用户态工具只负责把规则写入内核的这些挂载点。
| 工具 | 层级 | 适用场景 |
|---|---|---|
ufw | 上层封装,语法简单 | Ubuntu/Debian 上的单台服务器,常规规则 |
firewalld | 带区域(zone)的上层封装 | RHEL、Rocky、Fedora |
nftables(nft) | 底层,现代 | 复杂规则、NAT、地址集合 |
iptables | 底层,正在被淘汰 | 旧系统和旧脚本 |
重要的是:只使用一种管理工具。如果同时通过 ufw 和手动 iptables 修改规则,它们会相互覆盖,结果难以预料。
安装并检查 ufw
Ubuntu 通常已经安装了 ufw,但默认处于关闭状态。在 Debian 上需要单独安装:
$ sudo apt update
$ sudo apt install ufw
$ sudo ufw status
Status: inactive
状态 inactive 表示 ufw 规则没有生效。现在还不能启用防火墙:先允许 SSH,否则在远程服务器上会把自己锁在门外。
分步配置服务器上的 ufw
第 1 步:默认策略
默认策略是指没有任何规则匹配时如何处理数据包。服务器的安全方案是:入站拒绝,出站允许。
$ sudo ufw default deny incoming
Default incoming policy changed to 'deny'
$ sudo ufw default allow outgoing
Default outgoing policy changed to 'allow'
你自己发起的出站连接的回复会自动放行:ufw 会跟踪连接状态(connection tracking),因此无需为回复开放端口。
第 2 步:允许 SSH
$ sudo ufw allow OpenSSH
Rules updated
Rules updated (v6)
OpenSSH 是一个应用配置文件(profile)。如果 SSH 使用非标准端口,请明确允许该端口:sudo ufw allow 2222/tcp。要查看 sshd 实际监听的端口,可以运行 sudo ss -tlnp | grep sshd。
第 3 步:开放服务端口
$ sudo ufw allow 80/tcp
$ sudo ufw allow 443/tcp
请指定协议:如果不写 /tcp,ufw 会同时开放 TCP 和 UDP,多余的端口没有必要开放。
第 4 步:启用防火墙
$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
ufw 会立即启动,并在每次开机时自动启用。不要关闭当前的 SSH 会话:再打开一个新会话,确认连接正常。
第 5 步:检查规则
$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp (OpenSSH) ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)
80/tcp (v6) ALLOW IN Anywhere (v6)
443/tcp (v6) ALLOW IN Anywhere (v6)
如果 /etc/default/ufw 中设置了 IPV6=yes(默认值),ufw 会自动创建 IPv6 规则。
如何在 ufw 中开放端口:所有语法形式
ufw allow 支持简短和完整两种形式。简短形式适用于常规场景;完整形式则用于指定来源、接口或目标地址。
| 任务 | 命令 |
|---|---|
| TCP 端口 | sudo ufw allow 443/tcp |
| UDP 端口 | sudo ufw allow 51820/udp |
| 端口范围 | sudo ufw allow 6000:6007/tcp |
| 按服务名 | sudo ufw allow https |
| 来自单个 IP | sudo ufw allow from 203.0.113.10 |
| 来自子网的端口访问 | sudo ufw allow from 10.0.0.0/24 to any port 5432 proto tcp |
| 在指定接口上 | sudo ufw allow in on eth1 to any port 3306 proto tcp |
| 拒绝 | sudo ufw deny 23/tcp |
| 拒绝并返回响应 | sudo ufw reject 25/tcp |
deny 与 reject 的区别:deny 静默丢弃数据包,客户端只能等到超时;reject 会返回一个“端口已关闭”的响应。对外网通常选择 deny,对内网则 reject 更方便,客户端能立即看到错误。
只允许一台服务器访问数据库
常见需求:PostgreSQL 或 MySQL 不应暴露在互联网上,但相邻服务器上的应用需要连接它。
$ sudo ufw allow from 10.0.0.5 to any port 5432 proto tcp
Rule added
得益于 deny incoming 策略,5432 端口对其他所有来源仍然关闭。此外还应配置数据库只监听内网地址:防火墙与服务自身的设置相互补充、彼此保护。
应用配置文件(Profile)
软件包可以把自己的端口说明放在 /etc/ufw/applications.d/ 中。查看配置文件列表及内容:
$ sudo ufw app list
Available applications:
Nginx Full
Nginx HTTP
Nginx HTTPS
OpenSSH
$ sudo ufw app info "Nginx Full"
Profile: Nginx Full
Title: Web Server (Nginx, HTTP + HTTPS)
Description: Small, but very powerful and efficient web server
Ports:
80,443/tcp
允许某个配置文件:sudo ufw allow "Nginx Full"。名称中有空格时请加引号。你也可以用 INI 格式在同一目录中编写自己的配置文件,然后用 sudo ufw app update 名称 使其生效。
如何删除 ufw 规则
最可靠的方法是按编号删除:
$ sudo ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] OpenSSH ALLOW IN Anywhere
[ 2] 80/tcp ALLOW IN Anywhere
[ 3] 443/tcp ALLOW IN Anywhere
[ 4] OpenSSH (v6) ALLOW IN Anywhere (v6)
[ 5] 80/tcp (v6) ALLOW IN Anywhere (v6)
[ 6] 443/tcp (v6) ALLOW IN Anywhere (v6)
$ sudo ufw delete 2
Deleting:
allow 80/tcp
Proceed with operation (y|n)? y
Rule deleted
删除一条规则后,编号会重新排列,因此再次删除前请重新执行 status numbered。另一种方式是用 delete 加上原规则重复一遍:sudo ufw delete allow 80/tcp;它会同时删除 IPv4 和 IPv6 版本。
规则顺序与插入到开头
ufw 从上到下检查规则,第一条匹配的规则生效。因此,针对某个地址的拒绝规则必须位于通用允许规则之前:
$ sudo ufw insert 1 deny from 198.51.100.23
Rule inserted
如果用普通命令添加 deny,它会排在 allow 443/tcp 之后,因而对该端口不起作用。
用 ufw limit 防范 SSH 暴力破解
limit 规则允许连接,但会封锁在 30 秒内打开 6 个或更多连接的地址:
$ sudo ufw limit OpenSSH
Rule updated
这是防范大规模密码暴力破解的简单措施。要获得更可靠的保护,请改为仅允许密钥登录,并可以考虑使用 fail2ban,它能通过 ufw 封禁地址。
ufw 日志
使用 sudo ufw logging on 开启日志;级别有 low、medium、high 和 full。记录会写入 /var/log/ufw.log(通过 rsyslog),同时也进入内核日志:
$ sudo journalctl -k | grep 'UFW BLOCK' | tail -2
Oct 14 10:12:03 web kernel: [UFW BLOCK] IN=eth0 OUT= SRC=45.155.205.1 DST=192.0.2.10 PROTO=TCP SPT=51544 DPT=3389
Oct 14 10:12:09 web kernel: [UFW BLOCK] IN=eth0 OUT= SRC=89.248.165.7 DST=192.0.2.10 PROTO=TCP SPT=40312 DPT=23
SRC 和 DPT 字段显示谁、试图访问哪个端口。如果某个服务无法访问,而日志中有该端口的 UFW BLOCK 记录,说明还没有添加对应的规则。
ufw 与 Docker:为什么端口会在规则之外开放
这是最有名的陷阱。Docker 在发布端口(-p 8080:80)时会自行添加 NAT 规则和 DOCKER 链,容器的流量经过 forward 转发,绕过了 ufw 的入站规则。结果是 ufw status 中看不到 8080 端口,但从外部却可以访问它。
解决方案:
- 只在 localhost 上发布端口:
-p 127.0.0.1:8080:80,对外通过反向代理(nginx)提供服务。 - 在
DOCKER-USER链中添加自己的规则——Docker 不会覆盖这条链。 - 对内部服务的容器不发布端口,而是通过 Docker 网络相互连接。
重置与禁用
$ sudo ufw disable # 关闭,规则保留
$ sudo ufw reset # 删除所有规则并关闭
$ sudo ufw reload # 重新加载配置
执行 reset 前,ufw 会在 /etc/ufw/ 中保存带日期后缀的规则文件备份。规则本身保存在 /etc/ufw/user.rules 和 user6.rules 中——不要手动编辑它们,只通过命令修改。
nftables 基础
nftables 是 Linux 内核中 iptables 的现代替代者。在 Debian 10+ 和 Ubuntu 20.10+ 上,iptables 命令默认通过兼容层 iptables-nft 工作,因此 ufw 的规则最终也是以 nftables 形式存在。查看内核中加载的全部规则:
$ sudo nft list ruleset
nftables 的结构:表(协议族 ip、ip6,或同时适用于两者的 inet)包含链,链挂载到 netfilter 的挂载点上,链中包含规则。下面是保存在 /etc/nftables.conf 中的服务器最小防火墙配置:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
ct state invalid drop
iif "lo" accept
ip protocol icmp accept
meta l4proto ipv6-icmp accept
tcp dport { 22, 80, 443 } accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
应用配置并设置开机自启:
$ sudo nft -c -f /etc/nftables.conf # 仅检查语法
$ sudo systemctl enable --now nftables
规则也可以即时添加,但如果不写入文件,重启后会丢失:
$ sudo nft add rule inet filter input tcp dport 5432 ip saddr 10.0.0.5 accept
$ sudo nft -a list chain inet filter input # -a 显示规则的 handle
$ sudo nft delete rule inet filter input handle 9
nftables 的主要优势是集合({ 22, 80, 443 }),以及同时适用于 IPv4 和 IPv6 的 inet 表。不要同时运行你自己的 nftables.service 和 ufw:flush ruleset 会清除 ufw 的规则。
常见错误
- 启用了 ufw,却没有 SSH 规则——访问丢失。可以通过服务商的控制台(VNC/串口)挽救:执行
sudo ufw allow OpenSSH或sudo ufw disable。 - 端口已开放,但服务仍无法访问——服务监听的是
127.0.0.1,而不是外部地址。用ss -tlnp检查。 - 还有云防火墙——许多服务商提供安全组,需要在那里也开放端口。
- deny 不起作用——拒绝规则位于允许规则之后;使用
insert 1。
总结
ufw 的基本配置是:default deny incoming,允许 SSH 和服务端口,执行 ufw enable,然后用 status numbered 检查。精细的访问控制使用带 from 和 port 的规则,SSH 使用 limit;使用 Docker 时要记住它会绕过 ufw。需要 NAT 或复杂的地址集合时,请改用 nftables。服务器防火墙的实际配置会在 Linux 课程中详细讲解。
常见问题
ufw 与 iptables 有什么区别?
ufw 是语法简单的上层封装,它会自动生成 iptables 或 nftables 规则。iptables 和 nftables 是底层工具,控制能力更强,但需要更多的知识。
重启之后 ufw 规则还会保留吗?
会。执行 sudo ufw enable 后,防火墙会在开机时启动,规则保存在 /etc/ufw/user.rules 和 user6.rules 中。
出站连接需要开放端口吗?
在 allow outgoing 策略下不需要。ufw 通过连接状态跟踪自动放行出站连接的回复。
ufw 启用后如何允许 ping?
ufw 默认已经允许入站 ICMP echo 请求,这条规则位于 /etc/ufw/before.rules 中。要禁止 ping,请把该文件中的规则从 ACCEPT 改为 DROP,然后执行 sudo ufw reload。
CentOS 和 Rocky Linux 上有 ufw 吗?
这些系统的标准工具是 firewalld,对应命令为 firewall-cmd。ufw 可以从 EPEL 安装,但不建议与 firewalld 混用。