MySQL8で消してしまったrootユーザを復活させる

Web・情報技術
mysql> select * from mysql.user where User="root";
Empty set (0.00 sec)

そんなアホな!rootユーザを消してしまった場合の復活の手順を記す。概要はこう。

  1. MySQLを停止し、セーフモードで起動
  2. rootユーザを作成
  3. 権限を付与
  4. セーフモードのMySQLをkill
  5. 通常モードのMySQLでパスワードを設定

MySQLのバージョンは8.0、LinuxのディストリビューションはUbuntu 20.04。

# mysql --version
mysql  Ver 8.0.26-0ubuntu0.20.04.2 for Linux on x86_64 ((Ubuntu))

# cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=20.04
DISTRIB_CODENAME=focal
DISTRIB_DESCRIPTION="Ubuntu 20.04.3 LTS"

MySQLの停止、セーフモードで起動

権限を無視してMySQLを操作する必要があるため、オプションをつけてセーフモードでMySQLを起動する必要がある。これに先立ちMySQLを一旦停止する必要がある。

systemctlコマンドでMySQLを停止する。

# systemctl stop mysql

続いて、MySQLを権限テーブルを無視するオプションをつけて、セーフモードで起動する。

# mysqld_safe --skip-grant-tables
[1] 126431
root@i-12100000190719:/var/www/killtime# 2021-10-04T03:27:58.764196Z mysqld_safe Logging to '/var/log/mysql/error.log'.
2021-10-04T03:27:58.766445Z mysqld_safe Directory '/var/run/mysqld' for UNIX socket file don't exists.

……ソケットファイルに使う/var/run/mysqld/が存在しないと怒られる。 Ctrl+Cで中断して、ディレクトリを作成する。

# mkdir /var/run/mysqld
# chown mysql:mysql /var/run/mysqld
# mysqld_safe --skip-grant-tables
[1] 142867
root@host:/path/to/current/dir# 2021-10-07T03:29:22.417651Z mysqld_safe Logging to '/var/log/mysql/error.log'.
2021-10-07T03:29:22.445744Z mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql

なんかメッセージが出て動かせない状態になる。

セーフモードで起動するコマンド”mysqld_safe –skip-grant-tables”について、末尾に「&」をつけてバックグラウンド起動するように書いてある記事があるが、mysql_safeがmysqldをdaemonとして起動させるからか、私の環境ではフォアグラウンドでもバックグラウンドでも同じだった。

Ctrl+CないしCtrl+Zで↑を閉じ、プロセスを確認するとmysqlが起動していることがわかる。

# ps aux | grep mysql
root      126842  0.0  0.4  10956  4840 pts/1    S    12:36   0:00 sudo mysqld_safe --skip-grant-tables
root      126843  0.0  0.1   2608  1784 pts/1    S    12:36   0:00 /bin/sh /usr/bin/mysqld_safe --skip-grant-tables
mysql     127000  0.8 38.0 1285732 382696 pts/1  Sl   12:36   0:01 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --skip-grant-tables --log-error=/var/log/mysql/error.log --pid-file=i-12100000190719.pid

MySQLにログインする。パスワード不要。

# mysql
mysql> 

rootユーザの作成

データベースを変更。

mysql> use mysql;
Databases changed

ここでUserテーブルのカラムとかデフォルト値とか見てみる。

mysql> show columns in user;
+--------------------------+-----------------------------------+------+-----+-----------------------+-------+
| Field                    | Type                              | Null | Key | Default               | Extra |
+--------------------------+-----------------------------------+------+-----+-----------------------+-------+
| Host                     | char(255)                         | NO   | PRI |                       |       |
| User                     | char(32)                          | NO   | PRI |                       |       |
| Select_priv              | enum('N','Y')                     | NO   |     | N                     |       |
| Insert_priv              | enum('N','Y')                     | NO   |     | N                     |       |
| Update_priv              | enum('N','Y')                     | NO   |     | N                     |       |
| Delete_priv              | enum('N','Y')                     | NO   |     | N                     |       |
| Create_priv              | enum('N','Y')                     | NO   |     | N                     |       |
| Drop_priv                | enum('N','Y')                     | NO   |     | N                     |       |
| Reload_priv              | enum('N','Y')                     | NO   |     | N                     |       |
| Shutdown_priv            | enum('N','Y')                     | NO   |     | N                     |       |
| Process_priv             | enum('N','Y')                     | NO   |     | N                     |       |
| File_priv                | enum('N','Y')                     | NO   |     | N                     |       |
| Grant_priv               | enum('N','Y')                     | NO   |     | N                     |       |
| References_priv          | enum('N','Y')                     | NO   |     | N                     |       |
| Index_priv               | enum('N','Y')                     | NO   |     | N                     |       |
| Alter_priv               | enum('N','Y')                     | NO   |     | N                     |       |
| Show_db_priv             | enum('N','Y')                     | NO   |     | N                     |       |
| Super_priv               | enum('N','Y')                     | NO   |     | N                     |       |
| Create_tmp_table_priv    | enum('N','Y')                     | NO   |     | N                     |       |
| Lock_tables_priv         | enum('N','Y')                     | NO   |     | N                     |       |
| Execute_priv             | enum('N','Y')                     | NO   |     | N                     |       |
| Repl_slave_priv          | enum('N','Y')                     | NO   |     | N                     |       |
| Repl_client_priv         | enum('N','Y')                     | NO   |     | N                     |       |
| Create_view_priv         | enum('N','Y')                     | NO   |     | N                     |       |
| Show_view_priv           | enum('N','Y')                     | NO   |     | N                     |       |
| Create_routine_priv      | enum('N','Y')                     | NO   |     | N                     |       |
| Alter_routine_priv       | enum('N','Y')                     | NO   |     | N                     |       |
| Create_user_priv         | enum('N','Y')                     | NO   |     | N                     |       |
| Event_priv               | enum('N','Y')                     | NO   |     | N                     |       |
| Trigger_priv             | enum('N','Y')                     | NO   |     | N                     |       |
| Create_tablespace_priv   | enum('N','Y')                     | NO   |     | N                     |       |
| ssl_type                 | enum('','ANY','X509','SPECIFIED') | NO   |     |                       |       |
| ssl_cipher               | blob                              | NO   |     | NULL                  |       |
| x509_issuer              | blob                              | NO   |     | NULL                  |       |
| x509_subject             | blob                              | NO   |     | NULL                  |       |
| max_questions            | int unsigned                      | NO   |     | 0                     |       |
| max_updates              | int unsigned                      | NO   |     | 0                     |       |
| max_connections          | int unsigned                      | NO   |     | 0                     |       |
| max_user_connections     | int unsigned                      | NO   |     | 0                     |       |
| plugin                   | char(64)                          | NO   |     | caching_sha2_password |       |
| authentication_string    | text                              | YES  |     | NULL                  |       |
| password_expired         | enum('N','Y')                     | NO   |     | N                     |       |
| password_last_changed    | timestamp                         | YES  |     | NULL                  |       |
| password_lifetime        | smallint unsigned                 | YES  |     | NULL                  |       |
| account_locked           | enum('N','Y')                     | NO   |     | N                     |       |
| Create_role_priv         | enum('N','Y')                     | NO   |     | N                     |       |
| Drop_role_priv           | enum('N','Y')                     | NO   |     | N                     |       |
| Password_reuse_history   | smallint unsigned                 | YES  |     | NULL                  |       |
| Password_reuse_time      | smallint unsigned                 | YES  |     | NULL                  |       |
| Password_require_current | enum('N','Y')                     | YES  |     | NULL                  |       |
| User_attributes          | json                              | YES  |     | NULL                  |       |
+--------------------------+-----------------------------------+------+-----+-----------------------+-------+

↑のうち、Host, Userは主キー。ssl_cipher, x509_issuer,x509_subjectはNull値禁止かつデフォルト値Nullなので、この5つのカラムは何か埋めてrootユーザのレコードをインサートする。

rootユーザを作成:

mysql> insert into user (user , host, ssl_cipher, x509_issuer, x509_subject) values('root','localhost', '','','');
Query OK, 1 row affected (0.01 sec)

権限を付与

ここで各カラムの値を見る。

mysql> select * from mysql.user where User='root' \G
*************************** 1. row ***************************
                    Host: localhost
                    User: root
             Select_priv: N
             Insert_priv: N
             Update_priv: N
             Delete_priv: N
             Create_priv: N
               Drop_priv: N
             Reload_priv: N
           Shutdown_priv: N
            Process_priv: N
               File_priv: N
              Grant_priv: N
         References_priv: N
              Index_priv: N
              Alter_priv: N
            Show_db_priv: N
              Super_priv: N
   Create_tmp_table_priv: N
        Lock_tables_priv: N
            Execute_priv: N
         Repl_slave_priv: N
        Repl_client_priv: N
        Create_view_priv: N
          Show_view_priv: N
     Create_routine_priv: N
      Alter_routine_priv: N
        Create_user_priv: N
              Event_priv: N
            Trigger_priv: N
  Create_tablespace_priv: N
                ssl_type: 
              ssl_cipher: NULL
             x509_issuer: NULL
            x509_subject: NULL
           max_questions: 0
             max_updates: 0
         max_connections: 0
    max_user_connections: 0
                  plugin: caching_sha2_password
   authentication_string: NULL
        password_expired: N
   password_last_changed: NULL
       password_lifetime: NULL
          account_locked: N
        Create_role_priv: N
          Drop_role_priv: N
  Password_reuse_history: NULL
     Password_reuse_time: NULL
Password_require_current: NULL
         User_attributes: NULL
1 row in set (0.00 sec)

このうち、カラム名が *_priv となっているのが特権に関する情報。今デフォルト値ですべてNになっているので、Yで更新すればいい。rootユーザなんで全部Y。

以下のようなクエリを作る。手でタイプするにはダルく、プログラミングするほどでもない量だ。スプレッドシートなんかでうまいことやるといいかもしれない。私はそうした。

update user set Select_priv = 'Y' where user = 'root';
update user set Insert_priv = 'Y' where user = 'root';
update user set Update_priv = 'Y' where user = 'root';
update user set Delete_priv = 'Y' where user = 'root';
update user set Create_priv = 'Y' where user = 'root';
update user set Drop_priv = 'Y' where user = 'root';
update user set Reload_priv = 'Y' where user = 'root';
update user set Shutdown_priv = 'Y' where user = 'root';
update user set Process_priv = 'Y' where user = 'root';
update user set File_priv = 'Y' where user = 'root';
update user set Grant_priv = 'Y' where user = 'root';
update user set References_priv = 'Y' where user = 'root';
update user set Index_priv = 'Y' where user = 'root';
update user set Alter_priv = 'Y' where user = 'root';
update user set Show_db_priv = 'Y' where user = 'root';
update user set Super_priv = 'Y' where user = 'root';
update user set Create_tmp_table_priv = 'Y' where user = 'root';
update user set Lock_tables_priv = 'Y' where user = 'root';
update user set Execute_priv = 'Y' where user = 'root';
update user set Repl_slave_priv = 'Y' where user = 'root';
update user set Repl_client_priv = 'Y' where user = 'root';
update user set Create_view_priv = 'Y' where user = 'root';
update user set Show_view_priv = 'Y' where user = 'root';
update user set Create_routine_priv = 'Y' where user = 'root';
update user set Alter_routine_priv = 'Y' where user = 'root';
update user set Create_user_priv = 'Y' where user = 'root';
update user set Event_priv = 'Y' where user = 'root';
update user set Trigger_priv = 'Y' where user = 'root';
update user set Create_tablespace_priv = 'Y' where user = 'root';
update user set Create_role_priv = 'Y' where user = 'root';
update user set Drop_role_priv = 'Y' where user = 'root';

そして、権限テーブルの設定を反映させておく。

mysql> flush privileges;

さらにrootユーザのパスワードを設定すれば完了だが、権限テーブルをスキップした状態ではクエリを受け付けてくれない。

mysql> alter user root identified by '{PASSWORD}';
ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement

なので、一旦ログアウトし、セーフモードではなく普通のモードでログインする。

セーフモードのMySQLをkill

セーフモードで起動しているmysqlのプロセスIDを確認し、killする。

# ps aux | grep mysql
root      136115  0.0  0.3  10960  3360 pts/1    T    10月06   0:00 sudo mysqld_safe --skip-grant-tables
root      136116  0.0  0.1   2608  1892 pts/1    T    10月06   0:00 /bin/sh /usr/bin/mysqld_safe --skip-grant-tables
mysql     136277  0.2 38.3 1299052 384896 pts/1  Sl   10月06   3:57 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --skip-grant-tables --log-error=/var/log/mysql/error.log --pid-file=i-12100000190719.pid
root      142278  0.0  0.0   7476   676 pts/1    S+   12:08   0:00 grep --color=auto mysql

↑で言うと、上から3つをすべてkillすれば消えた。

# kill -9 136115

通常モードのMySQLでパスワードを設定

通常のモードでMySQLを起動する。

# systemctl start mysql

パスワードなしでrootログイン。

# mysql -u root -p
Enter password: <<何も入力せずReturn>>

ALTER USER でパスワードを変更。

mysql> alter user root@localhost identified by '{PASSWORD}';
Query OK, 0 rows affected (0.01 sec) #<-すごい不安になるが、これで反映されている
mysql> exit; #ログアウト

MySQLを再起動する。

# systemctl restart mysql

これで完了。

タイトルとURLをコピーしました