安全组、防火墙与端口规划
服务器被扫、数据库被拖,入门级事故里绝大多数都源于“端口开太多”。云上其实有两道门:云平台的安全组在实例外面,主机防火墙在系统里面。只配一层也能用,但两层配合才是稳的。本章先把两道门的分工讲清,再给出一张可直接照抄的端口规划表。
安全组与主机防火墙的分工
| 维度 | 云安全组 | 主机防火墙(ufw/firewalld) |
|---|---|---|
| 执行位置 | 云平台网络层,在实例之外 | 操作系统内核 netfilter |
| 生效时机 | 实例没起来、系统没启动也生效 | 系统起来后才生效 |
| 规则粒度 | 按安全组绑定实例,支持引用其他安全组 | 按端口、来源 IP、网卡、服务名 |
| 典型用途 | 挡住整个公网的扫描,只放行必要端口 | 精细控制本机访问,配合应用调试 |
| 常见问题 | 改了安全组忘记保存,规则未生效 | ufw enable 前没放行 22,把自己关在门外 |
做法:安全组做粗粒度准入(只放 22/80/443),主机防火墙做细粒度补充(例如只允许公司出口 IP 连 22)。两层都配置,并且明确知道“哪一条规则在拦我”。
端口规划表
| 端口 | 服务 | 建议监听地址 | 安全组 | 主机防火墙 | 说明 |
|---|---|---|---|---|---|
| 22 | SSH | 0.0.0.0 | 仅限公司/家庭出口 IP | 放行同源 | 有条件就改端口 + 密钥登录 |
| 80 | HTTP | 0.0.0.0 | 对 0.0.0.0/0 放行(备案后) | 放行 | 用于跳转 443 与证书校验 |
| 443 | HTTPS | 0.0.0.0 | 对 0.0.0.0/0 放行 | 放行 | 对外唯一正式入口 |
| 8000 | 应用(Gunicorn/uWSGI) | 127.0.0.1 | 不放行 | 不放行 | 只给本机 Nginx 访问 |
| 3000 | Node 应用 | 127.0.0.1 | 不放行 | 不放行 | 同上 |
| 8080 | Java 应用 | 127.0.0.1 | 不放行 | 不放行 | 同上 |
| 3306 | MySQL | 127.0.0.1 | 绝不开放 | 绝不开放 | 需要远程连就走跳板机或隧道 |
| 6379 | Redis | 127.0.0.1 | 绝不开放 | 绝不开放 | 未授权访问是最常见的数据泄露入口 |
| 27017 | MongoDB | 127.0.0.1 | 绝不开放 | 绝不开放 | 历史泄露重灾区 |
| 9090 | 监控指标 | 127.0.0.1 | 仅内网/VPC | 仅内网 | 需要外部访问就走 Nginx + 鉴权 |
只暴露必要端口原则
- 默认拒绝:入方向默认 deny,只显式放行需要的端口。
- 应用一律监听
127.0.0.1,公网入口只留 Nginx 的 80/443。 - 管理类端口(SSH、面板、数据库、Redis)用来源 IP 白名单,不用
0.0.0.0/0。 - 临时调试端口用完立即删除规则,别放在那里“下次方便”。
- 出方向一般可放开,但要关注被入侵后的外联行为,必要时对出方向也做限制。
配置 ufw(Ubuntu / Debian)
# 安装并设定默认策略:入站拒绝、出站允许
sudo apt -y install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 先放行 SSH,再启用,否则会把自己关在门外
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 更严格:SSH 只允许固定出口 IP 访问
sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
sudo ufw enable
sudo ufw status verbose
sudo systemctl enable ufw
配置 firewalld(Alibaba Cloud Linux / CentOS / RHEL)
sudo dnf -y install firewalld
sudo systemctl enable --now firewalld
# 按服务名放行(更语义化,端口变化时不用改规则)
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# 按端口放行
sudo firewall-cmd --permanent --add-port=8443/tcp
# 富规则:只允许指定网段访问 22
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" port port="22" protocol="tcp" accept'
# 让规则生效并查看全部配置
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
注意 --permanent:不加它只改运行时规则,重启即失效;加了它必须 --reload 才生效,两者容易记混,加 --permanent 再 --reload 是固定套路。
关键参数逐条解释
0.0.0.0/0:表示任意来源 IPv4 地址,即“对全世界开放”,只在 80/443 上使用。- 授权对象 CIDR:
203.0.113.0/24表示该网段全部地址;单机管理建议写/32。 - 安全组优先级:多数云厂商的规则是“拒绝优先”或按序号匹配,放行与拒绝混用时务必看厂商文档的匹配顺序。
ufw deny与未放行的区别:未放行即默认拒绝;deny用于显式拒绝一个默认允许的端口或来源。firewalld的 zone:默认public区域,--zone=不写就是默认区域;多网卡机器要确认规则加在了正确区域上。
数据库端口绝不能对公网开放
一个公开的 3306 端口,从被扫到被爆破往往只需要几十分钟。三种正确的远程访问方式:
# 方式一:SSH 本地端口转发,把远程 3306 映射到本地 13306
ssh -N -L 13306:127.0.0.1:3306 deploy@47.98.x.x
# 之后本地用 127.0.0.1:13306 连接,公网从未暴露数据库
# 方式二:仅允许同安全组/VPC 内的应用机器访问 3306
# 安全组来源写另一台机器的内网 IP 或安全组 ID,而不是 0.0.0.0/0
# 方式三:使用云数据库 RDS,走内网地址 + 白名单
验证方法
# 本机监听情况:必须看到 127.0.0.1 而不是 0.0.0.0
sudo ss -lntp
# ufw 规则是否生效
sudo ufw status verbose
# firewalld 生效后的规则集合
sudo firewall-cmd --list-all
# 从外部机器测试端口是否真的可达(在自己电脑上执行)
nc -vz 47.98.x.x 443
nc -vz 47.98.x.x 3306 # 期望:超时或 refused
ss -lntp 里 3306/6379 显示为 127.0.0.1:3306,且外部 nc 连不上,端口这一层才算合格。
常见坑
| 坑 | 现象 | 做法 |
|---|---|---|
| 只在安全组放行 80/443,没配主机防火墙 | 能通,但少了内层防线 | 两层都配,或明确只用安全组并记录在案 |
ufw enable 前没放行 22 | 立刻就断连,只能走 VNC 救援 | 先 ufw allow 22/tcp 再 enable |
应用监听 0.0.0.0:3306 | 即使防火墙拦着也随时可能被绕过 | 应用层直接绑 127.0.0.1 |
Docker -p 3306:3306 | 绕过 ufw 直接暴露(Docker 自己写 iptables) | 用 -p 127.0.0.1:3306:3306 或走自定义网络 |
| 调试端口忘记关 | 成了后门 | 用完立即删规则,并定期复核 |
firewall-cmd 忘了 --permanent | 重启后规则消失 | --permanent + --reload 成对使用 |
小结:安全组管外面、主机防火墙管里面,两层都要配;端口按“对外只开 80/443,应用与数据库只听 127.0.0.1”来规划;管理端口用来源 IP 白名单;Docker 的 -p 会绕过 ufw,必须显式绑 127.0.0.1;每次改完规则都用 ss -lntp 加外部 nc 双向验证一次。