Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow TLS connections in the driver #673

Merged
merged 15 commits into from
May 5, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Custom TLS config support in the driver
This is pretty rudimentary, but good enough to start a PR and 
review/discussion for how it should end up.

- There is just a single custom config called `custom`
- A program using the driver can only have one custom config. That 
"works for me", but for people using the driver in a single program to 
communicate with different endpoints that probably isn't going to work
- Adds a basic "wrapper" function `SetCustomTLSConfig` which passes 
through to `NewClientTLSConfig`. This means just the driver can be 
imported.

To use this you'd do:

```
import (
	"database/sql"
	"net/url"

	"github.com/go-mysql-org/go-mysql/driver" // full import required
)

[...]

var (
	CaPem = []byte(`-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----`)
	
)

[...]

// CA and domain, no Cert and Key
driver.SetCustomTLSConfig(CaPem, make([]byte, 0), make([]byte, 0), false, "my.domain.com")

[...]
```
atomicules committed Feb 1, 2022
commit 2757a0609925ba6cfa93bb259ceda83c1cecef7b
3 changes: 3 additions & 0 deletions client/tls.go
Original file line number Diff line number Diff line change
@@ -15,6 +15,8 @@ func NewClientTLSConfig(caPem, certPem, keyPem []byte, insecureSkipVerify bool,

var config *tls.Config

// Allow cert and key to be optional
// Send through `make([]byte, 0)` for "nil"
if string(certPem) != "" && string(keyPem) != "" {
cert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
@@ -36,3 +38,4 @@ func NewClientTLSConfig(caPem, certPem, keyPem []byte, insecureSkipVerify bool,

return config
}

42 changes: 23 additions & 19 deletions driver/driver.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
package driver

import (
"crypto/tls"
"database/sql"
sqldriver "database/sql/driver"
"fmt"
@@ -15,14 +16,11 @@ import (
"github.com/siddontang/go/hack"
)

var customTLSConfig *tls.Config

type driver struct {
}

// Testing custom tls by hard-coding something in
var CaPem = []byte(`-----BEGIN CERTIFICATE-----
MYCACERT
-----END CERTIFICATE-----`)

// Open: DSN user:password@addr[?db]
func (d driver) Open(dsn string) (sqldriver.Conn, error) {
lastIndex := strings.LastIndex(dsn, "@")
@@ -75,22 +73,22 @@ func (d driver) Open(dsn string) (sqldriver.Conn, error) {
return nil, errors.Errorf("invalid dsn, must user:password@addr[[?db[&param=X]]")
}

custom := client.NewClientTLSConfig(CaPem, make([]byte, 0), make([]byte, 0), false, "custom.host.name")

tlsConfigName, tls := params["ssl"]
if tls {
if tlsConfigName == "true" {
// This actually does insecureSkipVerify
// But not even sure if it makes sense to handle false? According to
// client_test.go it doesn't - it'd result in an error
c, err = client.Connect(addr, user, password, db, func(c *client.Conn) { c.UseSSL(true) })
} else {
// TODO: This is going to be trickier. Need a way of taking the
// `tlsConfigName` which is a string type and using that to point at the actual
// `tlsConfig` which is a `NewClientTLSConfig` type. Can probably draw
// inspiration from go-sql-driver/mysql
c, err = client.Connect(addr, user, password, db, func(c *client.Conn) {c.SetTLSConfig(custom)})
//return nil, errors.Errorf("Custom TLS configuration support not implemented yet")
switch tlsConfigName {
case "true":
// This actually does insecureSkipVerify
// But not even sure if it makes sense to handle false? According to
// client_test.go it doesn't - it'd result in an error
c, err = client.Connect(addr, user, password, db, func(c *client.Conn) { c.UseSSL(true) })
case "custom":
// I was too concerned about mimicking what go-sql-driver/mysql does which will
// allow any name for a custom tls profile and maps the query parameter value to
// that TLSConfig variable... there is no need to be that clever. We can just
// insist `"custom"` (string) == `custom` (TLSConfig)
c, err = client.Connect(addr, user, password, db, func(c *client.Conn) {c.SetTLSConfig(customTLSConfig)})
default:
return nil, errors.Errorf("Supported options are ssl=true or ssl=custom")
}
} else {
c, err = client.Connect(addr, user, password, db)
@@ -276,3 +274,9 @@ func (r *rows) Next(dest []sqldriver.Value) error {
func init() {
sql.Register("mysql", driver{})
}

func SetCustomTLSConfig(caPem []byte, certPem []byte, keyPem []byte, insecureSkipVerify bool, serverName string) {
// Basic pass-through function so we can just import the driver
// For now there is a single shared "custom" config. I.e. any program can only have one "custom" config
customTLSConfig = client.NewClientTLSConfig(caPem, certPem, keyPem, insecureSkipVerify, serverName)
}