skip to Main Content

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:

2

Answers


  1. Chosen as BEST ANSWER

    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 DB endpoint.
    I'm gonna fully test and confirm it next week, but for now it seems fully functional in QA environment.


  2. You can achieve this by below steps:

    1. Download the RDS Certificate Bundle. For this, you can run below command
      wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -O rds-combined-ca-bundle.pem
    2. Your Dockerfile should be
    FROM golang:1.22 AS build
    WORKDIR /app
    COPY . .
    RUN go mod tidy
    RUN go build -v -o server
    
    FROM golang:1.22
    COPY --from=build /app/server /app/server
    COPY --from=build /app/internal/certificates/global-bundle.pem /app/internal/certificates/global-bundle.pem
    CMD ["/app/server"]
    
    1. Your go code should be
    package main
    
    import (
        "crypto/tls"
        "crypto/x509"
        "database/sql"
        "encoding/json"
        "fmt"
        "log"
        "os"
    
        _ "github.com/go-sql-driver/mysql"
        "gorm.io/driver/mysql"
        "gorm.io/gorm"
    )
    
    type dbCredentials struct {
        Username string `json:"username"`
        Password string `json:"password"`
    }
    
    type ControllerService struct {
        Db       *gorm.DB
        DynamoDb interface{}
    }
    
    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")
        }
    
        // Load the RDS certificate bundle
        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.")
        }
    
        // Register custom TLS configuration
        err = mysql.RegisterTLSConfig("custom", &tls.Config{
            RootCAs: rootCertPool,
        })
        if err != nil {
            log.Fatal("Failed to register TLS config:", err)
        }
    
        // Build the DSN (Data Source Name)
        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)
    
        // Open the database connection
        customDb, err := sql.Open("mysql", dsn)
        if err != nil {
            log.Fatal("Failed to open database:", err)
        }
    
        // Use GORM to initialize the database connection
        db, err := gorm.Open(mysql.New(mysql.Config{
            Conn: customDb,
        }), &gorm.Config{})
        if err != nil {
            log.Fatal("Failed to initialize GORM:", err)
        }
    
        // Assuming dynamoDb is initialized elsewhere
        var dynamoDb interface{}
    
        return &ControllerService{Db: db, DynamoDb: dynamoDb}
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search