こんにちは。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;
+------+-------+
| 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
・
・
・
test
test2

先ほど insert した name 列の文字が確認できる状態ですね。
次は暗号化用の設定を行なっていきたいと思います。

プラグインのインストール

まずはプラグインのインストールです。

$ 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

パラメータの意味としては、
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/

MySQL 再起動

後は MySQL を再起動していきます。

$ 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 |                |
+--------------+------------+--------+----------------+
1 row in set (0.00 sec)

暗号化されると、上記の CREATE_OPTIONSENCRYPTION=’Y’ が入ります。

では alter table 文を実行します。

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" |
+--------------+------------+--------+----------------+
1 row in set (0.01 sec)

CREATE_OPTIONS が ENCRYPTION=”Y” となっているので暗号化されたようですね。
実際にどうなっているか確認していきましょう。

当然ながら、select 文は問題なく発行できます。

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
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'

今回キーリングファイルの置き先として
/var/lib/mysql/mysql-keyring ディレクトリ配下を指定しましたが、
datadir にて /var/lib/mysql/ を指定しているので、
mysql-keyring ディレクトリをデータベースと捉えてしまったようです。
そこで、「これ何? DB? いる?」 というメッセージが ERROR として出力された模様。

確かに show databases で確認すると以下のような表記になります。

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

ですので最終的な my.cnf としては以下のようになりました。

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

この後ログを確認しましたが、
もう [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)

まとめ

想定外もありましたが、透過的暗号化ができて良かったです。
特にアプリケーション側を変更せずに、インフラ側だけで対応できる点は非常に良いと思います。
皆さまも是非ご興味があれば。

この度は、ご清覧頂きありがとうございました。
Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。
興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。

ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!

Join Us !

ウエディングパークでは、一緒に働く仲間を募集しています!
ご興味ある方は、お気軽にお問合せください(カジュアル面談から可)

採用情報を見る