顧客管理の暗号鍵(CMEK)を使用して Cloud Functions を暗号化する

1. はじめに

概要

Cloud Functions は軽量なコンピューティング ソリューションであり、デベロッパーはサーバーやランタイム環境を管理することなく、Cloud イベントに応答する単一目的のスタンドアロン関数を作成できます。

Cloud Key Management Service 顧客管理の暗号鍵(CMEK)を使用すると、Cloud Functions と関連する保存データを保護できます。CMEK を使用して関数をデプロイすると、完全に制御可能な暗号鍵を使用して、関連するデータを保護します。このタイプの暗号化により、金融サービスなど特定の業界のコンプライアンス要件を満たすことができます。鍵はユーザーが所有するもので、Google によって管理されません。鍵が無効化または破棄されると、これらの暗号鍵で保護されたデータに誰もアクセスできなくなります。

Cloud Functions の場合、CMEK は次のものを暗号化します。

  • 関数のソースコードはデプロイ用にアップロードされ、Google によって Cloud Storage に保存され、ビルドプロセスで使用されます。
  • 関数のビルドプロセスの結果。関数のソースコードからビルドされたコンテナ イメージ、デプロイされた関数の各インスタンスが含まれます。
  • 内部イベント トランスポート チャネルの保存データ(第 1 世代のみ)。

暗号化されるデータの詳細については、Cloud Functions の CMEK のドキュメントをご覧ください。

作成するアプリの概要

この Codelab では、CMEK で暗号化された Cloud Functions の関数(第 1 世代または第 2 世代)をデプロイする方法について説明します。この Codelab ではデモ目的で、一般公開の Cloud Functions(認証を必要としない関数)を使用します。認証が必要な他の Cloud Functions の関数と同様に、認証済みの CMEK 対応関数を呼び出すことができます。

学習内容

  • 既存の対称キーリングに CMEK 鍵を作成する方法
  • Artifact Registry リポジトリを作成する方法
  • 第 1 世代と第 2 世代の両方の Cloud Functions の関数で CMEK を構成する方法

2. 設定と要件

前提条件

  • Cloud コンソールにログインしています
  • 以前に HTTP トリガーの Cloud Functions の関数をデプロイしている(適切なロールと API が有効になっていることを確認するため)

Cloud Shell をアクティブにする

  1. Cloud Console で、[Cloud Shell をアクティブにする] 853e55310c205094.png をクリックします。

55efc1aaa7a4d3ad.png

Cloud Shell を初めて起動する場合は、内容を説明する中間画面が表示されます。中間画面が表示されたら、[続行] をクリックします。

9c92662c6a846a5c.png

Cloud Shell のプロビジョニングと接続に少し時間がかかる程度です。

9f0e51b578fecce5.png

この仮想マシンには、必要なすべての開発ツールが読み込まれます。5 GB の永続的なホーム ディレクトリが用意されており、Google Cloud で稼働するため、ネットワークのパフォーマンスと認証が大幅に向上しています。この Codelab での作業のほとんどはブラウザを使って行うことができます。

Cloud Shell に接続すると、認証が完了し、プロジェクトに各自のプロジェクト ID が設定されていることがわかります。

  1. Cloud Shell で次のコマンドを実行して、認証されたことを確認します。
gcloud auth list

コマンド出力

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Cloud Shell で次のコマンドを実行して、gcloud コマンドがプロジェクトを認識していることを確認します。
gcloud config list project

コマンド出力

[core]
project = <PROJECT_ID>

上記のようになっていない場合は、次のコマンドで設定できます。

gcloud config set project <PROJECT_ID>

コマンド出力

Updated property [core/project].

3. Cloud Functions 用の新しいキーリングと鍵を作成する

次のコマンドを実行して、Cloud KMS API が有効になっていることを確認します。

gcloud services enable cloudkms.googleapis.com

まず、キーリング名、鍵名、リージョン、この Codelab で使用するその他の変数を格納する環境変数を作成します。

KEYRING_NAME="keyring-functions"
REGION="us-central1"
KEY_NAME="key-encrypted-function"
PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER="$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')"
USER_EMAIL="$(gcloud config list account --format "value(core.account)")"

次に、Cloud KMS 鍵と鍵バージョンのルートリソースであるキーリングを作成します。

gcloud kms keyrings create $KEYRING_NAME --location $REGION

最後に、Cloud KMS 内の新しいキーリングに対称鍵を作成できます。

gcloud kms keys create $KEY_NAME --keyring $KEYRING_NAME --location $REGION --purpose "encryption"

4. CMEK 対応の Docker 形式の Artifact Registry リポジトリを作成する

このセクションでは、CMEK が有効になっている Artifact Registry に Docker 形式のリポジトリを作成します。このキーは、Cloud Functions の関数のデプロイに使用するのと同じキーになります。

まず、Artifact Registry のサービス アカウントが必要です。環境変数を作成するには、次のコマンドを実行します。

gcloud beta services identity create --service=artifactregistry.googleapis.com --project=$PROJECT_ID

次のコマンドを使用して、Artifact Registry サービス アカウントに CryptoKey Encrypter/Decrypter IAM ロールroles/cloudkms.cryptoKeyEncrypterDecrypter)を付与し、鍵に対する権限を付与します。

gcloud kms keys add-iam-policy-binding \
  $KEY_NAME --location $REGION --keyring=$KEYRING_NAME \
  --member serviceAccount:service-$PROJECT_NUMBER@gcp-sa-artifactregistry.iam.gserviceaccount.com \
  --role roles/cloudkms.cryptoKeyEncrypterDecrypter

さらに、Artifact Registry にリポジトリを作成するプリンシパルにロールを付与します(例:。gcloud auth list を実行すると、現在アクティブなアカウントを確認できます。

gcloud kms keys add-iam-policy-binding \
       $KEY_NAME --location $REGION --keyring=$KEYRING_NAME \
       --member user:$USER_EMAIL \
       --role roles/cloudkms.cryptoKeyEncrypterDecrypter

これで、CMEK 対応の Docker 形式のリポジトリを作成できるようになりました。

注: リージョンは CMEK 鍵と同じリージョンにする必要があります。

REPO_NAME=my-cmek-encrypted-repo 

KEY_FULLPATH=projects/"$PROJECT_ID"/locations/"$REGION"/keyRings/"$KEYRING_NAME"/cryptoKeys/"$KEY_NAME" 

gcloud artifacts repositories create $REPO_NAME \
    --repository-format=docker \
    --location=$REGION \
    --kms-key=$KEY_FULLPATH \
    --async

次のコマンドを実行すると、新しい Artifact Registry リポジトリを表示できます。

gcloud artifacts repositories describe $REPO_NAME --location=$REGION

5. サービス アカウントに鍵へのアクセス権を付与する(第 2 世代)

このセクションでは、第 2 世代の関数のサービス アカウントの作成について説明します。第 1 世代の関数を作成する場合は、次のセクションに進んでください。

CryptoKey Encrypter/Decrypter IAM ロールroles/cloudkms.cryptoKeyEncrypterDecrypter)を付与して、複数のサービス エージェントに鍵へのアクセス権を付与する必要があります。これらのサービス エージェントは、Cloud Storage に保存されているソースコードへのアクセス権を取得し、Artifact Registry の CMEK で保護されたリポジトリに関数イメージを保存して、CMEK で暗号化された Cloud Functions の関数をデプロイするために使用されます。

第 2 世代の関数の手順

  1. Cloud Run サービス エージェントに鍵へのアクセス権を付与します。
CLOUDRUN_SA=service-$PROJECT_NUMBER@serverless-robot-prod.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$CLOUDRUN_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
  1. Eventarc サービス エージェントに鍵へのアクセス権を付与します。
EVENTARC_SA=service-$PROJECT_NUMBER@gcp-sa-eventarc.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$EVENTARC_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
  1. Artifact Registry サービス エージェントに鍵へのアクセス権を付与します。
AR_SA=service-$PROJECT_NUMBER@gcp-sa-artifactregistry.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$AR_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
  1. Cloud Storage サービス エージェントに鍵へのアクセス権を付与します。
STORAGE_SA=service-$PROJECT_NUMBER@gs-project-accounts.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$STORAGE_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter

次のセクションでは、CMEK で暗号化された関数を作成してデプロイする方法について説明します。

6. サービス アカウントに鍵へのアクセス権を付与する(第 1 世代)

このセクションでは、第 1 世代の関数のサービス アカウントの作成について説明します。第 2 世代の関数のサービス アカウントを作成済みである場合は、次のセクションに進んでください。

CryptoKey Encrypter/Decrypter IAM ロールroles/cloudkms.cryptoKeyEncrypterDecrypter)を付与して、複数のサービス エージェントに鍵へのアクセス権を付与する必要があります。これらのサービス エージェントは、Cloud Storage に保存されているソースコードへのアクセス権を取得し、Artifact Registry の CMEK で保護されたリポジトリに関数イメージを保存して、CMEK で暗号化された Cloud Functions の関数をデプロイするために使用されます。

第 1 世代の関数の手順

  1. Cloud Functions サービス エージェントに鍵へのアクセス権を付与します。
FUNCTION_SA=service-$PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$FUNCTION_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
  1. Artifact Registry サービス エージェントに鍵へのアクセス権を付与します。
AR_SA=service-$PROJECT_NUMBER@gcp-sa-artifactregistry.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$AR_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
  1. Cloud Storage サービス エージェントに鍵へのアクセス権を付与します。
STORAGE_SA=service-$PROJECT_NUMBER@gs-project-accounts.iam.gserviceaccount.com

gcloud kms keys add-iam-policy-binding $KEY_NAME \
--keyring=$KEYRING_NAME \
--location=$REGION \
--member=serviceAccount:$STORAGE_SA \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter

次のセクションでは、CMEK で暗号化された関数を作成してデプロイする方法について説明します。

7. CMEK で暗号化された関数(第 2 世代)を作成する

このセクションでは、第 2 世代の関数の作成について説明します。第 1 世代の手順については、次のセクションに進んでください。

CMEK を有効にして Artifact Registry リポジトリが構成され、Cloud Functions に鍵へのアクセス権を付与したので、CMEK 鍵を使用して暗号化された関数をデプロイできます。

第 2 世代の関数の手順:

関数のソースコードを作成する

この Codelab では Node.js を使用しますが、サポートされているランタイムであればどれでも使用できます。

まず、ディレクトリを作成し、そのディレクトリに移動します。

mkdir ~/cmek-function-2ndgen && cd $_

次に、package.json ファイルを作成します。

touch package.json

echo '{
  "dependencies": {
    "@google-cloud/functions-framework": "^2.1.0"
  }
}
' > package.json

次に、index.js ソースファイルを作成します。

touch index.js

echo 'const functions = require("@google-cloud/functions-framework");

functions.http("helloWorld", (req, res) => {
 res.send(`Hello ${req.query.name || req.body.name || "World"}!`);
});' > index.js

CMEK 暗号化を使用して Cloud Functions(第 2 世代)をデプロイする

注: 以下の例は、現在のディレクトリのソースを使用して関数をデプロイする方法を示しています。関数のソースコードと同じディレクトリにいることを確認します。

FUNCTION_NAME=protect-me-cmek-2ndgen
ENTRY_POINT=helloWorld

REPO_FULLPATH=projects/"$PROJECT_ID"/locations/"$REGION"/repositories/$REPO_NAME

gcloud beta functions deploy $FUNCTION_NAME  \
--gen2 \
--region $REGION \
--kms-key $KEY_FULLPATH \
--docker-repository $REPO_FULLPATH \
--source . \
--trigger-http \
--allow-unauthenticated \
--runtime nodejs16 \
--entry-point $ENTRY_POINT

次のコマンドを実行すると、結果の出力から CMEK 鍵を確認できます。

gcloud functions describe $FUNCTION_NAME –region $REGION |grep kmsKeyName

第 2 世代の関数をテストする

関数をテストするには、curl を実行します。

FUNCTION_URL="$(gcloud functions describe $FUNCTION_NAME --region $REGION --format='get(serviceConfig.uri)')"

curl $FUNCTION_URL

結果は次のようになります。

Hello World!

暗号鍵が有効である限り、関数は呼び出し元に成功を返します。ただし、暗号鍵が無効になると、呼び出し元はエラーを受け取ります。

次のセクションでは、キーを無効にした後で Function を呼び出すとどうなるかを確認します。

8. CMEK で暗号化された関数を作成する(第 1 世代)

このセクションでは、第 1 世代の関数の作成について説明します。以前に第 2 世代の関数を作成している場合は、次のセクションに進んでください。

CMEK を有効にして Artifact Registry リポジトリが構成され、Cloud Functions に鍵へのアクセス権を付与したので、CMEK 鍵を使用して暗号化された関数をデプロイできます。

第 1 世代の関数の手順:

第 1 世代の関数のソースコードを作成する

この Codelab では Node.js を使用しますが、サポートされているランタイムであればどれでも使用できます。

まず、ディレクトリを作成し、そのディレクトリに移動します。

mkdir ~/cmek-function-1stgen && cd $_

次に、package.json ファイルを作成します。

touch package.json

echo '{
    "name": "function-cmek-codelab",
    "version": "0.0.1"
}' > package.json

次に、index.js ソースファイルを作成します。

touch index.js

echo "exports.helloWorld = (req, res) => {
    let message = req.query.message || req.body.message || 'Hello World!';
    res.status(200).send(message);
};" > index.js

CMEK 暗号化を使用して Cloud Functions(第 1 世代)をデプロイする

注: 以下の例は、現在のディレクトリのソースを使用して関数をデプロイする方法を示しています。関数のソースコードと同じディレクトリにいることを確認します。

FUNCTION_NAME=protect-me-cmek-1stgen
ENTRY_POINT=helloWorld

REPO_FULLPATH=projects/"$PROJECT_ID"/locations/"$REGION"/repositories/$REPO_NAME

gcloud functions deploy $FUNCTION_NAME  \
--region $REGION \
--kms-key $KEY_FULLPATH \
--docker-repository $REPO_FULLPATH \
--source . \
--trigger-http \
--allow-unauthenticated \
--runtime nodejs16 \
--entry-point $ENTRY_POINT

次のコマンドを実行すると、結果の出力から CMEK 鍵を確認できます。

gcloud functions describe $FUNCTION_NAME –region $REGION |grep kmsKeyName

第 1 世代の関数をテストする

関数をテストするには、curl を実行します。

FUNCTION_URL="$(gcloud functions describe $FUNCTION_NAME --region $REGION --format='get(httpsTrigger.url)')"

curl $FUNCTION_URL

結果は次のようになります。

Hello World!

暗号鍵が有効である限り、関数は呼び出し元に成功を返します。ただし、暗号鍵が無効になると、呼び出し元はエラーを受け取ります。

次のセクションでは、キーを無効にした後で Function を呼び出すとどうなるかを確認します。

9. 暗号鍵が無効になっている CMEK で暗号化された関数を呼び出す

この最後のセクションでは、キーを無効にして関数を再度呼び出し、結果のエラーを確認します。

暗号鍵を無効にする

このコマンドを実行すると、鍵を無効にすることができます。この Codelab では 1 つのバージョンのキーのみが作成されるため、バージョン 1 を無効にします。

gcloud kms keys versions disable 1 \
    --key=$KEY_NAME \
    --keyring=$KEYRING_NAME \
    --location=$REGION

次のような結果が表示されます。

algorithm: GOOGLE_SYMMETRIC_ENCRYPTION
createTime: '2023-04-11T03:30:49.111832653Z'
generateTime: '2023-04-11T03:30:49.111832653Z'
name: projects/dogfood-gcf-saraford/locations/us-central1/keyRings/myKeyRing/cryptoKeys/encrypted-function/cryptoKeyVersions/1
protectionLevel: SOFTWARE
state: DISABLED

無効なキーで関数を呼び出す

ここで、関数を再度 curl します。

curl $FUNCTION_URL

今回は「Hello World」のレスポンスは返されません。

Cloud Functions の関数のログには、

User's CMEK key has been disabled. CMEK key: projects/<PROJECT-NAME>/locations/us-central1/keyRings/myKeyRing/cryptoKeys/encrypted-function

CMEK 鍵が無効になっているときにリソースの表示を試行する

このセクションでは、CMEK 鍵が無効になると次のリソースが利用できなくなります。

  • 関数のソースコード
  • ソースコードからのコンテナ イメージのビルド

たとえば、Cloud Functions の関数の [ソース] タブにアクセスすると、アーカイブの取得中にエラーが表示されます。ソースコードを含む .zip ファイルを Cloud Storage で直接表示しようとすると、同様のエラーが発生します。

ac3307bb05d30e19.png

また、Artifact Registry から関数のコンテナ イメージを使用するためのアクセス権がなくなります。たとえば、そのコンテナ イメージを Cloud Run にデプロイしようとすると、イメージが見つからないというエラーが表示されます。

暗号化されたリソースの完全なリストについては、CMEK 関数のドキュメントをご覧ください。

10.完了

お疲れさまでした。これでこの Codelab は終了です。

学習した内容

  • 既存の対称キーリングに CMEK 鍵を作成する方法
  • Artifact Registry リポジトリを作成する方法
  • Cloud Functions の関数で CMEK を構成する方法

詳細情報

Cloud Functions と CMEK の詳細については、次のリンクをご覧ください。