Skip to content

Commit

Permalink
Improve TLS documentation for older servers
Browse files Browse the repository at this point in the history
Older/EOL database server versions tend to be built with ancient OpenSSL or
yaSSL, which lack support for modern cipher suites and/or lack TLS 1.2+. At
the same time, recent Golang versions have updated the default client
tls.Config in ways that are incompatible with these old server versions. This
commit improves TLS documentation to mention this incompatibility, provide
sample code for solving it, and explain how "preferred" plaintext fallback
mode is not triggered in cases of TLS incompatibilities.

Closes #1635 by providing example code for solving the handshake failure.

Additional information which may be helpful for reviewers/maintainers:

TLS version
* Go 1.18+ changes the default client TLS MinVersion to be TLS 1.2
* MySQL 5.5 and 5.6 supports TLS 1.0
* MySQL 5.7.0-5.7.27 supports TLS 1.1
* MySQL 5.7.28+ supports TLS 1.2
* MariaDB 10.1+ supports TLS 1.2
* I did not examine MySQL 5.1 or MariaDB 10.0 or anything more ancient

Cipher suites
* Go 1.22+ changes the default client TLS config to remove cipher suites which
  use RSA key exchange
* MySQL 8.0+ and MariaDB 10.2+ fully support ECDHE cipher suites and are
  compatible with Go's current default cipher suite list.
* MySQL 5.x typically needs RSA key exchange cipher suites, due to
  https://bugs.mysql.com/bug.php?id=82935. Likewise for MariaDB 10.1.
* There are some exceptions, for example Percona Server 5.7 is built with a
  newer OpenSSL, https://docs.percona.com/percona-server/5.7/security/ssl-improvement.html
* It is also possible to custom compile MySQL 5.7 with a newer OpenSSL
  version to solve the cipher suite issue, but this is not common.
  • Loading branch information
evanelias committed Jan 3, 2025
1 parent 7403860 commit 0c6cf40
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 2 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,11 @@ Valid Values: true, false
Default: false
```

`allowFallbackToPlaintext=true` acts like a `--ssl-mode=PREFERRED` MySQL client as described in [Command Options for Connecting to the Server](https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#option_general_ssl-mode)
`allowFallbackToPlaintext=true` permits fallback to an unencrypted connection if the server is not configured to use TLS. This behavior is enabled automatically when using `tls=preferred`, but it is also available as a separate parameter to allow plaintext fallback when using a custom TLS configuration.

Plaintext fallback only occurs if the server is not configured to use TLS at all. MySQL 5.7+ and MariaDB 11.4+ ship with TLS support pre-enabled by default using a self-signed server cert, so plaintext fallback typically won't occur with these server versions.

Setting `allowFallbackToPlaintext=true` does **not** allow plaintext fallback in situations where the server has TLS enabled but is incompatible with the client configuration's cipher suites or TLS version. In recent Golang versions, establishing an encrypted connection to older server versions (e.g. MySQL 5.x or MariaDB 10.1) often requires a custom TLS configuration which allows RSA key exchange cipher suites. Very old server versions also require reducing the client's minimum TLS version. See documentation for [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig) for example code.

##### `allowNativePasswords`

Expand Down Expand Up @@ -431,8 +435,9 @@ Valid Values: true, false, skip-verify, preferred, <name>
Default: false
```

`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) or use `preferred` to use TLS only when advertised by the server. This is similar to `skip-verify`, but additionally allows a fallback to a connection which is not encrypted. Neither `skip-verify` nor `preferred` add any reliable security. You can use a custom TLS config after registering it with [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
`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) or use `preferred` to use TLS only when advertised by the server. Using `tls=preferred` is equivalent to setting `tls=skip-verify&allowFallbackToPlaintext=true`, which permits fallback to an unencrypted connection if the server is not configured to use TLS at all. Neither `skip-verify` nor `preferred` add any reliable security. You can use a custom TLS config after registering it with [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).

In recent Golang versions, establishing an encrypted connection to older server versions (MySQL 5.x or MariaDB 10.1) often requires a custom TLS configuration, in order to allow RSA key exchange cipher suites and a lower minimum TLS version. See documentation for [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig) for example code.

##### `writeTimeout`

Expand Down
24 changes: 24 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,33 @@ var (
// log.Fatal(err)
// }
// clientCert = append(clientCert, certs)
// cipherSuites := []uint16{
// // These 10 are the normal Go 1.22+ defaults
// tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
// tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
// tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
// tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
// tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
// tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
// tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
// tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
// tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
// tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
//
// // These 4 use RSA key exchange, no longer included by default in Go 1.22+
// // but often needed to connect to MySQL 5.7, MariaDB 10.1, or anything older
// tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
// tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
// tls.TLS_RSA_WITH_AES_128_CBC_SHA,
// tls.TLS_RSA_WITH_AES_256_CBC_SHA,
// }
// mysql.RegisterTLSConfig("custom", &tls.Config{
// RootCAs: rootCertPool,
// Certificates: clientCert,
//
// // Only include the following two lines when supporting older servers
// CipherSuites: cipherSuites, // in Go 1.22+, allow TLS connection to MySQL 5.x or MariaDB 10.1 or older
// MinVersion: tls.VersionTLS10, // in Go 1.18+, allow TLS connection to MySQL 5.6 or older
// })
// db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom")
func RegisterTLSConfig(key string, config *tls.Config) error {
Expand Down

0 comments on commit 0c6cf40

Please sign in to comment.