こんにちは。SREチーム インフラエンジニアの綿引です。
今回は MySQL の透過的暗号化 について記載したいと思います。
因みに透過的暗号化が使用できるのは MySQL 5.7.11 からですのでご認識のほど。
前回 は RDS の暗号化について軽く触れましたが、
その際はWebコンソールからボックスにチェックを入れるだけでした。
今回の MySQL の透過的暗号化を使用する場合には設定が必要です。
では早速やっていきたいと思います。
環境
・OS : CentOS 7.1
・データベース : MySQL 5.7.22
設定
まずは暗号化の前に検証用のテストテーブルを作成します。
・データベース : test
・テーブル : test
カラムは適当に id , name とし、2レコードほど、insert しております。
mysql> select * from test.test;
mysql> select * from test.test;
+------+-------+
| id | name |
+------+-------+
| 1 | test |
| 2 | test2 |
+------+-------+
2 rows in set (0.00 sec)
mysql> select * from test.test;
+------+-------+
| id | name |
+------+-------+
| 1 | test |
| 2 | test2 |
+------+-------+
2 rows in set (0.00 sec)
なお、エンジンは InnoDB です。
少し脱線致しますが MyIsam でも暗号化(alter table XX ENCRYPTION=’Y’)ができたのですが、
mysqldump を取得して、インポートする際に失敗したので、
暗号化する際はエンジンが InnoDB であることを確認した方が良いです。
こちらに関してはまた別途書きたいと思います。
では、次に OS 上でどのように見えるか確認します。
ibd ファイルはバイナリのため strings コマンドで確認します。
# strings /var/lib/mysql/test/test.ibd
# strings /var/lib/mysql/test/test.ibd
・
・
・
test
test2
# strings /var/lib/mysql/test/test.ibd
・
・
・
test
test2
先ほど insert した name 列の文字が確認できる状態ですね。
次は暗号化用の設定を行なっていきたいと思います。
プラグインのインストール
まずはプラグインのインストールです。
mysql> select PLUGIN_NAME,PLUGIN_STATUS from information_schema.plugins WHERE PLUGIN_NAME LIKE 'keyring%';
mysql> install plugin keyring_file soname 'keyring_file.so';
Query OK, 0 rows affected (0.01 sec)
mysql> select PLUGIN_NAME,PLUGIN_STATUS from information_schema.plugins WHERE PLUGIN_NAME LIKE 'keyring%';
+--------------+---------------+
| PLUGIN_NAME | PLUGIN_STATUS |
+--------------+---------------+
| keyring_file | ACTIVE |
+--------------+---------------+
$ mysql -uroot -p
mysql> select PLUGIN_NAME,PLUGIN_STATUS from information_schema.plugins WHERE PLUGIN_NAME LIKE 'keyring%';
Empty set (0.00 sec)
mysql> install plugin keyring_file soname 'keyring_file.so';
Query OK, 0 rows affected (0.01 sec)
mysql> select PLUGIN_NAME,PLUGIN_STATUS from information_schema.plugins WHERE PLUGIN_NAME LIKE 'keyring%';
+--------------+---------------+
| PLUGIN_NAME | PLUGIN_STATUS |
+--------------+---------------+
| keyring_file | ACTIVE |
+--------------+---------------+
1 row in set (0.00 sec)
$ mysql -uroot -p
mysql> select PLUGIN_NAME,PLUGIN_STATUS from information_schema.plugins WHERE PLUGIN_NAME LIKE 'keyring%';
Empty set (0.00 sec)
mysql> install plugin keyring_file soname 'keyring_file.so';
Query OK, 0 rows affected (0.01 sec)
mysql> select PLUGIN_NAME,PLUGIN_STATUS from information_schema.plugins WHERE PLUGIN_NAME LIKE 'keyring%';
+--------------+---------------+
| PLUGIN_NAME | PLUGIN_STATUS |
+--------------+---------------+
| keyring_file | ACTIVE |
+--------------+---------------+
1 row in set (0.00 sec)
なお、MySQL 5.7.11 ではデフォルトのプラグインでしたが、
MySQL 5.7.12 以降はデフォルトで入っていないので明示的にインストールする必要があります。
my.cnf修正
次に my.cnf に以下の2行を追加していきます。
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql/mysql-keyring/keyring
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql/mysql-keyring/keyring
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql/mysql-keyring/keyring
パラメータの意味としては、
1行目の early-plugin-load がロードするプラグインを指定します。
先ほどインストールしたキーリングプラグイン(keyring_file.so)ですね。
2行目の keyring_file_data はキーリングファイルの置き場所です。
他のデータベースファイルと混在しない場所が推奨されているので、
次の項目で専用のディレクトリを作成していきます。
因みに以下もパラメータとして必要なのですが、
MySQL 5.6.6以降はデフォルトで有効なのでいじってなければ大丈夫です。
innodb_file_per_table=1
キーリングファイルの置き場所(ディレクトリ)作成
上記で指定したディレクトリを作成していきます。
$ sudo mkdir -p /var/lib/mysql/mysql-keyring
$ sudo ls -ltr /var/lib/mysql/
$ sudo chown -R mysql:mysql /var/lib/mysql/mysql-keyring
$ sudo chmod 750 /var/lib/mysql/mysql-keyring
$ sudo ls -ltr /var/lib/mysql/
$ sudo mkdir -p /var/lib/mysql/mysql-keyring
$ sudo ls -ltr /var/lib/mysql/
$ sudo chown -R mysql:mysql /var/lib/mysql/mysql-keyring
$ sudo chmod 750 /var/lib/mysql/mysql-keyring
$ sudo ls -ltr /var/lib/mysql/
$ sudo mkdir -p /var/lib/mysql/mysql-keyring
$ sudo ls -ltr /var/lib/mysql/
$ sudo chown -R mysql:mysql /var/lib/mysql/mysql-keyring
$ sudo chmod 750 /var/lib/mysql/mysql-keyring
$ sudo ls -ltr /var/lib/mysql/
MySQL 再起動
後は MySQL を再起動していきます。
$ sudo systemctl status mysqld
$ sudo systemctl restart mysqld
$ sudo systemctl status mysqld
$ sudo systemctl status mysqld
$ sudo systemctl restart mysqld
$ sudo systemctl status mysqld
$ sudo systemctl status mysqld
$ sudo systemctl restart mysqld
$ sudo systemctl status mysqld
暗号化するぞ!
ここまで出来れば暗号化できる環境の準備は整いました。
暗号化の方法としては、以下です。
・新規テーブル : Create オプションに「ENCRYPTION=’Y’」を付与する
・既存テーブル : 対象テーブルに対して、「alter table テーブル名 ENCRYPTION=”Y”」を実施する
今回は検証用にテーブルを作成しているので、 alter table 文で暗号化していきます。
まずは対象テーブルが暗号化されていないことを確認します。
確認には information_schema の tables を使用します。
mysql> select TABLE_SCHEMA,TABLE_NAME,ENGINE,CREATE_OPTIONS from information_schema.tables
-> where TABLE_SCHEMA = 'test';
+--------------+------------+--------+----------------+
| TABLE_SCHEMA | TABLE_NAME | ENGINE | CREATE_OPTIONS |
+--------------+------------+--------+----------------+
| test | test | InnoDB | |
+--------------+------------+--------+----------------+
mysql> select TABLE_SCHEMA,TABLE_NAME,ENGINE,CREATE_OPTIONS from information_schema.tables
-> where TABLE_SCHEMA = 'test';
+--------------+------------+--------+----------------+
| TABLE_SCHEMA | TABLE_NAME | ENGINE | CREATE_OPTIONS |
+--------------+------------+--------+----------------+
| test | test | InnoDB | |
+--------------+------------+--------+----------------+
1 row in set (0.00 sec)
mysql> select TABLE_SCHEMA,TABLE_NAME,ENGINE,CREATE_OPTIONS from information_schema.tables
-> where TABLE_SCHEMA = 'test';
+--------------+------------+--------+----------------+
| TABLE_SCHEMA | TABLE_NAME | ENGINE | CREATE_OPTIONS |
+--------------+------------+--------+----------------+
| test | test | InnoDB | |
+--------------+------------+--------+----------------+
1 row in set (0.00 sec)
暗号化されると、上記の CREATE_OPTIONS に ENCRYPTION=’Y’ が入ります。
では alter table 文を実行します。
mysql> alter table test.test ENCRYPTION='Y';
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> alter table test.test ENCRYPTION='Y';
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> alter table test.test ENCRYPTION='Y';
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
再度、information_schema の tables を確認しましょう。
mysql> select TABLE_SCHEMA,TABLE_NAME,ENGINE,CREATE_OPTIONS from information_schema.tables
-> where TABLE_SCHEMA = 'test';
+--------------+------------+--------+----------------+
| TABLE_SCHEMA | TABLE_NAME | ENGINE | CREATE_OPTIONS |
+--------------+------------+--------+----------------+
| test | test | InnoDB | ENCRYPTION="Y" |
+--------------+------------+--------+----------------+
mysql> select TABLE_SCHEMA,TABLE_NAME,ENGINE,CREATE_OPTIONS from information_schema.tables
-> where TABLE_SCHEMA = 'test';
+--------------+------------+--------+----------------+
| TABLE_SCHEMA | TABLE_NAME | ENGINE | CREATE_OPTIONS |
+--------------+------------+--------+----------------+
| test | test | InnoDB | ENCRYPTION="Y" |
+--------------+------------+--------+----------------+
1 row in set (0.01 sec)
mysql> select TABLE_SCHEMA,TABLE_NAME,ENGINE,CREATE_OPTIONS from information_schema.tables
-> where TABLE_SCHEMA = 'test';
+--------------+------------+--------+----------------+
| TABLE_SCHEMA | TABLE_NAME | ENGINE | CREATE_OPTIONS |
+--------------+------------+--------+----------------+
| test | test | InnoDB | ENCRYPTION="Y" |
+--------------+------------+--------+----------------+
1 row in set (0.01 sec)
CREATE_OPTIONS が ENCRYPTION=”Y” となっているので暗号化されたようですね。
実際にどうなっているか確認していきましょう。
当然ながら、select 文は問題なく発行できます。
mysql> select * from test.test;
mysql> select * from test.test;
+------+-------+
| id | name |
+------+-------+
| 1 | test |
| 2 | test2 |
+------+-------+
2 rows in set (0.00 sec)
mysql> select * from test.test;
+------+-------+
| id | name |
+------+-------+
| 1 | test |
| 2 | test2 |
+------+-------+
2 rows in set (0.00 sec)
では、先ほどは確認できた OS から strings コマンドを発行したらどうでしょう。
# strings /var/lib/mysql/test/test.ibd
2063f200-7132-11e8-bf60-08002751d15e
# strings /var/lib/mysql/test/test.ibd
2063f200-7132-11e8-bf60-08002751d15e
T#*a
3omg
・
・
・
RaR&h
es{Ds
,j6Z
# strings /var/lib/mysql/test/test.ibd
2063f200-7132-11e8-bf60-08002751d15e
T#*a
3omg
・
・
・
RaR&h
es{Ds
,j6Z
先ほどは出力されていた、test などの文言が表示されず、
暗号化された文字列が出力されていますね。
想定通り暗号化できたようです。
最後にログを見て問題ないことを確認しましょう。
ERROR が出てる、、
以下のようなエラーが出ておりました。。
$ sudo cat /var/log/mysqld.log
2018-06-16T08:14:49.059936Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:15:00.149256Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:15:55.047206Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:16.390285Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:21.920555Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:28.583733Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:58.977404Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
$ sudo cat /var/log/mysqld.log
・
・
・
2018-06-16T08:14:49.059936Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:15:00.149256Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:15:55.047206Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:16.390285Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:21.920555Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:28.583733Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:58.977404Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
$ sudo cat /var/log/mysqld.log
・
・
・
2018-06-16T08:14:49.059936Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:15:00.149256Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:15:55.047206Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:16.390285Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:21.920555Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:28.583733Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
2018-06-16T08:16:58.977404Z 3 [ERROR] Invalid (old?) table or database name 'mysql-keyring'
今回キーリングファイルの置き先として
/var/lib/mysql/mysql-keyring ディレクトリ配下を指定しましたが、
datadir にて /var/lib/mysql/ を指定しているので、
mysql-keyring ディレクトリをデータベースと捉えてしまったようです。
そこで、「これ何? DB? いる?」 というメッセージが ERROR として出力された模様。
確かに show databases で確認すると以下のような表記になります。
+------------------------+
+------------------------+
| #mysql50#mysql-keyring |
+------------------------+
mysql> show databases;
+------------------------+
| Database |
+------------------------+
| information_schema |
| mysql |
| #mysql50#mysql-keyring |
| performance_schema |
| sys |
| test |
+------------------------+
6 rows in set (0.00 sec)
mysql> show databases;
+------------------------+
| Database |
+------------------------+
| information_schema |
| mysql |
| #mysql50#mysql-keyring |
| performance_schema |
| sys |
| test |
+------------------------+
6 rows in set (0.00 sec)
#mysql50#mysql-keyring というのがそれっぽいですね。
DML は問題なく発行できたので挙動的には問題なさそうですが、メッセージの量がハンパない、、
ざっと見た感じ MySQL の再起動時やテーブルアクセス時にも出力されているみたい。。
対応していきましょう。
方法としては2つあり、一つはキーリングファイルの置き場所を変更する。
もう一つは既存の設定はそのままにパラメータを一つ追加する方法です。
設定変更するのは面倒なので、後者を実施していきます。
my.cnf再修正
設定は簡単です。以下を追加します。
ignore-db-dir=mysql-keyring
ignore-db-dir=mysql-keyring
ignore-db-dir=mysql-keyring
ですので最終的な my.cnf としては以下のようになりました。
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql/mysql-keyring/keyring
ignore-db-dir=mysql-keyring
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql/mysql-keyring/keyring
ignore-db-dir=mysql-keyring
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql/mysql-keyring/keyring
ignore-db-dir=mysql-keyring
MySQL 再起動
再度、再起動していきます。
$ sudo systemctl status mysqld
$ sudo systemctl restart mysqld
$ sudo systemctl status mysqld
$ sudo systemctl status mysqld
$ sudo systemctl restart mysqld
$ sudo systemctl status mysqld
$ sudo systemctl status mysqld
$ sudo systemctl restart mysqld
$ sudo systemctl status mysqld
この後ログを確認しましたが、
もう [ERROR] Invalid (old?) table or database name ・・・というエラーは出てなさそう。。
show databases
の結果も問題なさそうです。
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test |
+--------------------+
5 rows in set (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test |
+--------------------+
5 rows in set (0.00 sec)
まとめ
想定外もありましたが、透過的暗号化ができて良かったです。
特にアプリケーション側を変更せずに、インフラ側だけで対応できる点は非常に良いと思います。
皆さまも是非ご興味があれば。
この度は、ご清覧頂きありがとうございました。
Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。
興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。
ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
Join Us !
ウエディングパークでは、一緒に働く仲間を募集しています!
ご興味ある方は、お気軽にお問合せください(カジュアル面談から可)
採用情報を見る