环境: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 类项目一致)
text
┌─────────────────────────────────────────┐
运维 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 现状诊断
bash
docker exec fastmongo mongo -u ... --eval 'printjson(db.adminCommand({getParameter:1,tlsMode:1}))'
# 整改前: "tlsMode" : "disabled"
docker exec fastmongo mongo --ssl --host localhost:27017 --eval "db.version()"
# 报错: stream truncated → 客户端要 TLS,服务端仍是明文
说明:--keyFile 是副本集成员认证,不能代替 TLS。
3.2 证书目录与 mongod.conf
宿主机:
text
/data/project/config/mongo-tls/
ca.pem
server.pem # server.crt + server.key
client-app.pem # 给应用/运维客户端
mongod.conf 片段:
yaml
net:
port: 27017
bindIp: 0.0.0.0
tls:
mode: requireTLS
certificateKeyFile: /data/mongo-tls/server.pem
CAFile: /data/mongo-tls/ca.pem
allowConnectionsWithoutCertificates: false # 强制客户端证书
allowInvalidCertificates: false
db-compose.yml 挂载:
yaml
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 示例:
text
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 下已有(首次初始化自动创建):
text
ca.pem server-cert.pem server-key.pem client-cert.pem client-key.pem
Issuer 为 MySQL_Server_8.2.0_Auto_Generated_CA_Certificate,2035 年前有效,整改演示可直接用。
bash
docker exec mysql mysql -uroot -p -e "SHOW VARIABLES LIKE '%ssl%';"
# have_ssl=YES, ssl_ca=ca.pem, ssl_cert=server-cert.pem
4.2 强制 SSL
/data/project/config/mysql/conf.d/ssl.cnf:
ini
[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 用户与双因素
sql
CREATE USER 'dba_admin'@'%' IDENTIFIED BY '强密码' REQUIRE X509;
-- 管理:证书 + 口令
ALTER USER 'oneapi'@'%' REQUIRE SSL;
-- 应用:仅加密通道
4.4 踩坑:VERIFY_IDENTITY 与 One-API
bash
# 错误:证书 CN 不是 mysql/127.0.0.1
mysql ... --ssl-mode=VERIFY_IDENTITY ...
# SSL connection error: certificate verify failed
# 管理端测试用
--ssl-mode=VERIFY_CA
# 无客户端证 + dba_admin → 1045(预期,说明 X509 生效)
One-API SQL_DSN (容器内连 mysql:3306):
text
# 错误
oneapi:***@tcp(mysql:3306)/one-api?tls=true
# 正确(内网自签)
oneapi:***@tcp(mysql:3306)/one-api?tls=skip-verify&parseTime=true
日志:
text
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
整改前(不合格):
text
local all all trust
host all all 127.0.0.1/32 trust
host all all all scram-sha-256 # 明文 TCP 即可连
整改后(示例):
text
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 reject
fastgpt:业务账号(不是示例里的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:
ini
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-256
5.2 双因素
dba_admin:scram-sha-256 + clientcert=verify-ca + Navicat 填 客户端证书 + 客户端密钥 + 根证书 + 密码。
常见遗漏 :只填了 client-admin.crt,未填 client-admin.key → 锁图标报红。
应用 JDBC:
text
jdbc:postgresql://postgres:5432/your_db?sslmode=require
5.3 安全审计
conf.d/audit.conf:
ini
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'
加强(需重启):
ini
shared_preload_libraries = 'pgaudit'
pgaudit.log = 'ddl, role, write'
pgaudit.log_parameter = on
sql
CREATE EXTENSION 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 + hostssl scram |
| 应用改连接 | MONGODB_URI tls 参数 |
SQL_DSN?tls=skip-verify |
JDBC sslmode=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 |
MySQL certificate verify failed |
VERIFY_IDENTITY 与自动证书 CN 不符 |
改用 VERIFY_CA 或 tls=skip-verify(应用) |
MySQL 1045 无客户端证 |
REQUIRE X509 正常拒绝 |
带 client-cert/key 再连 |
One-API wanted 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 应用层加密存储。
九、验收命令清单(收藏)
bash
# MongoDB
docker exec fastmongo mongo ... --tls --tlsCAFile /data/mongo-tls/ca.pem \
--tlsCertificateKeyFile /data/mongo-tls/client-app.pem \
--eval 'printjson(db.adminCommand({getParameter:1,tlsMode:1}))'
# MySQL
docker exec mysql 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"
# PostgreSQL
docker exec postgres psql "host=127.0.0.1 sslmode=verify-ca user=dba_admin sslrootcert=... sslcert=... sslkey=..." -c "SELECT current_user;"
docker network inspect fastgpt_shared_network --format '{{(index .IPAM.Config 0).Subnet}}'
十、结语
这套加固的核心不是「买企业版」,而是:
- 把 TLS 真正开起来(配置 + 挂卷 + recreate);
- 管理与应用账号分离 ,管理上 证书+口令;
- 应用连接串补上 tls/sslmode ,并处理 Docker 内网主机名与自签证书 的校验问题;
- PostgreSQL 改掉 trust ,补上 审计日志。
按本文逐项落地后,再配合截图与日志样本,一般可满足「传输加密」「双因素鉴别」「审计」类检查项;若检查卡「国密算法」字样,再在入口加国密网关,而不是强求 Navicat 直连 SM2。