Description
It is currently not possible to enable SSL through the ssl_mode parameter when using the MariaDB driver library.
This is critical because you can't connect to a MySQL server with a user that requires SSL.
How to reproduce?
Versions:
$ lsb_release -d
Description: Ubuntu 20.04.2 LTS
$ ruby --version
ruby 2.6.6p146 (2020-03-31 revision 67876) [x86_64-linux]
$ gem --version
3.0.9
$ bundle --version
Bundler version 1.17.3
$ dpkg --list mysql-server-8.0
ii mysql-server-8.0 8.0.23-0ubuntu0.20.04.1 amd64 MySQL database server binaries and system database setup
Create a MySQL database user with "REQUIRE SSL":
> CREATE USER IF NOT EXISTS test@127.0.0.1
IDENTIFIED WITH mysql_native_password BY 'test'
REQUIRE SSL;
Create a test script:
$ vim Gemfile
source 'https://rubygems.org'
gem 'mysql2'
$ vim test.rb
require 'mysql2'
client = Mysql2::Client.new(host: '127.0.0.1', username: 'test', password: 'test', ssl_mode: :required)
puts client.query('SHOW STATUS LIKE "Ssl_cipher"').first.inspect
🔴 Test with the MariaDB driver library
Install the driver library and the mysql2 gem:
$ sudo apt-get install libmariadb-dev-compat
$ dpkg --list libmariadb-dev-compat
...
ii libmariadb-dev-compat:amd64 1:10.3.25-0ubuntu0.20.04.1 amd64 MariaDB Connector/C, compatibility symlinks
$ bundle exec gem uninstall mysql2
Successfully uninstalled mysql2-0.5.3
$ bundle
...
Installing mysql2 0.5.3 with native extensions
...
$ ldd $(bundle show mysql2)/lib/mysql2/mysql2.so
...
libmariadb.so.3 => /usr/lib/x86_64-linux-gnu/libmariadb.so.3 (0x00007efe06293000)
...
Run the test script:
$ bundle exec ruby test.rb
/home/clemens/.rvm/gems/ruby-2.6.6/gems/mysql2-0.5.3/lib/mysql2/client.rb:51: warning: Your mysql client library does not support ssl_mode as expected.
Traceback (most recent call last):
3: from test.rb:2:in `<main>'
2: from test.rb:2:in `new'
1: from /home/clemens/.rvm/gems/ruby-2.6.6/gems/mysql2-0.5.3/lib/mysql2/client.rb:90:in `initialize'
/home/clemens/.rvm/gems/ruby-2.6.6/gems/mysql2-0.5.3/lib/mysql2/client.rb:90:in `connect': Access denied for user 'test'@'localhost' (using password: YES) (Mysql2::Error::ConnectionError)
🟢 Test with the MySQL driver library
Install the driver library and the mysql2 gem:
$ sudo apt-get install libmysqlclient-dev
$ dpkg --list libmysqlclient-dev
...
ii libmysqlclient-dev 8.0.23-0ubuntu0.20.04.1 amd64 MySQL database development files
$ bundle exec gem uninstall mysql2
Successfully uninstalled mysql2-0.5.3
$ bundle
...
Installing mysql2 0.5.3 with native extensions
...
$ ldd $(bundle show mysql2)/lib/mysql2/mysql2.so
...
libmysqlclient.so.21 => /usr/lib/x86_64-linux-gnu/libmysqlclient.so.21 (0x00007f0ac5a7b000)
...
Run the test script:
$ bundle exec ruby test.rb
{"Variable_name"=>"Ssl_cipher", "Value"=>"TLS_AES_256_GCM_SHA384"}
🟢 Solution?
With the MariaDB driver, the "mysql_get_client_version" at https://github.com/brianmario/mysql2/blob/0.5.3/ext/mysql2/client.c#L107 returns 100325 and HAVE_CONST_MYSQL_OPT_SSL_ENFORCE at https://github.com/brianmario/mysql2/blob/0.5.3/ext/mysql2/client.c#L113 is defined. The version that "mysql_get_client_version" returns is calculated at https://github.com/mariadb-corporation/mariadb-connector-c/blob/v3.1.12/CMakeLists.txt#L173.
With the MySQL driver, the "mysql_get_client_version" at https://github.com/brianmario/mysql2/blob/0.5.3/ext/mysql2/client.c#L107 returns 80023 and FULL_SSL_MODE_SUPPORT at https://github.com/brianmario/mysql2/blob/0.5.3/ext/mysql2/client.c#L131 is defined.
After adding a new version range for the MariaDB driver to https://github.com/brianmario/mysql2/blob/0.5.3/ext/mysql2/client.c#L117 the ssl_mode is correctly set and SSL is used for the database connection.
1. Create a patched mysql2 gem
$ git clone https://github.com/brianmario/mysql2.git
$ cd mysql2
$ git checkout 0.5.3
$ vim ext/mysql2/client.c
...
if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200) || (version >= 100200 && version < 110000)) {
...
$ git diff
diff --git a/ext/mysql2/client.c b/ext/mysql2/client.c
index 13fd2dd..e484fa0 100644
--- a/ext/mysql2/client.c
+++ b/ext/mysql2/client.c
@@ -114,7 +114,7 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
GET_CLIENT(self);
int val = NUM2INT( setting );
// Either MySQL 5.7.3 - 5.7.10, or Connector/C 6.1.3 - 6.1.x
- if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200)) {
+ if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200) || (version >= 100200 && version < 110000)) {
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
my_bool b = ( val == SSL_MODE_REQUIRED );
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
$ gem build mysql2
$ cp mysql2-0.5.3.gem /tmp
2. Install the MariaDB driver library and the patched mysql2 gem
$ sudo apt-get install libmariadb-dev-compat
$ dpkg --list libmariadb-dev-compat
...
ii libmariadb-dev-compat:amd64 1:10.3.25-0ubuntu0.20.04.1 amd64 MariaDB Connector/C, compatibility symlinks
$ bundle exec gem uninstall mysql2
Successfully uninstalled mysql2-0.5.3
$ gem install /tmp/mysql2-0.5.3.gem
...
Successfully installed mysql2-0.5.3
...
$ ldd $(bundle show mysql2)/lib/mysql2/mysql2.so
...
libmariadb.so.3 => /usr/lib/x86_64-linux-gnu/libmariadb.so.3 (0x00007f4576d6e000)
...
3. Run the test script
$ bundle exec ruby test.rb
{"Variable_name"=>"Ssl_cipher", "Value"=>"TLS_AES_256_GCM_SHA384"}
Activity
junaruga commentedon Apr 10, 2021
@vakuum thank you for the detailed report and the investigation!
When the
libmariadb-dev-compat:amd64 1:10.3.25
returns the client version100325
, why is the patch|| (version >= 100200 && version < 110000)
? For example why not|| (version >= 100100 && version < 110000)
. I do not understand it.We have CI cases on GitHub Actions. Could you sent the PR for your patch, adding your case to the GitHub Actions as one case in
build.yml
- include syntax?mysql2/.github/workflows/build.yml
Lines 30 to 39 in 346b4a4
You can use
ubuntu-20.04
on it. Maybe you see a test failure with themysql-server-8.0
(#1165), but you can setallow-failure: true
on the CI.junaruga commentedon Apr 10, 2021
Maybe we can modify the comment in
rb_set_ssl_mode_option
like this.junaruga commentedon Apr 10, 2021
I guess the
|| (version >= 100200 && version < 110000)
means "MariaDB 10.2.0 - (< 11)". If the range of the versions in the MariaDB client is right, the comment could be like this.brianmario#1182: Can't enable SSL with MariaDB driver library.
Can't enable SSL with MariaDB driver library. (brianmario#1182)
junaruga commentedon Apr 10, 2021
@vakuum Do you know how can we enable
HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
used inclient.c
? I think it is necessary to reproduce the warningwarning: Your mysql client library does not support ssl_mode as expected.
?junaruga commentedon Apr 10, 2021
@sodabrew Do you know how to enable the macro
HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
related to #1186 ?I see you worked on this kind of things in the past. ff05239
junaruga commentedon Apr 10, 2021
This one?
https://dev.mysql.com/doc/c-api/5.7/en/mysql-options.html
junaruga commentedon Apr 10, 2021
I got a feedback at #1186 . Maybe we should not add the new CI case: DB server: MySQL, and DB client: MariaDB for now. Instead of that, we need to add a new unit test that can detect this issue. Because on the current unit tests on the current CI cases, this issue (the warning) is not detected.
junaruga commentedon Apr 11, 2021
I captured the compiler's flags used for
ext/mysql2/client.c
by a patch #1187 . I just checked the following 3 cases. And I could not find theHAVE_CONST_MYSQL_OPT_SSL_ENFORCE
macro. Possibly there is no CI case enabling the macro right now on CI. It's better to add the case on CI, isn't it?GitHub - Build Ruby 3.0 case.
GitHub - Container CentOS 7 Ruby 2.0.0 case.
Travis Ruby 2.4 DB=mariadb10.3 case.
vakuum commentedon Apr 11, 2021
@junaruga The macro HAVE_CONST_MYSQL_OPT_SSL_ENFORCE is defined in the generated Makefile when using the MariaDB driver:
The following spec fails without the change of https://github.com/brianmario/mysql2/blob/0.5.3/ext/mysql2/client.c#L117 and the warning "Your mysql client library does not support ssl_mode as expected" is also printed:
After adding the new version range to https://github.com/brianmario/mysql2/blob/0.5.3/ext/mysql2/client.c#L117 the spec turns green.
junaruga commentedon Apr 11, 2021
@vakuum Thanks for the info.
OK. You see the macro definition. I also can see the macro on my local. The defined macro The macro in the
Makefile
CPPFLAGS
(right?) is used as a compiler's flags.So, if you run the following command. You can see the macro on the command line to compile
client.c
.I found the reason. How the macro is defined, and used.
First the maccos are checked from the MySQL/MariaDB client library's header.
mysql2/ext/mysql2/extconf.rb
Line 112 in 346b4a4
Then,
mysql2/ext/mysql2/extconf.rb
Lines 15 to 21 in 346b4a4
When
have_const('MYSQL_OPT_SSL_ENFORCE', header)
executes and thehave_const
returns true, "maybe" theHAVE_CONST_MYSQL_OPT_SSL_ENFORCE
is set to theMakefile
internally by Ruby'smkmf
library.For the above GitHub - Build Ruby 3.0 case, I found the
-DFULL_SSL_MODE_SUPPORT
in the commands to compileclient.c
.For the above GitHub - Container CentOS 7 Ruby 2.0.0 case and Travis Ruby 2.4 DB=mariadb10.3 case, I found the
-DNO_SSL_MODE_SUPPORT
in the commands to compileclient.c
.And seeing the logic in the
extconf.rb
, for both cases, thehave_const('MYSQL_OPT_SSL_ENFORCE', header)
is not executed. That's why we do not see the macroHAVE_CONST_MYSQL_OPT_SSL_ENFORCE
on the current CI cases.25 remaining items