mysql.global_priv テーブル – MariaDB Server 10.4

2019年6月18日にGAとなった MariaDB Server 10.4 ではユーザ認証を管理するテーブルが mysql.user から mysql.global_priv テーブルに変更されています。
今回は mysql.global_priv テーブルについて解説いたします。

mysql.global_priv テーブル

従来,ユーザ認証情報は mysql.user テーブルに収められておりました。

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
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 テーブルのデータを確認すると,以下のようになっています。

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 関数を用いると少し見やすくすることができます。

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:

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) などで実行,インストールする必要があります。

INSTALL SONAME 'auth_ed25519';

念のため,show plugins で確認します(実行結果は抜粋しています)。

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 認証を用いる新規ユーザを作成してみます。

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 として定義されております。

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               |
+---------+-----------+----------+-----------------------+
MariaDB [mysql]> SHOW CREATE VIEW mysql.user \G
*************************** 1. row ***************************
                View: user
         Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `user` AS select `global_priv`.`Host` AS `Host`,`global_priv`.`User` AS `User`,if(json_value(`global_priv`.`Priv`,'$.plugin') in ('mysql_native_password','mysql_old_password'),ifnull(json_value(`global_priv`.`Priv`,'$.authentication_string'),''),'') AS `Password`,if(json_value(`global_priv`.`Priv`,'$.access') & 1,'Y','N') AS `Select_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 2,'Y','N') AS `Insert_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 4,'Y','N') AS `Update_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 8,'Y','N') AS `Delete_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 16,'Y','N') AS `Create_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 32,'Y','N') AS `Drop_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 64,'Y','N') AS `Reload_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 128,'Y','N') AS `Shutdown_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 256,'Y','N') AS `Process_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 512,'Y','N') AS `File_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 1024,'Y','N') AS `Grant_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 2048,'Y','N') AS `References_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 4096,'Y','N') AS `Index_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 8192,'Y','N') AS `Alter_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 16384,'Y','N') AS `Show_db_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 32768,'Y','N') AS `Super_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 65536,'Y','N') AS `Create_tmp_table_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 131072,'Y','N') AS `Lock_tables_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 262144,'Y','N') AS `Execute_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 524288,'Y','N') AS `Repl_slave_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 1048576,'Y','N') AS `Repl_client_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 2097152,'Y','N') AS `Create_view_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 4194304,'Y','N') AS `Show_view_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 8388608,'Y','N') AS `Create_routine_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 16777216,'Y','N') AS `Alter_routine_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 33554432,'Y','N') AS `Create_user_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 67108864,'Y','N') AS `Event_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 134217728,'Y','N') AS `Trigger_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 268435456,'Y','N') AS `Create_tablespace_priv`,if(json_value(`global_priv`.`Priv`,'$.access') & 536870912,'Y','N') AS `Delete_history_priv`,elt(ifnull(json_value(`global_priv`.`Priv`,'$.ssl_type'),0) + 1,'','ANY','X509','SPECIFIED') AS `ssl_type`,ifnull(json_value(`global_priv`.`Priv`,'$.ssl_cipher'),'') AS `ssl_cipher`,ifnull(json_value(`global_priv`.`Priv`,'$.x509_issuer'),'') AS `x509_issuer`,ifnull(json_value(`global_priv`.`Priv`,'$.x509_subject'),'') AS `x509_subject`,cast(ifnull(json_value(`global_priv`.`Priv`,'$.max_questions'),0) as unsigned) AS `max_questions`,cast(ifnull(json_value(`global_priv`.`Priv`,'$.max_updates'),0) as unsigned) AS `max_updates`,cast(ifnull(json_value(`global_priv`.`Priv`,'$.max_connections'),0) as unsigned) AS `max_connections`,cast(ifnull(json_value(`global_priv`.`Priv`,'$.max_user_connections'),0) as signed) AS `max_user_connections`,ifnull(json_value(`global_priv`.`Priv`,'$.plugin'),'') AS `plugin`,ifnull(json_value(`global_priv`.`Priv`,'$.authentication_string'),'') AS `authentication_string`,'N' AS `password_expired`,elt(ifnull(json_value(`global_priv`.`Priv`,'$.is_role'),0) + 1,'N','Y') AS `is_role`,ifnull(json_value(`global_priv`.`Priv`,'$.default_role'),'') AS `default_role`,cast(ifnull(json_value(`global_priv`.`Priv`,'$.max_statement_time'),0.0) as decimal(12,6)) AS `max_statement_time` from `global_priv`
character_set_client: latin1
collation_connection: latin1_swedish_ci

まとめ

MariaDB Server 10.4 から変更されたユーザ認証テーブルに関して解説させて頂きました。