Agenda

  • 前言
  • Identity
  • Role
  • IAM policy
  • 整體架構
  • IAM Resource Hierarchy
  • 實際演練

1. 前言

CLOUD IAM 是 GCP 管理資源權限的服務,全名叫Identity and Access Management,中文意思即「身分識別與存取管理」,主要功能是讓GCP知道 誰(Identity) 可以對哪些 資源(Resource)哪些行為(Role)


2. Identity

  • Google Account : 可能是開發者或是管理員的電子郵件地址,通常是 gmail.com,也可以是其他域名。

  • Service Account : 提供給應用程式的帳號,例如應用程式要使用gcp的資源時,就會以被指定的服務帳號來驗證其身分,若有相關權限,即可對GCP資源做權限所允許的行為。

  • Google Group : 是一個由多個 Google Account 或 Service Account 組成的集合,而每一個Google Group都會有唯一的電子郵件帳號,其用途在管理者可透過這個帳號,直接對集合中的帳號統一授權或是權限調整,提高管理上的便利性 ; 惟須留意,Google Group帳號是無法作為登入GCP時所使用的帳號的。

  • Google Workspace : 主要是以一個域名來管理,例如某組織註冊域名 xxx.com,並用這個域名為底下員工註冊google帳戶,如 employee_1@xxx.com、employee_2@xxx.com,那麼設定iam的時候,就能一次性的使用xxx.com來管理組織下的權限。

  • Cloud Identity Domain : 類似於Google Workspace,差別在於如果沒有要使用Workspace的服務,那使用 Cloud Identity Domain 即可。


3. Roles

Roles 與兩項目有相關,即 Resource、Permission,故先簡單描述兩者,接著再介紹Roles。

Resource :

即GCP提供的各種資源,例如 storage、cloudsql、memorystore等等。

Permission :

可以對gcp資源做什麼行為,例如刪除gce的vm,即需要如下圖的權限。

image

Roles

Roles 是由一個或多個 Permission 所組成的角色,其類型有以下三種 :

  1. Basic Roles (基本角色) :
    有Owner/Editor/Viewer三種role,在啟用gcp project後就會存在了。其中Owner權限最大,擁有project內所有資源的存取權,而Editor是可以修改既已存在的資源並包含Viewer的權限,Viewer則只能對資源進行檢視。這種類型的role在管理權限上是非常不細致的,因此並不建議使用這種方式來管理權限。

  2. Predefined Roles (預定義角色) :
    由GCP提供並持續維護與更新的role,這種角色可以針對特定的資源去配置權限,相較於Basic Roles是對整個專案的資源都適用,Predefined Roles在權限控管上的精細度是有所提升的。實例如下圖紅框處,該權限僅限於GCS的建立、讀取、更新和刪除物件及分段上傳:

    image

  3. Custom Roles (自定義角色) :
    此種 role 比 Predefined role 又更精細,如下圖紅框處,可以只針對特定權限授予,例如只想授予storage的建立權限,那只加入 storage.bucket.create 即可達到需求。

    image


4. IAM policy

當角色都制定好,再來就是要如何授權給使用者了,此時就會使用到 IAM policy。

image
以上圖來說明,IAM policy是由一個或多個Binding的集合,其中Binding就是將某種Roles綁定給使用者的概念。


5. 整體架構

image
Cloud IAM 會根據 IAM policy 去查看誰(Identity) 可以對哪些 資源(Resource)哪些行為(Role),所以當使用者沒有被綁定到Roles,就無法執行該Roles所擁有的權限,此即為IAM的整體概念。


6. IAM Resource Hierarchy

在 GCP 上,所有資源都是以階層的方式進行組織,從上到下依序為:

  1. Organization: 根節點,資源的最上層
  2. Folder: Organization 的子項目
  3. Project: Organization 或是 Folder 的子項目
  4. Resource: Project 下面的子項目
    image

7. 實際演練

以下會以GCS作為示範的資源,建置則以terraform方式建立。
簡單描述演練項目及步驟:

  1. terraform前置準備
  2. 創建自定義的角色
  3. 創建服務帳號
  4. 創建iam_policy
  5. 驗證iam_policy
  6. condition功能演示

  • terraform前置準備 (此步驟只限terraform練習才要,用ui練習者可忽略)

    • ui介面操作:
      (1) 先為terraform建置服務帳號

      image
      接著產出服務帳戶的金鑰:
      image

      (2)創建一個自訂義的角色,角色要擁有以下權限:

      ​​​​​​  iam.roles.get
      ​​​​​​  iam.roles.create
      ​​​​​​  iam.roles.delete
      ​​​​​​  iam.roles.update
      ​​​​​​  iam.roles.undelete
      ​​​​​​  iam.serviceAccountKeys.create
      ​​​​​​  iam.serviceAccountKeys.delete
      ​​​​​​  iam.serviceAccountKeys.get
      ​​​​​​  iam.serviceAccounts.create
      ​​​​​​  iam.serviceAccounts.delete
      ​​​​​​  iam.serviceAccounts.get
      ​​​​​​  resourcemanager.projects.setIamPolicy
      ​​​​​​  resourcemanager.projects.getIamPolicy
      

      image
      (3) 建置好角色後,將角色綁訂至terraform的服務帳號
      image

    • terraform腳本:

      ​​​​// provider.tf ​​​​provider "google" { ​​​​ credentials = var.credentials ​​​​ project = var.project ​​​​ region = var.region ​​​​} ​​​​provider "google-beta" { ​​​​ credentials = var.credentials ​​​​ project = var.project ​​​​ region = var.region ​​​​} ​​​​// variable.tf ​​​​variable "credentials" { ​​​​ type = string ​​​​ default = "terraform-test.json" ​​​​} ​​​​variable "project" { ​​​​ type = string ​​​​ default = "iamrich-lab" ​​​​} ​​​​variable "region" { ​​​​ type = string ​​​​ default = "asia-east1" ​​​​} ​​​​ ​​​​//terraform-test.json ​​​​-> 將剛剛產出的金鑰內容貼到此份檔案
  • 創建自定義角色

    • ui的設定位置在:

      image

    • terraform建置腳本:

      ​​​​# main.tf ​​​​resource "google_project_iam_custom_role" "custom_role"{ ​​​​ for_each = var.custom_role_list ​​​​ role_id = each.value.role_id ​​​​ title = each.value.title ​​​​ permissions = each.value.permissions ​​​​ description = each.value.description ​​​​} ​​​​ ​​​​# variable.tf ​​​​variable "custom_role_list"{ ​​​​ type = map(object({ ​​​​ role_id = string ​​​​ title = string ​​​​ permissions = list(string) ​​​​ description = string ​​​​ })) ​​​​ default = { ​​​​ custom_role_1 = { ​​​​ role_id = "CustomRoleFortest1" ​​​​ title = "Custom Role Test 1" ​​​​ permissions = ["storage.buckets.create","storage.buckets.get"] ​​​​ description = "forIamTest-storage-create" ​​​​ } ​​​​ } ​​​​}
  • 創建服務帳號

    • ui的設定位置在:
      image
    • terraform建置腳本:
      ​​​​# *main.tf* ​​​​#--創建服務帳號 ​​​​resource "google_service_account" "for-test" { ​​​​ for_each = var.servcieaccount_list ​​​​ account_id = each.value.account_id ​​​​ display_name = each.value.display_name ​​​​} ​​​​#--產出服務帳號的金鑰 ​​​​resource "google_service_account_key" "mykey" { ​​​​ for_each = var.key_list ​​​​ service_account_id = google_service_account.for-test[each.key].name ​​​​ public_key_type = each.value.public_key_type ​​​​} ​​​​#--將上面服務帳號的私鑰 匯出至本機指定的目錄 (等等切換帳號時需用到) ​​​​resource "local_file" "download_key_json"{ ​​​​ for_each = var.keyjson_list ​​​​ content = base64decode(google_service_account_key.mykey[each.key].private_key) ​​​​ filename = each.value.filename ​​​​} ​​​​# variable.tf ​​​​variable "servcieaccount_list"{ ​​​​ type = map(map(string)) ​​​​ default = { ​​​​ for_test = { ​​​​ account_id = "for-test" ​​​​ display_name = "Allow to editor storage." ​​​​ } ​​​​ } ​​​​} ​​​​variable "key_list" { ​​​​ type = map(map(string)) ​​​​ default = { ​​​​ for_test = { ​​​​ public_key_type = "TYPE_X509_PEM_FILE" ​​​​ } ​​​​ } ​​​​} ​​​​variable "keyjson_list"{ ​​​​ type = map(map(string)) ​​​​ default = { ​​​​ for_test ={ ​​​​ filename = "./serviceaccount_demo/for-test.json" ​​​​ } ​​​​ } ​​​​}
  • 創建iam_policy

    • ui的設定位置在:

      image

    • terraform建置腳本:
      分別有兩種不同情境:

      1. 針對單一帳號設定 policy
      ​​​​# main.tf ​​​​resource "google_project_iam_member" "iam_policy" { ​​​​ for_each = var.google_project_iam_member_list ​​​​ project = var.project ​​​​ role = each.value.role ​​​​ member = each.value.member ​​​​ dynamic "condition" { ​​​​ for_each = each.value.condition_config ​​​​ content { ​​​​ title = condition.value["title"] ​​​​ description = condition.value["description"] ​​​​ expression = condition.value["expression"] ​​​​ } ​​​​ } ​​​​} ​​​​# variable.tf ​​​​variable "google_project_iam_member_list" { ​​​​ type = map(object({ ​​​​ role = string ​​​​ member = string ​​​​ condition_config = list(object({ ​​​​ title = string ​​​​ description = string ​​​​ expression = string ​​​​ })) ​​​​ })) ​​​​ default = { ​​​​ for_iam_member_1 = { ​​​​ role = "projects/iamrich-lab/roles/CustomRoleFortest1" ​​​​ member = "serviceAccount:for-test@iamrich-lab.iam.gserviceaccount.com" ​​​​ condition_config = [] ​​​​ } ​​​​ } ​​​​}
      1. 針對多組帳號設定 policy
      ​​​​# main.tf ​​​​resource "google_project_iam_binding" "iam_policy" { ​​​​ for_each = var.google_project_iam_binding_list ​​​​ project = var.project ​​​​ role = each.value.role ​​​​ member = each.value.muli_member ​​​​ dynamic "condition" { ​​​​ for_each = each.value.condition_config ​​​​ content { ​​​​ title = condition.value["title"] ​​​​ description = condition.value["description"] ​​​​ expression = condition.value["expression"] ​​​​ } ​​​​ } ​​​​} ​​​​# variable.tf ​​​​variable "google_project_iam_binding_list" { ​​​​ type = map(object({ ​​​​ role = string ​​​​ muli_member = list(string) ​​​​ condition_config = list(object({ ​​​​ title = string ​​​​ description = string ​​​​ expression = string ​​​​ })) ​​​​ })) ​​​​ default = { ​​​​ for_iam_muliMember = { ​​​​ role = "projects/iamrich-lab/roles/CustomRoleFortest1" ​​​​ muli_member = [ ​​​​ "serviceAccount:for-test@iamrich-lab.iam.gserviceaccount.com", ​​​​ ] ​​​​ condition_config = [] ​​​​ } ​​​​ } ​​​​}

      關於Permission有哪些 可參考GCP文件

  • 驗證iam_policy (使用剛創建的服務帳號創建+刪除bucket)

    • ui的設定位置在:

      image

    • terraform建置腳本:
      到這邊我們需要切換資料夾,因為接下來要使用剛剛建好的服務帳號執行,來驗證iam_policy生效。新的資料夾會有以下:

      ​​​​# main.tf ​​​​resource "google_storage_bucket" "bucket"{ ​​​​ name = "test-20240605" ​​​​ location = "ASIA" ​​​​ force_destroy = false ​​​​ storage_class = "STANDARD" ​​​​ cors { ​​​​ origin = ["*"] ​​​​ method = ["GET"] ​​​​ response_header = ["Access-Control-Allow-Origin"] ​​​​ max_age_seconds = 3600 ​​​​ } ​​​​} ​​​​ ​​​​# variable.tf ​​​​variable "credentials" { ​​​​ type = string ​​​​ default = "for-test.json" ​​​​} ​​​​variable "project" { ​​​​ type = string ​​​​ default = "iamrich-lab" ​​​​} ​​​​variable "region" { ​​​​ type = string ​​​​ default = "asia-east1" ​​​​} ​​​​ ​​​​# provider.tf ​​​​provider "google" { ​​​​ credentials = var.credentials ​​​​ project = var.project ​​​​ region = var.region ​​​​} ​​​​provider "google-beta" { ​​​​ credentials = var.credentials ​​​​ project = var.project ​​​​ region = var.region ​​​​}
  • condition功能演示

    • ui的設定位置在:

      image
      (在創建iam_policy時可以順便設定的)

    • terraform腳本:

      ​​​​variable "google_project_iam_member_list" { ​​​​ type = map(object({ ​​​​ role = string ​​​​ member = string ​​​​ condition_config = list(object({ ​​​​ title = string ​​​​ description = string ​​​​ expression = string ​​​​ })) ​​​​ })) ​​​​ default = { ​​​​ for_iam_member_1 = { ​​​​ role = "projects/iamrich-lab/roles/CustomRoleFortest1" ​​​​ member = "serviceAccount:for-test-1@iamrich-lab.iam.gserviceaccount.com" ​​​​ condition_config = [ ​​​​ { ​​​​ title = "condition_test" ​​​​ description = "for-condition-test" ​​​​ expression = "request.time.getHours('Asia/Taipei') >= 9 && request.time.getHours('Asia/Taipei') <= 15 && request.time.getDayOfWeek('Asia/Taipei') >= 1 && request.time.getDayOfWeek('Asia/Taipei') <= 5" ​​​​ } ​​​​ ] ​​​​ } ​​​​ } ​​​​}

參考文件

https://cloud.google.com/iam/docs/overview?hl=zh-cn
https://cloud.google.com/iam/docs/conditions-overview?hl=zh-cn
https://blog.cloud-ace.tw/identity-security/what-is-cloud-iam/
https://hackmd.io/@kevinhuangtw/SJ2KjoRKc

terraform項目:
google_project_iam_custom_role
google_service_account
google_service_account_key
local_file
google_project_iam
google_storage_bucket