如何在GoLang/Docker环境下建立与AWS RDS MySQL的SSL/TLS连接?
在Docker部署的Go服务中建立AWS RDS MySQL SSL/TLS连接的问题
我正尝试在Docker部署的Go服务中建立与AWS RDS MySQL的SSL/TLS连接,但无法让服务正常使用AWS提供的证书包,遇到以下两类问题:
- 本地Docker运行时:
- 初始连接错误:
2024/05/31 11:08:16 /app/internal/services/servicecontainer.go:252 driver: bad connection - 数据库交互时的证书验证错误:
tls: failed to verify certificate: x509: certificate is not valid for any names, but wanted to match host.docker.internal
- 初始连接错误:
- AWS环境运行时:
failed to initialize database, got error Error 3159: Connections using insecure transport are prohibited while --require_secure_transport=ON
根据AWS文档说明:
The SSL/TLS certificate includes the DB instance endpoint as the Common Name (CN) for the SSL/TLS certificate to guard against spoofing attacks.
请问应如何在代码中指定CN,或采取其他措施建立安全连接?
附:相关代码与配置
Dockerfile
FROM golang:1.22 # BUILD CODE I BELIEVE IS NOT RELEVANT ADD ./internal/certificates /usr/local/share/ca-certificates RUN update-ca-certificates CMD ["./main"]
GoLang 代码
import ( "crypto/tls" "crypto/x509" "database/sql" "os" "sync" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" mysqlDriver "github.com/go-sql-driver/mysql" "gorm.io/driver/mysql" "gorm.io/gorm" ) func InitializeKernel() *ControllerService { DB_HOST := os.Getenv("DB_HOST") DB_DBNAME := os.Getenv("DB_DBNAME") DB_CREDENTIALS := os.Getenv("DB_CREDENTIALS") AWS_REGION := os.Getenv("AWS_REGION") if AWS_REGION == "" { AWS_REGION = "us-west-2" } var dbCred dbCredentials err := json.Unmarshal([]byte(DB_CREDENTIALS), &dbCred) if err != nil { log.Fatal("could not parse db credentials from env") } rootCertPool := x509.NewCertPool() globPem, err := os.ReadFile("./internal/certificates/global-bundle.pem") if err != nil { log.Fatal(err) } if ok := rootCertPool.AppendCertsFromPEM(globPem); !ok { log.Fatal("Failed to append global PEM.") } mysqlDriver.RegisterTLSConfig("custom", &tls.Config{ RootCAs: rootCertPool, }) dsn := fmt.Sprintf("%s:%s@tcp(%s:3306)/%s?charset=utf8mb4&parseTime=True&tls=custom&loc=Local", dbCred.Username, dbCred.Password, DB_HOST, DB_DBNAME) customDb, err := sql.Open("mysql", dsn) if err != nil { fmt.Println(err.Error()) } db, err := gorm.Open(mysql.New(mysql.Config{ Conn: customDb, }), &gorm.Config{}) if err != nil { fmt.Println(err.Error()) } return &ControllerService{Db: db, DynamoDb: dynamoDb} }
解决方案
1. 修复本地Docker的CN匹配问题
本地出现host.docker.internal的证书验证错误,是因为你用该域名替代了RDS实际端点,但证书的CN是RDS的官方端点域名。两种解决方式:
- 直接使用RDS端点:将本地环境的
DB_HOST变量设置为RDS的实际端点(例如mydb.xxxxxx.us-west-2.rds.amazonaws.com),而非host.docker.internal - 强制指定ServerName:如果必须用
host.docker.internal做本地映射,在TLS配置中手动指定ServerName为RDS实际端点,让TLS验证时匹配正确的CN:
mysqlDriver.RegisterTLSConfig("custom", &tls.Config{ RootCAs: rootCertPool, ServerName: "mydb.xxxxxx.us-west-2.rds.amazonaws.com", // 替换为你的RDS端点 })
2. 确保AWS环境的SSL连接生效
AWS环境的Error 3159说明连接未使用SSL,可按以下步骤排查:
- 确认
DB_HOST设置为RDS的官方端点,而非内网IP(内网IP可能跳过SSL验证) - 简化DSN的TLS参数:Dockerfile中已执行
update-ca-certificates将AWS证书添加到系统信任库,可直接用tls=true代替自定义配置,让驱动自动使用系统CA:
dsn := fmt.Sprintf("%s:%s@tcp(%s:3306)/%s?charset=utf8mb4&parseTime=True&tls=true&loc=Local", dbCred.Username, dbCred.Password, DB_HOST, DB_DBNAME)
此方式无需手动加载证书,避免自定义配置出错。
3. 代码优化建议
- 不要忽略
sql.Open和gorm.Open的错误,应使用log.Fatal终止程序,避免后续逻辑在无效连接下运行 - 添加连接测试:打开连接后调用
customDb.Ping()验证连接有效性:
if err := customDb.Ping(); err != nil { log.Fatal("Failed to ping database:", err) }
内容的提问来源于stack exchange,提问作者ucios
相关产品推荐
相关产品推荐

