Securing the data encryption key#

透過的データ暗号化のためのキーデータキーは、通常initdb によって生成され、データディレクトリの下のファイルpg_encryption/key.bin に保存されます。このファイルには、実際には、実行時にさまざまな目的に使用される複数のキーが含まれています。ただし、データキーの観点からは、単一のシーケンスのランダムなバイトが含まれています。

これ以上のアクションがなければ、このファイルにはプレーンテキストのキーが含まれていますが、安全ではありません。暗号化されたデータディレクトリにアクセスできる人は誰でも平文キーにアクセスできます。これは、暗号化の目的を無効にします。したがって、この設定はテスト目的にのみ適しています。

データキーを適切に保護するには、別のキーで暗号化してデータキーを「ラップ」します。大まかに、2つのアプローチを使用してこれを調整できます。

  • パスフレーズでデータキーを保護します。ラッピングキーはパスフレーズから取得され、データキーの暗号化に使用されます。

  • ラッピングキーは、キーストアとしても知られるキー管理システムなどの他の場所に保存されます。この2番目のキーは、 キーラッピングキー または マスターキー とも呼ばれます。

テストなどでキーラッピングをしたくない場合は、wrapおよびunwrapコマンドを特別な値- に設定する必要があります。この設定は、それ以上の処理をせずにファイルのキーを使用することを指定します。このアプローチは、wrapまたはunwrapコマンドをまったく設定しないこと、およびいずれか/両方を空の文字列に設定することとは異なります。透過的データ暗号化が使用されるときにwrapまたはunwrapコマンドが設定されていないと、影響を受けるユーティリティプログラムを実行するときに致命的エラーが発生します。

Postgresは、この構成をユーザーに委ねるため、セットアップをローカル要件に合わせて調整し、既存のキー管理ソフトウェアなどと統合できます。データキー保護を構成するには、ラッピング暗号化とアンラップ復号化を処理する外部コマンドのペアを指定する必要があります。

パスフレーズを使用する#

opensslコマンドラインユーティリティを使用して、パスフレーズでデータキーを保護できます。次に、この保護を設定する例を示します。

initdb -D datadir -y --key-wrap-command=openssl enc -e -aes-128-cbc -pbkdf2 -out "%p" --key-unwrap-command=openssl enc -d -aes-128-cbc -pbkdf2 -in "%p"

この例では、ランダムに生成されたデータキーinitdbによって内部的に実行を、AES-128-CBC AESKWアルゴリズムで暗号化することによりラップします。暗号化には、PBKDF2キー導出ファンクションを使用してパスフレーズから導出されたキーと、ランダムに生成されたソルトが使用されます。ターミナルはパスフレーズの入力を求めます。これらのオプションの詳細については、openssl-encのマニュアルページを参照してください。使用可能なオプションはバージョンによって異なります。 initdbユーティリティは、 %p をラップしたキーを保存するファイルの名前に置き換えます。

unwrapコマンドは、逆の操作を実行します。 initdbは、 unwrap操作を必要としません。ただし、それは初期化されたクラスターのpostgresql.conf ファイルに保存され、起動時に使用されます。

key Wrapコマンドは、標準入力でプレーンテキストキーを受け取り、 %p プレースホルダーで指定されたファイルシステムの場所にラップされたキーを配置する必要があります。 key unwrapコマンドは、 %p プレースホルダーで指定されたファイルシステムの場所からラップされたキーを読み取り、ラップされていないキーを標準出力に書き込む必要があります。

pg_rewindやpg_upgradeなどのユーティリティプログラムは、データディレクトリまたはバックアップなどのコピーを直接動作します。状況に応じて、これらのプログラムにkey unwrapコマンドについて伝える必要があります。これらにはそれぞれ、この目的のためのコマンドラインオプションがあります。

操作を簡素化するために、環境変数にキーwrapおよびunwrapコマンドを設定することもできます。これらは、対応するコマンドラインオプションを提供しない場合、影響を受けるすべてのアプリケーションで受け入れられます。例

PGDATAKEYWRAPCMD=openssl enc -e -aes-128-cbc -pbkdf2 -out "%p"
PGDATAKEYUNWRAPCMD=openssl enc -d -aes-128-cbc -pbkdf2 -in "%p"
export PGDATAKEYWRAPCMD PGDATAKEYUNWRAPCMD

サーバーがpg_ctlまたはsystemdなどのサービスマネージャーを介して起動された場合、端末でパスワードの入力を求めるキーのラップ解除コマンドは機能しません。これらの環境では、サーバーはターミナルから切断されます。サーバー起動時に対話型パスワードプロンプトが必要な場合は、間接的なメカニズムを使用してパスワードを取得するより精巧な構成が必要です。

たとえば、systemdの場合、systemd-ask-password を使用できます。

PGDATAKEYWRAPCMD="bash -c openssl enc -e -aes-128-cbc -pbkdf2 -out %p -pass file:<(sudo systemd-ask-password --no-tty)"
PGDATAKEYUNWRAPCMD="bash -c openssl enc -d -aes-128-cbc -pbkdf2 -in %p -pass file:<(sudo systemd-ask-password --no-tty)"

/etc/sudoers のようなエントリーも必要です。

postgres ALL = NOPASSWD: /usr/bin/systemd-ask-password

キーストアを使用する#

外部キー管理システムのキーストアを使用して、データ暗号化キーを管理できます。テストおよびサポートされているキーストアは次のとおりです。

  • Amazon AWS Key Management ServiceKMS

  • Google Cloud - Cloud Key Management Service

  • HashiCorp Vault KMIP SecretsエンジンおよびTransit Secretsエンジン

  • Microsoft Azure Key Vault

  • Thales CipherTrust Manager

AWS Key Management Serviceの例#

AWS Key Management Serviceでキーを作成します。

aws kms create-key
aws kms create-alias --alias-name alias/pg-tde-master-1 --target-key-id "..."

aws kms コマンドとalias/pg-tde-master-1 キーを使用して、データ暗号化キーをラップおよびラップ解除します。

PGDATAKEYWRAPCMD=aws kms encrypt --key-id alias/pg-tde-master-1 --plaintext fileb:///dev/stdin --output text --query CiphertextBlob | base64 -d > "%p"
PGDATAKEYUNWRAPCMD=aws kms decrypt --key-id alias/pg-tde-master-1 --ciphertext-blob fileb://"%p" --output text --query Plaintext | base64 -d

注釈

この例のようなパイプを使用したシェルコマンドは、パイプの終了ステータスが最後のコマンドの終了ステータスであるため、問題があります。最初のより興味深いコマンドの失敗は、適切に報告されません。 Postgresは、wrapまたはunwrapコマンドが何も書き込んだかどうかを認識することにより、これをある程度処理します。ただし、これをより堅牢にすることをお勧めします。たとえば、一部のシェルで使用可能な`pipefail` オプション、または一部のオペレーティングシステムで使用可能な`mispipe` コマンドを使用します。より複雑なコマンドをインラインで定義するのではなく、外部シェルスクリプトまたは他のプログラムに配置します。

Azure Key Vaultの例#

Azure Key Vaultでキーを作成します。

az keyvault key create --vault-name pg-tde --name pg-tde-master-1

az keyvault key コマンドとpg-tde-master-1 キーを使用して、データ暗号化キーをラップおよびラップ解除します。

PGDATAKEYWRAPCMD=az keyvault key encrypt --name pg-tde-master-1 --vault-name pg-tde --algorithm A256GCM --value @- --data-type plaintext --only-show-errors --output json | jq -r .result > "%p"
PGDATAKEYUNWRAPCMD=az keyvault key decrypt --name pg-tde-master-1 --vault-name pg-tde --algorithm A256GCM --value @"%p" --data-type plaintext --only-show-errors --output json | jq -r .result

注釈

この例のようなパイプを使用したシェルコマンドは、パイプの終了ステータスが最後のコマンドの終了ステータスであるため、問題があります。最初のより興味深いコマンドの失敗は、適切に報告されません。 Postgresは、wrapまたはunwrapコマンドが何も書き込んだかどうかを認識することにより、これをある程度処理します。ただし、これをより堅牢にすることをお勧めします。たとえば、一部のシェルで使用可能な`pipefail` オプション、または一部のオペレーティングシステムで使用可能な`mispipe` コマンドを使用します。より複雑なコマンドをインラインで定義するのではなく、外部シェルスクリプトまたは他のプログラムに配置します。

Google Cloud KMSの例#

Google Cloud KMSでキーを作成します。

gcloud kms keys create pg-tde-master-1 --location=global --keyring=pg-tde --purpose=encryption

gcloud kms コマンドとpg-tde-master-1 キーを使用して、データ暗号化キーをラップおよびラップ解除します。

PGDATAKEYWRAPCMD=gcloud kms encrypt --plaintext-file=- --ciphertext-file=%p --location=global --keyring=pg-tde --key=pg-tde-master-1
PGDATAKEYUNWRAPCMD=gcloud kms decrypt --plaintext-file=- --ciphertext-file=%p --location=global --keyring=pg-tde --key=pg-tde-master-1

HashiCorp Vault Transit Secrets Engineの例#

#  enable once

vault secrets enable transit

#  create a key (pick a name)

vault write -f transit/keys/pg-tde-master-1

PGDATAKEYWRAPCMD=base64 | vault write -field=ciphertext transit/encrypt/pg-tde-master-1 plaintext=- > %p
PGDATAKEYUNWRAPCMD=vault write -field=plaintext transit/decrypt/pg-tde-master-1 ciphertext=- < %p | base64 -d

キーのローテーション#

マスターキーを変更するには、古いキーを指定してunwrapコマンドを手動で実行します。次に、結果をwrapコマンドにフィードして、新しいキーを指定します。同様に、データキーがパスフレーズで保護されている場合、パスフレーズを変更するには、古いパスフレーズを使用してunwrapコマンドを実行します。次に、新しいパスフレーズを使用して結果をwrapコマンドにフィードします。データベースサーバーの実行中にこれらの操作を実行できます。ファイル内のラップされたデータキーは、起動時にのみ使用されます。サーバーの実行中は使用されません。

opensslを使用する パスフレーズを使用する の例を基にパスフレーズを変更すると、次のことができます。

cd $PGDATA/pg_encryption/
openssl enc -d -aes-128-cbc -pbkdf2 -in key.bin | openssl enc -e -aes-128-cbc -pbkdf2 -out key.bin.new
mv key.bin.new key.bin
``` この方法では、復号化と暗号化コマンドが同時に端末でパスフレーズを要求しますが、これは厄介で混乱を招くものです。  代替案は次のとおりです。

```shell
cd $PGDATA/pg_encryption/
openssl enc -d -aes-128-cbc -pbkdf2 -in key.bin -pass pass:<replaceable>ACTUALPASSPHRASE</replaceable> | openssl enc -e -aes-128-cbc -pbkdf2 -out key.bin.new
mv key.bin.new key.bin
``` この手法は、古いパスフレーズを漏洩させますが、いずれにせよ置き換えられます。 opensslは、パスフレーズを提供する他の多くの方法をサポートしています。

キー管理システムを使用する場合、たとえば、 unwrapコマンドとwrapコマンドを接続できます。

```shell
cd $PGDATA/pg_encryption/
crypt decrypt aws --in key.bin --region us-east-1 | crypt encrypt aws --out key.bin.new --region us-east-1 --kms alias/pg-tde-master-2
mv key.bin.new key.bin

注釈

既存のデータディレクトリのデータキーマスターキーによってラップされたキーは変更できません。それを行う必要がある場合は、 pg_dump、pg_upgrade、または論理レプリケーションを使用したアップグレードプロセスでデータディレクトリを実行する必要があります。