2019/01/21(公式発表)に MySQL 8.0.14 がリリースされました。
おもちゃを手にした子供の如く、早速 MySQL おじさんたちが新機能をキャッキャしながら試しています(?)が、わたしの場合はやっぱり暗号化(Qiita 最初の記事も TDE ですし)、ということでバイナリログ・リレーログの暗号化を試してみました。
バイナリログ・リレーログの暗号化について
MySQL 8.0.14 のリリースノートに概要が書かれています。
- Logging Notes - Changes in MySQL 8.0.14 (2019-01-21, General Availability)(version 8.0 MySQL 8.0 Release Notes)
これによると、
-
binlog_encryption
をON
にするとバイナリログとリレーログ(レプリケーションスレーブ側のログ)が暗号化される - この項目はサーバ起動中に
SET
で動的にON
/OFF
ができる -
ON
/OFF
を切り替えると記録中のログがローテーションされる - バイナリログ・リレーログは 256 ビット AES で暗号化され、バイナリログ用のマスターキーはテーブルスペース暗号化用の鍵とは別に生成される(自動生成されるため、マニュアルでの設定はできない)
-
binlog_rotate_encryption_master_key_at_startup
をON
にすると、サーバを起動する度にバイナリログ用のマスターキーがローテーションされる - この指定をしても、通信経路上の暗号化は行われないので、そちらは通信の TLS 化設定で対応する
-
SHOW BINARY LOGS
でバイナリログが暗号化されているかどうかを確認できる
ということのようです。
※テーブルスペース等の暗号化については、以下を参照してください。
- MySQL 8.0.16 でテーブルスペース・REDO ログ/UNDO ログ・システムテーブル暗号化
- MySQL 8.0.13 で一般テーブルスペース(General Tablespace)を暗号化する
2019/01/24 追記:
テーブルスペース等の暗号化と同様、バイナリログの暗号化にもキーリング用プラグインの導入が必要です。
設定については↑のテーブルスペース暗号化+REDOログ/UNDOログ暗号化の記事を参照してください。
試してみる
Master/Slave 構成で試してみます。
まずは、それぞれのサーバの/etc/my.cnf
から。
なお、暗号化されたかどうかの判別がしやすいように、バイナリログのフォーマットをROW
からMIXED
に変更しています。
- Master 側
server-id=1
binlog_format=MIXED
binlog_encryption=ON
binlog_rotate_encryption_master_key_at_startup=ON
- Slave 側
server-id=2
binlog_format=MIXED
binlog_encryption=ON
binlog_rotate_encryption_master_key_at_startup=ON
read_only
#↑super_read_onlyのほうが良い
#skip-slave-start
注:
Slave を Master のディスクイメージからコピーして立てた場合、データディレクトリにあるauto.cnf
のserver-uuid
が重複するため、レプリケーション時にエラーが出ます。
Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs; these UUIDs must be different for replication to work.
この場合、auto.cnf
を削除してからサーバを起動すると、server-uuid
が自動生成されます。
また、システムテーブル等の一部が変更されていますので、MySQL 8.0.13(またはそれ以前のバージョン)から 8.0.14 にバージョンアップしたときは、忘れずにmysql_upgrade
しておきます。
# mysql_upgrade -u root -p
Enter password:
Checking if update is needed.
Checking server version.
Running queries to upgrade MySQL server.
Upgrading system table data.
Checking system database.
mysql.columns_priv OK
(中略)
ts1.table2 OK
Upgrade process completed successfully.
Checking if update is needed.
Master 側で実験
まずは Master 側で実験してみます。
※今回、MySQL はroot
ユーザで操作しています。別のユーザで操作する場合は、必要な権限が付与されていることを確認してください。
テスト用のデータベースとテーブルを作成します。
mysql> CREATE DATABASE enc_test;
Query OK, 1 row affected (0.01 sec)
mysql> USE enc_test;
Database changed
mysql> CREATE TABLE enc_test (id int(10) PRIMARY KEY AUTO_INCREMENT, value VARCHAR(100)) ENGINE=innodb ENCRYPTION='Y';
Query OK, 0 rows affected (0.03 sec)
テストデータを挿入します。
mysql> INSERT INTO enc_test SET value='hoge';
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO enc_test SET value='fuga';
Query OK, 1 row affected (0.00 sec)
バイナリログに平文で記録されていないか確認してみます。
# fgrep 'hoge' binlog.*
# fgrep 'fuga' binlog.*
見つかりませんでした。
バイナリログの暗号化を動的に無効にしてみます。
mysql> SHOW BINARY LOGS;
+---------------+-----------+-----------+
| Log_name | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 | 1855 | Yes |
+---------------+-----------+-----------+
1 row in set (0.00 sec)
mysql> SET GLOBAL binlog_encryption=0;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW BINARY LOGS;
+---------------+-----------+-----------+
| Log_name | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 | 1899 | Yes |
| binlog.000002 | 155 | No |
+---------------+-----------+-----------+
2 rows in set (0.00 sec)
ログがローテーションされ、暗号化がNo
に変わりました。
テストデータを挿入します。
mysql> USE enc_test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> INSERT INTO enc_test SET value='HOGE';
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO enc_test SET value='FUGA';
Query OK, 1 row affected (0.00 sec)
バイナリログを確認してみます。
# fgrep 'HOGE' binlog.*
Binary file binlog.000002 matches
# fgrep 'FUGA' binlog.*
Binary file binlog.000002 matches
今度は平文で見つかりました。
Slave 側で実験
次に、Slave 側で実験してみます。
その前に、Master 側に Slave 用のユーザを作成しておきます。
mysql> CREATE USER 'repl'@'%' IDENTIFIED BY 'T35+U53r';
Query OK, 0 rows affected (0.01 sec)
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
Query OK, 0 rows affected (0.01 sec)
続いて、Slave 側で Master への接続設定を行います。
mysql> CHANGE MASTER TO
-> MASTER_HOST='【ホスト名】',
-> MASTER_USER='repl',
-> MASTER_PASSWORD='T35+U53r',
-> MASTER_LOG_FILE='binlog.000001',
-> MASTER_LOG_POS=4;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Note
Code: 1759
Message: Sending passwords in plain text without SSL/TLS is extremely insecure.
*************************** 2. row ***************************
Level: Note
Code: 1760
Message: Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
2 rows in set (0.00 sec)
通信経路の暗号化をしていないことについて怒られました。
また、2 つ目の Warning は 5.6 あたりでも表示されるいつものヤツです…今回は実験なので手抜きしましたが、本番利用するときはどちらも怒られないように正しく設定しましょう。
気を取り直して…早速 Slave へのレプリケーションを開始してみます。
mysql> START SLAVE;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
(中略)
Last_Errno: 1410
Last_Error: Error 'You are not allowed to create a user with GRANT' on query. Default database: ''. Query: 'GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%''
(中略)
Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
(中略)
Last_SQL_Errno: 1410
Last_SQL_Error: Error 'You are not allowed to create a user with GRANT' on query. Default database: ''. Query: 'GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%''
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
(中略)
Get_master_public_key: 0
1 row in set (0.00 sec)
権限の関係でエラーが出ました。Slave 側でレプリケーション用のユーザを作成する必要はないのでスキップします。
mysql> SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
Query OK, 0 rows affected (0.00 sec)
mysql> START SLAVE;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
(中略)
Master_Log_File: binlog.000002
Read_Master_Log_Pos: 1396
(中略)
Seconds_Behind_Master: 0
(中略)
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
(中略)
Get_master_public_key: 0
1 row in set (0.00 sec)
mysql> SHOW BINARY LOGS;
+---------------+-----------+-----------+
| Log_name | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 | 2605 | Yes |
+---------------+-----------+-----------+
1 row in set (0.00 sec)
レプリケーションされたようです。
バイナリログ・リレーログをfgrep
して平文記録されていないか確認してみます。
# fgrep 'hoge' binlog.*
# fgrep 'fuga' binlog.*
# fgrep 'HOGE' binlog.*
# fgrep 'FUGA' binlog.*
# fgrep 'hoge' *relay-bin.*
# fgrep 'fuga' *relay-bin.*
# fgrep 'HOGE' *relay-bin.*
# fgrep 'FUGA' *relay-bin.*
どれも見つかりませんでした。Master 側で各々のバイナリログが暗号化されているか否かに関わらず、Slave 側の設定通りに保存されるようです。
おわりに
バイナリログ・リレーログが対応したことにより、必要な要素が一通り暗号化できるようになりました。
あとはダンプ…?
2019/01/23 追記:
某氏の呟きを見て、スロークエリログや一般ログ等のことを忘れていたのに気づきました。
2019/01/25 追記(4/3 修正):
システムテーブルスペースも忘れてました(8.0.1516 で対応される模様)。
- 1.4 What Is New in MySQL 8.0(MySQL 8.0 Reference Manual)より引用
- As of MySQL 8.0.
1516, the InnoDB tablespace encryption feature, which provides at-rest data encryption, supports encryption of the mysql system tablespace. The mysql system tablespace contains the mysql system database and the MySQL data dictionary tables. For more information, see Section 15.6.3.9, “Tablespace Encryption”.
2019/07/13 追記:
8.0.16 でALTER INSTANCE
によるマスターキーのローテーションに対応しました。
13.1.5 ALTER INSTANCE Syntax(MySQL 8.0 Reference Manual)