はじめに
2019年6月17日にGAとなった MariaDB Server 10.4 ではユーザ認証を管理するテーブルが mysql.user から mysql.global_priv テーブルに変更されています。
今回は mysql.global_priv テーブルについて解説いたします。
mysql.global_priv テーブル
従来,ユーザ認証情報は mysql.user テーブルに収められておりました。
1 2 3 4 5 6 7 8 |
MariaDB [(none)]> SELECT user,host,password,plugin FROM mysql.user; +----------+-------------+-------------------------------------------+--------+ | user | host | password | plugin | +----------+-------------+-------------------------------------------+--------+ | root | localhost | | | | root | 127.0.0.1 | | | | root | ::1 | | | +----------+-------------+-------------------------------------------+--------+ |
これに対して,10.4 ではユーザ情報を管理するテーブルが mysql.global_priv に変更され,その構成も変更されております。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# mysql Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 9 Server version: 10.4.5-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> DESC mysql.global_priv; +-------+----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+-------+ | Host | char(60) | NO | PRI | | | | User | char(80) | NO | PRI | | | | Priv | longtext | NO | | '{}' | | +-------+----------+------+-----+---------+-------+ |
Priv 列は longtext 型となっていますが,デフォルト値が ‘{}’ となっており,実際は JSON 型であることがわかります。
mysql.global_priv テーブルのデータを確認すると,以下のようになっています。
1 2 3 4 5 6 7 8 9 |
MariaDB [(none)]> SELECT * FROM mysql.global_priv; +-----------+-------+--------------------------------------------------------------------------------------------------------------------------------------------+ | Host | User | Priv | +-----------+-------+--------------------------------------------------------------------------------------------------------------------------------------------+ | localhost | root | {"access":18446744073709551615,"plugin":"mysql_native_password","authentication_string":"invalid","auth_or":[{},{"plugin":"unix_socket"}]} | | localhost | mysql | {"access":18446744073709551615,"plugin":"mysql_native_password","authentication_string":"invalid","auth_or":[{},{"plugin":"unix_socket"}]} | | localhost | | {} | | mdb104 | | {} | +-----------+-------+--------------------------------------------------------------------------------------------------------------------------------------------+ |
JSON_DETAILED 関数を用いると少し見やすくすることができます。
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 |
MariaDB [(none)]> SELECT CONCAT(user, '@', host, ' => ', JSON_DETAILED(priv)) FROM mysql.global_priv; +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | concat(user, '@', host, ' => ', json_detailed(priv)) | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | root@localhost => { "access": 18446744073709551615, "plugin": "mysql_native_password", "authentication_string": "invalid", "auth_or": [ { }, { "plugin": "unix_socket" } ] } | | mysql@localhost => { "access": 18446744073709551615, "plugin": "mysql_native_password", "authentication_string": "invalid", "auth_or": [ { }, { "plugin": "unix_socket" } ] } | | @localhost => { } | | @mdb104 => { } | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
root / mysql ユーザのパスワードハッシュ値は "authentication_string": "invalid" となっており,"plugin": "unix_socket" となっていますので,unix_socket プラグインで認証されています。
unix_socket プラグインは以前から Debian などでは有効になっていましたが,10.4.3 からデフォルトの認証形式となりました。この認証方式の場合,MariaDB ユーザ名と同じユーザが Linux OS上で存在すれば,MariaDB サーバに接続可能となります。
/etc/passwd:
1 2 |
root:x:0:0:root:/root:/bin/bash mysql:x:997:994:MySQL server:/var/lib/mysql:/sbin/nologin |
また,ユーザによって認証方式を変更することができますので,mysql_native_password を用いる既存ユーザがいて,新規ユーザにはよりセキュアな ed25519 認証を用いたいといった場合に,ユーザ毎に認証プラグインを変更することが可能となります。
Authentication Plugin – ed25519
mysql_native_password 認証では,SHA-1 を用いて計算されたハッシュ値が mysql.user テーブルの password 列に収められていましたが,SSL証明書などでも SHA-1 はかなり以前に非推奨となっており,SHA-256 を用いてハッシュを生成することが一般的で,現在の水準では十分に安全とはいえません。
そこで,10.1.22 から ed25519 認証プラグインが導入されています。ed25519 は Elliptic Curve Digital Signature Algorithm(ECSA) と呼ばれる OpenSSH でも用いられるアルゴリズムを用いています。
ed25519 プラグインはデフォルトではインストールされませんので,以下のコマンドを MariaDB monitor(mysql) などで実行,インストールする必要があります。
1 |
INSTALL SONAME 'auth_ed25519'; |
念のため,show plugins で確認します(実行結果は抜粋しています)。
1 2 3 4 5 6 7 8 |
MariaDB [(none)]> show plugins; +-------------------------------+----------+--------------------+-----------------+---------+ | Name | Status | Type | Library | License | +-------------------------------+----------+--------------------+-----------------+---------+ | mysql_native_password | ACTIVE | AUTHENTICATION | NULL | GPL | | mysql_old_password | ACTIVE | AUTHENTICATION | NULL | GPL | | ed25519 | ACTIVE | AUTHENTICATION | auth_ed25519.so | GPL | +-------------------------------+----------+--------------------+-----------------+---------+ |
正常にインストールされていることが確認できました。
ed25519 認証を用いる新規ユーザを作成してみます。
1 2 3 4 5 6 7 8 9 10 11 12 |
MariaDB [(none)]> GRANT ALL ON *.* TO safe@'%' IDENTIFIED VIA ed25519 USING PASSWORD('secret'); MariaDB [(none)]> SELECT CONCAT(user, '@', host, ' => ', JSON_DETAILED(priv)) FROM mysql.global_priv; +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | concat(user, '@', host, ' => ', json_detailed(priv)) | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | safe@% => { "access": 1073740799, "plugin": "ed25519", "authentication_string": "ZIgUREUg5PVgQ6LskhXmO+eZLS0nC8be6HPjYWR4YJY", "password_last_changed": 1560226756 } | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |
authentication_string にハッシュ値が入力されていることが確認できます。なお,ed25519 認証を用いるには,これに対応したバージョンの MariaDB Connector を用いる必要があります。
なお,互換性のため mysql.user は VIEW として定義されております。
1 2 3 4 5 6 7 8 |
MariaDB [(none)]> SELECT user,host,password,plugin FROM mysql.user; +---------+-----------+----------+-----------------------+ | User | Host | Password | plugin | +---------+-----------+----------+-----------------------+ | root | localhost | invalid | mysql_native_password | | mysql | localhost | invalid | mysql_native_password | | safe | % | | ed25519 | +---------+-----------+----------+-----------------------+ |
1 2 3 4 5 6 |
MariaDB [mysql]> SHOW CREATE VIEW mysql.user \G *************************** 1. row *************************** View: user Create View: CREATE ALGORITHM=UNDEFINED DEFINER=<code>root</code>@<code>localhost</code> SQL SECURITY DEFINER VIEW <code>user</code> AS select <code>global_priv</code>.<code>Host</code> AS <code>Host</code>,<code>global_priv</code>.<code>User</code> AS <code>User</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.plugin') in ('mysql_native_password','mysql_old_password'),ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.authentication_string'),''),'') AS <code>Password</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 1,'Y','N') AS <code>Select_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 2,'Y','N') AS <code>Insert_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 4,'Y','N') AS <code>Update_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 8,'Y','N') AS <code>Delete_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 16,'Y','N') AS <code>Create_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 32,'Y','N') AS <code>Drop_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 64,'Y','N') AS <code>Reload_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 128,'Y','N') AS <code>Shutdown_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 256,'Y','N') AS <code>Process_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 512,'Y','N') AS <code>File_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 1024,'Y','N') AS <code>Grant_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 2048,'Y','N') AS <code>References_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 4096,'Y','N') AS <code>Index_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 8192,'Y','N') AS <code>Alter_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 16384,'Y','N') AS <code>Show_db_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 32768,'Y','N') AS <code>Super_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 65536,'Y','N') AS <code>Create_tmp_table_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 131072,'Y','N') AS <code>Lock_tables_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 262144,'Y','N') AS <code>Execute_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 524288,'Y','N') AS <code>Repl_slave_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 1048576,'Y','N') AS <code>Repl_client_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 2097152,'Y','N') AS <code>Create_view_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 4194304,'Y','N') AS <code>Show_view_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 8388608,'Y','N') AS <code>Create_routine_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 16777216,'Y','N') AS <code>Alter_routine_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 33554432,'Y','N') AS <code>Create_user_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 67108864,'Y','N') AS <code>Event_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 134217728,'Y','N') AS <code>Trigger_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 268435456,'Y','N') AS <code>Create_tablespace_priv</code>,if(json_value(<code>global_priv</code>.<code>Priv</code>,'$.access') & 536870912,'Y','N') AS <code>Delete_history_priv</code>,elt(ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.ssl_type'),0) + 1,'','ANY','X509','SPECIFIED') AS <code>ssl_type</code>,ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.ssl_cipher'),'') AS <code>ssl_cipher</code>,ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.x509_issuer'),'') AS <code>x509_issuer</code>,ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.x509_subject'),'') AS <code>x509_subject</code>,cast(ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.max_questions'),0) as unsigned) AS <code>max_questions</code>,cast(ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.max_updates'),0) as unsigned) AS <code>max_updates</code>,cast(ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.max_connections'),0) as unsigned) AS <code>max_connections</code>,cast(ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.max_user_connections'),0) as signed) AS <code>max_user_connections</code>,ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.plugin'),'') AS <code>plugin</code>,ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.authentication_string'),'') AS <code>authentication_string</code>,'N' AS <code>password_expired</code>,elt(ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.is_role'),0) + 1,'N','Y') AS <code>is_role</code>,ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.default_role'),'') AS <code>default_role</code>,cast(ifnull(json_value(<code>global_priv</code>.<code>Priv</code>,'$.max_statement_time'),0.0) as decimal(12,6)) AS <code>max_statement_time</code> from <code>global_priv character_set_client: latin1 collation_connection: latin1_swedish_ci |
まとめ
MariaDB Server 10.4 から変更されたユーザ認証テーブルに関して解説させて頂きました。