From cf288525b2c12e3a5a3450e18b0809008ea3621a Mon Sep 17 00:00:00 2001 From: Songmu Date: Thu, 20 Jun 2019 15:14:29 +0900 Subject: [PATCH 1/6] tls support on mysql by using CA's pem --- cmd/goose/driver_mysql.go | 22 ++++++++++++++++++++-- cmd/goose/driver_no_mysql.go | 4 ++++ cmd/goose/main.go | 7 +++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/cmd/goose/driver_mysql.go b/cmd/goose/driver_mysql.go index 3fa0126..5475135 100644 --- a/cmd/goose/driver_mysql.go +++ b/cmd/goose/driver_mysql.go @@ -3,11 +3,13 @@ package main import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" "log" "github.com/go-sql-driver/mysql" - - _ "github.com/go-sql-driver/mysql" _ "github.com/ziutek/mymysql/godrv" ) @@ -34,3 +36,19 @@ func normalizeMySQLDSN(dsn string) (string, error) { config.ParseTime = true return config.FormatDSN(), nil } + +const tlsConfigKey = "custom" + +func registerTLSConfig(pemfile string) error { + rootCertPool := x509.NewCertPool() + pem, err := ioutil.ReadFile(pemfile) + if err != nil { + return err + } + if ok := rootCertPool.AppendCertsFromPEM(pem); !ok { + return fmt.Errorf("failed to append PEM: %q", pemfile) + } + return mysql.RegisterTLSConfig(tlsConfigKey, &tls.Config{ + RootCAs: rootCertPool, + }) +} diff --git a/cmd/goose/driver_no_mysql.go b/cmd/goose/driver_no_mysql.go index a18ccff..f67b360 100644 --- a/cmd/goose/driver_no_mysql.go +++ b/cmd/goose/driver_no_mysql.go @@ -10,3 +10,7 @@ import ( func normalizeDBString(driver string, str string) string { return str } + +func registerTLSConfig(_ string) error { + return nil +} diff --git a/cmd/goose/main.go b/cmd/goose/main.go index 44572f3..102f48f 100644 --- a/cmd/goose/main.go +++ b/cmd/goose/main.go @@ -15,6 +15,7 @@ var ( verbose = flags.Bool("v", false, "enable verbose mode") help = flags.Bool("h", false, "print help") version = flags.Bool("version", false, "print version") + sslCA = flags.String("ssl-ca", "", "file path to root CA's certificates in pem format (only support on mysql)") ) func main() { @@ -35,6 +36,12 @@ func main() { return } + if *sslCA != "" { + if err := registerTLSConfig(*sslCA); err != nil { + log.Fatalf("goose run: %v", err) + } + } + switch args[0] { case "create": if err := goose.Run("create", nil, *dir, args[1:]...); err != nil { From 521de29112831c90da0ec9b82da241ae17b1d24c Mon Sep 17 00:00:00 2001 From: Songmu Date: Thu, 20 Jun 2019 17:16:52 +0900 Subject: [PATCH 2/6] automatically set TLS config value to DSN when using TLS on mysql --- cmd/goose/driver_mysql.go | 13 ++++++++----- cmd/goose/driver_no_mysql.go | 2 +- cmd/goose/main.go | 5 +++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cmd/goose/driver_mysql.go b/cmd/goose/driver_mysql.go index 5475135..1ec4cfa 100644 --- a/cmd/goose/driver_mysql.go +++ b/cmd/goose/driver_mysql.go @@ -17,10 +17,10 @@ import ( // the parameter `parseTime` set to true. This allows internal goose logic // to assume that DATETIME/DATE/TIMESTAMP can be scanned into the time.Time // type. -func normalizeDBString(driver string, str string) string { +func normalizeDBString(driver string, str string, tls bool) string { if driver == "mysql" { var err error - str, err = normalizeMySQLDSN(str) + str, err = normalizeMySQLDSN(str, tls) if err != nil { log.Fatalf("failed to normalize MySQL connection string: %v", err) } @@ -28,17 +28,20 @@ func normalizeDBString(driver string, str string) string { return str } -func normalizeMySQLDSN(dsn string) (string, error) { +const tlsConfigKey = "custom" + +func normalizeMySQLDSN(dsn string, tls bool) (string, error) { config, err := mysql.ParseDSN(dsn) if err != nil { return "", err } config.ParseTime = true + if tls { + config.TLSConfig = tlsConfigKey + } return config.FormatDSN(), nil } -const tlsConfigKey = "custom" - func registerTLSConfig(pemfile string) error { rootCertPool := x509.NewCertPool() pem, err := ioutil.ReadFile(pemfile) diff --git a/cmd/goose/driver_no_mysql.go b/cmd/goose/driver_no_mysql.go index f67b360..c873c53 100644 --- a/cmd/goose/driver_no_mysql.go +++ b/cmd/goose/driver_no_mysql.go @@ -7,7 +7,7 @@ import ( _ "github.com/ziutek/mymysql/godrv" ) -func normalizeDBString(driver string, str string) string { +func normalizeDBString(driver string, str string, tls bool) string { return str } diff --git a/cmd/goose/main.go b/cmd/goose/main.go index 102f48f..9131c66 100644 --- a/cmd/goose/main.go +++ b/cmd/goose/main.go @@ -36,7 +36,8 @@ func main() { return } - if *sslCA != "" { + tls := *sslCA != "" + if tls { if err := registerTLSConfig(*sslCA); err != nil { log.Fatalf("goose run: %v", err) } @@ -62,7 +63,7 @@ func main() { driver, dbstring, command := args[0], args[1], args[2] - db, err := goose.OpenDBWithDriver(driver, normalizeDBString(driver, dbstring)) + db, err := goose.OpenDBWithDriver(driver, normalizeDBString(driver, dbstring, tls)) if err != nil { log.Fatalf("-dbstring=%q: %v\n", dbstring, err) } From 80f5bc709b41fb5c1468832015da83a8c8691cab Mon Sep 17 00:00:00 2001 From: Songmu Date: Sun, 12 Apr 2020 00:46:58 +0900 Subject: [PATCH 3/6] rename option var name to certfile from sslCA --- cmd/goose/main.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/goose/main.go b/cmd/goose/main.go index 9131c66..491421e 100644 --- a/cmd/goose/main.go +++ b/cmd/goose/main.go @@ -10,12 +10,12 @@ import ( ) var ( - flags = flag.NewFlagSet("goose", flag.ExitOnError) - dir = flags.String("dir", ".", "directory with migration files") - verbose = flags.Bool("v", false, "enable verbose mode") - help = flags.Bool("h", false, "print help") - version = flags.Bool("version", false, "print version") - sslCA = flags.String("ssl-ca", "", "file path to root CA's certificates in pem format (only support on mysql)") + flags = flag.NewFlagSet("goose", flag.ExitOnError) + dir = flags.String("dir", ".", "directory with migration files") + verbose = flags.Bool("v", false, "enable verbose mode") + help = flags.Bool("h", false, "print help") + version = flags.Bool("version", false, "print version") + certfile = flags.String("ssl-ca", "", "file path to root CA's certificates in pem format (only support on mysql)") ) func main() { @@ -36,9 +36,9 @@ func main() { return } - tls := *sslCA != "" + tls := *certfile != "" if tls { - if err := registerTLSConfig(*sslCA); err != nil { + if err := registerTLSConfig(*certfile); err != nil { log.Fatalf("goose run: %v", err) } } From a5db7de44c3675c536c5f558b33ecf6cad14b260 Mon Sep 17 00:00:00 2001 From: Songmu Date: Sun, 12 Apr 2020 01:08:15 +0900 Subject: [PATCH 4/6] using registerTLSConfig for MySQL only --- cmd/goose/driver_mysql.go | 10 ++++++++-- cmd/goose/driver_no_mysql.go | 6 +----- cmd/goose/main.go | 9 +-------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/cmd/goose/driver_mysql.go b/cmd/goose/driver_mysql.go index 1ec4cfa..3f2b9fe 100644 --- a/cmd/goose/driver_mysql.go +++ b/cmd/goose/driver_mysql.go @@ -17,10 +17,16 @@ import ( // the parameter `parseTime` set to true. This allows internal goose logic // to assume that DATETIME/DATE/TIMESTAMP can be scanned into the time.Time // type. -func normalizeDBString(driver string, str string, tls bool) string { +func normalizeDBString(driver string, str string, certfile string) string { if driver == "mysql" { + var isTLS = certfile != "" + if isTLS { + if err := registerTLSConfig(certfile); err != nil { + log.Fatalf("goose run: %v", err) + } + } var err error - str, err = normalizeMySQLDSN(str, tls) + str, err = normalizeMySQLDSN(str, isTLS) if err != nil { log.Fatalf("failed to normalize MySQL connection string: %v", err) } diff --git a/cmd/goose/driver_no_mysql.go b/cmd/goose/driver_no_mysql.go index c873c53..5603ade 100644 --- a/cmd/goose/driver_no_mysql.go +++ b/cmd/goose/driver_no_mysql.go @@ -7,10 +7,6 @@ import ( _ "github.com/ziutek/mymysql/godrv" ) -func normalizeDBString(driver string, str string, tls bool) string { +func normalizeDBString(driver string, str string, certfile string) string { return str } - -func registerTLSConfig(_ string) error { - return nil -} diff --git a/cmd/goose/main.go b/cmd/goose/main.go index 491421e..31be8c4 100644 --- a/cmd/goose/main.go +++ b/cmd/goose/main.go @@ -36,13 +36,6 @@ func main() { return } - tls := *certfile != "" - if tls { - if err := registerTLSConfig(*certfile); err != nil { - log.Fatalf("goose run: %v", err) - } - } - switch args[0] { case "create": if err := goose.Run("create", nil, *dir, args[1:]...); err != nil { @@ -63,7 +56,7 @@ func main() { driver, dbstring, command := args[0], args[1], args[2] - db, err := goose.OpenDBWithDriver(driver, normalizeDBString(driver, dbstring, tls)) + db, err := goose.OpenDBWithDriver(driver, normalizeDBString(driver, dbstring, *certfile)) if err != nil { log.Fatalf("-dbstring=%q: %v\n", dbstring, err) } From c868713e998b6b1472889586b3917031f5742e98 Mon Sep 17 00:00:00 2001 From: Songmu Date: Mon, 13 Apr 2020 19:21:12 +0900 Subject: [PATCH 5/6] fix option name --- cmd/goose/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/goose/main.go b/cmd/goose/main.go index 72bd0c7..84f0a41 100644 --- a/cmd/goose/main.go +++ b/cmd/goose/main.go @@ -16,7 +16,7 @@ var ( verbose = flags.Bool("v", false, "enable verbose mode") help = flags.Bool("h", false, "print help") version = flags.Bool("version", false, "print version") - certfile = flags.String("ssl-ca", "", "file path to root CA's certificates in pem format (only support on mysql)") + certfile = flags.String("certfile", "", "file path to root CA's certificates in pem format (only support on mysql)") ) func main() { From 522080545b49491eb8b0d9f84069616ce6688f0a Mon Sep 17 00:00:00 2001 From: Songmu Date: Wed, 15 Apr 2020 22:24:40 +0900 Subject: [PATCH 6/6] clear 'tls=value' from original dsn on mysql --- cmd/goose/driver_mysql.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/goose/driver_mysql.go b/cmd/goose/driver_mysql.go index 3f2b9fe..bff3243 100644 --- a/cmd/goose/driver_mysql.go +++ b/cmd/goose/driver_mysql.go @@ -8,6 +8,7 @@ import ( "fmt" "io/ioutil" "log" + "regexp" "github.com/go-sql-driver/mysql" _ "github.com/ziutek/mymysql/godrv" @@ -36,7 +37,12 @@ func normalizeDBString(driver string, str string, certfile string) string { const tlsConfigKey = "custom" +var tlsReg = regexp.MustCompile(`(\?|&)tls=[^&]*(?:&|$)`) + func normalizeMySQLDSN(dsn string, tls bool) (string, error) { + // If we are sharing a DSN in a different environment, it may contain a TLS + // setting key with a value name that is not "custom," so clear it. + dsn = tlsReg.ReplaceAllString(dsn, `$1`) config, err := mysql.ParseDSN(dsn) if err != nil { return "", err