Cloud Functions for Firebase + Cloud KMS でシークレット情報を扱う
開発時に後回しになりがちなシークレット情報を Cloud Functions for Firebase + Cloud KMS を利用して取り扱う方法を記載します。
--
(2020/12/10追記)
別途 Cloud Functions for Firebase + Secret Manager でシークレット情報を扱う という記事を書きました!
現在は Secret Manager を使った方が主流だと思いますので、合わせてご覧ください。
はじめに
サービスを開発し始めると、外部サービスのシークレットキーや認証用のID/PASSWORDなど、秘匿しなければいけない情報を扱いたいケースが多々あります。
これらの情報はソース管理するべきではない情報であるため、環境変数等を利用して対応するケースも多いかと思います。
Cloud Functions for Firebase にも 環境変数 はありますが、シークレット情報を直接扱うことには適していません。
Cloud Functionsのドキュメント にも以下のように記載があります。
シークレットの管理
環境変数は関数の構成に使用できますが、データベースの認証情報や API キーなどの機密情報の格納には適しません。 このような機密性の高い値は、ソースコードや外部の環境変数以外の場所に保存する必要があります。一部の実行環境やフレームワークでは、環境変数の内容がログに送信されることがあります。YAML ファイル、デプロイ スクリプト、ソース管理に重要な認証情報は保存しないでください。シークレットを保存する場合は、シークレット管理のベスト プラクティスを確認することをおすすめします。Cloud KMS と Cloud Functions の固有の統合はありません。
この記事では、Cloud KMS を使ってシークレット情報の暗号化/復号化を行う方法を記載します。
Cloud Key Management Service (Cloud KMS) について
Cloud KMS とは、GCPの暗号鍵管理サービスです。
詳しく知りたい方は詳細は公式ドキュメントをご覧ください。
対象読者
- Cloud Functions for Firebase を利用したことがある方
- GCP の扱いに少しでも慣れている方
GCP コンソール の細かい操作方法は省略している箇所がありますので、GCPを触ったことない方は分かりにくいかもしれません。
大まかなステップ
- Cloud KMS の設定
- Cloud KMS で暗号化する
- 暗号化した情報を Cloud Functions for Firebase の環境変数に登録する
- Functions で暗号化された環境変数を復号化する
Cloud KMS の設定
実践してみたい方はGCPの新規プロジェクトを作成して試してみることをお勧めします。
API の有効化
GCP コンソール → APIとサービス より、Cloud Key Management Service (KMS) API を有効化します。
キーリングの作成
GCP コンソール → セキュリティ → 暗号鍵 より、キーリングを作成します。
Cloud KMS で暗号化する
gcloud sdkを使って暗号化します。
事前にgcloudの対象プロジェクトを切り替えておいてください。
先ほど作成した キーリング/鍵 を使って暗号化します。
// 暗号化したいワード → some secret key とします。
$ echo -n "some secret key" | gcloud kms encrypt \
--location=asia-northeast1 \
--keyring=hoge-ring \
--key=hoge-key \
--plaintext-file=- \
--ciphertext-file=- | base64
CiQAkW+QvrLG2eA8WdcjdnA5LgND0wAdh3YDwnN4jkNaRjDz3RkSNwDx02zmdnf9DDGODE9tC4bkUg0M28wkMFY6LSru/Z+uMi/tavKCii1dzmbPKQV5ZsEIdaG8hSQ=
Cloud Functions for Firebase で復号化処理の作成
Functions 用の環境がない方は、firebase init
等で事前に環境を生成してください。
Cloud KMS を使うのに必要なモジュールをインポートします。
npm install @google-cloud/kms
Cloud KMSの情報と先ほど暗号化したものを環境変数に設定します。
$ firebase functions:config:set project.id="YOUR_PROJECT_ID"
$ firebase functions:config:set kms.keyring="hoge-ring"
$ firebase functions:config:set kms.key="hoge-key"
$ firebase functions:config:set kms.location="asia-northeast1"
$ firebase functions:config:set secret.encryptedData="CiQAkW+QvrLG2eA8WdcjdnA5LgND0wAdh3YDwnN4jkNaRjDz3RkSNwDx02zmdnf9DDGODE9tC4bkUg0M28wkMFY6LSru/Z+uMi/tavKCii1dzmbPKQV5ZsEIdaG8hSQ="
念のため設定した内容を確認します。
$ firebase functions:config:get
{
"kms": {
"keyring": "hoge-ring",
"key": "hoge-key",
"location": "asia-northeast1"
},
"secret": {
"encryptedData": "CiQAkW+QvrLG2eA8WdcjdnA5LgND0wAdh3YDwnN4jkNaRjDz3RkSNwDx02zmdnf9DDGODE9tC4bkUg0M28wkMFY6LSru/Z+uMi/tavKCii1dzmbPKQV5ZsEIdaG8hSQ="
},
"project": {
"id": "YOUR_PROJECT_ID"
}
実際の処理を書いていきましょう。
今回は HTTP トリガーとします。
import * as functions from 'firebase-functions';
import { KeyManagementServiceClient } from '@google-cloud/kms';
export const helloWorld = functions
.region("asia-northeast1")
.https
.onRequest(async (request, response) => {
// プロジェクトの情報を環境変数から取得します。
const projectId = functions.config().project.id;
// 暗号化されたデータを環境変数から取得します。
const encryptedData = functions.config().secret.encryptedData;
// KMS の情報を環境変数から取得します。
const keyring = functions.config().kms.keyring;
const key = functions.config().kms.key;
const location = functions.config().kms.location;
// 復号化します。
const client = new KeyManagementServiceClient();
const keyName = client.cryptoKeyPath(projectId, location, keyring, key);
const [result] = await client.decrypt({name: keyName, ciphertext: encryptedData,});
const decryptedData = result.plaintext?.toString();
// decryptedData を出力するのはセキュアではないですが、今回は検証のため出力しています。
response.send(`Hello World : ${decryptedData}`);
});
ではデプロイして動作を見てみましょう。
// デプロイ
$ firebase deploy --only functions
...
// 動作確認
// ******* はプロジェクトID
$ curl https://asia-northeast1-*******.cloudfunctions.net/helloWorld
Error: could not handle the request
何やらエラーがでました。
Functions のログをみてみると以下のようにエラーが出ているのが確認できます。
// ******* はプロジェクトID
PERMISSION_DENIED: Permission 'cloudkms.cryptoKeyVersions.useToDecrypt' denied on resource 'projects/*******/locations/asia-northeast1/keyRings/hoge-ring/cryptoKeys/hoge-key'
Functions から KMS にアクセスするには、権限が必要なようです。
Functions を動かしているサービスアカウントに復号化の権限を設定します。
権限の確認と設定
Functions を実行しているサービスアカウントの確認
GCPコンソール → Cloud Functions → helloWorld → 詳細タブ を開きます。
サービスアカウントが記載されています。
[email protected]
というサービスアカウントで実行されていることがわかります。
権限の設定
GCP コンソール → IAMと管理 を開きます。
先ほどの [email protected]
というメンバーが存在するので編集ボタンを押します。
新しいロールとして、Cloud KMS → クラウド KMS 暗号鍵の復号化 を追加します。
復号化の確認
権限の設定ができたので、復号化の確認をします。
// ******* はプロジェクトID
$ curl https://asia-northeast1-*******.cloudfunctions.net/helloWorld
Hello World : some secret key
できました!
まとめ
- シークレット情報を利用する際は Cloud KMS を利用して暗号化/復号化を行う。
- Functions から Cloud KMS にアクセスする際は権限をつける。
今回は Functions 内で復号化しかしていませんが、暗号化する際は暗号化の権限が必要です。 - シークレットでない環境情報は
firebase functions:config:set
で設定する。
補足
Cloud Functions を使えば特定の関数のみ復号化の権限をつけることもできます。
Cloud Functions にはサービスアカウントの設定がありますので、復号化権限を持つサービスアカウントを別途作成し、該当の関数にサービスアカウントを指定することで特定の関数のみ Cloud KMS にアクセスできるようになります。
Author And Source
この問題について(Cloud Functions for Firebase + Cloud KMS でシークレット情報を扱う), 我々は、より多くの情報をここで見つけました https://qiita.com/korih/items/dca9754d015b0308a149著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .