-
-
Notifications
You must be signed in to change notification settings - Fork 100
Expand file tree
/
Copy pathtls_mysql.go
More file actions
89 lines (76 loc) · 2.68 KB
/
tls_mysql.go
File metadata and controls
89 lines (76 loc) · 2.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//go:build !nomysql
package sql_exporter
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"log/slog"
"net/url"
"os"
"sync"
"github.com/go-sql-driver/mysql"
)
const (
mysqlTLSParamCACert = "tls-ca"
mysqlTLSParamClientCert = "tls-cert"
mysqlTLSParamClientKey = "tls-key"
)
// mysqlTLSParams is a list of TLS parameters that can be used in MySQL DSNs. It is used to identify and strip TLS
// parameters from the DSN after registering the TLS configuration, as these parameters are not recognized by the MySQL
// driver and would cause connection failure if left in the DSN.
var (
mysqlTLSParams = []string{mysqlTLSParamCACert, mysqlTLSParamClientCert, mysqlTLSParamClientKey}
onceMap sync.Map
)
// handleMySQLTLSConfig wraps the registration of a MySQL TLS configuration in a thread-safe manner. It uses a
// sync.Once to ensure that the TLS configuration for a given config name is registered only once, even if multiple
// goroutines attempt to register it concurrently.
func handleMySQLTLSConfig(configName string, params url.Values) error {
onceConn, _ := onceMap.LoadOrStore(configName, &sync.Once{})
once := onceConn.(*sync.Once)
var err error
once.Do(func() {
err = registerMySQLTLSConfig(configName, params)
if err != nil {
slog.Error("Failed to register MySQL TLS config", "error", err)
}
})
return err
}
// registerMySQLTLSConfig registers a custom TLS configuration for MySQL with the given config name and parameters.
func registerMySQLTLSConfig(configName string, params url.Values) error {
caCert := params.Get(mysqlTLSParamCACert)
clientCert := params.Get(mysqlTLSParamClientCert)
clientKey := params.Get(mysqlTLSParamClientKey)
slog.Debug("MySQL TLS config", "configName", configName, mysqlTLSParamCACert, caCert,
mysqlTLSParamClientCert, clientCert, mysqlTLSParamClientKey, clientKey)
var rootCertPool *x509.CertPool
if caCert != "" {
rootCertPool = x509.NewCertPool()
pem, err := os.ReadFile(caCert)
if err != nil {
return fmt.Errorf("failed to read CA certificate: %w", err)
}
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
return errors.New("failed to append PEM")
}
}
var certs []tls.Certificate
if clientCert != "" || clientKey != "" {
if clientCert == "" || clientKey == "" {
return errors.New("both tls-cert and tls-key must be provided for client authentication")
}
cert, err := tls.LoadX509KeyPair(clientCert, clientKey)
if err != nil {
return fmt.Errorf("failed to load client certificate and key: %w", err)
}
certs = append(certs, cert)
}
tlsConfig := &tls.Config{
RootCAs: rootCertPool,
Certificates: certs,
MinVersion: tls.VersionTLS12,
}
return mysql.RegisterTLSConfig(configName, tlsConfig)
}