这是「把自己部署到互联网上」系列的第 14 篇。
上一篇讲了防火墙和安全组的区别。安全组控制云平台入口流量,防火墙控制服务器系统层面的访问。理解这两层之后,端口就不能再凭感觉乱放行。
这一篇继续讲端口,重点是一个很容易踩的坑:为什么不要随便开放数据库端口?
部署个人博客时,必须要有数据库。可能是 MySQL,也可能是 PostgreSQL、Redis,或者项目自带的数据库服务。当本地工具连不上数据库时,很多人的第一反应是:是不是端口没开?于是去安全组里放行 3306、5432、6379,再改防火墙,甚至把数据库监听地址改成 0.0.0.0。工具能连上了,看起来问题解决了,但风险也被一起打开了(我承认,我以前是这么干的)。
端口有什么区别
我一开始也觉得数据库端口只是普通端口。既然 80、443 可以开放,SSH 端口也可以开放,那数据库端口为什么不行?问题在于,它们不是一类入口。
80 和 443 是给用户访问网站用的。数据库端口通常是给应用内部访问的。网站入口暴露公网是预期行为;数据库入口暴露公网,大多数场景下是风险行为。
数据库里存的不是页面,而是数据。对个人博客来说,真正重要的不是服务器本身,也不是代码本身。服务器可以重装,代码可以从仓库重新拉。真正需要保护的是文章内容、管理员账号、配置状态、评论数据、上传记录,以及项目运行过程中产生的数据。
数据库端口一旦暴露,攻击者盯上的就是这些内容。如果数据库密码弱,账号权限过大,版本存在漏洞,或者为了方便远程连接把访问来源放得很宽,风险会明显增加。更麻烦的是,数据库被访问后,页面不一定马上异常,服务也可能继续运行,但数据可能已经被读取、修改、删除,甚至被勒索。
会有什么风险
- 被扫描。公网 IP 上的常见数据库端口会被自动化脚本持续扫描。3306、5432、6379 都是高频目标。只要开放,就会有被人发现的概率。
- 被爆破。如果数据库允许密码登录,而密码不够强,就可能被暴力尝试。开发过程中,为了方便管理设置简单密码。
- 配置过宽。有些人为了让本地工具能连,会把监听地址改成
0.0.0.0,再把用户来源放成%。这意味着数据库不只是对本机IP开放,而是对所有IP开放。 - 权限过大。如果远程连接数据库使用的是 root 或超级管理员账号,一旦泄露,没问题还好,一旦有问题,就准备承担后果(小则提桶跑路,大则...)。
什么情况应该开
判断一个端口该不该开放,先问一个问题:这个服务应该被谁访问?
在博客里的数据库,应该被博客应用访问。外部用户访问博客时,应该经过浏览器、Nginx、后端服务,再由后端服务访问数据库。合理链路是:
用户 -> Nginx -> 博客服务 -> 数据库
而不是:
任意公网来源 -> 数据库
博客应用和数据库在同一台服务器上,可以通过本地地址、Docker 网络或内网访问。数据库也端口没有必要直接暴露给公网。
Docker 场景下还要特别注意端口映射。在 Docker Compose 里,如果写:
ports:
- "3306:3306"
通常表示把容器里的 3306 映射到宿主机的 3306。如果宿主机防火墙和云安全组也放行了这个端口,数据库就可以被公网访问。
但多数单机部署场景下,应用容器和数据库容器在同一个 Docker 网络里,容器可以直接通过服务名访问数据库,比如 mysql:3306。这种情况下,数据库不需要映射到宿主机,更不需要暴露公网。真正需要对公网开放的,通常是统一入口,比如 Nginx 的 80 和 443。
不得不开的情况
如果本地工具确实需要连接数据库,也不建议直接全网开放端口。更稳的方式是 SSH 隧道。数据库端口仍然只在服务器内部可访问,本地工具通过 SSH 管理入口转发进去。
编辑 docker-compose.yml 文件
# 新增 MySQL 容器端口映射,再使用ssh隧道链接
db:
image: mysql:8.4
restart: always
container_name: blog-db
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
env_file:
- .env
ports:
- "127.0.0.1:3306:3306"
volumes:
- /data/docker/blog/mysql:/var/lib/mysql
# 重新编译容器
docker compose up -d --force-recreate db
Navicat 工具连接
编辑完编排文件,重新编译容器,使用 navicat 工具连接
新建连接类型:MySQL 主机:127.0.0.1 端口:3306 用户名: 密码: 数据库:
-
使用
ssh链接,Navicat 先 SSH 登录服务器,然后从服务器本机访问 MySQL 容器映射出来的端口。
-
输入主机ip、端口、用户名、密码连接,再点击
SSH选项卡,输入服务器的ip、用户名、密码
测试账户、密码等信息没错,就可以连接成功
所以更改后的链路就变成了这样
Navicat -> SSH 到服务器 -> 服务器 127.0.0.1:3306 -> MySQL 容器 3306
端口开放小结
- 数据库默认不开放公网;
- 安全组不对全网放行 3306、5432、6379;
- Compose 里不要随手给数据库写
ports; - 应用和数据库优先走内部网络;
- 本地管理优先用 SSH 隧道;
- 临时开放必须限制来源 IP,用完关闭;
- 不要用数据库 root 账号远程连接;
- 定期检查监听端口和安全组规则。
数据库只服务内部应用,本地管理通过 SSH 隧道或临时受限规则完成,才是更稳健的做法。
下一篇,我会继续写服务器安全:我的服务器一天被扫了 500 多次。看到日志里的陌生 IP 和登录尝试后,就会更清楚公网服务器到底处在什么环境里。
本文由 楸木 原创,转载请注明出处。