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

Add support for custom certificates #111

Merged
merged 10 commits into from
Feb 21, 2024
85 changes: 84 additions & 1 deletion mysql/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ package mysql

import (
"context"
"crypto/tls"
"crypto/x509"
"database/sql"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net"
"net/url"
Expand Down Expand Up @@ -49,6 +53,13 @@ type MySQLConfiguration struct {
ConnectRetryTimeoutSec time.Duration
}

type CustomTLS struct {
ConfigKey string `json:"config_key"`
CACert string `json:"ca_cert"`
ClientCert string `json:"client_cert"`
ClientKey string `json:"client_key"`
}

var (
connectionCacheMtx sync.Mutex
connectionCache map[string]*OneConnection
Expand Down Expand Up @@ -111,6 +122,33 @@ func Provider() *schema.Provider {
}, false),
},

"custom_tls": {
Type: schema.TypeList,
Optional: true,
Default: nil,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"config_key": {
Type: schema.TypeString,
Default: "custom",
Optional: true,
},
"ca_cert": {
Type: schema.TypeString,
Required: true,
},
"client_cert": {
Type: schema.TypeString,
Required: true,
},
"client_key": {
Type: schema.TypeString,
Required: true,
},
},
},
},

"max_conn_lifetime_sec": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -175,6 +213,46 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}
var allowNativePasswords = authPlugin == nativePasswords
var password = d.Get("password").(string)
var iam_auth = d.Get("iam_database_authentication").(bool)
var tlsConfig = d.Get("tls").(string)
var tlsConfigStruct *tls.Config
var customTLS CustomTLS
petoju marked this conversation as resolved.
Show resolved Hide resolved

customTLSMap := d.Get("custom_tls").([]interface{})
if len(customTLSMap) > 0 {
customMap := customTLSMap[0].(map[string]interface{})
customTLSJson, err := json.Marshal(customMap)
if err != nil {
return nil, diag.Errorf("failed to marshal tls config: %v", customTLSMap)
}

err = json.Unmarshal(customTLSJson, &customTLS)
if err != nil {
return nil, diag.Errorf("failed to unmarshal tls config: %v", customTLSJson)
}

rootCertPool := x509.NewCertPool()
pem, err := ioutil.ReadFile(customTLS.CACert)
if err != nil {
return nil, diag.Errorf("failed to read CA cert: %v", err)
}

if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
return nil, diag.Errorf("failed to append pem: %v", pem)
}

clientCert := make([]tls.Certificate, 0, 1)
certs, err := tls.LoadX509KeyPair(customTLS.ClientCert, customTLS.ClientKey)
if err != nil {
return nil, diag.Errorf("error lading keypair: %v", err)
petoju marked this conversation as resolved.
Show resolved Hide resolved
}
clientCert = append(clientCert, certs)
tlsConfigStruct = &tls.Config{
RootCAs: rootCertPool,
Certificates: clientCert,
}
mysql.RegisterTLSConfig(customTLS.ConfigKey, tlsConfigStruct)
tlsConfig = customTLS.ConfigKey
}

proto := "tcp"
if len(endpoint) > 0 && endpoint[0] == '/' {
Expand Down Expand Up @@ -236,13 +314,17 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}
Passwd: password,
Net: proto,
Addr: endpoint,
TLSConfig: d.Get("tls").(string),
TLSConfig: tlsConfig,
AllowNativePasswords: allowNativePasswords,
AllowCleartextPasswords: allowClearTextPasswords,
InterpolateParams: true,
Params: connParams,
}

if tlsConfigStruct != nil {
conf.TLS = tlsConfigStruct
}

dialer, err := makeDialer(d)
if err != nil {
return nil, diag.Errorf("failed making dialer: %v", err)
Expand Down Expand Up @@ -359,6 +441,7 @@ func connectToMySQLInternal(ctx context.Context, conf *MySQLConfiguration) (*One
defer connectionCacheMtx.Unlock()

dsn := conf.Config.FormatDSN()
log.Printf("[DEBUG] Using dsn: %s", dsn)
if connectionCache[dsn] != nil {
return connectionCache[dsn], nil
}
Expand Down
16 changes: 16 additions & 0 deletions website/docs/index.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ resource "mysql_database" "app" {
}
```

Using encrypted connections can be done by using the `custom_tls` field in the provider
```hcl
petoju marked this conversation as resolved.
Show resolved Hide resolved
provider "mysql" {
endpoint = "my-database.example.com:3306"
username = "app-user"
custom_tls {
config_key = "custom_key"
ca_cert = "/path/to/certs/ca.pem"
client_cert = "/path/to/certs/client_cert.pem"
client_key = "/path/to/certs/client_key.pem"
}
}

```

### GCP CloudSQL Connection

For connections to GCP hosted instances, the provider can connect through the Cloud SQL MySQL library.
Expand Down Expand Up @@ -116,6 +131,7 @@ The following arguments are supported:
* `password` - (Optional) Password for the given user, if that user has a password, can also be sourced from the `MYSQL_PASSWORD` environment variable.
* `proxy` - (Optional) Proxy socks url, can also be sourced from `ALL_PROXY` or `all_proxy` environment variables.
* `tls` - (Optional) The TLS configuration. One of `false`, `true`, or `skip-verify`. Defaults to `false`. Can also be sourced from the `MYSQL_TLS_CONFIG` environment variable.
* `custom_tls` - (Optional) Sets custom tls options for the connection. This is a block containing an optional `config_key` and the following required arguments: `ca_cert`, `client_cert` and `client_key`. Documentation for encrypted connections can be found [here](https://dev.mysql.com/doc/refman/8.0/en/using-encrypted-connections.html).
petoju marked this conversation as resolved.
Show resolved Hide resolved
* `max_conn_lifetime_sec` - (Optional) Sets the maximum amount of time a connection may be reused. If d <= 0, connections are reused forever.
* `max_open_conns` - (Optional) Sets the maximum number of open connections to the database. If n <= 0, then there is no limit on the number of open connections.
* `conn_params` - (Optional) Sets extra mysql connection parameters (ODBC parameters). Most useful for session variables such as `default_storage_engine`, `foreign_key_checks` or `sql_log_bin`.
Expand Down
Loading