brianmario / mysql2

A modern, simple and very fast Mysql library for Ruby - binding to libmysql
http://github.com/brianmario/mysql2
MIT License
2.25k stars 550 forks source link

Support get_server_public_key option #1377

Closed samitani closed 2 months ago

samitani commented 2 months ago

What

This PR fixes below MySQL authentication error with SSL-disabled connection.

Mysql2::Error: Authentication plugin 'caching_sha2_password' reported error: 
Authentication requires secure connection.

Background

This error only happen in case of first authentication under cleartext TCP connection. caching_sha2_password authentication requires a secure connection at first authentication for each users. The first authentication means that the cache is not ready yet at server-side. The reason why secure connection is required is that MySQL server need to know user password to create cache. After the cache is created, MySQL server does not request secure connection anymore. Please refer to the MySQL manual to know details.

For each user account, the first client connection for the user after any of the following operations must use a secure connection (made using TCP using TLS credentials, a Unix socket file, or shared memory) or RSA key pair-based password exchange: https://dev.mysql.com/doc/refman/8.0/en/caching-sha2-pluggable-authentication.html

libmysqlclient provides MYSQL_OPT_GET_SERVER_PUBLIC_KEY option which enables clients to create a requested secure connection automatically during authentication. This PR carries it to mysql2.

Sample code

require 'mysql2'

port     = 3306
hostname = 'hostname'
username = 'user'
password = 'password'

# connect with SSL to issue FLUSH PRIVILEGES
client = Mysql2::Client.new(:host => hostname, :port => port, :username => username, :password => password, :ssl_mode => :required)

# force clear cache
client.query("FLUSH PRIVILEGES")

print "# without get_server_public_key\n"
# this returns Authentication requires secure connection.
begin
  client = Mysql2::Client.new(:host => hostname, :port => port, :username => username, :password => password, :ssl_mode => :disabled)
rescue => e
  printf("connect ng %s\n", e)
end

print "# with get_server_public_key\n"
begin
  client = Mysql2::Client.new(:host => hostname, :port => port, :username => username, :password => password, :ssl_mode => :disabled, :get_server_public_key => true)
  print "connect ok\n"
rescue => e
  printf("connect ng %s\n", e)
end
$ ruby ../test2.rb
# without get_server_public_key
connect ng Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection.
# with get_server_public_key
connect ok
sodabrew commented 2 months ago

Thank you for the PR!