Python での Secret Manager の使用

1. 概要

この Codelab では、Python での Secret Manager の使用に焦点を当てます。

Secret Manager を使用すると、シークレットをバイナリ blob またはテキスト文字列として保存、管理し、シークレットにアクセスできます。適切な権限があれば、シークレットの内容を表示できます。

Secret Manager は、実行時にアプリケーションが必要とするデータベース パスワード、API キー、TLS 証明書などの構成情報を保存するのに適しています。

学習内容

  • Cloud Shell の使い方
  • Python 用 Secret Manager クライアント ライブラリをインストールする方法
  • Python クライアント ライブラリを使用してシークレットを作成し、アクセスする方法
  • Python クライアント ライブラリを使用して Cloud Functions のシークレットにアクセスする方法

必要なもの

  • Google Cloud プロジェクト
  • ブラウザ(ChromeFirefox など)
  • Python 3 の基本知識

アンケート

このチュートリアルをどのように使用されますか?

通読のみ 通読して演習を行う

Python のご利用経験はどの程度ありますか?

初心者 中級者 上級者

Google Cloud サービスの使用経験はどの程度ありますか?

<ph type="x-smartling-placeholder"></ph> 初心者 中級 上達 をご覧ください。

2. 設定と要件

セルフペース型の環境設定

  1. Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Gmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列です。この値はいつでも更新できます。
  • プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud コンソールでは一意の文字列が自動生成されます。通常、それが何であるかは関係ありません。ほとんどの Codelab では、プロジェクト ID を参照する必要があります(通常は PROJECT_ID として識別されます)。生成された ID が気に入らない場合は、別のランダムな ID を生成できます。または、ご自身でお試しになることもできます。このステップを終えた後は変更できず、プロジェクト期間中は維持されます。
  • なお、3 つ目の値は、一部の API で使用される [プロジェクト番号] です。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
  1. 次に、Cloud のリソースや API を使用するために、Cloud コンソールで課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアルの終了後に課金が発生しないようにリソースをシャットダウンするには、作成したリソースを削除するか、プロジェクト全体を削除します。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。

Cloud Shell の起動

Google Cloud はノートパソコンからリモートで操作できますが、この Codelab では、Google Cloud Shell(Cloud 上で動作するコマンドライン環境)を使用します。

Cloud Shell をアクティブにする

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

55efc1aaa7a4d3ad.png

Cloud Shell を初めて起動する場合は、その内容を説明する中間画面(スクロールしなければ見えない範囲)が表示されます。その場合は、[続行] をクリックします(今後表示されなくなります)。この中間画面は次のようになります。

9c92662c6a846a5c.png

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

9f0e51b578fecce5.png

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

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. Secret Manager API を有効にする

Secret Manager API を使用するには、この API を有効にする必要があります。Cloud Shell を使用し、次のコマンドで API を有効にできます。

gcloud services enable secretmanager.googleapis.com

次のような出力が表示されます。

Operation "operations/acf.cc11852d-40af-47ad-9d59-477a12847c9e" finished successfully.

4. Python 用 Secret Manager クライアント ライブラリのインストール

Secret Manager クライアント ライブラリをインストールします。

pip3 install --user google-cloud-secret-manager==2.10.0

5. インタラクティブな Python を開始する

このチュートリアルでは、IPython というインタラクティブな Python インタープリタを使用します。このインタープリタは Cloud Shell にプリインストールされています。Cloud Shell で ipython を実行してセッションを開始します。

ipython

次のように表示されます。

Python 3.9.2 (default, Feb 28 2021, 17:03:44)
Type 'copyright', 'credits' or 'license' for more information
IPython 8.3.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]:

6. Secret の作成

1 つのシークレットには 1 つ以上のシークレット バージョンが含まれています。gcloud コマンドラインを使用して作成できますが、Python を使用して作成することもできます。

シークレットを使用するには、まずシークレットの名前でシークレットを作成し、次にシークレットのとなるシークレットのバージョンを追加する必要があります。

IPython 内でプロジェクト ID を設定します。

PROJECT_ID = "<PROJECT_ID>"

シークレットを作成する

次のコードを IPython セッションにコピーします。

from google.cloud import secretmanager

def create_secret(secret_id):
    # Create the Secret Manager client.
    client = secretmanager.SecretManagerServiceClient()

    # Build the resource name of the parent project.
    parent = f"projects/{PROJECT_ID}"

    # Build a dict of settings for the secret
    secret = {'replication': {'automatic': {}}}

    # Create the secret
    response = client.create_secret(secret_id=secret_id, parent=parent, secret=secret)

    # Print the new secret name.
    print(f'Created secret: {response.name}')   

関数を呼び出して、my_secret_value という新しいシークレットを作成します。

create_secret("my_secret_value")

次の出力が表示されます。

Created secret: projects/<PROJECT_NUM>/secrets/my_secret_value

シークレット バージョンを追加する

シークレットが作成されたので、バージョンを作成して値を割り当てることができます。

次のコードを IPython セッションにコピーします。

def add_secret_version(secret_id, payload):
    # Create the Secret Manager client.
    client = secretmanager.SecretManagerServiceClient()

    # Build the resource name of the parent secret.
    parent = f"projects/{PROJECT_ID}/secrets/{secret_id}"

    # Convert the string payload into a bytes. This step can be omitted if you
    # pass in bytes instead of a str for the payload argument.
    payload = payload.encode('UTF-8')

    # Add the secret version.
    response = client.add_secret_version(parent=parent, payload={'data': payload})

    # Print the new secret version name.
    print(f'Added secret version: {response.name}')   

関数を呼び出して、新しいシークレット バージョンを追加します。

add_secret_version("my_secret_value", "Hello Secret Manager")

次の出力が表示されます。

Added secret version: projects/<PROJECT_NUM>/secrets/my_secret_value/versions/1

シークレットには複数のバージョンを含めることができます。別の値で関数を再度呼び出します。

add_secret_version("my_secret_value", "Hello Again, Secret Manager")

次の出力が表示されます。

Added secret version: projects/<PROJECT_NUM>/secrets/my_secret_value/versions/2

新しいバージョンのシークレットが元のシークレットよりも大幅に長いことに注目してください。この属性は後で参照します。

7. シークレットへのアクセス

シークレット バージョンにアクセスすると、シークレットの内容と、シークレット バージョンに関する追加のメタデータが返されます。シークレット バージョンにアクセスするときは、特定のバージョンを指定するか、「latest」を指定して最新バージョンを要求できます。

Secret は非公開にする必要があります。データベースの認証情報をシークレットとして保存し、それを使用して認証するか、証明書を保存して使用する。ただし、機密事項を直接出力しないでください。秘密を守るという目的が果たされません。

これから Secret を操作し、Secret を直接出力せずにその値を評価します。代わりに、シークレットの値のハッシュを出力します。

次のコードを IPython セッションにコピーします。

def access_secret_version(secret_id, version_id="latest"):
    # Create the Secret Manager client.
    client = secretmanager.SecretManagerServiceClient()

    # Build the resource name of the secret version.
    name = f"projects/{PROJECT_ID}/secrets/{secret_id}/versions/{version_id}"

    # Access the secret version.
    response = client.access_secret_version(name=name)

    # Return the decoded payload.
    return response.payload.data.decode('UTF-8')
    
import hashlib

def secret_hash(secret_value): 
  # return the sha224 hash of the secret value
  return hashlib.sha224(bytes(secret_value, "utf-8")).hexdigest()

関数を呼び出して、シークレットを値のハッシュとして取得します。

secret_hash(access_secret_version("my_secret_value"))

ハッシュに似た出力が表示されます(正確な値がこの出力と一致しない場合があります)。

83f8a4edb555cde4271029354395c9f4b7d79706ffa90c746e021d11

バージョンを指定していないため、最新の値が取得されました。

想定されるバージョン番号を追加して関数を呼び出し、確認します。

secret_hash(access_secret_version("my_secret_value", version_id=2))

最後のコマンドと同じ出力が表示されます。

83f8a4edb555cde4271029354395c9f4b7d79706ffa90c746e021d11

関数を再度呼び出します。ただし、今回は最初のバージョンを指定します。

secret_hash(access_secret_version("my_secret_value", version_id=1))

今回は異なるハッシュが表示されるはずです。これは、出力が異なることを示しています。

9a3fc8b809ddc611c82aee950c636c7557e220893560ec2c1eeeb177

8. Cloud Functions での Secret Manager の使用

Google Cloud のさまざまな部分でシークレットを利用できます。このセクションでは、Google のイベント ドリブン サーバーレス コンピューティング サービスである Cloud Functions を中心に説明します。

Cloud Functions で Python を使用することに関心がある場合は、Python の Codelab における HTTP Google Cloud Functions のコースを進めることができます。

exit 関数を呼び出して IPython を閉じます。

exit

Cloud Shell に戻ります。

yourname@cloudshell:~ (<PROJECT_ID>)$

Cloud Functions API を使用する前に、この API を有効にする必要があります。Cloud Shell を使用し、次のコマンドで API を有効にできます。

gcloud services enable cloudfunctions.googleapis.com cloudbuild.googleapis.com

関数をビルドするための新しいフォルダを作成し、書き込み先となる空のファイルを作成します。

mkdir secret-manager-api-demo
cd secret-manager-api-demo
touch main.py
touch requirements.txt

Cloud Shell の右上からコードエディタを開きます。

7651a97c51e11a24.png

secret-manager-api-demo フォルダ内の main.py ファイルに移動します。ここにすべてのコードを配置します

9. シークレットにアクセスする Cloud Functions の関数を作成する

コマンドラインまたは IPython ターミナルからシークレットの値の保存や取得を行うのは便利ですが、関数内でこれらのシークレットにアクセスできるほうがはるかに便利です。

先ほど作成した access_secret_version 関数を使用して、Cloud Functions の関数のベースとして使用できます。

次のコードを main.py ファイルにコピーします。

main.py

import os

from google.cloud import secretmanager

project_id = os.environ["PROJECT_ID"]

client = secretmanager.SecretManagerServiceClient()
name = f"projects/{project_id}/secrets/my_secret_value/versions/latest"
response = client.access_secret_version(name=name)
my_secret_value = response.payload.data.decode("UTF-8")


def secret_hello(request):
    if "Again" in my_secret_value:
        return "We meet again!\n"

    return "Hello there.\n"

関数をデプロイする前に、環境の設定を完了する必要があります。そのためには、関数の依存関係を設定する必要があります。

requirements.txt という新しいファイルを作成し、google-cloud-secret-manager パッケージを追加します。

requirements.txt

google-cloud-secret-manager==2.10.0

これで、main.pyrequirements.txt だけを含むフォルダが作成されました。

シークレットへのアクセスを許可する

関数をデプロイする前に、Cloud Functions にシークレットへのアクセスを許可する必要があります。

ターミナルに戻ります。

c5b686edf94b5222.png

シークレットにアクセスするためのアクセス権を Cloud Functions サービス アカウントに付与します。

export PROJECT_ID=$(gcloud config get-value core/project)

gcloud secrets add-iam-policy-binding my_secret_value \
    --role roles/secretmanager.secretAccessor \
    --member serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com

次の出力が表示されます。

Updated IAM policy for secret [my_secret_value].
bindings:
- members:
  - serviceAccount:<PROJECT_ID>@appspot.gserviceaccount.com
  role: roles/secretmanager.secretAccessor
etag: BwWiRUt2oB4=
version: 1

10. Cloud Functions の関数をデプロイする

前のセクションのセットアップが完了したら、Cloud Functions の関数をデプロイしてテストできます。

作成した 2 つのファイルのみを含むフォルダ内で、関数をデプロイします。

gcloud functions deploy secret_hello \
    --runtime python39 \
    --set-env-vars PROJECT_ID=${PROJECT_ID} \
    --trigger-http \
    --allow-unauthenticated

次の出力が表示されます(一部が切り捨てられています)。

Deploying function (may take a while - up to 2 minutes)...done.

...

entryPoint: secret_hello
httpsTrigger:
  url: https://<REGION>-<PROJECT_ID>.cloudfunctions.net/secret_hello
...
status: ACTIVE
...

次のコマンドを使用して、関数の URL(httpsTrigger.url メタデータ)を取得します。

FUNCTION_URL=$(gcloud functions describe secret_hello --format 'value(httpsTrigger.url)')

次に、関数を呼び出して、期待される戻り値で関数にアクセスできるかどうかをテストします。

curl $FUNCTION_URL

次の出力が表示されます。

We meet again!

この関数は、文字列「Again」を含むように設定されている最新バージョンのシークレットを参照するため、この関数は想定どおりに動作します。

11. 完了

Python を使用して Secret Manager API を使用する方法を学びました。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにする手順は次のとおりです。

  • Cloud コンソールで、[リソースの管理] ページに移動します。
  • プロジェクト リストで、プロジェクトを選択し、[削除] をクリックします。
  • ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

詳細

ライセンス

この作業はクリエイティブ・コモンズの表示 2.0 汎用ライセンスにより使用許諾されています。