Linux 防火墙入门:只开必要的端口
2025-10-18
服务器上每开一个监听端口,就多一个被攻击的入口。防火墙的原则很简单:默认拒绝,按需放行。这篇文章是我给自己的小服务器配置防火墙的笔记,系统是 Debian / Ubuntu,主要用 ufw,也会简单介绍 nftables。如果你的服务器还是新的,建议先看《VPS 初始化》和《SSH 安全》。
防火墙到底在做什么
防火墙按规则决定哪些网络包可以进、可以出。对个人服务器来说,最实用的策略是:入站默认全部拒绝,只放行确实需要对外提供的服务,比如 SSH、HTTP、HTTPS;出站默认放行,否则系统更新、证书申请都会受影响。它不能替代软件更新和强密码,但能显著缩小暴露面。
先看看开了什么端口
sudo ss -ltnp
这条命令列出所有处于监听状态的 TCP 端口以及对应进程。重点看“Local Address”这一列:
0.0.0.0:端口或*:端口:监听所有 IPv4 地址,公网可达(如果防火墙放行)。[::]:端口:监听所有 IPv6 地址。127.0.0.1:端口:只监听本机,外部无法直接访问。
如果发现不认识的服务在对外监听,先搞清楚它是什么,再决定是停掉、改成监听本机,还是用防火墙挡住。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 端口已经放行,否则会把自己关在外面。
防止把自己锁在外面
这是新手最容易犯的错,我自己也踩过。几条建议:
- 启用前执行
sudo ufw show added,确认规则列表里有你的 SSH 端口。 - 保持当前 SSH 会话不要关闭,再另开一个终端测试能否新建连接,确认没问题再关旧会话。
- 知道你的服务商是否提供网页版的救援控制台(VNC / 串口),万一锁死还能进去改规则。
- 如果改过 SSH 端口,要先放行新端口、确认能连上,再去掉旧端口的规则。
- 如果你只从固定 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 就够了。
常见问题排查
- 服务启动了但外部访问不了:先
ss -ltnp看是否只监听了 127.0.0.1,再看 ufw 和安全组。 - 启用 ufw 后 SSH 断开:通过服务商控制台登录,执行
sudo ufw allow 22/tcp或sudo ufw disable。 - 规则改了没生效:
sudo ufw reload,并用status verbose确认。 - 想从外部验证端口是否开放:在另一台机器上用
nc -vz 203.0.113.10 443,或用 nmap 扫描自己的服务器(只扫你自己的)。
日志与日常维护
ufw 默认会记录被拦截的连接,可以用 sudo ufw logging low 开启低级别日志,再用 sudo journalctl -k --no-pager | grep UFW | tail 查看最近的拦截记录。公网服务器上会看到不少陌生来源的扫描,这很正常,不必紧张,也不要因此把日志级别开到最高,否则日志会迅速占满磁盘。每次新增服务后,我都会回头检查一遍规则,把不再使用的端口及时关掉;规则越少,越容易看明白。关于磁盘和日志的监控,可以参考《服务器备份与基础监控》。
小结
- 默认拒绝入站,只放行 SSH、80、443 等必要端口。
- 启用前确认 SSH 已放行,另开终端测试,避免锁死。
- Docker 发布的端口会绕过 ufw,务必绑定 127.0.0.1。
- 系统防火墙和云安全组两层都要检查。
- 不需要公网的服务,从一开始就只监听本机。