将 Secret Manager 与 Python 搭配使用

1. 概览

在此 Codelab 中,您将重点学习如何在 Python 中使用 Secret Manager。

借助 Secret Manager,您可以将 Secret 作为二进制 blob 或文本字符串进行存储、管理和访问。如果您具有适当的权限,则可以查看密文的内容。

Secret Manager 非常适合用于存储应用运行时所需的数据库密码、API 密钥或 TLS 证书等配置信息。

学习内容

  • 如何使用 Cloud Shell
  • 如何安装 Python 版 Secret Manager 客户端库
  • 如何使用 Python 客户端库创建和访问 Secret
  • 如何使用 Python 客户端库在 Cloud Functions 中访问密钥

所需条件

  • Google Cloud 项目
  • 一个浏览器,例如 ChromeFirefox
  • 熟悉如何使用 Python 3

调查问卷

您将如何使用本教程?

仅阅读教程内容 阅读并完成练习

您如何评价使用 Python 的体验?

新手水平 中等水平 熟练水平

您如何评价自己在使用 Google Cloud 服务方面的经验水平?

新手水平 中等水平 熟练水平

2. 设置和要求

自定进度的环境设置

  1. 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时更新。
  • 项目 ID 在所有 Google Cloud 项目中必须是唯一的,并且不可变(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(通常用 PROJECT_ID 标识)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且此 ID 在项目期间会一直保留。
  • 此外,还有第三个值,即部分 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档
  1. 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。若要关闭资源以避免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除整个项目。Google Cloud 的新用户符合参与 $300 USD 免费试用计划的条件。

启动 Cloud Shell

虽然可以通过笔记本电脑对 Google Cloud 进行远程操作,但在此 Codelab 中,您将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。

激活 Cloud Shell

  1. 在 Cloud Console 中,点击激活 Cloud Shell853e55310c205094.png

55efc1aaa7a4d3ad.png

如果您以前从未启动过 Cloud Shell,将看到一个中间屏幕(非首屏),描述它是什么。如果是这种情况,请点击继续(您将永远不会再看到它)。一次性屏幕如下所示:

9c92662c6a846a5c.png

预配和连接到 Cloud Shell 只需花几分钟时间。

9f0e51b578fecce5.png

这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 5GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证。只需使用一个浏览器或 Google Chromebook 即可完成本 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. 启用 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

密文包含一个或多个密文版本。可以使用 gcloud 命令行创建它们,也可以使用 Python 创建它们。

如需使用密文,您首先需要使用密文的名称创建密文,然后添加密文的版本,即密文的

在 IPython 中设置项目 ID:

PROJECT_ID = "<PROJECT_ID>"

创建 Secret

将以下代码复制到 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 的新 Secret:

create_secret("my_secret_value")

您应该会看到以下输出内容:

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

添加密文版本

现在,Secret 已存在,您可以通过创建版本为其分配

将以下代码复制到 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

请注意,新版 Secret 比原版长得多。此属性将在稍后引用。

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. 将 Secret Manager 与 Cloud Functions 搭配使用

您可以在 Google Cloud 的许多部分中使用 Secret。在本部分中,您将重点了解 Cloud Functions,这是 Google 的事件驱动型无服务器计算产品。

如果您有兴趣在 Cloud Functions 中使用 Python,可以按照 Python 中的 HTTP Google Cloud Functions Codelab 操作。

通过调用 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. 编写用于访问 Secret 的 Cloud Functions 函数

虽然从命令行或 IPython 终端存储和检索 Secret 值很有用,但能够在函数中访问这些 Secret 会更有用。

使用您之前创建的 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

允许访问您的 Secret

您需要先允许 Cloud Functions 访问您的 Secret,然后才能部署函数。

切换回终端:

c5b686edf94b5222.png

向 Cloud Functions 服务账号授予对您的 Secret 的访问权限:

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 函数了。

在仅包含您创建的两个文件的文件夹中,部署函数:

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
...

使用以下命令检索函数的网址(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 Console 中,转到管理资源页面。
  • 在项目列表中,选择您的项目,然后点击删除
  • 在对话框中输入项目 ID,然后点击关停以删除项目。

了解详情

许可

此作品已获得 Creative Commons Attribution 2.0 通用许可授权。