MariaDBのデータの暗号化
以前、MariaDBをSSL化しました。これはデータベースサーバーとクライアント間の通信を暗号化するもので、データベースそのものは暗号化していませんでしたので、今回はデータベースのデータを暗号化したいと思います。データを暗号化しておくことにより、MariaDBが動くサーバーのシェルにログインしてそのファイルを参照することでデータを閲覧されることを防ぎます。
公式のMariaDb:「Encryption」を参考に暗号化の設定をし、その後解除の方法も試してみます。
概要
mariaDBの暗号化は10.1.3から利用が可能ですが、10.1.4で暗号化の仕様が大幅に変更されました。ここでは10.1.4以降の仕様を紹介します。
InnoDBストレージエンジンにおいては完全な形で暗号化がサポートされています。他にはXtraDBもフルサポートのようですが、Ariaでは制限があります。
完全にサポートされたストレージエンジンでは、すべてのテーブルスペース、テーブル指定、テーブル以外のパターンで暗号化の対象を設定できるようです。
クエリログや低速クエリログ、エラーログは暗号化されません。
10.1.5以降なら、「encrypt_tmp_files = ON」を設定することで、バイナリログファイルが書き込まれることがある、一時ファイルも暗号化します。
暗号化のオーバーヘッドにより、3~5%程パフォーマンスが落ちます。
キーの管理とプラグインの設定
データの暗号化にはそれをするためのキーが必要ですが、それを管理するプラグインが必要になります。
キーは複数もつことができます。またプラグインがローテーション(キーの変更)に対応していればそれも可能です。
キー管理用のプラグインにはいくつか種類がありますが、「File Key Management Encryption Plugin」を設定します。
このプラグインは暗号キー管理プラグインの中で一番簡単なものになります。Fileという名前がつくように、キーはプレーンテキストのファイルから読み取ります。このプラグインではキーローテーションは使えません。
Debianの場合はfile_key_management.so、Windowsの場合はfile_key_management.dllライブラリとして初期のパッケージに含まれています。
設定ファイルの[mariadb]セクションに、plugin_load_addエントリーとして追加します。
/etc/mysql/mariadb.conf.d/50-server.cnf
... [mariadb] ... plugin_load_add = file_key_management
また、サーバー起動時に--plugin-loadオプションを使ってプラグイン加えることもできます。
次にキーファイルを作成します。キーファイルには「キー識別子; キー」のセットを1行にして、複数のキーを作成しておくことができます。キー識別子は連続している必要はありませんが1は必須のようです。キー自体はランダムな文字列です。以前はopensslで証明書を発行しましたが、今回はこれを使って鍵となるランダムな文字列を生成します。
# echo -n >keyfile # echo -n "1;" >>keyfile # openssl rand -hex 32 >>keyfile # echo -n "2;" >>keyfile # openssl rand -hex 32 >>keyfile # echo -n "5;" >>keyfile # openssl rand -hex 32 >>keyfile # cat keyfile 1;f621002490a562eb7ae6fb0d49de2996fbde34e34eaf7fa31e45c6623d0ae51f 3;80709724826acff604029bab9d4a10e749ed9255904b2a8bac44ee949a1948c5 5;0e3bab4f8be77a3db2c07c754bbd7f2808dbb77c87621dc97ae68e34cac2ae22 # mv keyfile /etc/mysql/
作成したキーファイルを設定ファイルに記述します。
/etc/mysql/mariadb.conf.d/50-server.cnf
... [mariadb] ... plugin_load_add = file_key_management file_key_management_filename = /etc/mysql/keyfile
プレーンテキストで保存するのが心配な場合は、keyfileを暗号化することができます。利用できるアルゴリズムはAESのCBCモードで、キー長は128,192,256ビットから選べます。
先ほどはデータベース暗号化の為のキーを作りそのリストをファイル化ましたが、今度はそのファイルを暗号化するためのキーを作ります。
この時はキー識別子はいりません。
# openssl rand -hex 128 >/etc/mysql/keyfile.key
opensslを使って元のキーリストファイルを暗号化します。暗号化キーは作成したキーのsha1ハッシュを使います。
# openssl enc -aes-256-cbc -md sha1 ¥ -pass file:/etc/mysql/keyfile.key ¥ -in /etc/mysql/keyfile ¥ -out /etc/mysql/keyfile.enc *** WARNING : deprecated key derivation used. Using -iter or -pbkdf2 would be better.
ワーニングについてはQuiita@akase244:「opensslコマンドを使って暗号化したり...」によると、-pbkdf2オプションをつけて暗号化を複数回実行するもののようですが、そうするとMariaDBでは復元できなくなる(と思います)ので無視します。
キーリストファイルを暗号化した場合は、.cnfファイルの記述を次のように変更します。
/etc/mysql/mariadb.conf.d/50-server.cnf
... [mariadb] ... plugin_load_add = file_key_management file_key_management_filename = /etc/mysqln/keyfile.enc file_key_management_filekey = FILE:/etc/mysql/keyfile.key
データの暗号化
データの暗号化で使えるアルゴリズムはAESの128,192,256ビットで、AES_CBCモードとAES_CTRモードがあります。推奨されるのはAES_CTRモードですがこれは、最近のopenSSLと共にビルドされたMariaDBでのみ利用できます。OpenSSLの他にもwolfSSLやyaSSLでビルドされてるものがあるそうです。その確認をするには次のようにします。
MariaDb[]> show variables like '%ssl_library%' +---------------------+-----------------------------+ | Variable_name | Value | +---------------------+-----------------------------+ | version_ssl_library | OpenSSL 1.1.1k 25 Mar 2021 | +---------------------+-----------------------------+ /* WSLのDebian10でインストールされたMariaDB */ +---------------------+-------------+ | Variable_name | Value | +---------------------+-------------+ | version_ssl_library | YaSSL 2.4.4 | +---------------------+-------------+
ここでは推奨されているAES_CTRを設定します。
/etc/mysql/mariadb.conf.d/50-server.cnf
... [mariadb] ... plugin_load_add = file_key_management file_key_management_filename = /etc/mysql/keyfile.enc file_key_management_filekey = FILE:/etc/mysql/keyfile.key file_key_management_encryption_algorithm = AES_CTR
ここまで設定したら、一度MariaDBを再起動して設定がおかしくないか確かめます。
起動できないようなら、.cnfファイルの記述にミスがあるか、キーリストファイルや暗号化したキーリストファイルがおかしい可能性が高いです。また、先ほども書きましたが、SSLライブラリがAES_CTRに対応していない場合も起動できません。
再起動後MariaDBのコンソールからSHOW PLUGINSを実行してみて、file_key_managementが出力されていればここまでは問題ありません。
MariaDB [(none)]> show plugins; +---------------------+--------+-- | Name | Status | +---------------------+--------+-- | file_key_management | ACTIVE | +---------------------+--------+--
つぎにinnodbに暗号化の設定をしていきます。
/etc/mysql/mariadb.conf.d/50-server.cnf
... [mariadb] ... # key managementのエントリーの後 innodb_encrypt_tables = ON innodb_encrypt_temporary_tables = ON innodb_encrypt_log = ON innodb_default_encryption_key_id = 1 innodb_encryption_threads = 2 innodb_encryption_rotate_key_age = 0
テーブル、テンポラリテーブル、ログの暗号化を有効にするところまでは説明不要だと思いますので省略します。
innodb_default_encryption_key_idの値にキーリストを作成した時に設定した識別子の値をセットします。
innodb_encryption_threadsでは暗号化で利用するスレッド数を指定します。この値はデフォルトで0ですが、暗号化する時には1~255の値が必要です。
rotate_key_ageはキーローテーションを利用している際に、指定した値より古い世代の暗号化を検索して新しいキーで再暗号化するものです。file_key_managementプラグインではキーローテーションをサポートしていなので0を指定して無効にします。
これらの設定を入れて再起動すると、InnoDBストレージエンジンのテーブルは暗号化されます。
どのテーブルが暗号化されているかはinformation_schemaより取得可能です。
MariaDB [(none)]> SELECT st.SPACE, st.NAME, te.ENCRYPTION_SCHEME,te.ROTATING_OR_FLUSHING FROM information_schema.INNODB_TABLESPACES_ENCRYPTION te JOIN information_schema.INNODB_SYS_TABLES st ON te.SPACE = st.SPACE ¥G 1. row *************************** SPACE: 0 NAME: SYS_DATAFILES ENCRYPTION_SCHEME: 1 ROTATING_OR_FLUSHING: 0 ... 10. row *************************** SPACE: 5 NAME: test/enctest1 ENCRYPTION_SCHEME: 1 ROTATING_OR_FLUSHING: 0
- SPACE
テーブルスペースの識別子です
- NAME
データベース/テーブル名が入っています。
- ENCRYPTION_SCHEME
この値が1なら暗号化されているテーブルです
- ROTATING_OR_FLUSHING
ローテショーン(再暗号化)やフラッシュ(復号化等)の作業中かを示します。
暗号化されたテーブルを元に戻す
暗号化を戻すには、先ほど暗号化時に設定した.cnfファイルの次の3行をOFFにしてサービスを再起動します。
/etc/mysql/mariadb.conf.d/50-server.cnf
... [mariadb] ... innodb_encrypt_tables = OFF innodb_encrypt_temporary_tables = OFF innodb_encrypt_log = OFF ...
再起動後、先ほどのSQLコマンドを実行してもらうとわかると思いますが、.cnfファイルを変更しただけでは、既に暗号化されたテーブルの暗号化のフラグは1のまま変わりません。
複合するには、明示的にグローバル変数を設定する必要があるようです。
再度先ほどのSQLコマンドを実行します。復号化の進み具合によりROTATING_OR_FLUSHINGに1が入っていることもあります。すべてのテーブルのENCRYPTION_SCHEMEとROTATING_OR_FLUSHINGが0になるまで待ちます。
暗号化の際には紹介しませんでしたが、CREATE TABLE時に「CREATE TABLE (...) ENCRYPTED=YES ENCRYPTION_KEY_ID=1」というように明示的に暗号化やその鍵を指定できます。このようにして暗号化されたテーブルはグローバル変数を変える方法でも元には戻りませんので、 その場合はALTER TABLEを使います。
MariaDB[]> ALTER TABLE 明示的に暗号化したテーブル名 ENCRYPTED=NO;
筆者の環境では作業しなくても問題ありませんでしたが、プラグインのアンインストールをして、サービスを停止した後、.cnfファイル内の一連の暗号化用の記述を削除し、再度起動するというのが丁寧な方法だと思います。
MariaDB[]> UNINSTALL SONAME 'file_key_management';
バイナリログの暗号化
バイナリログの暗号化はテーブルの暗号化より簡単で、次の1行を加えてりサービスを再起動するだけで暗号化が始まります。
/etc/mysql/mariadb.conf.d/50-server.cnf
... [mariadb] ... # 前述のエントリーの後 encrypt_binlog=ON
バイナリログの暗号化の場合は出力済みのデータに関しては暗号化の対象としません。なので厳格な運用が必要なら「RESET MASTER」や「PURGE BINARY LOGS」を実行して古いログを削除します。
元に戻す時は、encrypt_binlog=OFFとしてサービスを再起動します。この時も一度暗号化されてしまったバイナリログは元に戻らないので、ログを消すか、暗号化プラグイン自体の設定は残し古いログにサーバーがアクセスできるようにしておきます。
ちなみにレプリケーションを設定している場合でも、それを意識せずにバイナリログを暗号化することができます。レプリケーション用のログは親サーバー(プライマリ)が一度解読した後に子サーバー(レプリカ)に送られるからです。逆に、その通信を暗号化したい場合はレプリケーションの設定でSSL通信を指定する必要がありますが、その方法は。以前MariaDBでレプリケーションを設定した際の記事を参照してください。