欢迎光临
我们一直在努力

MariaDB 传输加密实战:从自建 CA 到 JDBC `VERIFY_CA`

文章目录

  • 引言
  • 改造目标与边界
  • 为什么不是直接 `useSSL=true`
  • 第一步:生成 CA 与 MariaDB 服务端证书
  • 第二步:配置 MariaDB TLS,但先不强制
  • 第三步:验证 MariaDB 端口 TLS 能力
  • 第四步:为 Java 应用创建 truststore
  • 第五步:修改 JDBC URL 为 `VERIFY_CA`
  • 第六步:验证 JDBC 确实走了 TLS
  • 第七步:确认所有客户端完成迁移
  • 第八步:最后启用强制加密
  • 回滚策略
  • 什么时候升级到 `VERIFY_IDENTITY`
  • 结论

在这里插入图片描述

引言

数据库传输链路加密的核心目标很明确:让应用与 MariaDB 之间的账号、SQL、结果集和业务数据不再以明文形式在网络中传输。对内网系统来说,这一步经常被低估,但在合规审计、等保整改、零信任网络和容器化部署逐渐普及的背景下,数据库连接启用 TLS 已经不再是“增强项”,而是基础安全能力。

本文以 MariaDB 10.5 系列为例,完整拆解一套稳妥的 TLS 改造路径:自建 CA、签发服务端证书、配置 MariaDB TLS、让 Java 应用通过 JDBC sslMode=VERIFY_CA 校验证书链,最后再启用 require-secure-transport=ON 拒绝明文连接。

改造目标与边界

这套方案解决的是“传输加密”和“服务端证书链可信”两个问题:

  • MariaDB 服务端启用 TLS。
  • 应用连接数据库时必须使用加密通道。
  • 应用通过指定 CA 校验 MariaDB 服务端证书是否可信。
  • 暂不启用客户端证书认证。
  • 暂不强制校验数据库主机名或 IP 与证书 SAN 是否一致。

换句话说,客户端不需要向数据库提交自己的证书,数据库也不依赖双向 TLS。客户端只需要信任内部 CA,并确认服务端证书是由该 CA 签发的。

典型环境如下:

MariaDB 版本:10.5.29
主配置文件:/etc/my.cnf
额外配置目录:/etc/my.cnf.d
TLS 配置文件:/etc/my.cnf.d/mariadb-tls.cnf
证书目录:/etc/mysql/tls

建议目录结构:

/etc/mysql/tls
├── ca
│ ├── ca.pem
│ └── ca-key.pem
├── server
│ ├── server-cert.pem
│ ├── server-key.pem
│ └── server.csr
└── backups

为什么不是直接 useSSL=true

很多 Java 项目里还能看到这样的 JDBC URL:

jdbc:mysql://127.0.0.1:3306/framework?useSSL=true

useSSL=true 的问题在于语义不够清晰。它通常表示“尝试使用 SSL/TLS”,但是否严格校验证书链、是否校验主机名、使用哪个 truststore,往往依赖驱动版本和额外参数。

在 MySQL Connector/J 8.x 中,更推荐使用 sslMode 明确表达安全等级:

sslMode=DISABLED
sslMode=PREFERRED
sslMode=REQUIRED
sslMode=VERIFY_CA
sslMode=VERIFY_IDENTITY

其中:

  • REQUIRED:要求使用 TLS,但不校验证书链。
  • VERIFY_CA:要求使用 TLS,并校验证书是否由可信 CA 签发。
  • VERIFY_IDENTITY:在 VERIFY_CA 基础上继续校验主机名或 IP。

如果当前服务端证书只有 CN=node44,但 JDBC URL 使用的是 127.0.0.1,并且证书没有 Subject Alternative Name,直接切到 VERIFY_IDENTITY 很可能失败。现代 TLS 实践中,SAN 已经是主机名校验的关键字段,不能再依赖 CN 解决所有场景。

因此,生产迁移可以先采用 VERIFY_CA:先把链路从明文升级为加密并校验证书链,再规划证书 SAN 和连接地址规范化,最后升级到 VERIFY_IDENTITY。

第一步:生成 CA 与 MariaDB 服务端证书

内部系统可以使用自建 CA。这样做的好处是签发可控、轮换可控、后续扩展到更多数据库实例也比较简单。

生成 CA 与服务端证书的核心逻辑如下:

openssl req -x509 -newkey rsa:4096 -nodes \\
-days 3650 \\
-keyout /etc/mysql/tls/ca/ca-key.pem \\
-out /etc/mysql/tls/ca/ca.pem \\
-config /etc/mysql/tls/ca/ca-openssl.cnf

服务端证书建议使用 4096 位 RSA,并添加服务端用途:

[v3_req]
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

签发服务端证书:

openssl genrsa -out /etc/mysql/tls/server/server-key.pem 4096

openssl req -new \\
-key /etc/mysql/tls/server/server-key.pem \\
-out /etc/mysql/tls/server/server.csr \\
-config /etc/mysql/tls/server/server-openssl.cnf

openssl x509 -req \\
-in /etc/mysql/tls/server/server.csr \\
-CA /etc/mysql/tls/ca/ca.pem \\
-CAkey /etc/mysql/tls/ca/ca-key.pem \\
-CAcreateserial \\
-out /etc/mysql/tls/server/server-cert.pem \\
-days 825 \\
-sha256 \\
-extfile /etc/mysql/tls/server/server-openssl.cnf \\
-extensions v3_req

生成后立即检查证书:

openssl x509 \\
-in /etc/mysql/tls/server/server-cert.pem \\
-noout -subject -issuer -dates

示例结果:

subject=CN = node44
issuer=CN = MariaDB Internal CA
notBefore=Jun 4 03:07:57 2026 GMT
notAfter=Sep 6 03:07:57 2028 GMT

私钥权限必须收紧:

chmod 600 /etc/mysql/tls/ca/ca-key.pem
chmod 640 /etc/mysql/tls/server/server-key.pem
chown -R root:mysql /etc/mysql/tls
chmod 750 /etc/mysql/tls /etc/mysql/tls/ca /etc/mysql/tls/server

MariaDB 进程需要能读取服务端私钥,但普通用户不应读取 CA 私钥和服务端私钥。

第二步:配置 MariaDB TLS,但先不强制

TLS 配置建议单独放在一个文件里,便于启用、审计和回滚:

[mariadb-10.5]
ssl-ca=/etc/mysql/tls/ca/ca.pem
ssl-cert=/etc/mysql/tls/server/server-cert.pem
ssl-key=/etc/mysql/tls/server/server-key.pem
tls-version=TLSv1.2,TLSv1.3

文件路径:

/etc/my.cnf.d/mariadb-tls.cnf

这里先不要写:

require-secure-transport=ON

原因很简单:一旦立刻强制加密,所有没有完成 TLS 改造的应用、定时任务、报表脚本、同步程序都会连接失败。正确顺序是“先支持 TLS,再逐个切客户端,最后强制”。

重启 MariaDB 后,检查启动参数是否已经加载:

mariadbd –print-defaults \\
| tr ' ' '\\n' \\
| rg 'ssl-ca|ssl-cert|ssl-key|tls-version'

应看到类似输出:

–ssl-ca=/etc/mysql/tls/ca/ca.pem
–ssl-cert=/etc/mysql/tls/server/server-cert.pem
–ssl-key=/etc/mysql/tls/server/server-key.pem
–tls-version=TLSv1.2,TLSv1.3

第三步:验证 MariaDB 端口 TLS 能力

可以先用 OpenSSL 验证 MySQL STARTTLS 握手:

openssl s_client \\
-starttls mysql \\
-connect 127.0.0.1:3306 \\
-servername node44 \\
-CAfile /etc/mysql/tls/ca/ca.pem \\
-verify_return_error </dev/null

关键结果应包含:

Verification: OK
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Verify return code: 0 (ok)

再用 MySQL/MariaDB 客户端验证会话加密状态:

mysql -h 127.0.0.1 -P 3306 -u root -p \\
–ssl-ca=/etc/mysql/tls/ca/ca.pem \\
-e "SHOW STATUS LIKE 'Ssl_cipher'; SHOW STATUS LIKE 'Ssl_version';"

如果返回非空 cipher,说明当前会话已加密:

Ssl_cipher TLS_AES_256_GCM_SHA384
Ssl_version TLSv1.3

还可以查看全局 TLS 状态:

SHOW VARIABLES LIKE 'have_ssl';
SHOW VARIABLES LIKE 'tls_version';
SHOW VARIABLES LIKE 'require_secure_transport';
SHOW GLOBAL STATUS LIKE 'Ssl_accepts';
SHOW GLOBAL STATUS LIKE 'Ssl_finished_accepts';

其中 require_secure_transport 在强制前可以是 OFF,这符合分阶段迁移策略。

第四步:为 Java 应用创建 truststore

Java 应用不能只拿 PEM 文件就结束。对于 MySQL Connector/J 8.0.28 这类驱动,稳妥做法是把内部 CA 导入 Java truststore。

示例路径:

/xxx/jks/mariadb-ca-truststore.jks

导入命令:

keytool -importcert \\
-noprompt \\
-alias mariadb-internal-ca \\
-file /etc/mysql/tls/ca/ca.pem \\
-keystore /xxx/jks/mariadb-ca-truststore.jks \\
-storetype JKS \\
-storepass changeit

核查 truststore:

keytool -list \\
-keystore /xxx/jks/mariadb-ca-truststore.jks \\
-storepass changeit \\
-storetype JKS

应看到一个 trustedCertEntry:

mariadb-internal-ca, Jun 4, 2026, trustedCertEntry

生产环境中,changeit 只是示例密码。实际项目应使用受控密码,并避免把敏感配置散落在多个不可审计的位置。

第五步:修改 JDBC URL 为 VERIFY_CA

原连接:

jdbc:mysql://127.0.0.1:3306/artisa?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8

改为:

jdbc:mysql://127.0.0.1:3306/artia?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8&sslMode=VERIFY_CA&trustCertificateKeyStoreUrl=file:/xxx/jks/mariadb-ca-truststore.jks&trustCertificateKeyStoreType=JKS&trustCertificateKeyStorePassword=changeit

关键参数是:

sslMode=VERIFY_CA
trustCertificateKeyStoreUrl=file:/xxx/jks/mariadb-ca-truststore.jks
trustCertificateKeyStoreType=JKS
trustCertificateKeyStorePassword=changeit

这组参数的效果是:

  • JDBC 必须使用 TLS。
  • 服务端证书必须能被指定 truststore 中的 CA 验证通过。
  • 不校验 JDBC URL 中的主机名或 IP 是否匹配证书 SAN。

修改配置前一定要备份 ,修改后重启应用

第六步:验证 JDBC 确实走了 TLS

验证不能只看“应用启动成功”。更可靠的方式是构造证据链。

第一,确认应用是在配置修改后启动的:

stat -c '%y %n' \\
/xxx/config/application-prod-mybatisplus.yaml \\
/xxx/jks/mariadb-ca-truststore.jks

ps -o pid,lstart,cmd -p <pid>

第二,确认应用进程持有到 MariaDB 3306 的连接:

lsof -Pan -p <pid> -iTCP:3306 -sTCP:ESTABLISHED

第三,在 MariaDB 中用连接端口反查会话:

SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE
FROM information_schema.PROCESSLIST
WHERE HOST IN (
'localhost:42344',
'localhost:36994'
)
ORDER BY HOST;

第四,用同一 JDBC 驱动测试 VERIFY_CA 行为:

  • 不配置 truststore 时,连接应失败,典型错误包含 Path does not chain with any of the trust anchors。
  • 配置 truststore 后,查询 Ssl_cipher 应返回非空值。

示例结果:

Ssl_cipher=TLS_AES_256_GCM_SHA384

如果 MariaDB 的 performance_schema 关闭,可能无法直接按每条连接查询 cipher。这时可以使用组合证据:应用进程启动时间、JDBC URL、进程连接、PROCESSLIST 会话、Connector/J 独立验证、MariaDB 全局 TLS 计数共同证明链路状态。

如审计要求必须展示每条应用连接的 TLS cipher,需要规划启用 performance_schema,这通常涉及数据库配置变更和重启窗口。

第七步:确认所有客户端完成迁移

强制加密前,必须把所有数据库连接点列出来:

  • Web 应用
  • 后台服务
  • 定时任务
  • 运维脚本
  • 监控程序
  • 报表程序
  • 数据同步任务
  • 临时巡检工具

每个连接点至少验证三件事:

  • 连接配置已经启用 TLS。
  • 客户端能校验 CA。
  • 实际会话的 Ssl_cipher 非空,或者有等价证据证明连接已加密。
  • 这是整个改造中最容易漏的环节。数据库本身配置 TLS 并不代表业务系统都已经使用 TLS。

    第八步:最后启用强制加密

    所有客户端完成验证后,再开启:

    require-secure-transport=ON

    可以追加到 /etc/my.cnf.d/mariadb-tls.cnf:

    printf '\\nrequire-secure-transport=ON\\n' >> /etc/my.cnf.d/mariadb-tls.cnf

    或者把已有配置替换为 ON:

    sed -i 's/^require-secure-transport=.*/require-secure-transport=ON/' \\
    /etc/my.cnf.d/mariadb-tls.cnf

    重启 MariaDB 后检查:

    SHOW VARIABLES LIKE 'require_secure_transport';

    结果应为:

    require_secure_transport ON

    从这一刻开始,MariaDB 会拒绝不安全传输。没有完成 TLS 改造的客户端将无法连接。

    回滚策略

    如果数据库启用 TLS 后无法启动,优先检查:

    • ssl-ca、ssl-cert、ssl-key 路径是否正确。
    • MariaDB 进程是否有权限读取私钥。
    • 服务端证书和私钥是否匹配。
    • 配置文件 section 是否适配当前 MariaDB 版本。

    如果应用连接失败,优先检查:

    • truststore 路径是否正确。
    • truststore 密码是否正确。
    • CA 是否导入成功。
    • JDBC URL 是否仍残留冲突参数。
    • 驱动版本是否支持当前 sslMode 参数。

    恢复应用配置:

    如果启用 require-secure-transport=ON 后发现遗留明文客户端,应先临时关闭强制加密,定位遗漏连接点,修复后再重新开启。

    什么时候升级到 VERIFY_IDENTITY

    VERIFY_CA 能证明“服务端证书由可信 CA 签发”,但不能证明“当前连接的主机名或 IP 正是证书声明的身份”。

    更严格的目标是:

    sslMode=VERIFY_IDENTITY

    切换前需要重新签发 MariaDB 服务端证书,并加入 SAN:

    DNS:node44
    IP Address:127.0.0.1

    同时,JDBC URL 中使用的主机名或 IP 必须与 SAN 一致。比如连接写的是 127.0.0.1,证书就必须包含 IP Address:127.0.0.1;连接写的是 node44,证书就必须包含 DNS:node44。

    这一步不建议和第一次 TLS 改造混在一起做。更稳妥的节奏是:

  • 先启用 TLS。
  • 使用 VERIFY_CA 完成证书链校验。
  • 清理和统一数据库连接地址。
  • 重新签发包含 SAN 的证书。
  • 切换到 VERIFY_IDENTITY。
  • 结论

    MariaDB TLS 改造最忌讳“一步到位式强切”。可靠路径应该是分阶段推进:先让数据库具备 TLS 能力,再让应用逐个切换到加密连接并校验证书链,确认所有连接点都完成迁移后,最后开启 require-secure-transport=ON。

    对于 Java 应用,useSSL=true 不足以表达清晰的安全意图。使用 MySQL Connector/J 的 sslMode=VERIFY_CA,配合专用 truststore,可以把“必须加密”和“必须信任指定 CA”落到可验证的配置上。

    真正成熟的数据库传输加密,不只是打开一个开关,而是证书、权限、客户端配置、连接池、验证证据和回滚策略共同闭环。这样做出来的 TLS 改造,既能经得住生产流量,也能经得住安全审计。

    在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » MariaDB 传输加密实战:从自建 CA 到 JDBC `VERIFY_CA`
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址