本教程介绍如何使用工作负载身份联合对 Google Cloud 外部运行的工作负载进行身份验证,以便其可以访问由 Cloud Run 托管的微服务。本教程适用于希望将工作负载身份联合与现有身份提供方 (IdP) 集成的管理员。借助工作负载身份联合,您可以将外部工作负载连接到 Google Cloud 中运行的工作负载。Cloud Run 可让您运行无状态容器化微服务。
本教程介绍如何将 Jenkins 配置为外部工作负载,将 Keycloak 配置为 IdP、Cloud Run 和工作负载身份联合。完成本教程后,您可以了解工作负载身份联合如何使用 OpenID Connect 身份验证向 Google Cloud 验证您的 Jenkins 应用。
使用服务帐号密钥进行外部工作负载身份验证
如果您的工作负载在 Google Cloud 之外运行,则必须对该工作负载进行安全身份验证,以便它可以与 Cloud Run 托管的微服务进行交互。传统上,您可以使用 Cloud 客户端库等工具进行身份验证和传递服务帐号密钥,以对外部工作负载执行身份验证。Cloud 客户端库使用名为应用默认凭据 (ADC) 的库来自动查找您的服务帐号凭据。此库设置名为 GOOGLE_APPLICATION_CREDENTIALS 的环境变量。ADC 使用该变量指向的服务帐号密钥或配置文件,以自动对您的帐号进行身份验证。
但是,此方法具有以下安全风险:
- 默认情况下,服务帐号密钥不包含失效日期。如果代码被泄露,您需要公开附加到该密钥的每项权限。
- 由于服务帐号密钥是静态的,因此您必须构建一项策略来管理和轮替这些密钥。
虽然创建服务帐号密钥是一个相当简单的过程,但为了简单起见,您可以牺牲安全性。
使用工作负载身份联合进行外部工作负载身份验证
工作负载身份联合解决了创建和下载静态服务帐号密钥产生的安全风险。借助工作负载身份联合,您无需使用静态服务帐号密钥即可对 Google Cloud 外部的工作负载进行身份验证。任何需要在 Google Cloud 中使用服务的外部工作负载都可以受益于此功能。
借助工作负载身份联合,您可以使用 IdP 直接进行 Google Cloud 身份验证。要进行身份验证,请使用 OpenID Connect。Cloud Run 接受来自 IdP 的 OpenID Connect 令牌以进行身份验证。
使用工作负载身份联合时的身份验证过程如下所示:
- 您的身份验证 (AUTHN) 库会向 IdP 发送 JSON Web 令牌 (JWT) 请求。
- IdP 会为 JSON Web 令牌 (JWT) 签名。AUTHN 库从变量中读取此数据。
- 该库会向包含签名令牌的 Security Token Service 发送 POST 命令。
- Security Token Service 会查看您配置的工作负载身份池提供方以建立信任并验证凭据上的身份。
- Security Token Service 会发回联合令牌。
- 库会将令牌发送到 IAM。
- IAM 将令牌交换为服务帐号的 OpenID Connect 令牌。如需了解详情,请参阅生成 OpenID Connect ID 令牌。
- 该库为 Jenkins 提供 OpenID Connect 令牌。
- Jenkins 使用此令牌向 Cloud Run 进行身份验证。
下图展示了身份验证流程:
目标
- 将 Jenkins 配置为外部工作负载。
- 将 Keycloak 配置为与 OpenID Connect 兼容的 IdP。
- 将 Jenkins 与 Keycloak 连接起来。
- 安装 Cloud 客户端库以获取从 Keycloak 到 Google Cloud 的 JWT 令牌。
- 将 Google Cloud 连接到 Keycloak 和 Jenkins。
- 从 Keycloak 获取经过身份验证的用户的 JWT。
虽然本教程使用 Keycloak,但您可以使用支持 OpenID Connect 的任何身份提供方,例如 GitLab、Okta 或 OneLogin。
费用
在本文档中,您将使用 Google Cloud 的以下收费组件:
您可使用价格计算器根据您的预计使用情况来估算费用。
完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。
准备工作
-
在 Google Cloud Console 中,转到项目选择器页面。
-
选择或创建 Google Cloud 项目。
- 在 Cloud Run 上设置微服务。如需了解详情,请参阅《快速入门:将容器部署到 Cloud Run》。
配置 Jenkins
在非 Google Cloud 环境(例如本地环境或其他云)中完成这些任务。
如果您已有支持 OpenID Connect 和外部工作负载的身份提供方,则可以跳过此步骤并转到安装 Cloud 客户端库。
如需模拟外部工作负载,您可以使用安装了 Jenkins 的虚拟机。您可以将 Jenkins 作为 Docker 映像运行,也可以将其直接安装到您的服务器上。以下步骤演示了如何直接在服务器上安装它。
- 在您选择的虚拟机上,打开命令行。
安装 Java:
$ sudo apt update $ sudo apt install openjdk-11-jre $ java -version
安装 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
验证您可以通过端口 8080 访问 Jenkins 服务器。如果您使用的是位于防火墙后的虚拟机,请确保相应的端口已打开。
获取管理员密码并设置 Jenkins。如需查看说明,请参阅安装后设置向导。
完成以下步骤以设置 SSL:
- 如果您有网域提供方,则可以使用其证书授权机构 (CA) 请求签名证书。或者,您也可以从 zerossl.com 获取有效期为 90 天的免费签名证书。
下载证书 zip 文件并将其转移到运行 Jenkins 的服务器:
scp -i CERTFILE.pem -r CERTFILE.zip VM_FQDN:/home/USERNAME
请替换以下内容:
- 将
CERTFILE
替换为包含公钥的证书文件的名称。 - 将
VM_FQDN
替换为 Google Cloud 外部的服务器的 FQDN。 - 将
USERNAME
替换为您的用户名。
- 将
重命名这些文件并生成 Jenkins 可以使用的 .pkcs12 文件:
openssl rsa -in KEYFILE.com.key -out KEYFILE.com.key
将
KEYFILE
替换为证书文件的名称。
更新
/etc/sysconfig/jenkins
文件。在文本编辑器中打开该文件:
vi /etc/sysconfig/jenkins
将
JENKINS_PORT
设置为-1
。将
JENKINS_HTTPS_PORT
设置为8443
。在文件的底部,添加以下参数:
JENKINS_ARGS="--httpsCertificate=/var/lib/jenkins/.ssl/CERTFILE.crt --httpsPrivateKeys=/var/lib/jenkins/.ssl/KEYFILE.pkcs1.key"
请替换以下内容:
- 将
CERTFILE
替换为采用 .crt 格式的证书文件的文件名。 - 将
KEYFILE
替换为 PKCS 密钥的文件名。
- 将
重启 Jenkins 服务器。
验证您是否在防火墙上打开了端口 8443,并通过端口 8443 访问 Jenkins。
安装将 Keycloak 与 Jenkins 集成所需的 Jenkins 插件。您可以通过以下任一方法选择一个目标:
要安装该插件,请执行以下操作:
- 在 Jenkins 信息中心,转到管理 Jenkins > 管理插件。
选择可用,然后搜索您选择的插件。以下屏幕截图显示了已选择 Available 标签页的 Plugin Manager。
安装插件。
配置 Keycloak
在本教程中,Keycloak 会管理用户、群组和角色。Keycloak 使用大区来管理用户。
在 Google Cloud 外部运行的虚拟机上,安装 Keycloak 服务器。在本教程中,我们建议从 Docker 容器安装 Keycloak。
打开 Keycloak 管理控制台。
转到大区设置。
在常规标签页中,验证字段是否按如下方式设置:
- 已启用:开启
- 用户管理的访问权限:关闭
- 端点:OpenID 端点配置和 SAML 2.0 身份提供方元数据
以下屏幕截图显示了您必须配置的字段。
创建一个客户端,以便您有一个可以请求 Keycloak 对用户进行身份验证的实体。通常,客户端是使用 Keycloak 提供单点登录 (SSO) 解决方案的应用和服务。
- 在 Keycloak 管理控制台中,点击客户端 > 创建。
输入以下内容:
- 客户端 ID:jenkins
- 客户端协议:openid-connect
- 根网址:http://JENKINS_IP_ADDRESS:8080,其中 JENKINS_IP_ADDRESS 是 Jenkins 服务器的 IP 地址。
以下屏幕截图显示了您必须配置的字段。
点击保存。
在安装标签页中,验证令牌格式是否为 Keycloak OIDC JSON。复制此令牌,因为您将需要它来完成 Jenkins 设置。
要创建测试群组,请执行以下操作:
- 在 Keycloak 管理控制台中,点击群组 > 新建。
- 输入群组名称,然后点击保存。
- 再创建一个测试组。您可以为群组分配角色,但本教程不需要它们。
如需创建要添加到群组的测试用户,请执行以下操作:
- 在 Keycloak 管理控制台中,点击管理用户 > 添加用户。
填写用户信息,然后点击保存。
以下屏幕截图显示了用户帐号的示例信息。
点击凭据标签页,并验证临时是否设置为关闭。
重置密码。
稍后您将在 JWT 中使用此帐号进行身份验证。
以下屏幕截图显示了凭据标签页,其中包含您必须配置的字段。
点击群组标签页,然后选择您之前创建的其中一个群组。
点击加入。
重复执行此步骤,创建更多测试用户。
为 OpenID Connect 配置 Jenkins
本部分介绍如何为 Jenkins 配置 OpenID Connect 插件。
- 在 Jenkins 服务器上,转到管理 Jenkins > 配置全局安全。
在“安全大区”下,选择 Keycloak 身份验证插件。点击保存。
点击配置系统。
在全局 Keycloak 设置下,复制您在配置 Keycloak 中创建的 Keycloak 安装 JSON。如果您需要再次获取 JSON 数据,请完成以下操作:
在 Keycloak 管理控制台中,转到客户端。
点击您的客户端名称。
在安装标签页中,点击格式选项,然后选择 Keyclak OIDC JSON。
以下是 Keycloak JSON 的示例:
{ "realm":"master" "auth-server-url":"AUTHSERVERURL" "ssl-required":"none" "resource":"jenkins" "public-client":true "confidential-port":0 }
AUTHSERVERURL 是您的身份验证服务器的网址。
如需保存 OIDC 配置,请点击保存。
Jenkins 现在可以重定向到 Keycloak 以获取用户信息。
安装 Cloud 客户端库
如需将 JWT 从 Keycloak 发送到 Google Cloud,您必须在 Jenkins 服务器上安装 Cloud 客户端库。本教程使用 Python 通过 SDK 与 Google Cloud 进行交互。
在 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
安装 pip3,以便下载和导入 Cloud 客户端库:
pip3 –version sudo apt update sudo apt install python3-pip pip3 –version
使用 pip3 安装 Python 版 Cloud 客户端库:
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 替换为您的用户名。
在 Jenkins 服务器上安装 Google Cloud CLI。如需查看相关说明,请参阅《快速入门:安装 gcloud CLI》。
配置 Google Cloud 环境
本部分介绍确保托管无服务器容器的 Google Cloud 环境可以与 Jenkins 和 Keycloak 连接必须完成的步骤。
在 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 创建服务帐号,请参阅创建和管理服务帐号。
创建工作负载身份池。如需使用 gcloud CLI 创建池,请运行以下命令:
gcloud iam workload-identity-pools create cloudrun-oidc-pool \ --location="global" \ —-description="cloudrun-oidc" \ —-display-name="cloudrun-oidc"
为 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 管理控制台的大区窗口中点击常规标签页。端点必须是 HTTPS,这意味着您必须使用 HTTPS 配置 Keycloak 服务器。
从 Keycloak 获取经过身份验证的用户的 JWT
在运行 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 脚本。
- 将
在 jwt.io 验证令牌。
在虚拟机上,创建凭据文件:
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
是您的项目编号。在虚拟机上,将
sts.creds.json
文件设置为 ADC 的变量:export GOOGLE_APPLICATION_CREDENTIALS=/Users/USERNAME/sts-creds.json
将 USERNAME 替换为您的 UNIX 用户名。
在启动工作负载身份联合之前,此值是服务帐号密钥。使用工作负载身份联合时,此值是新创建的凭据文件。
为用户创建角色绑定,以模拟服务帐号:
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
请替换以下内容:
将
SERVICE_ACCOUNT
替换为您在配置 Google Cloud 环境中创建的服务帐号的电子邮件地址。如需了解详情,请参阅 gcloud iam service-accounts add-iam-policy-binding。将
USER_EMAIL
替换为您的电子邮件地址。
允许服务帐号访问 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。
生成 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,它将为您提供调用 Cloud Run 服务所需的新 JWT。
您可以使用 Jenkins 流水线中的令牌调用您在 Cloud Run 中运行的无服务器容器。但是,这些步骤不在本教程的探讨范围内。
清理
为避免系统因本教程中使用的资源向您的 Google Cloud 帐号收取费用,您可以删除您的项目。
删除项目
- 在 Google Cloud 控制台中,进入管理资源页面。
- 在项目列表中,选择要删除的项目,然后点击删除。
- 在对话框中输入项目 ID,然后点击关闭以删除项目。