cloudsql/mysql/database-sql/connect_tcp.go (76 lines of code) (raw):

// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // [START cloud_sql_mysql_databasesql_connect_tcp] // [START cloud_sql_mysql_databasesql_connect_tcp_sslcerts] // [START cloud_sql_mysql_databasesql_sslcerts] package cloudsql import ( "crypto/tls" "crypto/x509" "database/sql" "errors" "fmt" "log" "os" "github.com/go-sql-driver/mysql" ) // connectTCPSocket initializes a TCP connection pool for a Cloud SQL // instance of MySQL. func connectTCPSocket() (*sql.DB, error) { mustGetenv := func(k string) string { v := os.Getenv(k) if v == "" { log.Fatalf("Fatal Error in connect_tcp.go: %s environment variable not set.", k) } return v } // Note: Saving credentials in environment variables is convenient, but not // secure - consider a more secure solution such as // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help // keep secrets safe. var ( dbUser = mustGetenv("DB_USER") // e.g. 'my-db-user' dbPwd = mustGetenv("DB_PASS") // e.g. 'my-db-password' dbName = mustGetenv("DB_NAME") // e.g. 'my-database' dbPort = mustGetenv("DB_PORT") // e.g. '3306' dbTCPHost = mustGetenv("INSTANCE_HOST") // e.g. '127.0.0.1' ('172.17.0.1' if deployed to GAE Flex) ) dbURI := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", dbUser, dbPwd, dbTCPHost, dbPort, dbName) // [END cloud_sql_mysql_databasesql_connect_tcp] // (OPTIONAL) Configure SSL certificates // For deployments that connect directly to a Cloud SQL instance without // using the Cloud SQL Proxy, configuring SSL certificates will ensure the // connection is encrypted. if dbRootCert, ok := os.LookupEnv("DB_ROOT_CERT"); ok { // e.g., '/path/to/my/server-ca.pem' var ( dbCert = mustGetenv("DB_CERT") // e.g. '/path/to/my/client-cert.pem' dbKey = mustGetenv("DB_KEY") // e.g. '/path/to/my/client-key.pem' ) pool := x509.NewCertPool() pem, err := os.ReadFile(dbRootCert) if err != nil { return nil, err } if ok := pool.AppendCertsFromPEM(pem); !ok { return nil, errors.New("unable to append root cert to pool") } cert, err := tls.LoadX509KeyPair(dbCert, dbKey) if err != nil { return nil, err } mysql.RegisterTLSConfig("cloudsql", &tls.Config{ RootCAs: pool, Certificates: []tls.Certificate{cert}, // InsecureSkipVerify and a custom VerifyPeerCertificate function is // required to handle Cloud SQL's custom certificates. // As an alternative it's also possible to inspect the server // certificate and extract the SAN field and use that a ServerName // while removing InsecureSkipVerify and VerifyPeerCertificate. InsecureSkipVerify: true, VerifyPeerCertificate: verifyPeerCertFunc(pool), }) dbURI += "&tls=cloudsql" } // [START cloud_sql_mysql_databasesql_connect_tcp] // dbPool is the pool of database connections. dbPool, err := sql.Open("mysql", dbURI) if err != nil { return nil, fmt.Errorf("sql.Open: %w", err) } // [START_EXCLUDE] configureConnectionPool(dbPool) // [END_EXCLUDE] return dbPool, nil } // [END cloud_sql_mysql_databasesql_connect_tcp] // verifyPeerCertFunc returns a function that verifies the peer certificate is // in the cert pool. func verifyPeerCertFunc(pool *x509.CertPool) func([][]byte, [][]*x509.Certificate) error { return func(rawCerts [][]byte, _ [][]*x509.Certificate) error { if len(rawCerts) == 0 { return errors.New("no certificates available to verify") } cert, err := x509.ParseCertificate(rawCerts[0]) if err != nil { return err } opts := x509.VerifyOptions{Roots: pool} if _, err = cert.Verify(opts); err != nil { return err } return nil } } // [END cloud_sql_mysql_databasesql_sslcerts] // [END cloud_sql_mysql_databasesql_connect_tcp_sslcerts]