-
Notifications
You must be signed in to change notification settings - Fork 976
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 MySQL 8.0 and support for TLS/SSL for both Server and Client #312
Conversation
…bug when sha256_password is empty
Great work, thank you @michael2008 |
Hi @michael2008 seem you have supported two features - 8.0 and TLS here, can we split into two PRs? |
@siddontang About two PRs, a bit difficult. The reason to supporting TLS/SSL is that it is required for sha256_password and caching_sha2_password authentication. Basically MySQL supports two ways to send the client password to the server: one is to send as plain text via an established TLS channel; the other one is to send as encrypted text using a public key retrieved from the server. So the support of TLS connection may be taken as the side-effect of supporting new authentication methods. However, for the users, it seems that you can use TLS at the very beginning of connecting to the server. |
Great work @michael2008, we would review and test it |
@GregoryIan I am do more tests with clients other than go-sql-driver. There are still some incompatibilities. I am working on them. I will give you feedback when it's done. |
@GregoryIan Hi, more tests are done. Now the Server is more compatible with MySQL server regarding the authentication part. Tested using the native MySQL Client (both version 5.7.23 and 8.0.12) against the Server. Tests all passed and showed promising results. I think my works are almost done here. If you guys should find any problems, please let me know and I will fix them. |
} | ||
|
||
// encode length of the auth plugin data | ||
var authRespLEIBuf [9]byte |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does LEI mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's Length-Encoded-Integer(LEI), I will add comment here
client/auth.go
Outdated
func (c *Conn) writeAuthHandshake() error { | ||
if c.authPluginName != "mysql_native_password" && c.authPluginName != "caching_sha2_password" && c.authPluginName != "sha256_password" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you extract to a function like authPluginAllowed(name)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
@@ -5,11 +5,16 @@ import ( | |||
"crypto/tls" | |||
"encoding/binary" | |||
|
|||
"fmt" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the above blank line to group the standard libraries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
client/auth.go
Outdated
@@ -50,9 +61,8 @@ func (c *Conn) readInitialHandshake() error { | |||
|
|||
c.status = binary.LittleEndian.Uint16(data[pos : pos+2]) | |||
pos += 2 | |||
|
|||
//capability flags (upper 2 bytes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// capability
leave a space here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added space before comments
return nil, true, nil | ||
} | ||
if c.tlsConfig != nil || c.proto == "unix" { | ||
// write cleartext auth packet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clear text?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reference for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see here: https://dev.mysql.com/doc/refman/8.0/en/sha256-pluggable-authentication.html
If the connection is secure, an RSA key pair is unnecessary and is not used. This applies to encrypted connections that use TLS. The password is sent as cleartext but cannot be snooped because the connection is secure.
If the connection is not secure, and an RSA key pair is available, the connection remains unencrypted. This applies to unencrypted connections without TLS. RSA is used only for password exchange between client and server, to prevent password snooping. When the server receives the encrypted password, it decrypts it. A scramble is used in the encryption to prevent repeat attacks.
If a secure connection is not used and RSA encryption is not available, the connection attempt fails because the password cannot be sent without being exposed as cleartext.```
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added ref as comment in the code
} | ||
|
||
return nil | ||
} | ||
|
||
// generate auth response data according to auth plugin | ||
func (c *Conn) genAuthResponse(authData []byte) ([]byte, bool, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which does bool mean here? can you add a comment here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good question. Sure, the bool determines whether to add a \NUL to the end of data. That is quite tricky actually because mysql dev doc is very unclear about the message endings. The rules here are interpreted from MySQL server response packets. Besides, the MySQL implementation does not quite follow their docs in some response messages.
client/client_test.go
Outdated
|
||
// the certificates are in go-mysql/docker/resources | ||
|
||
var caPem = []byte(`-----BEGIN CERTIFICATE----- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find that many places use the same keys, can we use move them to one place to reduce the code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe move to the siddontang/mysql folder? but I think it's a bit confusing since the keys are for testing purpose only and the mysql folder is an actual working code base and if we do move the keys there, we have to expose them outside the package and that for users will be an unnecessary leak.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to create a test_util mod which only the tests can use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok that will do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
already fixed
client/resp.go
Outdated
} | ||
|
||
// handle caching_sha2_password | ||
if c.authPluginName == "caching_sha2_password" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better to use const for all plugin names to avoid a typo. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all replace in Client and Server
|
||
s.db, err = sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s", *testUser, *testPassword, *testAddr, *testDB)) | ||
s.db, err = sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?tls=%s", *testUser, *testPassword, *testAddr, *testDB, s.tlsPara)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
must we use tls for the test now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, it's controlled by the test configs. some tests set this value to 'false' to use non-tls connections and for TLS tests the value is set to 'skip-verify'
PTAL @GregoryIan @holys This PR is awesome, I can't wait to merge it, but I know we need detailed reviews and hope we can merge it next week. :-) @michael2008 can you give us your email? we can keep in touch later. |
@siddontang Sure, I will send you an email. Love to collaborate with you guys. BTW, PingCap is really great and you guys are awesome. You've contributed a lot to the open source community. |
sorry to have waited so long, we would focus on it next week @michael2008 @siddontang |
@@ -109,6 +112,18 @@ func (c *Conn) Ping() error { | |||
return nil | |||
} | |||
|
|||
// use default SSL | |||
// pass to options when connect | |||
func (c *Conn) UseSSL(insecureSkipVerify bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about insecureVerify
? while I call UseSSL((true)
, I think it need to verify
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func UseSSL() means we use secure link. For golang TLS, the default 'insecureSkipVerify=false' requires certificates verification for security reasons. If one needs to use self-signed certs, the 'insecureSkipVerify' must be explicitly set to true. So I think it better be the same as golang crypto lib.
seem some wrong with table map event in 8.0.12 MySQL. can u solve it ? @michael2008 |
hi @GregoryIan can we fix it by ourselves? |
I think this is a different issue from MySQL Authentication. More of a binlog format changing in MySQL 8. Should we open a new issue for this? |
ok, we can merge it now, we would fix it later @michael2008 @siddontang |
can you solve the conflict? @michael2008 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolve conflict and merge error
@siddontang conflict resolved |
This PR:
Since the upgrade of auth methods affects the Client and Server design, I made lots of changes to the code while trying to reuse the old code. Some minor existing bugs are fixed too. For instance, the buffer reader for the net.Conn is now removed because it causes the SSL handshake to fail.
Changing and refactoring can be a bad thing though as they can introduce new bugs. However, I tried my best to make the old tests pass and added new feature tests for different MySQL versions using docker compose to increase testing coverage. For now all test cases passed.