From 481dc97d60568a0e84e95660dbd1a45b16a2e49c Mon Sep 17 00:00:00 2001 From: Luke Scott Date: Wed, 19 Jun 2013 14:58:53 -0700 Subject: [PATCH 1/4] Add support for customizing tls.Config --- tlsconfig.go | 15 +++++++++++++++ utils.go | 3 +++ 2 files changed, 18 insertions(+) create mode 100644 tlsconfig.go diff --git a/tlsconfig.go b/tlsconfig.go new file mode 100644 index 000000000..612a9240f --- /dev/null +++ b/tlsconfig.go @@ -0,0 +1,15 @@ +package mysql + +import ( + "crypto/tls" +) + +type TLSConfig interface { + SetTLSConfig(key string, config *tls.Config) +} + +var tlsConfigMap = make(map[string]*tls.Config) + +func (d *mysqlDriver) SetTLSConfig(key string, config *tls.Config) { + tlsConfigMap[key] = config +} diff --git a/utils.go b/utils.go index 097ecd0aa..c8de75fd0 100644 --- a/utils.go +++ b/utils.go @@ -152,6 +152,9 @@ func parseDSN(dsn string) (cfg *config, err error) { cfg.tls = &tls.Config{} } else if strings.ToLower(value) == "skip-verify" { cfg.tls = &tls.Config{InsecureSkipVerify: true} + } else if tlsConfig, ok := tlsConfigMap[value]; ok { + cfg.tls = &tls.Config{} + *cfg.tls = *tlsConfig } default: From 497472037293b1338ac91d5a8666c5147d1f423c Mon Sep 17 00:00:00 2001 From: Luke Scott Date: Sat, 22 Jun 2013 08:10:45 -0700 Subject: [PATCH 2/4] Change SetTLSConfig to exported RegisterTLSConfig and add documentation --- README.md | 11 ++++++++++- tlsconfig.go | 15 --------------- utils.go | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 55 insertions(+), 18 deletions(-) delete mode 100644 tlsconfig.go diff --git a/README.md b/README.md index 8cd6ac38c..97cf29ae5 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa * [Address](#address) * [Parameters](#parameters) * [Examples](#examples) + * [TLS support](#tls-support) * [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support) * [time.Time support](#timetime-support) * [Unicode support](#unicode-support) @@ -113,7 +114,7 @@ Possible Parameters are: * `parseTime`: `parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string` * `strict`: Enable strict mode. MySQL warnings are treated as errors. * `timeout`: **Driver** side connection timeout. The value must be a string of decimal numbers, each with optional fraction and a unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout). - * `tls`: `true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side) + * `tls`: `true` enables TLS / SSL encrypted connection to the server. For other values see [TLS support](#tls-support). All other parameters are interpreted as system variables: * `autocommit`: *"SET autocommit=`value`"* @@ -143,6 +144,14 @@ No Database preselected: user:password@/ ``` +### TLS support +For TLS support set the `tls` parameter to one of the following values: + + * `true`: Server certificate is signed by a trusted authority. + * `skip-verify`: Server certificate is self-signed with no root authority. + * `custom`: Server certifiate is signed by a self-managed authority, and/or a client certificate is used. `custom` can be any value that coorisponds to a custom `tls.Config` registered with [`mysql.RegisterTLSConfig`](http://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig). + + ### `LOAD DATA LOCAL INFILE` support For this feature you need direct access to the package. Therefore you must change the import path (no `_`): ```go diff --git a/tlsconfig.go b/tlsconfig.go deleted file mode 100644 index 612a9240f..000000000 --- a/tlsconfig.go +++ /dev/null @@ -1,15 +0,0 @@ -package mysql - -import ( - "crypto/tls" -) - -type TLSConfig interface { - SetTLSConfig(key string, config *tls.Config) -} - -var tlsConfigMap = make(map[string]*tls.Config) - -func (d *mysqlDriver) SetTLSConfig(key string, config *tls.Config) { - tlsConfigMap[key] = config -} diff --git a/utils.go b/utils.go index c8de75fd0..de8bc8648 100644 --- a/utils.go +++ b/utils.go @@ -77,6 +77,50 @@ func (nt NullTime) Value() (driver.Value, error) { return nt.Time, nil } +var tlsConfigMap map[string]*tls.Config + +// Registers a custom tls.Config to be used with sql.Open. +// Use the key as a value in the DSN where tls=value. +// +// rootCertPool := x509.NewCertPool() +// { +// pem, err := ioutil.ReadFile("/path/ca-cert.pem") +// if err != nil { +// log.Fatal(err) +// } +// if ok := rootCAs.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(clientCerts, certs) +// } +// mysql.RegisterTLSConfig("custom", tls.Config{ +// RootCAs: rootCertPool, +// Certificates: clientCert, +// }) +// db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom") +// +func RegisterTLSConfig(key string, config *tls.Config) { + if tlsConfigMap == nil { + tlsConfigMap = make(map[string]*tls.Config) + } + tlsConfigMap[key] = config +} + +// Removes tls.Config associated with key. +func DeregisterTLSConfig(key string) { + if tlsConfigMap == nil { + return + } + delete(tlsConfigMap, key) +} + // Logger var ( errLog *log.Logger @@ -153,8 +197,7 @@ func parseDSN(dsn string) (cfg *config, err error) { } else if strings.ToLower(value) == "skip-verify" { cfg.tls = &tls.Config{InsecureSkipVerify: true} } else if tlsConfig, ok := tlsConfigMap[value]; ok { - cfg.tls = &tls.Config{} - *cfg.tls = *tlsConfig + cfg.tls = tlsConfig } default: From b18f20a1e23689d49d61c041f2f32f6315bfa3be Mon Sep 17 00:00:00 2001 From: Luke Scott Date: Sat, 29 Jun 2013 14:18:08 -0700 Subject: [PATCH 3/4] Revise documentation for RegisterTLSConfig --- README.md | 11 +---------- utils.go | 5 +++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 97cf29ae5..b2f5d1739 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa * [Address](#address) * [Parameters](#parameters) * [Examples](#examples) - * [TLS support](#tls-support) * [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support) * [time.Time support](#timetime-support) * [Unicode support](#unicode-support) @@ -114,7 +113,7 @@ Possible Parameters are: * `parseTime`: `parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string` * `strict`: Enable strict mode. MySQL warnings are treated as errors. * `timeout`: **Driver** side connection timeout. The value must be a string of decimal numbers, each with optional fraction and a unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout). - * `tls`: `true` enables TLS / SSL encrypted connection to the server. For other values see [TLS support](#tls-support). + * `tls`: `true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](http://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig). All other parameters are interpreted as system variables: * `autocommit`: *"SET autocommit=`value`"* @@ -144,14 +143,6 @@ No Database preselected: user:password@/ ``` -### TLS support -For TLS support set the `tls` parameter to one of the following values: - - * `true`: Server certificate is signed by a trusted authority. - * `skip-verify`: Server certificate is self-signed with no root authority. - * `custom`: Server certifiate is signed by a self-managed authority, and/or a client certificate is used. `custom` can be any value that coorisponds to a custom `tls.Config` registered with [`mysql.RegisterTLSConfig`](http://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig). - - ### `LOAD DATA LOCAL INFILE` support For this feature you need direct access to the package. Therefore you must change the import path (no `_`): ```go diff --git a/utils.go b/utils.go index de8bc8648..bca6ceb59 100644 --- a/utils.go +++ b/utils.go @@ -88,7 +88,7 @@ var tlsConfigMap map[string]*tls.Config // if err != nil { // log.Fatal(err) // } -// if ok := rootCAs.AppendCertsFromPEM(pem); !ok { +// if ok := rootCertPool.AppendCertsFromPEM(pem); !ok { // log.Fatal("Failed to append PEM.") // } // } @@ -98,7 +98,7 @@ var tlsConfigMap map[string]*tls.Config // if err != nil { // log.Fatal(err) // } -// clientCert = append(clientCerts, certs) +// clientCert = append(clientCert, certs) // } // mysql.RegisterTLSConfig("custom", tls.Config{ // RootCAs: rootCertPool, @@ -196,6 +196,7 @@ func parseDSN(dsn string) (cfg *config, err error) { cfg.tls = &tls.Config{} } else if strings.ToLower(value) == "skip-verify" { cfg.tls = &tls.Config{InsecureSkipVerify: true} + // TODO: Check for Boolean false } else if tlsConfig, ok := tlsConfigMap[value]; ok { cfg.tls = tlsConfig } From 17ee918f8db541d623e13a42f814f86cf33609d2 Mon Sep 17 00:00:00 2001 From: Luke Scott Date: Mon, 1 Jul 2013 09:24:11 -0700 Subject: [PATCH 4/4] Add test for RegisterTLSConfig --- driver_test.go | 11 ++++++++++- utils.go | 26 +++++++++++--------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/driver_test.go b/driver_test.go index 7be65a476..246e83ee5 100644 --- a/driver_test.go +++ b/driver_test.go @@ -1,6 +1,7 @@ package mysql import ( + "crypto/tls" "database/sql" "fmt" "io" @@ -840,7 +841,7 @@ func TestStrict(t *testing.T) { } func TestTLS(t *testing.T) { - runTests(t, dsn+"&tls=skip-verify", func(dbt *DBTest) { + tlsTest := func(dbt *DBTest) { if err := dbt.db.Ping(); err != nil { if err == errNoTLS { dbt.Skip("Server does not support TLS") @@ -861,7 +862,15 @@ func TestTLS(t *testing.T) { dbt.Fatal("No Cipher") } } + } + + runTests(t, dsn+"&tls=skip-verify", tlsTest) + + // Verify that registering / using a custom cfg works + RegisterTLSConfig("custom-skip-verify", &tls.Config{ + InsecureSkipVerify: true, }) + runTests(t, dsn+"&tls=custom-skip-verify", tlsTest) } // Special cases diff --git a/utils.go b/utils.go index bca6ceb59..e40fcb2f1 100644 --- a/utils.go +++ b/utils.go @@ -83,24 +83,20 @@ var tlsConfigMap map[string]*tls.Config // Use the key as a value in the DSN where tls=value. // // rootCertPool := x509.NewCertPool() -// { -// pem, err := ioutil.ReadFile("/path/ca-cert.pem") -// if err != nil { -// log.Fatal(err) -// } -// if ok := rootCertPool.AppendCertsFromPEM(pem); !ok { -// log.Fatal("Failed to append PEM.") -// } +// pem, err := ioutil.ReadFile("/path/ca-cert.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) +// certs, err := tls.LoadX509KeyPair("/path/client-cert.pem", "/path/client-key.pem") +// if err != nil { +// log.Fatal(err) // } -// mysql.RegisterTLSConfig("custom", tls.Config{ +// clientCert = append(clientCert, certs) +// mysql.RegisterTLSConfig("custom", &tls.Config{ // RootCAs: rootCertPool, // Certificates: clientCert, // })