Docker 部署 MongoDB / MySQL / PostgreSQL 安全加固实录:TLS 双向认证、双因素鉴别与审计
环境:Linux 宿主机 + Docker Compose、
fastgpt_shared_network(典型网段172.18.0.0/16)
场景:等保 / 行业安全检查中的数据库整改项(传输加密、身份鉴别、审计)
版本:MongoDB 5.0.18、MySQL 8.2.0、PostgreSQL(官方镜像)
一、写在前面:检查项与社区版能力
近期连续做了三套库的加固,检查方常见要求可归纳为:
| 类别 | 典型表述 | 社区版能否做 |
|---|---|---|
| 传输加密 | 启用 TLS/SSL,requireSSL/ 双向证书 | 能(OpenSSL,国际算法) |
| 国密 SM2/SM4 | 传输/存储使用国密算法 | 库本身不原生支持 TLCP;需网关/VPN/应用层 SM4 |
| 双因素鉴别 | 口令 + 证书 / 动态口令 / 堡垒机 | 能(证书+口令;OTP 在堡垒机) |
| 安全审计 | logging_collector、登录与 DDL 等 | 能(PG 推荐 pgAudit) |
结论:不必迷信「SSL 版」「企业版才有 TLS」——官方 Docker 镜像已带 OpenSSL;企业版主要多 TDE、LDAP 等,不等于国密 TLCP。
二、整体架构(与 FastGPT 类项目一致)
┌─────────────────────────────────────────┐ 运维 Navicat │ Docker: fastgpt_shared_network │ (证书+口令) ────► │ fastmongo:27017 (映射宿主机 27018) │ │ mysql:3306 (通常不映射公网) │ │ postgres:5432 (通常不映射公网) │ │ fastapi / One-API / 业务容器 ──► 各库 │ └─────────────────────────────────────────┘ ▲ 堡垒机 SSH + OTP(可选第三层)统一原则:
- 管理账号(
dba_admin/ 带REQUIRE X509或 mTLS):证书 + 强口令。 - 应用账号(
fastgpt/oneapi):仅传输加密 + 口令,不配客户端证书。 - 端口:能内网就不映射;必须映射时防火墙收窄源 IP。
- 口令:不要写在 compose 明文里,用
.env/ secrets,整改后轮换。
三、MongoDB 5.0:TLS 双向认证 + 双因素
3.1 现状诊断
dockerexecfastmongo mongo-u...--eval'printjson(db.adminCommand({getParameter:1,tlsMode:1}))'# 整改前: "tlsMode" : "disabled"dockerexecfastmongo mongo--ssl--hostlocalhost:27017--eval"db.version()"# 报错: stream truncated → 客户端要 TLS,服务端仍是明文说明:--keyFile是副本集成员认证,不能代替 TLS。
3.2 证书目录与mongod.conf
宿主机:
/data/project/config/mongo-tls/ ca.pem server.pem # server.crt + server.key client-app.pem # 给应用/运维客户端mongod.conf片段:
net:port:27017bindIp:0.0.0.0tls:mode:requireTLScertificateKeyFile:/data/mongo-tls/server.pemCAFile:/data/mongo-tls/ca.pemallowConnectionsWithoutCertificates:false# 强制客户端证书allowInvalidCertificates:falsedb-compose.yml挂载:
volumes:-/data/project/config/mongod.conf:/data/mongod.conf:ro-/data/project/config/mongo-tls:/data/mongo-tls:ro关键坑:改 compose 后必须docker compose up -d --force-recreate fastmongo,仅restart不会挂上mongo-tls卷。入口脚本里until mongo ...也要加--tls和证书参数,否则容器永远 Waiting。
3.3 双因素(整改表述)
| 因子 | 实现 |
|---|---|
| 持有 | mTLS 客户端证书 |
| 知晓 | SCRAM 用户口令,authSource=admin |
应用 URI 示例:
mongodb://fastgpt:***@fastmongo:27017/fastgpt?authSource=admin&tls=true&tlsCAFile=...&tlsCertificateKeyFile=...3.4 Navicat
| 项 | 值 |
|---|---|
| CA | ca.pem |
| 客户端密钥 | client-app.pem(或 cert+key) |
| 验证数据库 | admin(必填) |
| 允许无效主机名 | 用 IP 连时建议勾选 |
3.5 国密 SM2 与 Navicat
- 国际 TLS(RSA):Navicat可以直连。
- 端到端 TLCP/SM2:普通 Navicat不能直连;需国密网关/VPN,或内网仍用国际 TLS、国密放在网段入口。
四、MySQL 8.2:自动证书 +require_secure_transport
4.1 自动生成证书
数据目录/data/project/data/mysql下已有(首次初始化自动创建):
ca.pem server-cert.pem server-key.pem client-cert.pem client-key.pemIssuer 为MySQL_Server_8.2.0_Auto_Generated_CA_Certificate,2035 年前有效,整改演示可直接用。
dockerexecmysql mysql-uroot-p-e"SHOW VARIABLES LIKE '%ssl%';"# have_ssl=YES, ssl_ca=ca.pem, ssl_cert=server-cert.pem4.2 强制 SSL
/data/project/config/mysql/conf.d/ssl.cnf:
[mysqld] require_secure_transport=ON ssl_ca=/var/lib/mysql/ca.pem ssl_cert=/var/lib/mysql/server-cert.pem ssl_key=/var/lib/mysql/server-key.pem挂载:./config/mysql/conf.d:/etc/mysql/conf.d:ro,然后force-recreate。
4.3 用户与双因素
CREATEUSER'dba_admin'@'%'IDENTIFIEDBY'强密码'REQUIREX509;-- 管理:证书 + 口令ALTERUSER'oneapi'@'%'REQUIRESSL;-- 应用:仅加密通道4.4 踩坑:VERIFY_IDENTITY与 One-API
# 错误:证书 CN 不是 mysql/127.0.0.1mysql... --ssl-mode=VERIFY_IDENTITY...# SSL connection error: certificate verify failed# 管理端测试用--ssl-mode=VERIFY_CA# 无客户端证 + dba_admin → 1045(预期,说明 X509 生效)One-APISQL_DSN(容器内连mysql:3306):
# 错误 oneapi:***@tcp(mysql:3306)/one-api?tls=true # 正确(内网自签) oneapi:***@tcp(mysql:3306)/one-api?tls=skip-verify&parseTime=true日志:
tls: certificate is not valid for any names, but wanted to match mysql只改应用连接串这一处(grep 仅db-compose.yml的SQL_DSN),MySQL 侧 SSL + 用户权限需已配好。
4.5 Navicat(dba_admin)
- CA:
ca.pem - 客户端证书/私钥:
client-cert.pem、client-key.pem - 不要用
server-cert当客户端证
五、PostgreSQL:pg_hba+ SSL + 审计
5.1 改掉trust和明文host
整改前(不合格):
local all all trust host all all 127.0.0.1/32 trust host all all all scram-sha-256 # 明文 TCP 即可连整改后(示例):
local all postgres peer local all all scram-sha-256 hostssl all fastgpt 172.18.0.0/16 scram-sha-256 hostssl all dba_admin 172.18.0.0/16 scram-sha-256 clientcert=verify-ca hostssl all fastgpt 127.0.0.1/32 scram-sha-256 hostssl all dba_admin 127.0.0.1/32 scram-sha-256 clientcert=verify-ca hostnossl all all all rejectfastgpt:业务账号(不是示例里的app_user)。172.18.0.0/16:以docker network inspect fastgpt_shared_network的Subnet为准,不是 Navicat 里填的10.102.53.251。- 从宿主机连映射端口时,先
SELECT inet_client_addr();,常见为172.18.0.1,需单独加一条hostssl。
postgresql.conf:
ssl = on ssl_cert_file = '/etc/postgresql/ssl/server.crt' ssl_key_file = '/etc/postgresql/ssl/server.key' ssl_ca_file = '/etc/postgresql/ssl/ca.crt' password_encryption = scram-sha-2565.2 双因素
dba_admin:scram-sha-256+clientcert=verify-ca+ Navicat 填客户端证书 + 客户端密钥 + 根证书 + 密码。
常见遗漏:只填了client-admin.crt,未填client-admin.key→ 锁图标报红。
应用 JDBC:
jdbc:postgresql://postgres:5432/your_db?sslmode=require5.3 安全审计
conf.d/audit.conf:
logging_collector = on log_destination = 'csvlog' log_directory = 'log' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' log_rotation_age = 1d log_connections = on log_disconnections = on log_failed_login_attempts = on # PG14+ log_line_prefix = '%m [%p] %u@%d %h %a ' log_statement = 'ddl'加强(需重启):
shared_preload_libraries = 'pgaudit' pgaudit.log = 'ddl, role, write' pgaudit.log_parameter = onCREATEEXTENSION pgaudit;日志目录挂宿主机:/data/project/logs/postgres,保留 ≥6 个月。
六、三套库对照表(写报告用)
| 项目 | MongoDB | MySQL | PostgreSQL |
|---|---|---|---|
| 强制加密传输 | tls.mode: requireTLS | require_secure_transport=ON | ssl=on+hostssl |
| 管理双因素 | mTLS + SCRAM | REQUIRE X509+ 口令 | clientcert=verify-ca+ SCRAM |
| 应用账号 | fastgpt + TLS | oneapi +REQUIRE SSL | fastgpt +hostsslscram |
| 应用改连接 | MONGODB_URItls 参数 | SQL_DSN?tls=skip-verify | JDBCsslmode=require |
| 管理客户端 | Navicat:CA+客户端证+口令 | 同上 + client-key | 同上,key 必填 |
| 主机名校验 | 允许无效主机名 / SAN | skip-verify内网 | verify-ca或 allow invalid hostname |
| 审计 | auditLog(企业)/ 应用日志 | general/audit plugin | logging_collector + pgAudit |
七、高频踩坑汇总
| 现象 | 原因 | 处理 |
|---|---|---|
stream truncated | 服务端未开 TLS,客户端--ssl | 先requireTLS/require_secure_transport |
No such file ... mongo-tls/client-app.pem | compose 未挂卷或未 recreate | force-recreate+ls /data/mongo-tls |
MySQLcertificate verify failed | VERIFY_IDENTITY与自动证书 CN 不符 | 改用VERIFY_CA或tls=skip-verify(应用) |
MySQL1045无客户端证 | REQUIRE X509正常拒绝 | 带 client-cert/key 再连 |
One-APIwanted to match mysql | tls=true校验主机名 | tls=skip-verify |
| PG Navicat 红锁 | 缺client-admin.key | 补私钥,与 crt 成对 |
| PG 连不上 hba | 客户端 IP 不是 10.102.53.251 | inet_client_addr()后改pg_hba |
只restart不 recreate | 卷/环境变量未更新 | docker compose up -d --force-recreate |
八、整改报告可粘贴段落(示例)
已对 MongoDB、MySQL、PostgreSQL 实施传输层加密与身份鉴别加固。三套数据库均启用 TLS/SSL 强制加密(MongoDB
requireTLS、MySQLrequire_secure_transport、PostgreSQLssl+hostssl)。管理用户使用数字证书与强口令双因素鉴别(MongoDB mTLS、MySQLREQUIRE X509、PostgreSQLscram-sha-256 clientcert=verify-ca);应用使用独立账号与加密通道。数据库端口不对公网开放,运维经内网/堡垒机访问。PostgreSQL 已启用logging_collector及 pgAudit(或log_statement=ddl)记录登录、权限变更与 DDL 操作,日志集中存储并定期归档。
国密专项(若检查明确要求 SM2/SM4)可补充:
数据库引擎采用国际标准 TLS 保障传输机密性与完整性;跨网访问通过国密 VPN/SSL 网关实现国密算法保护;敏感字段采用 SM4 应用层加密存储。
九、验收命令清单(收藏)
# MongoDBdockerexecfastmongo mongo...--tls--tlsCAFile/data/mongo-tls/ca.pem\--tlsCertificateKeyFile/data/mongo-tls/client-app.pem\--eval'printjson(db.adminCommand({getParameter:1,tlsMode:1}))'# MySQLdockerexecmysql mysql... --ssl-mode=VERIFY_CA\--ssl-ca=/var/lib/mysql/ca.pem --ssl-cert=/var/lib/mysql/client-cert.pem\--ssl-key=/var/lib/mysql/client-key.pem-e"SELECT 1"# PostgreSQLdockerexecpostgres psql"host=127.0.0.1 sslmode=verify-ca user=dba_admin sslrootcert=... sslcert=... sslkey=..."-c"SELECT current_user;"dockernetwork inspect fastgpt_shared_network--format'{{(index .IPAM.Config 0).Subnet}}'十、结语
这套加固的核心不是「买企业版」,而是:
- 把 TLS 真正开起来(配置 + 挂卷 + recreate);
- 管理与应用账号分离,管理上证书+口令;
- 应用连接串补上 tls/sslmode,并处理Docker 内网主机名与自签证书的校验问题;
- PostgreSQL改掉 trust,补上审计日志。
按本文逐项落地后,再配合截图与日志样本,一般可满足「传输加密」「双因素鉴别」「审计」类检查项;若检查卡「国密算法」字样,再在入口加国密网关,而不是强求 Navicat 直连 SM2。
