FieryWing 的技术博客

Linux 防火墙入门:只开必要的端口

2025-10-18

服务器上每开一个监听端口,就多一个被攻击的入口。防火墙的原则很简单:默认拒绝,按需放行。这篇文章是我给自己的小服务器配置防火墙的笔记,系统是 Debian / Ubuntu,主要用 ufw,也会简单介绍 nftables。如果你的服务器还是新的,建议先看《VPS 初始化》和《SSH 安全》。

防火墙到底在做什么

防火墙按规则决定哪些网络包可以进、可以出。对个人服务器来说,最实用的策略是:入站默认全部拒绝,只放行确实需要对外提供的服务,比如 SSH、HTTP、HTTPS;出站默认放行,否则系统更新、证书申请都会受影响。它不能替代软件更新和强密码,但能显著缩小暴露面。

先看看开了什么端口

sudo ss -ltnp

这条命令列出所有处于监听状态的 TCP 端口以及对应进程。重点看“Local Address”这一列:

如果发现不认识的服务在对外监听,先搞清楚它是什么,再决定是停掉、改成监听本机,还是用防火墙挡住。UDP 端口用 sudo ss -lunp 查看。

用 ufw 快速配置(Debian / Ubuntu)

先确认安装:

sudo apt update
sudo apt install -y ufw

然后设置默认策略并放行必要端口:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

把示例里的 22 换成你实际使用的 SSH 端口。启用前一定确认 SSH 端口已经放行,否则会把自己关在外面。

防止把自己锁在外面

这是新手最容易犯的错,我自己也踩过。几条建议:

  1. 启用前执行 sudo ufw show added,确认规则列表里有你的 SSH 端口。
  2. 保持当前 SSH 会话不要关闭,再另开一个终端测试能否新建连接,确认没问题再关旧会话。
  3. 知道你的服务商是否提供网页版的救援控制台(VNC / 串口),万一锁死还能进去改规则。
  4. 如果改过 SSH 端口,要先放行新端口、确认能连上,再去掉旧端口的规则。
  5. 如果你只从固定 IP 登录,可以限制来源,例如:sudo ufw allow from 203.0.113.50 to any port 22 proto tcp。但 IP 变了就进不去,要权衡。

SSH 端口也可以用 sudo ufw limit 22/tcp,它会对短时间内大量连接的来源做限速,对付暴力扫描有一点帮助。

查看和删除规则

sudo ufw status verbose
sudo ufw status numbered
sudo ufw delete 3

status numbered 会给每条规则编号,delete 后面跟编号即可删除。删除前先确认编号,规则变动后编号会重排,连续删多条时每次都要重新查看。也可以按规则内容删除,例如 sudo ufw delete allow 80/tcp。

IPv6 也别忘了

检查 /etc/default/ufw 里 IPV6=yes,这样 ufw 规则同时作用于 IPv6。如果服务器有 IPv6 地址却没管 IPv6 规则,等于留了后门。修改后执行 sudo ufw reload。

Docker 会绕过 ufw

这是个很重要的坑。Docker 通过自己的 iptables 规则发布端口,流量不经过 ufw 管理的 INPUT 链,所以即使 ufw 没有放行,-p 8080:80 发布的端口仍然可能从公网访问到。最简单的办法是发布端口时绑定到本机:

docker run -d --name web -p 127.0.0.1:8080:80 nginx

再由 Caddy 之类的反向代理对外提供 HTTPS,见《Caddy 入门》和《Docker 入门》。部署后用 sudo ss -ltnp 再核对一遍监听地址,别只看 ufw status。

别忘了云厂商的安全组

很多云服务器在系统防火墙之外,还有一层控制台里的安全组或防火墙规则,两处都要放行,端口才真正可访问。排查“端口不通”时,我习惯按这个顺序:服务有没有在监听、系统防火墙、云安全组、域名解析是否正确(见《DNS 入门》)。

只监听本机

数据库、管理后台等不需要公网访问的服务,应当监听 127.0.0.1,再通过反向代理或隧道访问。比如 MySQL 配置 bind-address = 127.0.0.1,Redis 配置 bind 127.0.0.1。从源头不监听公网,比事后用防火墙挡更稳妥,这是“纵深防御”的思路:多层措施叠加,任何一层出错都不至于直接暴露。

了解一下 nftables

较新的 Debian / Ubuntu 底层使用 nftables,ufw 实际上也是对它的封装。如果你想直接写规则,一份最小的示例如下。注意:在远程服务器上应用前,务必确保 SSH 规则正确,并保留一个已登录的会话。

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;
		ct state established,related accept
		iif lo accept
		ip protocol icmp accept
		ip6 nexthdr icmpv6 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;
	}
}

保存为 /etc/nftables.conf 后,先检查语法再加载:

sudo nft -c -f /etc/nftables.conf
sudo nft -f /etc/nftables.conf
sudo systemctl enable nftables

需要提醒:ufw 和手写 nftables 不要同时管理,会互相冲突;而且上面的 forward 链丢弃策略会影响 Docker 容器网络,装了 Docker 的机器不要照抄 flush ruleset,那会清掉 Docker 创建的规则。我自己的小机器上用 ufw 就够了。

常见问题排查

日志与日常维护

ufw 默认会记录被拦截的连接,可以用 sudo ufw logging low 开启低级别日志,再用 sudo journalctl -k --no-pager | grep UFW | tail 查看最近的拦截记录。公网服务器上会看到不少陌生来源的扫描,这很正常,不必紧张,也不要因此把日志级别开到最高,否则日志会迅速占满磁盘。每次新增服务后,我都会回头检查一遍规则,把不再使用的端口及时关掉;规则越少,越容易看明白。关于磁盘和日志的监控,可以参考《服务器备份与基础监控》。

小结