MariaDB による DB の透過的暗号化 (Data-at-Rest Encryption)

MariaDB 透過的暗号化を設定したときのメモ。

■概要

MariaDB は標準で、InnoDB のテーブルスペースに加えて、Undo ログスペースや InnoDB ログファイル、バイナリログ/リレーログなども暗号化可能。

暗号化方式は、AES の 128-bit, 192-bit, 256-bit キー長に対応している。

暗号化の鍵管理プラグインはいくつかある模様だけれど、「File Key Management Encryption Plugin」を利用してみた。これは MariaDB 10.1.18 で安定版となった模様。

キーファイルはテキストファイルで、各テーブルの暗号化に使われるキーが格納されている。なお、このキーファイル自体を別の暗号化キーで暗号化することも可能。

「File Key Management Encryption Plugin」ではキーのローテーションには対応していない。

ちなみに、MySQL ではテーブル毎の .ibd ファイルのみ暗号化は可能なようなのだが、マスター暗号化キーはローテーションが可能という長所がある。

■動作確認

Debian 11.0 (ARM64) + MariaDB 10.5.10 で確認した。

■キーファイルの作成

以下のような形式のテキストファイルを作成する

<encryption_key_id_1>;<hex-encoded_encryption_key_1>
<encryption_key_id_2>;<hex-encoded_encryption_key_2>
・
<encryption_key_id_n>;<hex-encoded_encryption_key_n>

encryption_key_id の値について:
1: InnoDB REDO ログ、バイナリログなどのシステムデータを暗号化に利用される。暗号化を行う場合は必須。
2: 一時ファイルや一時テーブルなどの一時データを暗号化に利用される。オプション。(これが存在しない場合は ID 1 のキーが使われる。)

※暗号化の際のキー ID はテーブルごとに指定できる。
例) CREATE TABLE t (i int) ENCRYPTED=YES ENCRYPTION_KEY_ID=2;
※「ENCRYPTION_KEY_ID」を指定しない場合は、innodb_default_encryption_key_id のデフォルト値、つまり 1 が使用される。

hex-encoded_encryption_key の値について:
256-bit の場合、ランダムな 32 文字を設定する。

・キーファイルの作成

# mkdir /etc/mysql/keys
# chmod 750 /etc/mysql/keys
# chown root:mysql /etc/mysql/keys
# cd /etc/mysql/keys
# echo "1;`openssl rand -hex 32`" > /etc/mysql/keys/encryption_keys
# echo "2;`openssl rand -hex 32`" >> /etc/mysql/keys/encryption_keys

・作成したファイルのパーミッションを root:mysql 640 に設定する

# chown root:mysql /etc/mysql/keys/encryption_keys
# chmod 640 /etc/mysql/keys/encryption_keys

■キーファイルを暗号化

・パスワードファイルを作成

# cd /etc/mysql/keys
# openssl rand -hex 128 > /etc/mysql/keys/keyfile.key

・パスワードファイルをもとに /etc/mysql/keys/encryption_keys を暗号化

# openssl enc -aes-256-cbc -md sha1 -pass file:/etc/mysql/keys/keyfile.key -in /etc/mysql/keys/encryption_keys -out /etc/mysql/keys/encryption_keys.enc

・作成したファイルのパーミッションを root:mysql 640 に設定する

# chown root:mysql /etc/mysql/keys/keyfile.key /etc/mysql/keys/encryption_keys.enc
# chmod 640 /etc/mysql/keys/keyfile.key /etc/mysql/keys/encryption_keys.enc

■ MariaDB への設定

/etc/mysql/mariadb.conf.d/50-server.cnf を編集する。

[mariadb]

# file_key_management プラグインのロード
plugin_load_add = file_key_management

# キーファイル
loose_file_key_management_filename = /etc/mysql/keys/encryption_keys.enc

# /etc/mysql/keys/encryption_keys.enc のパスワードファイル
loose_file_key_management_filekey = FILE:/etc/mysql/keys/keyfile.key

# アルゴリズム
loose_file_key_management_encryption_algorithm = AES_CBC

# 暗号化を有効 (ON: 有効 / FORCE: 有効かつ非暗号化テーブルを許可しない / OFF: 無効)
innodb_encrypt_tables = ON

# InnoDB temporary tablespace を暗号化
innodb_encrypt_temporary_tables = ON

# InnoDB redo log を暗号化
innodb_encrypt_log = ON

# 暗号化を行う際のスレッド数
innodb_encryption_threads = 4

# バイナリログ暗号化
encrypt_binlog = ON

# テンポラリーファイルの暗号化
encrypt-tmp-disk-tables = ON
encrypt-tmp-files = ON

※プラグインの動的なアンインストールも可能である。(UNINSTALL SONAME 'file_key_management';)
※プラグインに依存する機能を利用していないことが条件。

・テスト

# mysql -u root
CREATE DATABASE enctest;
USE enctest;
CREATE TABLE t (i int, text text) ENCRYPTED=YES;
INSERT t VALUES (1, "TEST1");
INSERT t VALUES (2, "TEST2");
SELECT * FROM t;
FLUSH TABLE t FOR EXPORT;
\q

# strings /var/lib/mysql/enctest/t.ibd
(値がそのまま格納されていないことを確認。)

※暗号化されるのは、/var/lib/mysql 以下の DB そのもの。
※ mysqldump とかでダンプファイルを作成すると、普通にデータは見える。(透過的なので)
※ DB のレストアも普通に mysqldump と mysql コマンドで出来る。

・既存テーブルの暗号化

# mysql -u root
CREATE DATABASE enctest;
USE enctest;
CREATE TABLE t2 (i int, text text);
INSERT t2 VALUES (1, "TEST1");
INSERT t2 VALUES (2, "TEST2");
SELECT * FROM t2;
FLUSH TABLE t2 FOR EXPORT;
\q

# string /var/lib/mysql/enctest/t2.ibd
(生データが見える。)

# mysql -u root enctest
ALTER TABLE t2 ENCRYPTED=YES;
FLUSH TABLE t2 FOR EXPORT;
\q

# string /var/lib/mysql/enctest/t2.ibd
(生データは見えない。)

・暗号化解除

# mysql -u root enctest
ALTER TABLE t2 ENCRYPTED=NO;
\q

■備考

上記の MariaDB への設定した後、テーブルを作成するときに特に「ENCRYPTED=YES」を宣言しなくても、DB は暗号化されている。(「ENCRYPTED=NO」とすれば暗号化されないことは確認済み。)

# mysql -u root enctest
CREATE TABLE t3 (i int, text text);
SELECT NAME, ENCRYPTION_SCHEME, CURRENT_KEY_ID FROM information_schema.INNODB_TABLESPACES_ENCRYPTION
WHERE NAME='enctest/t3';
+------------+-------------------+----------------+
| NAME       | ENCRYPTION_SCHEME | CURRENT_KEY_ID |
+------------+-------------------+----------------+
| enctest/t3 |                 1 |              1 |
+------------+-------------------+----------------+

CREATE TABLE t4 (i int, text text) ENCRYPTED=YES;
SELECT NAME, ENCRYPTION_SCHEME, CURRENT_KEY_ID FROM information_schema.INNODB_TABLESPACES_ENCRYPTION
WHERE NAME='enctest/t4';
+------------+-------------------+----------------+
| NAME       | ENCRYPTION_SCHEME | CURRENT_KEY_ID |
+------------+-------------------+----------------+
| enctest/t4 |                 1 |              1 |
+------------+-------------------+----------------+

CREATE TABLE t5 (i int, text text) ENCRYPTED=NO;
SELECT NAME, ENCRYPTION_SCHEME, CURRENT_KEY_ID FROM information_schema.INNODB_TABLESPACES_ENCRYPTION
WHERE NAME='enctest/t5';
+------------+-------------------+----------------+
| NAME       | ENCRYPTION_SCHEME | CURRENT_KEY_ID |
+------------+-------------------+----------------+
| enctest/t5 |                 0 |              1 |
+------------+-------------------+----------------+

それどころか、設定を行う前の DB も暗号化対象となっているようだ。(この辺りはよくわからないが、もし巨大テーブルがあったとして、後から暗号化設定を入れたら、暗号化の負荷で大変なことになるのかもしれない)
と言うことは一回 DB を暗号化した場合、これを解除するには、全 DB をダンプして、SQL 文から CREATE TABLE の「ENCRYPTED」部分を削除してリストアする必要があるのか)

本当に透過的な暗号化なので、プログラム側の修正は不要で暗号化できる。その代わり mysqldump 等で DB をダンプしたら普通にデータを見ることはできる。

この辺り、PostgreSQL の暗号化ソフト「Transparent Data Encryption for PostgreSQL」などとはちょっと違う。

■参考サイト

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です