Google Cloudの「Cloud Run」は、コンテナを手軽にデプロイできるサーバレスプラットフォームとして人気がありますが、利用の際にはシークレット情報の慎重な取り扱いが必要です。今回は、シークレットの管理ツール「Google Secret Manager」の使い方を紹介します。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
本連載「Google Cloudチートシート」では、Google Cloudを活用する上でのさまざまなコツを、できるだけ分かりやすく、簡潔に紹介してきました。今回は最終回として、「Cloud Run」における「Google Secret Manager」の利用について説明します。
Cloud Runは、コンテナを手軽にデプロイできるGoogle Cloudの人気のサーバレスプラットフォームです。Cloud RunアプリケーションでデータベースやAPIを利用する際には、APIキー、パスワード、証明書などの機密情報(以下、シークレット)の安全な取り扱いは重要な課題です。万が一、シークレットが流出すれば、不正アクセスやランサムウェア攻撃の対象となります。
コードや環境変数として、プログラム上に直接記述するケースがありますが、漏えいリスクを伴います。そこでSecret Managerの出番です。Secret Managerは、シークレットを一元的、かつ、安全に保存・管理できるサービスです。本記事では、これをCloud Runで活用するための方法を紹介します。
Cloud Runでは、Secret Managerのシークレットをサービスに安全に連携させる方法として、主に次の2つのパターンがあります。
Cloud Runサービスをデプロイまたは更新する時に、使用するシークレットと特定バージョン(もしくは 最新を意味する「latest」)を設定します。
アプリケーションは、環境変数を参照するか、設定されたファイルパスからシークレット値を読み取るだけで済みます。
この連携を利用するためには、Cloud Runサービスが使用するサービスアカウントに「Secret Managerシークレットアクセサー (roles/secretmanager.secretAccessor)」のロールが必要です。
上記の2つの方法の重要な違いは、シークレットの値がいつ読み込まれ、更新がいつ反映されるかという点です。
環境変数として読み込む場合、設定がシンプルで簡単に扱える利点があります。値の更新タイミングの特性から、特定バージョンのシークレットを利用したい場合や単純な文字列のシークレットを扱いたい場合に適しています。
ファイルとしてマウントする場合、証明書やJSONの設定ファイル、バイナリデータなど、さまざまな形式のシークレットを扱える利点があります。また、シークレットの更新をアプリケーションへより動的に反映させたい場合や、latestバージョンを利用したい場合に適しています。
シークレットの値はCloud Runインスタンスの起動時に環境変数として設定されます。
これは、一度インスタンスが起動すると、そのインスタンスが稼働している間は同じシークレット値が使用され続けることを意味します。
Secret Manager側でシークレットのバージョン(例:バージョン1)が更新されても、既に稼働中のインスタンスの環境変数の値は自動的には更新されません。
新しいシークレット値を反映させるためには、Cloud Runサービスの新しいリビジョンをデプロイするか、インスタンスを再起動する必要があります。
環境変数としてシークレットを利用する場合は、意図しないバージョンの利用を避けるためにも、latestではなく特定バージョンを明示的に指定することがおすすめです。
[Cloud Run]の画面の[コンテナ]タブから[変数とシークレット]で設定できます。
このように、シークレットを環境変数として簡単に読み込めます。
package main import ( "fmt" "log" "os" ) func main() { secret := os.Getenv("MY_SECRET") if secret == "" { return log.Fatalf("シークレットの読み取りに失敗しました") } // 注意:実際のアプリケーションでは、取得したシークレットをログに出力しないでください。 fmt.Printf("シークレット -> %s\n", secret) }
ファイルマウントの場合、ファイルを読み込むたびにSecret Managerから最新のシークレットの値が取得されます。
参照するシークレットのバージョンがlatestの場合、稼働中のサービスの再デプロイをすることなく、シークレットの更新を反映させることが可能となります。これは継続的なシークレットローテーションの運用に適しています。
手軽で便利なlatestバージョンの利用ですが、一方で、新しく登録したシークレットのバージョンに何らかの意図しない問題(値のtypoエラーなど)が含まれていた場合、その問題のあるバージョンが即座に本番環境のアプリケーションに読み込まれてしまいます。
これにより、予期せぬエラーやサービス停止といった重大なインシデントを引き起こす可能性があることに注意が必要です。
ファイルとしてマウントする場合、まず[Cloud Run]の画面の[ボリューム]タブから[ボリュームを追加]を選び、ボリュームを作成します。
次に、作成したボリュームを、[コンテナ]タブの[ボリュームのマウント]タブから選択します。
ファイルとして読み込むことも簡単にできます。
package main import ( "fmt" "log" "os" ) func main() { secretData, err := os.ReadFile("/secret/MY_SECRET") if err != nil { log.Fatalf("シークレットファイルの読み取りに失敗しました: %v", err) } secret := string(secretData) if secret == "" { log.Fatalf("シークレットファイルの内容が空です") } // 注意:実際のアプリケーションでは、取得したシークレットをログに出力しないでください。 fmt.Printf("シークレット -> %s\n", secret) }
Secret Managerの利用規模が拡大すると、管理するシークレットの数が増えます。どの環境、どのアプリケーションに関連したシークレットなのかを見つけ出しにくくなり、運用負荷が高くなります。
そこで、シークレットをグルーピングするラベル機能が運用の効率化に役立ちます。具体的な使い方としては以下の2つの例があります。
なお、ラベルはキーと値のペアで、各シークレットに複数付与することができます。
例1:環境や用途に応じてラベル付け
シークレットを使用する環境に応じて 、env:prdやenv:dev、app:sample-serviceなどのラベルを付与することでシークレットを識別しやすくでき、また、フィルタでの検索のしやすさが向上します。
なお、ラベルを付与することで、IAMの条件機能と連携させて、特定のラベルを持つシークレットへのアクセス権限を細かく制御することも可能です。
例2:シークレット利用のコスト管理
シークレットに利用しているプロジェクトやサービス、環境といったラベルを付与しておくと、Google Cloudの請求レポートでこれらのラベルに基づいてコストを分類し、追跡することができます。例えば「どのアプリケーションに関連するシークレットのコストが高いか」といった分析が容易になり、コスト最適化に役立ちます。
Google Secret Managerは、シークレットを安全に管理するための強力なプロダクトです。これらの小技を活用することで、さらに便利に利用できます。
Copyright © ITmedia, Inc. All Rights Reserved.