Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you create a TLS connection to a Cloud SQL database using Go?

I'm trying to create a TLS connection to a Cloud SQL database but I'm getting the following error when trying to prepare a statement:

x509: cannot validate certificate for <cloud sql instance ip>
      because it doesn't contain any IP SANs

Here is my setup code:

rootCertPool := x509.NewCertPool()

pem, err := ioutil.ReadFile("/path/server-ca.pem")
if err != nil {
    log.Fatal(err)
}

if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
    log.Fatal("Failed to append PEM.")
}

clientCert := make([]tls.Certificate, 0, 1)
certs, err := tls.LoadX509KeyPair("/path/client-cert.pem",
                                  "/path/client-key.pem")
if err != nil {
    log.Fatal(err)
}

clientCert = append(clientCert, certs)
mysql.RegisterTLSConfig("custom", &tls.Config{
    RootCAs: rootCertPool,
    Certificates: clientCert,
})

db, err := sql.Open("mysql",
        "<user>:<password>@tcp(<cloud sql ip>:3306)/<db_name>?tls=custom")
like image 355
roudy16 Avatar asked Dec 12 '18 22:12

roudy16


People also ask

How do I connect to a SQL database in cloud?

In the Google Cloud console, go to the Cloud SQL Instances page. To open the Overview page of an instance, click the instance name. Select Connections from the SQL navigation menu. In the Authorized networks section, click Add network and enter the IP address of the machine where the client is installed.

What do you need in order to connect to your Cloud SQL instance using SSL?

A server Certificate Authority (CA) certificate is required in SSL connections. Cloud SQL creates a server certificate automatically when you create your instance. As long as the server certificate is valid, you do not need to actively manage your server certificate.


1 Answers

They key things I was missing was that the version of Go I was using was several months old and did not contain a specific fix and I did not specify the hostname associated with my Cloud SQL instance. I could not find an answer for this problem anywhere and found the solution myself by stepping through the TLS handshake code to see what went wrong and why.

Versions of Go released before September 2018 would not correctly validate the hostnames that Cloud SQL uses in the TLS server certificate. Cloud SQL hostnames contain a ':' character and that caused the hostname and therefore the server certificate to be considered invalid. That has been fixed.

The correct way to connect to a Cloud SQL instance using TLS is to follow these steps:

  1. Update your Go so that you have the change that allows validation of Cloud SQL hostnames that are in the server certificate.

  2. Create client certificates using the Cloud SQL console.

  3. Create the TLS connection as follows:

import (
    "crypto/tls"
    "crypto/x509"
    "database/sql"
    "github.com/go-sql-driver/mysql"
    "io/ioutil"
)

rootCertPool := x509.NewCertPool()

pem, err := ioutil.ReadFile("/path/server-ca.pem")
if err != nil {
    log.Fatal(err)
}

if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
    log.Fatal("Failed to append PEM.")
}

clientCert := make([]tls.Certificate, 0, 1)
certs, err := tls.LoadX509KeyPair("/path/client-cert.pem",
                                  "/path/client-key.pem")
if err != nil {
    log.Fatal(err)
}

clientCert = append(clientCert, certs)
mysql.RegisterTLSConfig("custom", &tls.Config{
    RootCAs: rootCertPool,
    Certificates: clientCert,
    ServerName: "<gcp-project-id>:<cloud-sql-instance>", // hostname
})

db, err := sql.Open("mysql",
        "<user>:<password>@tcp(<cloud sql ip>:3306)/<db_name>?tls=custom")
like image 187
roudy16 Avatar answered Oct 03 '22 03:10

roudy16