教程

在 Linux 中配置 ufw:服务器防火墙与 nftables 基础

更新于: · Skillaria.Top

配置 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
来自单个 IPsudo 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 混用。

通过视频学习本主题

课程“Linux: с нуля до уверенного администрирования”结合实践详细讲解了这一主题。

前往课程