【How-to Guides】MultiCloud — 以 Identity Federation 建立跨雲信任機制(Azure to GCP)

Establishing Trust Between Azure AD and GCP

Kellen
28 min readJun 1, 2024

TL;DR

本篇介紹如何設定 GCP Workload Identity Federation,讓 Azure workload 無需服務帳號金鑰即可向 Google Cloud 進行驗證。而 Workload Identity Federation 是基於 IAM 機制,允許第三方服務整合 GCP 資源,背後的技術原理是基於 OIDC 的基礎發展。

🎬 What is GCP Workload Identity Federation?(摘錄)

當您的外部應用程序需要訪問 Google Cloud 的服務時,一般會直接想到使用 Service accounts,建好 Service accounts 後再傳送金鑰給對方。而 Service accounts 允許開發人員為應用程序分配一個身份,然後給予該身份特定的權限,例如訪問 Cloud Storage。

但 Service accounts 密鑰類似於用戶密碼,允許的持有者皆可作為 Service accounts 並獲得資源的訪問權限。但是,卻是無法驗證持有密鑰的應用程序是否有權使用它,例如流入到不該擁有的人身上。

Workload identity federation enables applications to replace Long-lived service account keys with short-lived access tokens.

因為這個風險,管理 Service accounts 密鑰的存儲、分發和更新變成了首要任務,有效地將身份管理問題轉換為密鑰管理問題,那麼跨雲間或是外部溝通好的解決方案是什麼呢?

把 Key 丟掉最好!

放棄密鑰,改使用 Workload identity federation 使在 Google Cloud 之外運行的應用程序能夠用臨時性的憑證,也稱為令牌,替換掉長期存在的服務帳戶密鑰。

Workload identity federation 資訊流

Workload identity federation 是一種用於調用 Google Cloud API 的無金鑰應用程序身份驗證機制,它的工作方式如下:

首先讓您的應用程序向您的身份提供者進行身份驗證(1️⃣)並獲得帳戶憑證(2️⃣), 應用程序可以調用我們的安全令牌服務(3️⃣),將身份提供者發行的帳戶憑證換成短暫的 Google Cloud 存取令牌(4️⃣)。

然後,這個存取令牌可以用來冒充服務帳戶(5️⃣),繼承該服務帳戶的權限來訪問 Google Cloud 資源(6️⃣)。

如何設定 Workload identity federation

  1. 建立工作負載身分集區:集區會整理及管理外部身分。身分與存取權管理可讓您將存取權授予集區中的身分
  2. 連結識別資訊提供者:新增 Azure 或 OpenID Connect(OIDC)提供者至集區
  3. 設定供應商對應:設定要在身分與存取權管理中顯示的屬性和憑證附加資訊
  4. 授予存取權:使用服務帳戶授予存取權,允許集區中的身分存取 Google Cloud 中的資源

要使用工作負載身份聯合,首先需要在 Google Cloud Project 創建 workload identity pool(工作負載身份池)。權限上只需要在 Porject Level 擁有管理 workload identity pools, service accounts 和 IAM policies 權限即可

前置作業

準備 Azure 身分提供方

您只需為每個 Azure AD 租用戶或 AWS 帳號執行一次這些步驟即可

  1. 您必須在 Azure AD 租用戶中建立新的 Azure AD 應用程式並對其進行配置,以便將其用於 workload identity federation。
  2. 當您設定工作負載身份池提供者時,您需要將其連接到您在 Azure AD 中建立的應用程式,以便允許身份池中的使用者和服務主帳號向該應用程式請求訪問權限。這是將外部身份提供方(Azure AD)與 Google Cloud 的 Workload Identity Federation 整合的一部分。
  3. 設定 APP ID URI。 您可以使用預設應用程式 ID URI ( APPID) 或指定自訂 URI。稍後設定工作負載身分池提供者時,您需要套用 ID URI。
至 App Registration 建一個應用程式

決定 attribute mapping and condition

AWS 或 Azure 工作負載的特定於環境的憑證包含多個特性,您必須決定要在Google Cloud 中用作主體的識別碼( google.subject) 的 attribute。

Google Cloud 在 Cloud Audit Logs 和主帳號識別碼中使用主體識別碼來唯一標識 AWS 或Azure 使用者或角色。您也可以視需要映射其他特性。然後,您可以在授予對資源的存取權限時引用這些附加特性。

看官方文件滿清楚的,個人是直接選用 sub 這個 attribute,原來跨雲及 log 後續操作也要考量

如果您不確定可以引用的聲明列表,請執行以下操作:

  1. 連接到具有已指派的代管式身分的 Azure 虛擬機器。
  2. Azure Instance Metadata Service(IMDS)取得存取權令牌
Azure 執行個體中繼資料服務,169.254.169.254 是個 magic IP,架設在私有位址範圍,保留用於特定用途,例如在 Azure 虛擬機器實例中提供 Instance Metadata Service (IMDS),GCP, AWS 也有喔!

當您的工作負載在雲端運算上下文中運行時,169 IP 用於獲取用戶或服務資訊,可以從 Azure VM 內部請求此服務,為附加到 VM 的託管識別產生令牌,而無需金鑰!在 Azure 環境執行以下:

curl -H "Metadata: true" \
"http://169.254.169.254/metadata/identity/oauth2/token?resource=APP_ID_URI&api-version=2018-02-01" \
| jq -r .access_token

# 模擬結果如下
{
"typ": "JWT",
"alg": "RS256",
"x5t": "L1KfKFI_jnXbwWcxxxxxx1sUHH0",
"kid": "L1KfKFI_jnXbwWcxxxxxx1sUHH0"
}.{
"aud": "1234567-1234-oooo-cccc-0ffc858bf3bb", # app id
"iss": "https://sts.windows.net/tenantid-xxxx-oooo-ffff-347de7dda184/",
"iat": 1717243075,
"nbf": 1717243075,
"exp": 1717329775,
"aio": "11111111111111111111111BQA=",
"appid": "000000000-ffff-xxxx-cccc-f4a08ae9afbb",
"appidacr": "2",
"idp": "https://sts.windows.net/tenantid-xxxx-oooo-ffff-347de7dda184/",
"oid": "00000000-0000-1234-cccc-ae51d837eb3e",
"rh": "0.111111111111111111111111111111111111111vCAAA.",
"sub": "00000000-0000-1234-cccc-ae51d837eb3e",
"tid": "tenantid-xxxx-oooo-ffff-347de7dda184", # tenant id
"uti": "uQzwIaJJkUKUftXAixEXAQ",
"ver": "1.0"
}.[Signature]

GCP workload identity federation 設定

👣 Step01. 建立新的工作負載身分池

首先,需要在 Google Cloud 中註冊您的身份提供者。這可以通過 Google Cloud Console 或 gcloud 來完成。

gcloud iam workload-identity-pools create "MY_POOL" \   
--location="global" \
--descro[topm="description" \
--project="MY_PROJECT" \
--display-name="My Identity Pool"


# 實際執行指令與結果
gcloud iam workload-identity-pools create "azure2gcp" \
--location="global" \
--description="Azure workload to GCP" \
--project="helloworld" \
--display-name="Azure workload to GCP pool"
Created workload identity pool [azure2gcp].

👣 Step02. 新增工作負載身分池提供者(Provider)

Workload identity federation 允許您組織和管理外部身份。一個專案可以擁有多個池,每個池允許來自不同外部身份提供者的訪問。這使您可以創建身份集合,並控制授予每個身份提供者的身份的權限。

接下來,配置身份提供者,以便 Google Cloud 能夠信任它。這裡使用的是 OpenID Connect(OIDC)身份提供者

gcloud iam workload-identity-pools providers create-oidc "MY_PROVIDER" \
--workload-identity-pool="MY_POOL" \
--issuer-uri="https://accounts.google.com \
--project="MY_PROJECT" \
--location="global" \
--display-name="My OIDC Provider" \
--attribute-mapping="google.subject=assertion.sub"


# 實際執行指令與結果
gcloud iam workload-identity-pools providers create-oidc "azure2gcp" \
--workload-identity-pool="azure2gcp" \
--issuer-uri="https://sts.windows.net/{tenantid}-347de7dda184" \
--project="helloworld" \
--location="global" \
--allowed-audiences="api://{app-id}-0ffc858bf3bb" \
--display-name="Azure to GCP OIDC Provider" \
--attribute-mapping="google.subject=assertion.sub"

Created workload identity pool provider [azure2gcp].
  • PROVIDER_ID:Provider ID
  • POOL_ID:Pool ID
  • ISSUER_URI:Azure AD 租戶的ID(GUID),有時格式設定為。頒發者URI 可能有所不同,如需尋找頒發者 URI,您可以使用 JWT.io 調試 JWT https://sts.windows.net/TENANT_ID
  • APPLICATION_ID_URI:在 Azure AD 中註冊應用程式時所使用的應用程式 ID URI
  • MAPPINGS:您先前確定的以英文逗號分隔的屬性映射清單
  • CONDITIONS:(可選)您先前確定的屬性條件
  • attribute-mapping:這是屬性映射規則。google.subject 是 Google Cloud 使用的標準屬性,assertion.sub 是 OIDC ID 令牌中的 sub 屬性(通常是用戶的唯一標識符)。這個映射將 OIDC ID 令牌中的 sub 屬性值映射到 Google Cloud 的 google.subject 屬性。這樣,Google Cloud 就能理解和使用這些身份驗證令牌。

這邊就是在 Google Cloud 中創建一個 OpenID Connect(OIDC) Provider,並將其添加到一個指定的工作負載身份池(Workload Identity Pool)。這樣可以讓 Google Cloud 使用該 OIDC 提供者進行身份驗證,以便從 Azure workload (或甚至是 AWS 或其他支援 OIDC 的身份提供者)安全地訪問 Google Cloud 資源。

👣 Step03. 設置工作負載身份聯合

  1. 創建服務帳戶並授予存取權限
gcloud iam service-accounts create "my-service-account" \
--project="MY_PROJECT"

# 實際執行指令與結果
gcloud iam service-accounts create "azure-to-gcp" \
--project="project-id"
Created service account [azure-to-gcp].

2. 授予角色和權限

授予角色和權限 使用 IAM 政策授予服務帳戶適當的角色和權限

gcloud projects add-iam-policy-binding "MY_PROJECT" \
--member="serviceAccount:my-service-account@MY_PROJECT.iam.gserviceaccount.com" \
--role="roles/iam.workloadIdentityUser"

# 實際執行指令與結果
gcloud projects add-iam-policy-binding "helloworld" \
--member="serviceAccount:azure-to-gcp@helloworld.iam.gserviceaccount.com" \
--role="roles/iam.workloadIdentityUser"

Updated IAM policy for project [helloworld].
bindings:
- members:
- serviceAccount:service-123456789@gcp-sa-apigateway.iam.gserviceaccount.com
role: roles/apigateway.serviceAgent
****
etag: BwYZziFcS_Y=
version: 1

3. 設置工作負載池中的關聯

將工作負載池與服務帳戶關聯起來,以便身份提供者可以代替服務帳戶進行身份驗證

binging service account with roles/iam.workloadIdentityUser
gcloud iam service-accounts add-iam-policy-binding "my-service-account@MY_PROJECT.iam.gserviceaccount.com" \
--project="MY_PROJECT" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/MY_PROJECT_number/locations/global/workloadIdentityPools/MY_POOL/*"

# 實際執行指令與結果
gcloud iam service-accounts add-iam-policy-binding "azure-to-gcp@helloworld.iam.gserviceaccount.com" \
--project="helloworld" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/01234567890/locations/global/workloadIdentityPools/azure2gcp/*"


Updated IAM policy for serviceAccount [azure-to-gcp@project-id.iam.gserviceaccount.com].
bindings:
- members:
- principalSet://iam.googleapis.com/projects/1234567890/locations/global/workloadIdentityPools/azure-to-gcp/*
role: roles/iam.workloadIdentityUser
etag: BwYZzkeXQ4s=
version: 1

👣 Step04. 配置細粒度的存取控制

  • 設定 IAM 政策綁定
# 建立一個客製化角色
# 1
gcloud iam roles create myCustomRole \
--project="MY_PROJECT" \
--title="My Custom Role" \
--permissions="compute.instances.start,compute.instances.stop"

# 2
gcloud projects add-iam-policy-binding "MY_PROJECT" \
--member="principalSet://iam.googleapis.com/projects/MY_PROJECT/locations/global/workloadIdentityPools/MY_POOL/attribute.oidc.subject/YOUR_SUBJECT" \
--role="roles/myCustomRole"


# 實際執行指令與結果
# 1
gcloud iam roles create azuretogcpRole \
--project="helloworld" \
--title="Azure to GCP Custom Role" \
--permissions="storage.objects.create,storage.objects.delete,storage.objects.get,storage.objects.list,storage.objects.update"

Created role [azuretogcpRole].
etag: BwYZzzRyf6M=
includedPermissions:
- storage.objects.create
- storage.objects.delete
- storage.objects.get
- storage.objects.list
- storage.objects.update
name: projects/helloworld/roles/azuretogcpRole
stage: ALPHA
title: Azure to GCP Custom Role

# 2
gcloud projects add-iam-policy-binding "helloworld" \
--member="principalSet://iam.googleapis.com/projects/helloworld/locations/global/workloadIdentityPools/azure2gcp/attribute.oidc.subject/{sub}-86aad338145d" \
--role="roles/azuretogcpRole"

要找到 YOUR_SUBJECT(即 Azure AD 發出的 OIDC 令牌中的用户 GUID),你需要通过 Azure AD 生成 OIDC 令牌並解碼其中的 sub 字段,以下是操作步驟:

1. 获取 OIDC 令牌

從 Azure AD 獲取 OIDC 令牌的過程通常包括以下步骤:註冊應用程序、配置权限與獲取訪問令牌(使用應用程序憑證從 Azure AD 獲取訪問令牌)

以下展示如何使用 Azure AD 註冊應用程序獲取訪問令牌:

# 獲取訪問令牌
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=YOUR_CLIENT_ID&scope=https://graph.microsoft.com/.default&client_secret=YOUR_CLIENT_SECRET&grant_type=client_credentials" \
https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token

# 實際執行指令與結果
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=xxxx-0ffc858bf3bb&scope=https://graph.microsoft.com/.default&client_secret=HDc8Qxxxx&grant_type=client_credentials" \
https://login.microsoftonline.com/{tenantid}-347de7dda184/oauth2/v2.0/token

2. 解碼 OIDC 令牌

獲取令牌后,可以使用工具解碼 JWT 令牌來提取 sub 字段

👣 Step05. 下載或建立憑證配置

https://google.aip.dev/auth/4117

如需建立憑證設定文件,執行下列操作

gcloud iam workload-identity-pools create-cred-config \
projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID \
--service-account=SERVICE_ACCOUNT_EMAIL \
--service-account-token-lifetime-seconds=SERVICE_ACCOUNT_TOKEN_LIFETIME \
--azure \
--app-id-uri APPLICATION_ID_URI \
--output-file=FILEPATH.json

# 實際執行指令與結果
gcloud iam workload-identity-pools create-cred-config \
projects/936858407363/locations/global/workloadIdentityPools/azure2gcp/providers/azure2gcp \
--service-account=azure-to-gcp@helloworld.iam.gserviceaccount.com \
--azure \
--app-id-uri api://{app-id}-0ffc858bf3bb \
--output-file=client-config-mapping.json
  • SERVICE_ACCOUNT_TOKEN_LIFETIME:如果您使用服務帳號模擬,請替換為服務帳號存取權杖的生命週期(以秒為單位);如果未提供,則預設為一小時。如果您不使用服務帳號模擬,請省略此標誌。如需指定超過一小時的生命週期,必須配置constraints/iam.allowServiceAccountCredentialLifetimeExtension 組織政策限制條件
# client-config-mapping.json 這邊未有機密!
{
"universe_domain": "googleapis.com",
// 用於指定 Google API 的域名。

"type": "external_account",
// 指定憑證類型,這裡是 external_account,表示這是一個外部帳戶配置。

"audience": "//iam.googleapis.com/projects/01234567890/locations/global/workloadIdentityPools/azure2gcp/providers/azure2gcp",
// 用於標識目標資源,這裡指向特定的工作負載身份池和提供者。

"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
// 指定主體令牌類型,這裡是 JWT(JSON Web Token)。

"token_url": "https://sts.googleapis.com/v1/token",
// 用於獲取 Google Cloud 訪問令牌的 URL。

"credential_source": {
// 憑證來源配置,描述如何獲取源憑證。
"url": "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=api:/{app-id}-0ffc858bf3bb",
// 來源 URL,指向 Azure 的元數據服務,從中獲取 OAuth2 令牌。

"headers": {
"Metadata": "True"
// 必要的請求標頭,用於告訴 Azure 這是元數據請求。
},

"format": {
"type": "json",
// 指定返回的憑證格式為 JSON。

"subject_token_field_name": "access_token"
// 指定 JSON 中主體令牌所在的字段名稱為 access_token。
}
},

"service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/azure-to-gcp@helloworld.iam.gserviceaccount.com:generateAccessToken"
// 用於服務帳戶模擬的 URL,通過這個 URL 獲取 Google Cloud 訪問令牌。
}

👣 Step06. 在 Azure 使用憑證設定存取 Google Cloud

https://google.aip.dev/auth/4117

這個 JSON 文件配置了如何在 Azure 環境中通過身份聯邦來訪問 Google Cloud 資源,具體描述了如何獲取和使用憑證進行身份驗證和授權操作。如需讓工具和用戶端程式庫使用您的憑證配置(或存放 KeyVault),以下都是在 Azure 環境中執行下列操作:

  • 初始化環境變數 GOOGLE_APPLICATION_CREDENTIALS 將其指向憑證設定檔
export GOOGLE_APPLICATION_CREDENTIALS=`pwd`/FILEPATH.json
  • 使用支援工作負載身分聯合並且可以自動尋找憑證的用戶端程式庫或工具
gcloud auth login --cred-file=FILEPATH.json


# 實際執行指令與結果
You are already authenticated with 'azure-to-gcp@helloworld.iam.gserviceaccount.com'.
Authenticated with external account credentials for: [azure-to-gcp@helloworld.iam.gserviceaccount.com].
Your current project is [helloworld]. You can change this setting by running:
$ gcloud config set project PROJECT_ID

👣 Step07. 驗證

在 Azure VM 使用 Python 呼叫對應程式碼

from google.cloud import storage

def download_gcs_file(bucket_name, source_blob_name, destination_file_name):
"""Downloads a blob from the bucket."""
storage_client = storage.Client(project='helloworld')

bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(source_blob_name)

blob.download_to_filename(destination_file_name)

print(f'File {source_blob_name} downloaded to {destination_file_name}.')

if __name__ == "__main__":
bucket_name = "gcf-sources"
source_blob_name = "demo-wif.txt"
destination_file_name = "/tmp/from-gcp-file.txt"
download_gcs_file(bucket_name, source_blob_name, destination_file_name)
實際要去抓取 GCS 中的檔案

需要等個 1 分鐘待權限套用,即可使用 workload identity federation 來進行認證與授權,並成功抓取 GCS 相關的資訊與內容,亦從 GCP Pools 可以對照日誌。

補充

之前寫了一篇使用 Defender,也是使用 GCP workload identity federation 處理
📙 【Reference】Azure Security — 使用 Microsoft Defender for Cloud 進行 CSPM 與跨雲管理

灰框則是滿滿在 GCP 對於 workload identity federatoin 的設定

總結

是時候把 Service Key 丟掉了,雲端上都有 Identity Federation 可以設置以進行工作負載聯合驗證,加上對 service account 實現細粒度的存取控制。不同的工作負載可以根據其身份提供者中的屬性,動態地獲取不同的權限,而地端也有著對 AD 的支持,在跨雲或雲地間都可透過此方式,提高安全性和管理靈活性。

--

--

Kellen

Backend(Python)/K8s and Container eco-system/Technical&Product Manager/host Developer Experience/早期投入資料創新與 ETL 工作,近期研究 GCP/Azure/AWS 相關的解決方案的 implementation