集成 Cloud Run 和工作负载身份联合


本教程介绍了如何使用工作负载身份联合对 Google Cloud 外部运行的工作负载进行身份验证,以便它们可以访问由 Cloud Run 托管的微服务。本教程适用于希望将工作负载身份联合与现有身份提供方 (IdP) 集成的管理员。借助工作负载身份联合,您可以将外部工作负载连接到在 Google Cloud 中运行的工作负载。Cloud Run 可让您运行无状态容器化微服务。

本教程介绍了如何将 Jenkins 配置为外部工作负载,将 Keycloak 配置为 IdP、Cloud Run 以及工作负载身份联合。完成本教程后,您可以了解工作负载身份联合如何让您使用 OpenID Connect 身份验证通过 Google Cloud 对 Jenkins 应用进行身份验证。

使用 Workload Identity Federation 进行外部工作负载身份验证

借助工作负载身份联合,您可以对 Google Cloud 外部的工作负载进行身份验证,而无需使用静态服务账号密钥。任何需要使用 Google Cloud 中服务的外部工作负载都可以从此功能中受益。

借助工作负载身份联合,您可以使用 IdP 直接向 Google Cloud 进行身份验证。要进行身份验证,请使用 OpenID Connect。Cloud Run 接受来自 IdP 的 OpenID Connect 令牌以进行身份验证。

使用 Workload Identity Federation 时的身份验证流程如下:

  1. 您的身份验证 (AUTHN) 库会向 IdP 发送 JSON Web 令牌 (JWT) 请求。
  2. IdP 对 JSON Web 令牌 (JWT) 签名。AUTHN 库从变量中读取此数据。
  3. 该库会向包含签名令牌的 Security Token Service 发送 POST 命令。
  4. Security Token Service 会查看您配置的工作负载身份池提供方以建立信任并验证凭据上的身份。
  5. Security Token Service 会发回联合令牌。
  6. 该库将此令牌发送到 IAM。
  7. IAM 将此令牌交换为服务账号的 OpenID Connect 令牌。如需了解详情,请参阅生成 OpenID Connect ID 令牌
  8. 该库会向 Jenkins 提供 OpenID Connect 令牌。
  9. Jenkins 会使用此令牌对 Cloud Run 进行身份验证。

下图演示了身份验证流程:

身份验证流程。

目标

  • 将 Jenkins 配置为外部工作负载。
  • Keycloak 配置为与 OpenID Connect 兼容的 IdP。
  • 将 Jenkins 与 Keycloak 连接。
  • 安装 Cloud 客户端库,以将 JWT 令牌从 Keycloak 获取到 Google Cloud。
  • 将 Google Cloud 连接到 Keycloak 和 Jenkins。
  • 从 Keycloak 获取经过身份验证的用户的 JWT。

虽然本教程使用 Keycloak,但您可以使用支持 OpenID Connect 的任何身份提供方,例如 GitLab、Okta 或 OneLogin。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

准备工作

  1. In the Google Cloud console, go to the project selector page.

    Go to project selector

  2. Select or create a Google Cloud project.

  3. Make sure that billing is enabled for your Google Cloud project.

  4. 在 Cloud Run 上设置微服务。如需了解详情,请参阅《快速入门:将容器部署到 Cloud Run》

配置 Jenkins

在非 Google Cloud 环境(例如本地环境或其他云)中完成这些任务。

如果您已有支持 OpenID Connect 和外部工作负载的身份提供方,则可以跳过此步骤并转到安装 Cloud 客户端库

如需模拟外部工作负载,您可以使用安装了 Jenkins 的虚拟机。您可以将 Jenkins 作为 Docker 映像运行,也可以直接将其安装到服务器中。以下步骤演示了如何直接将 Jenkins 安装在服务器上。

  1. 在您选择的虚拟机上,打开命令行。
  2. 安装 Java:

    $ sudo apt update
    $ sudo apt install openjdk-11-jre
    $ java -version
    
  3. 安装 Jenkins:

    curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo tee \
    /usr/share/keyrings/jenkins-keyring.asc > /dev/null
    echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
    https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
    /etc/apt/sources.list.d/jenkins.list > /dev/null
    sudo apt-get update
    sudo apt-get install jenkins
    
  4. 验证您是否可以通过端口 8080 访问 Jenkins 服务器。如果您使用的是位于防火墙后面的虚拟机,请确保相应的端口处于开放状态。

  5. 获取管理员密码并设置 Jenkins。如需查看说明,请参阅安装后设置向导

  6. 完成以下操作来设置 SSL:

    1. 如果您有网域提供方,则可以使用其证书授权机构 (CA) 请求签名证书。或者,您也可以从 zerossl.com 获取有效期为 90 天的免费签名证书。
    2. 下载证书 ZIP 文件,并将其传输到运行 Jenkins 的服务器:

      scp -i CERTFILE.pem -r CERTFILE.zip VM_FQDN:/home/USERNAME
      

      请替换以下内容:

      • CERTFILE 替换为包含公钥的证书文件的名称。
      • VM_FQDN 替换为您在 Google Cloud 之外的服务器的 FQDN。
      • USERNAME 替换为您的用户名。
    3. 重命名这些文件并生成 Jenkins 可以使用的 .pkcs12 文件:

      openssl rsa -in KEYFILE.com.key -out KEYFILE.com.key

      KEYFILE 替换为证书文件的名称。

  7. 更新 /etc/sysconfig/jenkins 文件。

    1. 在文本编辑器中打开该文件:

      vi /etc/sysconfig/jenkins
      
    2. JENKINS_PORT 设置为 -1

    3. JENKINS_HTTPS_PORT 设置为 8443

    4. 在文件底部添加以下参数:

      JENKINS_ARGS="--httpsCertificate=/var/lib/jenkins/.ssl/CERTFILE.crt --httpsPrivateKeys=/var/lib/jenkins/.ssl/KEYFILE.pkcs1.key"

      请替换以下内容:

      • CERTFILE 替换为证书文件的文件名,采用 .crt 格式。
      • KEYFILE 替换为 PKCS 密钥的文件名。
  8. 重启 Jenkins 服务器。

  9. 验证是否已在防火墙上开放端口 8443,并通过端口 8443 访问 Jenkins。

  10. 安装将 Keycloak 与 Jenkins 集成所需的 Jenkins 插件。您可以通过以下任一方法选择一个目标:

    如需安装此插件,请执行以下操作:

    1. 在 Jenkins 信息中心,转到管理 Jenkins > 管理插件
    2. 选择 Available,然后搜索您选择的插件。以下屏幕截图展示了已选定 Available 标签页的插件管理器。

      Jenkins 插件管理器。

    3. 安装插件。

配置 Keycloak

在本教程中,Keycloak 用于管理用户、群组和角色。Keycloak 使用大区来管理用户。

  1. 在 Google Cloud 外部运行的虚拟机上,安装 Keycloak 服务器。在本教程中,我们建议您从 Docker 容器安装 Keycloak

  2. 打开 Keycloak 管理控制台。

  3. 前往 Realm settings

  4. General 标签页中,验证字段是否按如下所示设置:

    • 已启用开启
    • User-Managed AccessOFF
    • 端点OpenID 端点配置SAML 2.0 身份提供方元数据

    以下屏幕截图展示了您必须配置的字段。

    Keycloak 常规设置。

  5. 创建客户端,以便您有一个可以请求 Keycloak 对用户进行身份验证的实体。通常,客户端是使用 Keycloak 提供单点登录 (SSO) 解决方案的应用和服务。

    1. 在 Keycloak 管理控制台中,点击 Clients > Create
    2. 输入以下内容:

      • Client IDjenkins
      • 客户端协议openid-connect
      • Root URLhttp://JENKINS_IP_ADDRESS:8080,其中 JENKINS_IP_ADDRESS 是 Jenkins 服务器的 IP 地址。

      以下屏幕截图展示了您必须配置的字段。

      Keycloak 添加客户端。

    3. 点击保存

  6. 安装标签页中,验证令牌格式是否为 Keycloak OIDC JSON。请创建此令牌的副本,因为您在完成 Jenkins 设置时需要用到它。

  7. 要创建测试群组,请执行以下操作:

    1. 在 Keycloak 管理控制台中,点击 Groups > New
    2. 输入群组的名称,然后点击 Save
    3. 再创建一个测试组。您可以为群组分配角色,但本教程不需要分配角色。
  8. 如需创建要添加到群组的测试用户,请执行以下操作:

    1. 在 Keycloak 管理控制台中,点击 Manage user > Add users
    2. 填写用户信息,然后点击 Save

      以下屏幕截图展示了用户账号的示例信息。

      Keycloak 添加用户。

    3. 点击 Credentials 标签页,然后验证 Temporary 是否设置为 Off

    4. 重置密码。

      稍后,您将在 JWT 中使用此账号进行身份验证。

      以下屏幕截图展示了 Credentials 标签页,其中包含您必须配置的字段。

      Keycloak 更改密码。

    5. 点击 Groups 标签页,然后选择您之前创建的一个群组。

    6. 点击加入

    7. 重复执行此步骤,创建更多测试用户。

为 OpenID Connect 配置 Jenkins

本部分介绍如何为 Jenkins 配置 OpenID Connect 插件。

  1. 在 Jenkins 服务器上,依次点击 Manage Jenkins > Configure Global Security
  2. 在“安全大区”下,选择 Keycloak 身份验证插件。点击保存

  3. 点击配置系统

  4. Global Keycloak 设置下,复制您在配置 Keycloak 中创建的 Keycloak 安装 JSON。如果您需要再次获取 JSON 数据,请完成以下操作:

    1. 在 Keycloak 管理控制台中,转到客户端

    2. 点击您的客户端名称。

    3. 安装标签页中,点击格式选项,然后选择 Keyclak OIDC JSON

    以下是一个 Keycloak JSON 示例:

    {
        "realm":"master"
        "auth-server-url":"AUTHSERVERURL"
        "ssl-required":"none"
        "resource":"jenkins"
        "public-client":true
        "confidential-port":0
    }
    

    AUTHSERVERURL 是您的身份验证服务器的网址。

  5. 如需保存 OIDC 配置,请点击 Save

Jenkins 现在可以重定向到 Keycloak 以获取用户信息。

安装 Cloud 客户端库

如需将 JWT 从 Keycloak 发送到 Google Cloud,您必须在 Jenkins 服务器上安装 Cloud 客户端库。本教程使用 Python 通过 SDK 与 Google Cloud 进行交互。

  1. 在 Jenkins 服务器上,安装 Python。以下步骤展示了如何安装 python3:

    sudo apt update
    sudo apt install software-properties-common
    sudo add-apt-repository ppa:deadsnakes/ppa
    sudo apt update
    sudo apt install python3.8
    
  2. 安装 pip3,以便下载和导入Cloud 客户端库

    pip3 –version
    sudo apt update
    sudo apt install python3-pip
    pip3 –version
    
  3. 使用 pip3 安装 Cloud Client Libraries for Python

    pip3 install –upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
    

    例如:

    pip3 install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
    Collecting google-api-python-client
        Downloading google_api_python_client-2.42.0-py2.py3-none-any.whl (8.3 MB)
            USERNAME | 8.3 MB 19.9 MB/s
    Collecting google-auth-httplib2
        Downloading google_auth_httplib2-0.1.0-py2.py3-none-any.whl (9.3 MB)
    Collecting google-auth-oauthlib
    Downloading google_auth_oauthlib-0.5.1-py2.py3-non-any.whl (19 KB)
    

    USERNAME 替换为您的用户名。

  4. 在 Jenkins 服务器上安装 Google Cloud CLI。如需查看相关说明,请参阅《快速入门:安装 gcloud CLI》

配置 Google Cloud 环境

本部分介绍了为确保托管无服务器容器的 Google Cloud 环境能够与 Jenkins 和 Keycloak 连接而必须完成的步骤。

  1. 在 Google Cloud 中,创建一个服务账号,以便 Cloud Run 上的微服务可以访问附加到该服务账号的权限。例如,如需使用 gcloud CLI 创建服务账号,请执行以下操作:

    gcloud iam service-accounts create cloudrun-oidc \
      –-description="cloud run oidc sa"  \
      –-display-name="cloudrun-oidc"
    

    默认情况下,Cloud Run 会为您创建一个默认服务账号。但是,使用默认服务账号不是安全方面的最佳实践,因为该账号具有一组广泛的权限。因此,我们建议您为微服务创建单独的服务账号。如需了解如何为 Cloud Run 创建服务账号,请参阅创建和管理服务账号

  2. 创建工作负载身份池。如需使用 gcloud CLI 创建池,请运行以下命令:

    gcloud iam workload-identity-pools create cloudrun-oidc-pool \
      --location="global" \
      —-description="cloudrun-oidc" \
      —-display-name="cloudrun-oidc"
    
  3. 为 OpenID Connect 创建工作负载身份池提供方:

    gcloud iam workload-identity-pools providers create-oidc cloud-run-provider \
      --workload-identity-pool="cloudrun-oidc-pool" \
      --issuer-uri="VAR_LINK_TO_ENDPOINT" \
      --location="global" \
      --attribute-mapping ="google.subject=assertion.sub,attribute.isadmin-assertion.isadmin,attribute.aud=assertion.aud" \
      --attribute-condition="attribute.isadmin=='true'"
    

    VAR_LINK_TO_ENDPOINT 替换为包含指向 Keycloak OIDC 端点链接的变量。如需查找此链接,请在 KeyCloud 管理控制台的 Realm 窗口中点击 General 标签页。端点必须是 HTTPS,这意味着您必须使用 HTTPS 配置 Keycloak 服务器。

从 Keycloak 获取经过身份验证的用户的 JWT

  1. 在运行 Keycloak 的虚拟机上,将令牌下载到文本文件中。例如,在 Linux 上运行以下命令:

    curl -L -X POST 'https://IP_FOR_KEYCLOAK:8080/auth/realms/master/protocol/openid-connect/token' -H 'Content-Type: application/x-www-form-urlencoded' \
      --data-urlencode 'client_id=jenks' \
      --data-urlencode 'grant_type=password' \
      --data-urlencode 'client_secret=CLIENT_SECRET \
      --data-urlencode 'scope=openid' \
      --data-urlencode 'username=USERNAME' \
      --data-urlencode 'password=PASSWORD' | grep access_token | cut -c18-1490 > token.txt
    

    请替换以下内容:

    • IP_FOR_KEYCLOAK 替换为 Keycloak 服务器 IP 地址。
    • CLIENT_SECRET 替换为 Keycloak 客户端密钥。
    • USERNAME 替换为 Keycloak 用户。
    • PASSWORD 替换为 Keycloak 用户的密码。

    此命令包含客户端 ID、客户端密钥、用户名和密码。作为安全性最佳实践,我们建议使用环境变量(而不是使用命令行)来遮盖这些值。示例命令会将凭据重定向到名为 token.txt 的文件。

    (可选)如需自动执行此步骤,您可以创建一个 bash 脚本。

  2. jwt.io 中验证令牌。

  3. 在虚拟机上,创建凭据文件:

    gcloud iam workload-identity-pools create-cred-config \
    projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/cloudrun-oidc-pool/providers/cloud-run/provider \
      --output-file=sts-creds.json \
      --credential-source-file=token.txt
    

    如需了解详情,请参阅 gcloud iam workload-identity-pools create-cred-config

    输出文件应如下所示:

    {
        "type": "external_account",
        "audience": "//iam.google.apis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/cloudrun-oidc-pool/subject/USER_EMAIL",
        "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
        "token_url": "https://sts.googleapis.com/v1/token",
        "credential_source": {
            "file" "token.txt" }
    }
    

    PROJECT_NUMBER 是您的项目编号。

  4. 在虚拟机上,将 sts.creds.json 文件设置为 ADC 的变量:

    export GOOGLE_APPLICATION_CREDENTIALS=/Users/USERNAME/sts-creds.json
    

    USERNAME 替换为您的 UNIX 用户名。

    在推出 Workload Identity Federation 之前,此值是服务账号密钥。使用 Workload Identity Federation 时,此值是指新创建的凭据文件。

  5. 为用户创建角色绑定以模拟服务账号:

    gcloud iam service-accounts add-iam-policy-binding SERVICE_ACCOUNT \
        --role roles/iam.workloadIdentityUser \
        --member "principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/cloudrun-oidc-pool/subject/USER_EMAIL
    

    请替换以下内容:

  6. 允许服务账号访问 Cloud Run 服务:

    gcloud run services add-iam-policy-binding SERVICE_NAME
      --member-"serviceAccount:SERVICE_ACCOUNT" \
      --role="roles/run.invoker"
    

    请替换以下内容:

    • SERVICE_NAME 替换为在 Cloud Run 上运行的微服务的名称。

    • SERVICE_ACCOUNT 替换为 Cloud Run 服务账号的电子邮件地址。

    如需了解详情,请参阅 gcloud run services add-iam-policy-binding

  7. 生成 ID 令牌:

    #!/usr/bin/python
    from google.auth import credentials
    from google.cloud import  iam_credentials_v1
    
    import google.auth
    import google.oauth2.credentials
    
    from google.auth.transport.requests import AuthorizedSession, Request
    
    url = "https://WORKLOAD_FQDN"
    aud = "https://WORKLOAD_FQDN"
    service_account = 'SERVICE_ACCOUNT'
    
    name = "projects/-/serviceAccounts/{}".format(service_account)
    id_token = client.generate_id_token(name=name,audience=aud, include_email=True)
    
    print(id_token.token)
    
    creds = google.oauth2.credentials.Credentials(id_token.token)
    authed_session = AuthorizedSession(creds)
    r = authed_session.get(url)
    print(r.status_code)
    print(r.text)
    

    请替换以下内容:

    • WORKLOAD_FQDN 替换为工作负载的 FQDN。

    • SERVICE_ACCOUNT 替换为 Cloud Run 服务账号的电子邮件地址。

您使用的令牌可以调用 Identity and Access Management API,该 API 将为您提供调用 Cloud Run 服务所需的新 JWT。

您可以在 Jenkins 流水线中使用令牌来调用您在 Cloud Run 中运行的无服务器容器。不过,这些步骤超出了本教程的探讨范围。

清理

为避免系统因本教程中使用的资源向您的 Google Cloud 账号收取费用,您可以删除您的项目。

删除项目

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

后续步骤