I’m trying to establish SSL/TLS connection to AWS RDS MySQL. The server is build in Docker and I can’t force it to use certificate bundle provided by AWS…
When I run the service in Docker container I receive first ERROR:
2024/05/31 11:08:16 /app/internal/services/servicecontainer.go:252 driver: bad connection
and with each interaction with DB I receive:
tls: failed to verify certificate: x509: certificate is not valid for any names, but wanted to match host.docker.internal
and this is while run locally. If I try same in AWS I get:
failed to initialize database, got error Error 3159: Connections using insecure transport are prohibited while --require_secure_transport=ON
According to AWS docs. The SSL/TLS certificate includes the DB instance endpoint as the Common Name (CN) for the SSL/TLS certificate to guard against spoofing attacks.
How should I specify CN in my code or what else should I do to establish secure connection?
Thank you for your help!
Docker file:
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 code:
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}
}
I’ve searched for solution and found next information:
- Issue with golang function for verification of CN name: https://github.com/docker/for-linux/issues/248
- AWS documentation about using TLS connection: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html#UsingWithRDS.SSL.CertificatesAllRegions
- Stack issue: How to use the go-mysql-driver with ssl on aws with a mysql rds instance
- GoLang Gorm docs: https://gorm.io/docs/connecting_to_the_database.html
- GoLang driver docs: https://github.com/go-sql-driver/mysql#parameters
2
Answers
It turned out that this code (has room for improvements :)) is not working in local environment only. For production (meaning it should run on AWS) I had to change host name from
host.docker.internal
to RDS DBendpoint
.I'm gonna fully test and confirm it next week, but for now it seems fully functional in QA environment.
You can achieve this by below steps:
wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -O rds-combined-ca-bundle.pem