14th Ithome Ironman Iac Aws Workshop 06 Provision Iam Group Policy

昨天我們將 root account IAM user import 到 terraform 中

  • 示範 terraform import
  • 增加 iam user 的功能到 module repository 中

今天要完成 root account 中 IAM Group + Policy,順便聊聊 aws IAM policy 管理原則

  • root 中設定 IAM User
    • 將手動產生的 Administrator 的 IAM User import terraform 中
    • 補上 root account IAM Policy
    • 補上 root account IAM Group

iThome 鐵人賽好讀版

賽後文章會整理放到個人的部落格上 http://chechia.net/

追蹤粉專可以收到文章的主動推播

https://ithelp.ithome.com.tw/upload/images/20210901/20120327NvpHVr2QC0.jpg


承接昨天的 plan 結果,我們今天要把 IAM policy 與 group 開出來

Iam User

首先 review aws_iam_user 的 resource

1# module.iam_user["Administrator"].aws_iam_user.this[0] will be updated in-place
2  ~ resource "aws_iam_user" "this" {
3      + force_destroy = true
4        id            = "Administrator"
5        name          = "Administrator"
6        tags          = {}
7        # (4 unchanged attributes hidden)
8    }`

force_destroy 這個參數我們需要嗎?可以一找以下的步驟查文件判斷

Iam Group & Policy

今天要來調整 Iam Group & Policy

  • Gruntwork 文件: Root Account 說明,root account 下應該有兩個 policy group
    • group/full-access 給予 root account 超級管理員完整的控制權限
    • billing 給予 billing 的會計人員進來報帳

首先 full-access 的 iam_group plan 後的結果如下

 1  # module.iam_group_with_policies_full_access.aws_iam_group.this[0] will be created
 2  + resource "aws_iam_group" "this" {
 3      + arn       = (known after apply)
 4      + id        = (known after apply)
 5      + name      = "full-access"
 6      + path      = "/"
 7      + unique_id = (known after apply)
 8    }
 9
10  # module.iam_group_with_policies_full_access.aws_iam_group_membership.this[0] will be created
11  + resource "aws_iam_group_membership" "this" {
12      + group = (known after apply)
13      + id    = (known after apply)
14      + name  = "full-access"
15      + users = [
16          + "Administrator",
17        ]
18    }

有了 group,接下來就是要配 policy

  • 一樣透過 group + policy -> attachment 的 resource,把 group 跟 policy 綁在一起
  • 我們這邊的程式碼 直接使用 aws 預先定義好的 policy arn:aws:iam::aws:policy/AdministratorAccess
1  # module.iam_group_with_policies_full_access.aws_iam_group_policy_attachment.custom_arns[0] will be created
2  + resource "aws_iam_group_policy_attachment" "custom_arns" {
3      + group      = (known after apply)
4      + id         = (known after apply)
5      + policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
6    }

IAM AWS 預先定義的 policy

有哪些 aws 已經預先定義好的 policy 可以使用?

  • 可以上 aws web console -> iam -> policies 下查詢

AWS Web Console IAM Policies

這些預先定義的 policy,是 aws 依照最常出現的使用情境,事先建立的 policy

  • 使用者不用再建議
  • ex. AWS 覺得 administrator 大概會需要這些權限,都先開到這個 policy/admin 上
  • ex. AWS 覺得 billing 大概會需要這些權限,都先開到這個 policy/billing 上
  • 由於是 AWS 依照大部分人的需求開的 policy,所以會多開許多權限
    • 這也是用預先定義 policy 的缺點,就是為了滿足很多人的需求,權限太大
    • 違反最小權限原則,給予權限過多也造成安全性的風險

不使用預先定義的 policy 的話,我們可以自己寫 policy

  • 一個 policy 開出來 default 沒有任何 permission
  • 依據最小權限原則,一句一句增加 permission 到 policy 上
  • 如此可以確保 policy 上的 permission 都是需要的,而不會有多開但是用不到的權限

權限愈大,安全性風險越高,所以最佳實踐會希望所有 policy 都是配得剛剛好夠用就好

  • 然而配 policy 很花時間,用 aws 已經寫好的 policy 馬上可以用
  • 實務上可以考量專案時間與整體人力,以及需要的安全等級,來考慮要不要做到這麼細

AWS policy access advisor

通常寫 policy 很難配到非常完美剛剛好,通常都會多開 permission

  • 權限多開功能不會壞,少開直接 permission denied
  • 那多開的權限沒有用到,之後要如何把沒有用到的權限收回來?

AWS web console 提供根據使用紀錄,提建議修改 policy

https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_access-advisor.html

aws 會統計各個 IAM 元件,存取 AWS API 使用/沒使用的權限

  • 例如這個 User 過去 90 天使用了哪些 permission 去打 API
  • 以及 User 還有哪些 policy permission,是過去 90 天都沒有用到的

這些長時間都沒有用到的 policy,我們就應該定期 review,然後移除這些不必要的權限

  • User / Group / … 等等都需要做一樣的事情

module 作為一個組成元件使用

由於我們在 module input 中開啟了 attach_iam_self_management_policy = true 參數,在 module 中便連帶產生

  • .aws_iam_policy.iam_self_management[0],裡面定義的要做 self management 所需要的 permission
  • 再把 policy 透過 .aws_iam_group_policy_attachment.iam_self_management[0] 綁到 group 上
 1  # module.iam_group_with_policies_full_access.aws_iam_policy.iam_self_management[0] will be created
 2  + resource "aws_iam_policy" "iam_self_management" {
 3    ...
 4    }
 5
 6  # module.iam_group_with_policies_full_access.aws_iam_group_policy_attachment.iam_self_management[0] will be created
 7  + resource "aws_iam_group_policy_attachment" "iam_self_management" {
 8      + group      = (known after apply)
 9      + id         = (known after apply)
10      + policy_arn = (known after apply)
11    }

上面就是我們的 root account group/full-access 與對應的 policy

billing account

接著是 root account group/full-access 與對應的 policy

  • 我們可以自己手寫 policy,依照最小權限原則,一條一條加入 permission
  • 或是我們可以上 aws web console 找看看有無預先定義好的 policy

AWS Web Console IAM Policies: billing

AWS Web Console IAM Policies: billing difference

我們搜尋 billing 有看到四個 policy,每個 policy 都有附上 description 說明使用的目的

  • Billing
  • AWSBillingConductorReadOnlyAccess
  • AWSBillingConductorFullAccess
  • AWSBillingReadOnlyAccess

這裡就要依據使用的需求選擇

  • 給予 billing 管理員的權限,就會是 ConductorFullAccess 或 ConductorReadOnlyAccess
  • 給予 billing 報帳人員,應該不用更改 aws 的其他設定,只需要讀取跟輸出,就會是 AWSBillingReadOnlyAccess

我們這邊選擇 AWSBillingConductorFullAccess,做個示範

  • 點擊 AWSBillingConductorFullAccess,跳到 Policies » AWSBillingConductorFullAccess 頁面
  • 可以看到 policy 的完整 arn,Amazon Resource Name (ARN) 唯一識別AWS 資源,類似於 ID
  • 然後把 arn 填入 module.am_group_with_policies_billing

AWS Web Console IAM Policies: AWSBillingConductorFullAccess

plan & apply

上面的變更我們的 PR 如下

由於我們 module 中有新增 module "iam_group_with_policies_billing",記得要先執行 init

  1aws-vault exec terraform-30day-root-iam-user --no-session  --  terragrunt plan
  2
  3
  4 Error: Module not installed
  5
  6   on group.tf line 29:
  7   29: module "iam_group_with_policies_billing" {
  8
  9 This module is not yet installed. Run "terraform init" to install all
 10 modules required by this configuration.
 11
 12
 13aws-vault exec terraform-30day-root-iam-user --no-session  --  terragrunt init
 14aws-vault exec terraform-30day-root-iam-user --no-session  --  terragrunt plan
 15
 16Terraform used the selected providers to generate the following execution
 17plan. Resource actions are indicated with the following symbols:
 18  + create
 19
 20Terraform will perform the following actions:
 21
 22  # module.iam_group_with_policies_billing.aws_iam_group.this[0] will be created
 23  + resource "aws_iam_group" "this" {
 24      + arn       = (known after apply)
 25      + id        = (known after apply)
 26      + name      = "billing"
 27      + path      = "/"
 28      + unique_id = (known after apply)
 29    }
 30
 31  # module.iam_group_with_policies_billing.aws_iam_group_membership.this[0] will be created
 32  + resource "aws_iam_group_membership" "this" {
 33      + group = (known after apply)
 34      + id    = (known after apply)
 35      + name  = "billing"
 36      + users = [
 37          + "Accounting",
 38        ]
 39    }
 40
 41  # module.iam_group_with_policies_billing.aws_iam_group_policy_attachment.custom_arns[0] will be created
 42  + resource "aws_iam_group_policy_attachment" "custom_arns" {
 43      + group      = (known after apply)
 44      + id         = (known after apply)
 45      + policy_arn = "arn:aws:iam::aws:policy/AWSBillingConductorFullAccess"
 46    }
 47
 48  # module.iam_group_with_policies_billing.aws_iam_group_policy_attachment.iam_self_management[0] will be created
 49  + resource "aws_iam_group_policy_attachment" "iam_self_management" {
 50      + group      = (known after apply)
 51      + id         = (known after apply)
 52      + policy_arn = (known after apply)
 53    }
 54
 55  # module.iam_group_with_policies_billing.aws_iam_policy.iam_self_management[0] will be created
 56  + resource "aws_iam_policy" "iam_self_management" {
 57      + arn         = (known after apply)
 58      + id          = (known after apply)
 59      + name        = (known after apply)
 60      + name_prefix = "IAMSelfManagement-"
 61      + path        = "/"
 62      + policy      = jsonencode(
 63            {
 64              + Statement = [
 65                  + {
 66                      + Action   = [
 67                          + "iam:UploadSigningCertificate",
 68                          + "iam:UploadSSHPublicKey",
 69                          + "iam:UpdateUser",
 70                          + "iam:UpdateLoginProfile",
 71                          + "iam:UpdateAccessKey",
 72                          + "iam:ResyncMFADevice",
 73                          + "iam:List*",
 74                          + "iam:Get*",
 75                          + "iam:GenerateServiceLastAccessedDetails",
 76                          + "iam:GenerateCredentialReport",
 77                          + "iam:EnableMFADevice",
 78                          + "iam:DeleteVirtualMFADevice",
 79                          + "iam:DeleteLoginProfile",
 80                          + "iam:DeleteAccessKey",
 81                          + "iam:CreateVirtualMFADevice",
 82                          + "iam:CreateLoginProfile",
 83                          + "iam:CreateAccessKey",
 84                          + "iam:ChangePassword",
 85                        ]
 86                      + Effect   = "Allow"
 87                      + Resource = [
 88                          + "arn:aws:iam::706136188012:user/*/${aws:username}",
 89                          + "arn:aws:iam::706136188012:user/${aws:username}",
 90                          + "arn:aws:iam::706136188012:mfa/${aws:username}",
 91                        ]
 92                      + Sid      = "AllowSelfManagement"
 93                    },
 94                  + {
 95                      + Action   = [
 96                          + "iam:List*",
 97                          + "iam:Get*",
 98                        ]
 99                      + Effect   = "Allow"
100                      + Resource = "*"
101                      + Sid      = "AllowIAMReadOnly"
102                    },
103                  + {
104                      + Action    = "iam:DeactivateMFADevice"
105                      + Condition = {
106                          + Bool            = {
107                              + "aws:MultiFactorAuthPresent" = "true"
108                            }
109                          + NumericLessThan = {
110                              + "aws:MultiFactorAuthAge" = "3600"
111                            }
112                        }
113                      + Effect    = "Allow"
114                      + Resource  = [
115                          + "arn:aws:iam::706136188012:user/*/${aws:username}",
116                          + "arn:aws:iam::706136188012:user/${aws:username}",
117                          + "arn:aws:iam::706136188012:mfa/${aws:username}",
118                        ]
119                      + Sid       = "AllowDeactivateMFADevice"
120                    },
121                ]
122              + Version   = "2012-10-17"
123            }
124        )
125      + policy_id   = (known after apply)
126      + tags_all    = (known after apply)
127    }
128
129  # module.iam_group_with_policies_full_access.aws_iam_group.this[0] will be created
130  + resource "aws_iam_group" "this" {
131      + arn       = (known after apply)
132      + id        = (known after apply)
133      + name      = "full-access"
134      + path      = "/"
135      + unique_id = (known after apply)
136    }
137
138  # module.iam_group_with_policies_full_access.aws_iam_group_membership.this[0] will be created
139  + resource "aws_iam_group_membership" "this" {
140      + group = (known after apply)
141      + id    = (known after apply)
142      + name  = "full-access"
143      + users = [
144          + "Administrator",
145        ]
146    }
147
148  # module.iam_group_with_policies_full_access.aws_iam_group_policy_attachment.custom_arns[0] will be created
149  + resource "aws_iam_group_policy_attachment" "custom_arns" {
150      + group      = (known after apply)
151      + id         = (known after apply)
152      + policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
153    }
154
155  # module.iam_group_with_policies_full_access.aws_iam_group_policy_attachment.iam_self_management[0] will be created
156  + resource "aws_iam_group_policy_attachment" "iam_self_management" {
157      + group      = (known after apply)
158      + id         = (known after apply)
159      + policy_arn = (known after apply)
160    }
161
162  # module.iam_group_with_policies_full_access.aws_iam_policy.iam_self_management[0] will be created
163  + resource "aws_iam_policy" "iam_self_management" {
164      + arn         = (known after apply)
165      + id          = (known after apply)
166      + name        = (known after apply)
167      + name_prefix = "IAMSelfManagement-"
168      + path        = "/"
169      + policy      = jsonencode(
170            {
171              + Statement = [
172                  + {
173                      + Action   = [
174                          + "iam:UploadSigningCertificate",
175                          + "iam:UploadSSHPublicKey",
176                          + "iam:UpdateUser",
177                          + "iam:UpdateLoginProfile",
178                          + "iam:UpdateAccessKey",
179                          + "iam:ResyncMFADevice",
180                          + "iam:List*",
181                          + "iam:Get*",
182                          + "iam:GenerateServiceLastAccessedDetails",
183                          + "iam:GenerateCredentialReport",
184                          + "iam:EnableMFADevice",
185                          + "iam:DeleteVirtualMFADevice",
186                          + "iam:DeleteLoginProfile",
187                          + "iam:DeleteAccessKey",
188                          + "iam:CreateVirtualMFADevice",
189                          + "iam:CreateLoginProfile",
190                          + "iam:CreateAccessKey",
191                          + "iam:ChangePassword",
192                        ]
193                      + Effect   = "Allow"
194                      + Resource = [
195                          + "arn:aws:iam::706136188012:user/*/${aws:username}",
196                          + "arn:aws:iam::706136188012:user/${aws:username}",
197                          + "arn:aws:iam::706136188012:mfa/${aws:username}",
198                        ]
199                      + Sid      = "AllowSelfManagement"
200                    },
201                  + {
202                      + Action   = [
203                          + "iam:List*",
204                          + "iam:Get*",
205                        ]
206                      + Effect   = "Allow"
207                      + Resource = "*"
208                      + Sid      = "AllowIAMReadOnly"
209                    },
210                  + {
211                      + Action    = "iam:DeactivateMFADevice"
212                      + Condition = {
213                          + Bool            = {
214                              + "aws:MultiFactorAuthPresent" = "true"
215                            }
216                          + NumericLessThan = {
217                              + "aws:MultiFactorAuthAge" = "3600"
218                            }
219                        }
220                      + Effect    = "Allow"
221                      + Resource  = [
222                          + "arn:aws:iam::706136188012:user/*/${aws:username}",
223                          + "arn:aws:iam::706136188012:user/${aws:username}",
224                          + "arn:aws:iam::706136188012:mfa/${aws:username}",
225                        ]
226                      + Sid       = "AllowDeactivateMFADevice"
227                    },
228                ]
229              + Version   = "2012-10-17"
230            }
231        )
232      + policy_id   = (known after apply)
233      + tags_all    = (known after apply)
234    }
235
236  # module.iam_user["Accounting"].aws_iam_user.this[0] will be created
237  + resource "aws_iam_user" "this" {
238      + arn           = (known after apply)
239      + force_destroy = false
240      + id            = (known after apply)
241      + name          = "Accounting"
242      + path          = "/"
243      + tags_all      = (known after apply)
244      + unique_id     = (known after apply)
245    }
246
247Plan: 11 to add, 0 to change, 0 to destroy.
248
249aws-vault exec terraform-30day-root-iam-user --no-session  --  terragrunt apply

眼尖的同學應該有注意到我們把 create_login_profile = false 先關掉

  • 因為我們還沒有準備 pgp key,這個又要明天再來了

TODO 與進度

  • 透過 root account 設定一組 IAM User
  • 透過 root account 設定多個 aws child accounts
  • root 中設定 IAM User
    • 將手動產生的 Administrator 的 IAM User import terraform 中
    • 補上 root account IAM Policy
    • 補上 root account IAM Group
  • security 中設定 IAM User
    • security 設定 password policy
    • security 設定 MFA policy
  • security 中設定 IAM Policy & Group
  • dev 中設定 IAM role
  • 允許 security assume dev IAM role