概要
HashiCorp VaultのTransit Secrets Engineを利用して、
PostgreSQLに格納するデータを暗号化して保存するユーザ定義型(Cipher型)を学習のため実装してみました。
・pg_vault_encrypt
https://github.com/ssl-oyamata/pg_vault_encrypt
→ Cipher型を実装してみたGithubレポジトリです(途中です)
HashiCorp Vault概要
Vaultは機密情報(シークレット)の管理を行うソフトウェアです。
データベースの接続パスワード、クラウドサービスのシークレットキー、証明書などの動的な払い出しとシークレットのライフサイクル管理を行います。
Vaulthへの認証にはトークンやユーザ・パスワードの他にLDAP,GitHubなどの多くのソフトウェアやサービスを利用可能です。
また、アクセスポリシーで要件に合わせた権限制御も可能です。
Transit Secrets Engine概要
VaultのTransit Secrets Engineはクライアントから送付されたデータの暗号化・復号を行う機能です。
暗号化するデータをクライアントからVaultに送信するとVaultが暗号化し、暗号化データをクライアントに返却します。
暗号化されたデータをVaultに送付するとVaultが復号しクライアントに返却します。
暗号化キーはVaultでセキュアに管理されており、暗号化キーのローテーションやバージョン管理が容易に実現可能になっています。
Transit Secrets Engineを使ってみる
手元のCentOS Linux release 7.8.2003 (Core)の環境で試してみます。
Vaultインストール
# wget https://releases.hashicorp.com/vault/1.6.0/vault_1.6.0_linux_amd64.zip
# unzip vault_1.6.0_linux_amd64.zip -d /usr/local/bin/
# vault version
Vault v1.6.0 (7ce0bd9691998e0443bc77e98b1e2a4ab1e965d4)
Vault起動
Vaultをdevモードで起動します。devモードではVaultのデータはメモリ上に保持され、
Vault停止時にデータは失われますので注意して下さい。
# export VAULT_ADDR="http://127.0.0.1:8200"
# vault server -dev
[省略]
Root Token: s.j0NkNK4zRglchBwvkTuTRVxt
Root TokenはVaultにログインする際に利用するので、メモしておきます。
Vaultへのログイン
# export VAULT_ADDR="http://127.0.0.1:8200"
# vault login
Token (will be hidden): [Root Tokenを入力]
Transit Secrets Engineの有効化
Transit Secrets Engineを有効化します。
# vault secrets enable transit
暗号化キーリングの作成
testという名前で暗号化キーリングを作成します。
# vault write -f transit/keys/test
データ暗号化
Transit Secrets Engineで暗号化するためにはデータをbase64にエンコードする必要があります。
# vault write transit/encrypt/test \
plaintext=$(base64 <<< "宇宙人になりたい")
Key Value
--- -----
ciphertext vault:v1:HYS1O0IEnf+IiI3WsSGULYXmDrGSxjmaMEKehLJvcSbejve9w58XhWJXHH7MXMAo559DtUc=
key_version 1
データ復号化
Vaultから返却された暗号化データを復号します。
Base64で返却されるため、デコードします。
# vault write transit/decrypt/test \
ciphertext="vault:v1:HYS1O0IEnf+IiI3WsSGULYXmDrGSxjmaMEKehLJvcSbejve9w58XhWJXHH7MXMAo559DtUc="
Key Value
--- -----
plaintext 5a6H5a6Z5Lq644Gr44Gq44KK44Gf44GECg==
# base64 --decode <<< "5a6H5a6Z5Lq644Gr44Gq44KK44Gf44GECg=="
宇宙人になりたい
キーのローテート
容易にキーのローテートも可能です。
# vault write -f transit/keys/test/rotate
キーのローテートを行うと暗号化データの接頭語がvault:v2になりました。
# vault write transit/encrypt/test \
plaintext=$(base64 <<< "宇宙人になりたい2")
Key Value
--- -----
ciphertext vault:v2:n9krlq4iPxoAlKD894isR7qUbAdiQ/mynJFvEBVxQSUha4f8UHiDp321FuzgMN7AwpaUgTjV
key_version 2
デフォルトでは暗号化はaes256-gcm96に設定されていました。
# vault read transit/keys/test
[省略]
type aes256-gcm96
容易に高度なデータ暗号化とキーのローテートが実現できました。
PostgreSQLのデータ暗号化・復号
VaultのTransit Secrets Engineを利用し、PostgreSQLに格納するテーブルのデータの一部を暗号化したいと考えました。
既存のアプリケーションを修正せずに、PostgreSQLのデータを暗号化したいと考え、ユーザ定義型(cipher型)を作成しました。
cipher型は下記のように動作します。
・データ保存時(入力) : Transit Secrets Engineでデータを暗号化し保存
・データ参照時(出力) : Transit Secrets Engineで暗号化データを復号し返却
インストール
インストール方法は下記をご参照いただけると幸いです。
https://github.com/ssl-oyamata/pg_vault_encrypt
Cipher型の動作
Cipher型を利用する場合、pg_vault_encrypt.vault_tokenパラメータなどを設定する必要があります。
$ psql -d cipherdb
=# SET pg_vault_encrypt.vault_token = 's.v8AYIpHOXGvNSKSpEfcsL1Dk';
SET
=# CREATE TABLE test(id int , data cipher);
=# INSERT INTO test values(1, 'secret data1'); # 暗号化したデータが保存されています。
=# INSERT INTO test values(2, 'secret data2'); # 暗号化したデータが保存されています。
=# SELECT * FROM test;
id | data
----+--------------
1 | secret data1
2 | secret data2
(2 行)
=# SELECT * FROM test where data = 'secret data2';
id | data
----+--------------
2 | secret data2
(1 行)
cipher型ではpg_vault_encrypt.vault_tokenパラメータに指定したVaultのトークンが誤っていた場合にはエラーになり、データが参照できません。
=# SET pg_vault_encrypt.vault_token = 'dummy';
=# SELECT * FROM test where data = 'secret data2';
ERROR: Cannot decrypt data (vault:v1:tA85X0C42FxEFVhX4VLXG49rfrTpHU4WoXl4fux9/5uJPET3iohAUA==): Error making API request.
URL: PUT https://192.168.0.30:8200/v1/encryption/decrypt/pgtest
Code: 403. Errors:
* permission denied
暗号化の確認
PostgreSQLのテーブルを構成するデータファイルが暗号化されていることを確認します。
データベースのOIDを確認
pg_stat_databaseビューのdatid(データベースのOID)列で、確認します。
# psql -d cipherdb
=# SELECT datid, datname FROM pg_stat_database WHERE datname='cipherdb';
datid | datname
---------+----------
1411807 | cipherdb
(1 行)
テスト用に用意したデータベース(cipherdb)は、$PGDATA/base/1411807ディレクトリに格納されています。
テーブルのファイル名を確認
pg_classカタログのrelfilenode(リレーションのディスク上のファイルの名前)列で、確認します。
=# SELECT relname, relfilenode from pg_class WHERE relname='test';
relname | relfilenode
---------+-------------
test | 1411946
(1 行)
テスト用に用意したデータベース(cipherdb)の、
testテーブルはファイル($PGDATA/base/1411807/1411946)に保存されていることを確認できました。
暗号化の確認
testテーブルが保存されているファイル($PGDATA/base/1411807/1411946)をstringsコマンドで参照してみます。
$ strings $PGDATA/base/1411807/1411946
vault:v1:41AB/8k2yiuth9rHdUAaiqOrqwpngg7CbuC3etKu195yCe3Bo/tOaA==
vault:v1:tA85X0C42FxEFVhX4VLXG49rfrTpHU4WoXl4fux9/5uJPET3iohAUA==
データが暗号化されていることを確認できました。
補足.
暗号化していないテーブルの場合、以下のように表示されます。
$ strings $PGDATA/base/1411807/1411949
secret data1
secret data2
以上です、間違いなどあればコメントください!
参考.