Files
杭州明婳科技 08ed43385d fix(config): 模块 01 评审修复 + 对抗复核
config 模块(glm_check_report_module_01_config.md)11 项 finding 修复:

- H-config-1: Set(cfg) 用 mapstructure 重建 m.v,消除 Get/GetString/viper 视图分裂(C1)
- M-config-1: 热重载回调独立 recover,panic 不杀 watcher、不阻断后续回调(C9)
- M-config-2: DB SSL/TLS -- Postgres SSLMode(默认 prefer)+ MySQL TLS/TLSRootCA;
  database.ensureMySQLTLSRegistered 注册私有 CA 命名配置,fail-fast 不回退明文。
  O-1 文档准确性修正:同名注册会覆盖 master,改为 per-host 不同名 + 自建 replica DSN 指引
- M-config-3: App.Shutdown closeResources 停 configManager watcher(C7)
- M-config-4: Clone 切片/map 字段反射守卫测试
- M-config-5: WithConfig/WithConfigPath 全局可见性文档强化
- L-config-2: watchLoop 增 ctx 逃生通道,StopWatcher cancel+Close 双重退出
- L-config-3/4/5/6: Validate 连接池交叉校验、哨兵 errors.New、去冗余 TrimSpace、nil receiver 防御

含契约级回归测试(同源一致性/生命周期闭环/扩展点防御),-race/vet/build 全绿。
附 module 01 评审报告 + 全局结构图 + 扫描收敛协议。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 12:54:52 +08:00

73 lines
3.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package database
import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"strings"
"github.com/EthanCodeCraft/xlgo-core/config"
mysqldriver "github.com/go-sql-driver/mysql"
)
// ensureMySQLTLSRegistered 按 cfg.Database 的 TLS 配置注册命名 TLS 配置到 go-sql-driver/mysqlM-config-2)。
//
// go-sql-driver/mysql v1.7.0 的 tls DSN 参数语义:
// - tls=true:内置安全(&tls.Config{},系统根 CA + ServerName 自动取自 host + 证书校验),无需注册。
// - tls=<name>:引用经 RegisterTLSConfig 注册的命名配置,用于私有 CA/自签证书。
//
// 本函数仅在「TLS=true 且 TLSRootCA 非空」时注册 config.MySQLTLSConfigName 命名配置:
// 加载 TLSRootCA 的 PEM 到 RootCAsServerName 取自 HostMinVersion=TLS1.2。其余情况(TLS 未启用、
// 或启用但用内置 tls=true)直接返回 nil。
//
// 非 MySQL 驱动(如 postgres,用 SSLMode)跳过。失败返回错误,由 initDB fail-fast
// 绝不静默回退明文连接(生产 DB 流量明文是安全风险)。
//
// 注意:RegisterTLSConfig 是驱动级全局状态——一个名字对应唯一 *tls.Config(含唯一 ServerName),
// 同名重复注册会覆盖前者且不报错,故无法用「同名」同时匹配多个不同 host。
//
// 覆盖范围(注册配置的 ServerName 固定取自主库 Host):
// - 单 host 集群:replica DSN 的 host 与主库 Host 相同(同机不同端口,或经同一 LB/主机名暴露)——
// master 与 replica DSN 均用 tls=MySQLTLSConfigNameServerName 一致,握手通过。
// - 多 host replicas + 私有 CAreplica DSN 的 host 与主库 Host 不同时,本注册的 ServerName(主库
// host)与 replica 证书 SAN 不匹配,握手失败。此场景须用户为每个 replica host 注册**不同名**的
// TLS 配置(各自 ServerName=该 replica host)并自行构造对应 replica DSNtls=<该名>);框架
// MySQLDSN() 硬编码 tls=MySQLTLSConfigName,不能用于这些 replica DSN。切勿对 MySQLTLSConfigName
// 同名重复注册——会覆盖主库注册、导致主库握手失败。
//
// replica 路径(InitDBWithReplicas 的 replicaDSNs)由调用方传原始 DSN,不经本函数注册。
// 多集群不同 CA 亦属已知多 App 限制,需用户按上述方式自行注册。
func ensureMySQLTLSRegistered(cfg *config.Config) error {
if cfg == nil {
return nil
}
db := &cfg.Database
if !db.TLS || strings.TrimSpace(db.TLSRootCA) == "" {
return nil
}
// 仅 MySQL 走注册路径;postgres 用 SSLMode,其他驱动自管 TLS。空 driver 默认 MySQL。
drv := normalizeDriver(db.Driver)
if drv != "" && drv != DriverMySQL {
return nil
}
pem, err := os.ReadFile(db.TLSRootCA)
if err != nil {
return fmt.Errorf("读取 MySQL TLS CA 文件失败 %q: %w", db.TLSRootCA, err)
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(pem) {
return fmt.Errorf("解析 MySQL TLS CA 文件失败 %q: 非 PEM 格式或无有效证书", db.TLSRootCA)
}
tlsCfg := &tls.Config{
RootCAs: pool,
ServerName: db.Host,
MinVersion: tls.VersionTLS12, // 禁用 TLS1.0/1.1,符合安全基线
}
if err := mysqldriver.RegisterTLSConfig(config.MySQLTLSConfigName, tlsCfg); err != nil {
return fmt.Errorf("注册 MySQL TLS 配置 %q 失败: %w", config.MySQLTLSConfigName, err)
}
return nil
}