💡 先搞懂問題
虛構公司「青松物流」把訂單系統搬上 Google Cloud 的第一個月,為了趕上線,IT 主管在專案上把五位工程師都設成 Editor,外包廠商也拿到同樣的權限;部署管線則用一把下載下來的服務帳戶金鑰,放在程式碼儲存庫的設定檔裡。上線第二個月,一位工程師在清理測試資料時刪錯了值區,正式環境的出貨單 PDF 全部消失;資安顧問掃描儲存庫時又發現那把金鑰已經被推到公開的分支上。稽核要求「每個人只拿工作需要的權限」,團隊卻不知道該從哪裡收:Editor 拿掉了,某些人照樣能改,因為他們在資料夾或機構層級還有別的授權。
新手在 Google Cloud 最常卡住的有三件事。第一,不知道授權放在哪一層差多少:同一個角色授予在資料夾上,底下現在與未來的每個專案、每個值區都適用。第二,想用「在下層把權限拿掉」來收斂,結果發現做不到,因為允許政策只會往下加,不會往下減。第三,把「誰能做」和「能不能這樣設定」混在一起,例如想用 IAM 禁止全公司建立服務帳戶金鑰,或想用 IAM 規定資源只能開在台灣,這些其實是機構政策的工作。
Google Cloud 處理「誰能對什麼做什麼」的服務叫 Cloud IAM(Identity and Access Management)。它的核心是掛在資源上的允許政策(allow policy),政策裡是一筆筆角色繫結(role binding):把一個角色(role),也就是一組權限,授予一或多個主體(principal),例如 Google 帳戶、Google 群組或服務帳戶(service account)。允許政策可以掛在資源階層(resource hierarchy)的任何一層:機構(organization)、資料夾(folder)、專案(project),以及支援 IAM 的個別資源,例如 Cloud Storage 的值區(bucket)或 Compute Engine 的 VM。
生活比喻:集團門禁卡
青松集團底下有物流、零售幾個事業群,每個事業群有好幾間分公司,分公司倉庫裡有堆高機、檔案櫃這些設備。總務處發門禁時,不會一台設備一台設備設定,而是決定這張卡「在哪一層有效」:集團稽核主任的卡在總部層級生效,所有事業群、所有分公司都刷得過;物流事業群的值班主管只在物流事業群生效,旗下每間分公司都能進,但進不了零售事業群;倉庫的堆高機操作員只拿到那幾台設備。卡片權限也分等級,有的卡只能進門看,有的能操作設備,總務處的人還能替別人發卡。
同一個人可以同時從好幾個地方拿到權限:他所屬的「倉儲組」在事業群層級有一張卡,他自己又被加上某間分公司的卡,他能進的地方就是兩者的總和。這時候在分公司把他的卡收回,他還是刷得進去,因為事業群那張卡沒動。總部若真的要擋,只能發一道禁令:「除了保全組,任何人都不准進機房」,不管手上有幾張卡,禁令都優先。另外,集團還有一本規章,規定倉庫不准加蓋夾層、檔案櫃只能放在台灣的據點,這和門禁無關,就算你是能進門的主管也得照規章來。
roles/compute.instanceAdmin.v1、能替別人授權的 Owner;把角色授予主體就是一筆角色繫結,一層上所有繫結合起來是那一層的允許政策。門禁往下延伸是繼承,多張卡取總和就是官方說的有效允許政策(effective allow policy)是聯集;總部的禁令是拒絕政策(deny policy),IAM 一定先檢查它;集團規章則是機構政策服務(Organization Policy Service):IAM 管「誰(who)」能做,機構政策管「什麼(what)設定」被允許。
比喻有幾個地方和 Google Cloud 不一樣,剛好也是最常考錯的地方。第一,實體門禁可以單獨把某人從某樓層「除名」,但 Google Cloud 的允許政策只能加、不能減:上層給了,下層的允許政策再怎麼寫都收不回來,要擋只能用拒絕政策,或把授權從上層移掉重新設計。第二,門禁系統通常同時管員工名冊;Google Cloud 的使用者帳號不在 IAM 裡建立,而是來自 Google 帳戶、Cloud Identity 或 Google Workspace,IAM 只負責授權。第三,堆高機不會自己刷卡,但雲端裡的程式會:程式要用的身分是服務帳戶,它既是能被授權的主體,本身也是一個資源,「誰能借用這個服務帳戶」同樣要用 IAM 管。
🎮 互動實驗室一:繼承模擬器
「資源樹與有效權限」是青松物流的資源階層(桌機在右邊,手機在下方)。先在左邊選主體與角色,再點樹上的節點當作授予層級,按「新增角色繫結」。樹上每個節點會即時標出這個主體在那裡的有效權限:綠色是可以、灰色刪除線是不行、紅色是被拒絕政策擋下、藍色是靠 IAM 條件才有效。下方可以打開兩條拒絕政策,看看它們怎麼壓過允許政策。上方有四個任務,做完按「檢查任務」;點樹上的節點,下方會依「先拒絕、後允許」的順序說明這個權限從哪裡來。
新增角色繫結(允許政策)
資源樹與有效權限
🎮 互動實驗室二:最小權限情境分診
每張卡是一個虛構公司的需求。先挑「做法」,再挑「授予或設定的層級」,按「確認」。兩個都要對:做法要夠用但不多給,層級要涵蓋需求但不擴散。有些需求根本不是「給誰什麼角色」的問題,而是要用機構政策、拒絕政策,或用 Workload Identity Federation 換掉服務帳戶金鑰。答完會說明原因,選錯時也會說明你選的那個什麼時候才適合。
🛠️ 操作教學:授予角色並建立服務帳戶
情境:青松物流要讓「維運組」群組能管理訂單專案的 VM,並替報表程式建立一個只能讀取值區物件的服務帳戶。左邊是簡化的控制台,照步驟填欄位、按「下一步」;右邊的 gcloud CLI 與 Terraform 會跟著你填的專案 ID、主體、角色、條件與服務帳戶 ID 即時更新,目前步驟對應的那幾行會用底色標出來。可以故意把專案 ID 或服務帳戶 ID 打錯,或替基本角色加上條件,看看驗證訊息。
換一個層級:資料夾授權與自訂角色
上面的步驟都授予在專案。要讓稽核群組看得到整個正式環境資料夾底下所有專案的 IAM 政策,就把繫結放在資料夾;要讓外包人員只能檢視、啟動、停止 VM,預先定義角色又都太大時,才建立自訂角色。注意自訂角色只能建在機構或專案,不能建在資料夾;建在專案的自訂角色,也只能在那個專案裡授予。
# 在資料夾授權:底下所有子資料夾、專案與資源都會繼承
gcloud resource-manager folders add-iam-policy-binding FOLDER_ID \
--member="group:auditors@example.com" \
--role="roles/iam.securityReviewer"
# 在專案建立自訂角色(角色 ID 不能有連字號)
gcloud iam roles create vmOperator \
--project=my-project-id \
--title="VM Operator" \
--description="檢視、啟動與停止 Compute Engine VM" \
--permissions=compute.instances.get,compute.instances.list,compute.instances.start,compute.instances.stop \
--stage=GA
# 授予自訂角色時,要寫完整路徑 projects/專案 ID/roles/角色 ID
gcloud projects add-iam-policy-binding my-project-id \
--member="group:contractors@example.com" \
--role="projects/my-project-id/roles/vmOperator" \
--condition=None
terraform {
required_providers {
google = {
source = "hashicorp/google"
}
}
}
provider "google" {
project = var.project_id
region = "asia-east1"
}
variable "project_id" {
type = string
description = "專案 ID(例如 my-project-id)"
}
variable "folder_id" {
type = string
description = "資料夾 ID,格式 folders/FOLDER_ID"
}
# 在資料夾授權:底下所有子資料夾、專案與資源都會繼承
resource "google_folder_iam_member" "auditors_security_reviewer" {
folder = var.folder_id
role = "roles/iam.securityReviewer"
member = "group:auditors@example.com"
}
# 專案層級的自訂角色:只能檢視、啟動與停止 VM
resource "google_project_iam_custom_role" "vm_operator" {
project = var.project_id
role_id = "vmOperator"
title = "VM Operator"
description = "檢視、啟動與停止 Compute Engine VM"
stage = "GA"
permissions = [
"compute.instances.get",
"compute.instances.list",
"compute.instances.start",
"compute.instances.stop",
]
}
# 自訂角色只能在建立它的專案內授予
resource "google_project_iam_member" "contractors_vm_operator" {
project = var.project_id
role = google_project_iam_custom_role.vm_operator.name
member = "group:contractors@example.com"
}
Terraform 的 _member、_binding、_policy 差在哪
Terraform 的 Google provider 對每一種能掛允許政策的資源,都提供三種寫法,差別在「Terraform 認為自己管多大範圍」。google_project_iam_member 是非權威式(non-authoritative),只負責加上自己這一位主體,同一個角色的其他主體保留;google_project_iam_binding 對「某一個角色」是權威式,清單以外的人會被移出這個角色;google_project_iam_policy 對整份允許政策是權威式,會用你寫的內容取代專案上既有的整份政策。官方文件特別提醒:_policy 不能和 _binding、_member 混用,否則會互相覆蓋;誤用 _policy 可能把自己鎖在專案外,刪除這個資源還會移除所有沒有機構層級權限的人的存取權,所以一般只用在完全由 Terraform 管理的專案,而且要先匯入既有政策。_binding 和 _member 可以一起用,前提是兩者不要授予同一個角色。
variable "project_id" {
type = string
}
# _member:非權威式,只新增這一位主體,同一角色的其他主體保留
resource "google_project_iam_member" "sre_log_viewer" {
project = var.project_id
role = "roles/logging.viewer"
member = "group:sre@example.com"
}
# _binding:對「這個角色」是權威式,清單以外的主體會被移出這個角色
resource "google_project_iam_binding" "monitoring_viewers" {
project = var.project_id
role = "roles/monitoring.viewer"
members = [
"group:sre@example.com",
"group:auditors@example.com",
]
}
自己動手時要注意
權限:要在專案上新增角色繫結,需要能修改專案允許政策的權限(resourcemanager.projects.setIamPolicy),常見的是 Project IAM Admin(roles/resourcemanager.projectIamAdmin)或 Owner;在資料夾授權則要資料夾層級的對應權限,例如 Folder IAM Admin。建立服務帳戶需要 Create Service Accounts(roles/iam.serviceAccountCreator),建立自訂角色需要 Role Administrator(roles/iam.roleAdmin),建立拒絕政策則要在機構上有 Deny Admin(roles/iam.denyAdmin)。只有 Editor 的人照著做到「授予存取權」就會被擋,因為 Editor 不能修改允許政策,這是正常的。
API 與生效時間:用 gcloud 或 Terraform 操作前,專案要啟用 IAM API 與 Cloud Resource Manager API(gcloud services enable iam.googleapis.com cloudresourcemanager.googleapis.com)。IAM API 是最終一致的,授權或建立服務帳戶後,可能要等一分鐘以上才會在存取檢查生效,自動化腳本要有重試。
費用:授予角色、建立服務帳戶、自訂角色與拒絕政策本身不會建立任何計費的運算或儲存資源;但如果你接著用這些權限建立 VM、值區,就會依用量計費。用免費試用帳戶練習時,留意試用帳戶的限制(例如不能申請提高配額)。
清理:練習完把繫結、服務帳戶與自訂角色移除;如果整個專案都只是練習用,最乾淨的做法是直接刪除專案,專案會先進入關閉狀態,30 天後才永久刪除,期間可以還原。用 Terraform 建立的,terraform destroy 會刪除它建立的服務帳戶、角色繫結與自訂角色。刪除的自訂角色在一段期間內(官方文件寫 44 天的刪除流程)不能重複使用同一個角色 ID。
# 移除練習用的角色繫結(--all 會一併移除同一主體、同一角色的所有條件式繫結)
gcloud projects remove-iam-policy-binding my-project-id \
--member="group:ops-team@example.com" \
--role="roles/compute.instanceAdmin.v1" --all
# 刪除服務帳戶與自訂角色
gcloud iam service-accounts delete order-reader@my-project-id.iam.gserviceaccount.com
gcloud iam roles delete vmOperator --project=my-project-id
# 用 Terraform 建立的資源
terraform destroy
# 整個練習專案都不要了:最乾淨
gcloud projects delete my-project-id
不要寫進範例的東西:本頁的專案 ID 用 my-project-id、資料夾用 FOLDER_ID、信箱用保留給範例的 example.com。真實的專案 ID、機構 ID、同事信箱不要貼進公開的儲存庫或教學文件。也不要建立或下載服務帳戶金鑰:在 Google Cloud 裡執行的程式,把服務帳戶附加到 VM、Cloud Run 等資源,由應用程式預設憑證(Application Default Credentials)自動取得短期權杖;在自己電腦上操作,用 gcloud auth login 的使用者憑證,必要時以模擬(impersonation)暫時借用服務帳戶;跑在其他雲或 CI/CD 平台上的工作負載,用 Workload Identity Federation。2024 年 5 月 3 日以後建立的機構,預設就會強制禁止建立服務帳戶金鑰。
📘 原理補完
資源階層:每個資源剛好一個父層
Google Cloud 的資源階層由上到下是機構、資料夾、專案,最下面是各服務的資源,例如值區、VM、BigQuery 資料集。官方文件的說法是除了最上層的資源之外,每個資源都剛好有一個父層。機構是根節點,前提是要有 Google Workspace 或 Cloud Identity 帳戶;資料夾是選用的分組層,可以巢狀,最多 10 層,一個父資料夾底下最多 300 個子資料夾;專案是「最基本的組織單位」,API 在專案上啟用、費用依專案記到帳單帳戶,使用 Google Cloud 一定要有專案(以上數字查證於 2026 年 10 月)。允許政策、拒絕政策與機構政策都會沿著這棵樹往下繼承。
有效權限怎麼算:先拒絕,再看允許的聯集
當某個主體對某個資源呼叫 API 時,IAM 會先收集這個資源與它所有祖先上的拒絕政策。官方文件寫得很直接:IAM 一定先檢查相關的拒絕政策,再檢查允許政策。只要有一條拒絕規則命中這個主體與權限,而且主體不在例外清單,這個權限就不能用,不管他拿到幾個角色。拒絕政策可以掛在機構、資料夾或專案,每個資源最多 500 份、合計 500 條拒絕規則;規則可以設例外主體與例外權限,條件(denialCondition)只能用資源代碼(tag)判斷。不是每個權限都能被拒絕,要查官方的支援權限清單;權限寫法也和允許政策不同,是 cloudresourcemanager.googleapis.com/projects.delete 這種 v2 格式。
通過拒絕政策之後,IAM 看允許政策:資源本身的、專案的、資料夾的、機構的全部加起來,官方稱為有效允許政策,是所有這些政策的聯集。只要有一筆繫結(條件成立時)給了需要的權限,請求就被允許;沒有任何一筆給,就預設拒絕。所以把某人在專案上的角色拿掉,如果他所屬的群組在資料夾上還有同樣的角色,他照樣能做。除此之外還有兩道選用的護欄:主體存取邊界政策(Principal Access Boundary,PAB)從「這些主體最多能碰哪些資源」畫邊界;VPC Service Controls 從 API 與網路情境畫服務邊界。它們都只會擋,不會給權限。
拒絕政策用 gcloud 建立時,先把規則寫成 JSON 檔,再指定掛載點(attachment point)。下面這份政策掛在專案上,除了平台組以外,任何人都不能刪除這個專案,就算他是 Owner 也一樣:
{
"displayName": "Only platform team can delete project",
"rules": [
{
"denyRule": {
"deniedPrincipals": ["principalSet://goog/public:all"],
"exceptionPrincipals": ["principalSet://goog/group/platform-admins@example.com"],
"deniedPermissions": ["cloudresourcemanager.googleapis.com/projects.delete"]
}
}
]
}
# 把上面的 JSON 存成 deny-project-delete.json 再建立(需要機構上的 Deny Admin 角色)
gcloud iam policies create deny-project-delete \
--attachment-point=cloudresourcemanager.googleapis.com/projects/my-project-id \
--kind=denypolicies \
--policy-file=deny-project-delete.json
variable "project_id" {
type = string
}
# 拒絕政策:除了平台組,任何人都不能刪除這個專案(即使有 Owner)
resource "google_iam_deny_policy" "protect_project" {
parent = urlencode("cloudresourcemanager.googleapis.com/projects/${var.project_id}")
name = "deny-project-delete"
display_name = "Only platform team can delete project"
rules {
description = "Block project deletion except platform admins"
deny_rule {
denied_principals = ["principalSet://goog/public:all"]
exception_principals = ["principalSet://goog/group/platform-admins@example.com"]
denied_permissions = ["cloudresourcemanager.googleapis.com/projects.delete"]
}
}
}
注意兩邊的寫法差異:gcloud 的 --attachment-point 直接寫斜線路徑,Terraform 的 parent 要用 urlencode() 編碼;主體也不是允許政策的 group: 寫法,而是 principalSet://goog/group/… 這種識別碼。
三種角色怎麼選
角色是權限的集合,權限的格式是「服務.資源.動作」,例如 storage.objects.get;權限不能直接授予主體,一定要包在角色裡。基本角色是很早期就有的 Owner、Editor、Viewer,權限橫跨幾乎所有服務,官方現在把它們稱為舊版基本角色(legacy basic roles),並建議正式環境除非沒有替代方案,不要授予;官方文件另列出新的基本角色 Admin、Writer、Reader(roles/admin 等),目前是 Preview。預先定義角色由各服務維護,名稱像 roles/storage.objectViewer、roles/compute.instanceAdmin.v1,服務新增功能時 Google 會更新它們,是日常授權的首選。自訂角色讓你自己挑權限,只能建在機構或專案,每個機構、每個專案各最多 300 個,而且新權限不會自動加進去,要自己維護。
| 角色類型 | 誰維護、粒度 | 可以授予在哪 | IAM 條件 | 典型用途與風險 |
|---|---|---|---|---|
| 舊版基本角色 Owner/Editor/Viewer | Google 定義;橫跨幾乎所有服務,粒度最粗 | 專案或更上層(Cloud Storage 文件寫明不能授予在單一值區) | 不能加條件 | 個人沙箱、短期測試。Owner 還能修改允許政策;Viewer 透過值區預設政策的便利值,常常也能讀物件 |
| 預先定義角色 例:roles/storage.objectViewer | 各服務維護,隨新功能更新;粒度到單一服務的某類工作 | 機構、資料夾、專案,或角色支援的最低層級資源(例如值區) | 可以 | 日常授權首選;先找有沒有剛好的,再考慮自訂 |
| 自訂角色 例:projects/…/roles/vmOperator | 你自己維護;粒度到單一權限 | 建在機構:機構內都能用;建在專案:只能在那個專案內授予。不能建在資料夾 | 可以 | 預先定義角色明顯過大時;權限不會自動更新,部分權限不支援自訂角色 |
服務帳戶:程式的身分,重點是不要用金鑰
服務帳戶是給工作負載用的身分,分三種:你自己建立的使用者管理服務帳戶;啟用 Compute Engine 等服務時自動建立的預設服務帳戶,歷史上常帶著 Editor 這類過大的權限;以及 Google 建立與管理、讓 Google 服務代你存取資源的服務代理(service agent)。服務帳戶同時是主體與資源:在專案上把角色授予它,決定它能做什麼;在它身上把 Service Account User(roles/iam.serviceAccountUser)授予某人,決定誰能把它附加到 VM 或 Cloud Run 上,等於誰能用它的身分做事,所以這個角色要謹慎發。
服務帳戶取得憑證有好幾條路,安全性差很多。服務帳戶金鑰是可下載的長期憑證,外洩了很難察覺,也要自己輪替;在 Google Cloud 裡執行的程式,應該把服務帳戶附加到資源上,透過應用程式預設憑證自動拿到短期權杖;人需要暫時以服務帳戶身分操作,用模擬(impersonation);跑在 AWS、Azure、地端或 GitHub Actions 這類 CI/CD 平台的工作負載,用 Workload Identity Federation,拿自己環境發的憑證向 Security Token Service 換短期權杖。機構可以用機構政策禁止建立與上傳服務帳戶金鑰,舊版限制條件名稱是 iam.disableServiceAccountKeyCreation,新的 managed 版本是 iam.managed.disableServiceAccountKeyCreation。
機構政策:管設定,不管人
機構政策服務讓你在機構、資料夾或專案上設定限制條件(constraint),每份政策強制一個限制條件,預設往下繼承,下層可依規則覆寫;支援 dry-run,先記錄違規再正式強制。它和 IAM 是兩道獨立的關卡:IAM 說你有權限建立 VM,機構政策照樣可以說「這個 Region 不准」。常見的限制條件有資源位置(gcp.resourceLocations)、禁止建立服務帳戶金鑰、禁止 VM 有外部 IP。用 gcloud 設定時,先寫一份 YAML,再用 gcloud org-policies set-policy 套用:
# key-policy.yaml:在整個機構禁止建立服務帳戶金鑰(布林型限制條件)
name: organizations/ORGANIZATION_ID/policies/iam.disableServiceAccountKeyCreation
spec:
rules:
- enforce: true
# 需要機構上的 Organization Policy Administrator 角色
gcloud org-policies set-policy key-policy.yaml
判斷步驟:先選做法,再選層級
- 先問這是「誰能做」還是「能不能這樣設定」。後者(資源位置、外部 IP、服務帳戶金鑰)用機構政策,設在需要的機構、資料夾或專案。
- 是「誰能做」,再問要不要強制擋下某些操作、而且不管對方被授予什麼角色。是的話用拒絕政策,並設好例外主體。
- 要授權的是程式還是人。程式在 Google Cloud 裡:建立專用服務帳戶、附加到資源;程式在外面:Workload Identity Federation;人:授權給 Google 群組而不是個人。
- 挑角色:先找預先定義角色;明顯過大才建自訂角色;基本角色只用在沙箱或測試專案。
- 挑層級:需求所在的最小層級。一個值區就授予在值區,一個應用就授予在它的專案;整個環境(包含未來新增的專案)都要一致,才放在資料夾或機構。
- 需要臨時或限時的權限,用 IAM 條件設到期時間(基本角色不行);事後用 IAM Recommender 收斂長期沒用到的權限。
容易考錯的地方
下層收不回上層的權限:題目描述「在專案上把某人的 Editor 拿掉了,他還能改」,原因幾乎都是資料夾或機構上還有繫結,或他所屬的群組有。正解是去上層移除,或用拒絕政策擋,不是在專案上再加什麼。
拒絕政策與機構政策常被對調:「不管是誰都不能建立服務帳戶金鑰」「資源只能建在台灣」是限制設定值,用機構政策;「除了平台組,誰都不能刪除專案」是限制某些主體使用某個權限,用拒絕政策。
基本角色的兩個陷阱:基本角色不能加 IAM 條件,所以「讓外包人員的 Editor 月底自動失效」做不到,要換成預先定義角色再加條件;Viewer 看起來只能看,但透過值區預設政策的便利值,常常也讀得到物件。
自訂角色的層級:選項出現「在資料夾建立自訂角色」一定是錯的;建在專案的自訂角色也不能拿到別的專案或資料夾授予。
服務帳戶的情境題:程式在 VM 或 Cloud Run 上,答「附加服務帳戶」;在 GitHub Actions、AWS 或地端,答「Workload Identity Federation」;選項若出現「建立金鑰並存到 Secret Manager」,通常是比較差的做法。另外,授予 Service Account User 等於讓對方能以服務帳戶身分做事,題目問「誰能把這個服務帳戶附加到 VM」時就是在問它。
Workload 和 Workforce 名字很像:Workload Identity Federation 給程式;Workforce Identity Federation 給人,讓員工用外部身分提供者直接登入 Google Cloud。
這些內容在 ACE 的 Section 1(資源階層與機構政策、IAM 角色)與 Section 4(IAM 政策、角色繼承、自訂角色、服務帳戶、模擬、短期憑證、Workload Identity Federation)都有點名;PCSE 的 Configuring access 領域考服務帳戶金鑰的限制、IAM 條件、拒絕政策與預建、自訂機構政策;PCDOE 的 Bootstrapping 領域考資源階層、IAM 與機構政策、服務帳戶;CDL 則在成本與營運領域考資源階層的觀念。PCA 的 Designing for security and compliance 領域也點名 IAM、資源階層與職責分離,從架構設計的角度問同樣的取捨。
✅ 自我檢測
以下 6 題都是原創題,選完會立即顯示對錯與解析,全部作答後會出現總分。目前得分:0 / 6