透過 AWS Tags 實作 Attribute-Based Access Control (ABAC)
在 AWS IAM 中,使用者依據不同部門、單位、產品、服務或者是功能,幫不同需求各自創建 IAM User 或 IAM Role,並依據 AWS Best Practice 之一:Grant Least Privilege,賦予最小權限至該身份,使該身分剛好可以完成 Job Functions。
這種模式稱為 Role-based access control (RBAC),在管理上時常搭配最小權限原則、IAM Policy 和 Tags,去受限 IAM身份針對不同 AWS Resources 的操作行為。如下範例,當 IAM 身份要創建 EC2 Instance、EBS Volume,必須要新增指定的標籤如Tags:key
才能執行該動作,但無法限制到 Tag Value 輸入的內容。
{
"Sid": "AllowRunInstancesWithRestrictions",
"Effect": "Allow",
# 允許做創建 EC2 Instance、EBS Volume
"Action": [
"ec2:CreateVolume",
"ec2:RunInstances"
],
# 針對符合ARN (AWS Resource Name) 格式的AWS Resources做操作
"Resource": [
"arn:aws:ec2:*:*:volume/*",
"arn:aws:ec2:*:*:instance/*"
],
# 必須帶有Tags,Tag為"key",Value為任意值
"Condition": {
"StringEquals": {
"aws:RequestTag/key": "value"
},
"ForAllValues:StringEquals": {
"aws:TagKeys": [
"key"
]
}
}
}
如下圖範例,管理員創建了三個 IAM Role 給三個專案的關係人員使用,分別為Cloud
、Wave
和Hexagon
,IAM Policy 當中透過 Tags 限制了關係人員僅能訪問指定 Resources。
當中有人員調動職務、專案轉移的話,只需要替換 IAM Role 即能訪問不同專案。但是,假設專案 Wave
當中多了一些 Resources,如 EC2 Instance、S3 Bucket、Lambda Function…等,管理員需要將這些新建立的 Resources Name 新增至 IAM Policy 中,否則 Wave
中的人員會沒有權限訪問。
ABAC 是什麼?
Attribute-Based Access Control (ABAC) 是一種透過 Attribute (屬性) 為依據的權限控管策略。AWS 上提供 Tags (標籤) 的功能,來讓使用者們幫使用資源增加識別性、做 Cost allocation…等辨識需求,甚至可以搭配 IAM Policy 來要求 IAM User 在建立 AWS Resources 時必須同時新增 Tags。
Tags (標籤)也能使用在 IAM User 和 IAM Role 上,搭配 ABAC Policy 可以輕易地實現透過 Tags 配對 Key、Value 決定該 IAM 身份能不能對該 AWS Resources 做操作。
如下圖範例,建立了三個 IAM Roles 並 Assign Tag Key Access-Project-Shape
,Value 分別為 Cloud
、Wave
和 Hexagon
,透過同一份 IAM Policy,三個 IAM Role 各自可以 Access 帶有 Tag keyAccess-Project-Shape
並Value 等同於 IAM Role 的 AWS Resources。
ABAC 和 RBAC 的差異
基於兩種授權模組的差異,在管理上會有以下不同:
-
ABAC 會隨著資源部署,擴展訪問權限: 管理者不用再隨著專案需求、使用者變更專案等狀況去修改訪問權限。
-
ABAC 用到的 IAM Policy 數量較少: 不用再針對每個不同部門、單位或產品去拆分成不同的 IAM Policies,在 IAM Policy 透過 Tags Key/Value 決定該身份擁有訪問哪些 AWS 資源的權限。
-
ABAC 更為便捷地實現最小權限原則: 管理員在 IAM Policy 設計當 Resources 符合 Tags 準則的條件下,允許針對該 Resources 操作所有 Actions;RBAC 的 IAM Policy 則是設計成允許對特定的 Resources 做 Actions。
Scenario Overview
管理員透過 Tags Key/Value 搭配 IAM Policy,以 ABAC 策略來設計 Developer、QA 兩個團隊針對兩個專案 EC、CRM 的存取權限:
access-team
=Dev
代表 Developer 團隊access-team
=Sys
代表 System Admin 團隊access-project
=EC
代表 EC電子商務 專案access-project
=CRM
代表 客戶關係管理 專案
存取權限設計如下圖,在隸屬於 EC
的 Developer 擁有完整 CRUD 權限在開發環境當中,但在生產環境僅擁有 Read
權限;反過來 System Admin 也套用相同設定,僅能針對符合自身 Tags 的 Resources 做存取。
在以下流程會以 IAM User 登入後,透過 Switch Role 取得 Access 權限,再透過 Lambda Function 去執行 AWS API 嘗試取得存放在 Secret Manager 中的資訊。
Create IAM Users
- 建立測試用的 IAM Users 之前,先建立 APAC based IAM Policy
Demo-APAC-AssumeRole-Policy
,請參閱Creating IAM Policies,允許 IAM Users 執行 Switch Role 取得 Access 權限進而去操作 Lambda functions、Secrets Manager resources。 - 此 IAM Policy 僅允許同時帶有
access-team
和access-project
Tags 資訊的 IAM User 才能執行sts:AssumeRole
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowUserPerformAssumeRole",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::123456789012:role/Demo-*",
"Condition": {
"StringEquals": {
"iam:ResourceTag/access-team": "${aws:PrincipalTag/access-team}",
"iam:ResourceTag/access-project": "${aws:PrincipalTag/access-project}"
}
}
}
]
}
Tags 會以變數方式
${aws:PrincipalTag/key}
帶入 IAM Policy
- 建立 IAM User、賦予 IAM Policy
Demo-APAC-AssumeRole-Policy
,並輸入 Tags 資訊。這邊透過建立下列 User 進行 Demo 使用。
User Name | Tags |
---|---|
Demo-Alice-EC-Dev |
access-team = Dev , access-project = EC |
Demo-Bob-EC-Sys |
access-team = Sys , access-project = EC |
Demo-Chris-CRM-Dev |
access-team = Dev , access-project = CRM |
Demo-David-CRM-Sys |
access-team = Sys , access-project = CRM |
Create IAM Role for Users with APAC Policy
建立帶有 APAC Policy Demo-APAC-User-Access-Policy
的 IAM Roles,讓 IAM User 做 switch-role
切換至 IAM Role 身份、取得操作授權使用 AWS 服務:
- 帶有同樣
access-team
跟access-project
的 IAM User 可以 Switch 至對應的 IAM Role,取得 Lambda、Secret Manager Full Access - 帶有同樣
access-team
的 IAM User 能取得 Read Secrets Manager resources、Read & Invoke Lambda functions - 若 Tags 不符則無法操作 Lambda、Secrets Manager resources
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllActionsSecretsManagerSameProjectSameTeam",
"Effect": "Allow",
"Action": "secretsmanager:*",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/access-project": "${aws:PrincipalTag/access-project}",
"aws:ResourceTag/access-team": "${aws:PrincipalTag/access-team}"
},
"ForAllValues:StringEquals": {
"aws:TagKeys": [
"access-project",
"access-team",
"Name",
"OwnedBy"
]
},
"StringEqualsIfExists": {
"aws:RequestTag/access-project": "${aws:PrincipalTag/access-project}",
"aws:RequestTag/access-team": "${aws:PrincipalTag/access-team}"
}
}
},
{
"Sid": "AllResourcesSecretsManagerNoTags",
"Effect": "Allow",
"Action": [
"secretsmanager:GetRandomPassword",
"secretsmanager:ListSecrets"
],
"Resource": "*"
},
{
"Sid": "ReadSecretsManagerSameTeam",
"Effect": "Allow",
"Action": [
"secretsmanager:Describe*",
"secretsmanager:Get*",
"secretsmanager:List*"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/access-team": "${aws:PrincipalTag/access-team}"
}
}
},
{
"Sid": "DenyUntagSecretsManagerReservedTags",
"Effect": "Deny",
"Action": "secretsmanager:UntagResource",
"Resource": "*",
"Condition": {
"StringLike": {
"aws:TagKeys": "access-*"
}
}
},
{
"Sid": "DenyPermissionsManagement",
"Effect": "Deny",
"Action": "secretsmanager:*Policy",
"Resource": "*"
},
{
"Sid": "AllActionsLambda",
"Effect": "Allow",
"Action": "lambda:*",
"Resource": "*"
},
{
"Sid": "ListIAMRole",
"Effect": "Allow",
"Action": [
"iam:ListRoles"
],
"Resource": "*"
},
{
"Sid": "PassIAMRoleWithSpecificName",
"Effect": "Allow",
"Action": [
"iam:PassRole",
"iam:GetRole"
],
"Resource": "arn:aws:iam::*:role/Demo-*"
},
{
"Sid": "PassIAMRoleSameProject",
"Effect": "Allow",
"Action": [
"iam:PassRole"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/access-project": "${aws:PrincipalTag/access-project}"
}
}
},
{
"Sid": "DenyUntagLambdaReservedTags",
"Effect": "Deny",
"Action": "lambda:UntagResource",
"Resource": "*",
"Condition": {
"StringLike": {
"aws:TagKeys": "access-*"
}
}
}
]
}
Role Name | Tags |
---|---|
Demo-EC-Developer |
access-team = Dev , access-project = EC |
Demo-EC-SysAdmin |
access-team = Sys , access-project = EC |
Demo-CRM-Developer |
access-team = Dev , access-project = CRM |
Demo-CRM-SysAdmin |
access-team = Sys , access-project = CRM |
Create IAM Role for Lambda Functions with APAC Policy
建立帶有 APAC Policy Demo-APAC-LambdaRole-Access-Policy
的 IAM Roles,讓 Lambda function 有權限取得放置於 Secret Manager 的 credentials:
- 帶有同樣
access-team
跟access-project
的 Lambda functions 可以存取擁有相同 Tags 的 Secret Manager resources - 若 Tags 不符則無法操作 Lambda、Secret Manager resources
-
IAM Policy 如下:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllLambdaPushLogToCloudWatch", "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" }, { "Sid": "AllActionsSecretsManagerSameProjectSameTeam", "Effect": "Allow", "Action": "secretsmanager:*", "Resource": "*", "Condition": { "StringEquals": { "aws:ResourceTag/access-project": "${aws:PrincipalTag/access-project}", "aws:ResourceTag/access-team": "${aws:PrincipalTag/access-team}" }, "ForAllValues:StringEquals": { "aws:TagKeys": [ "access-project", "access-team", "Name", "OwnedBy" ] }, "StringEqualsIfExists": { "aws:RequestTag/access-project": "${aws:PrincipalTag/access-project}", "aws:RequestTag/access-team": "${aws:PrincipalTag/access-team}" } } }, { "Sid": "AllResourcesSecretsManagerNoTags", "Effect": "Allow", "Action": [ "secretsmanager:GetRandomPassword", "secretsmanager:ListSecrets" ], "Resource": "*" }, { "Sid": "DenyUntagSecretsManagerReservedTags", "Effect": "Deny", "Action": "secretsmanager:UntagResource", "Resource": "*", "Condition": { "StringLike": { "aws:TagKeys": "access-*" } } }, { "Sid": "DenyPermissionsManagement", "Effect": "Deny", "Action": "secretsmanager:*Policy", "Resource": "*" } ] }
Role Name | Tags |
---|---|
Demo-EC-Lambda-Role |
access-team = Sys , access-project = EC |
Demo-CRM-Lambda-Role |
access-team = Sys , access-project = CRM |
Test Access Actions
這部分將透過 IAM Users 做 switch-role
切換至 IAM Roles 取得權限後,再去操作 Lambda function、Secrets Manager credential,並加以確認不同 Tag value、IAM Role 的動作行為結果如何
SystemAdmins 身份
Access Secret Manager
- 已
Demo-Bob-EC-Sys
登入至 AWS Console,會發現沒有權限操作 AWS 服務
- 透過
switch-role
切換至 IAM RoleDemo-EC-Developer
會出現存取被拒,其原因為 IAM User Tag values 與 IAM Role 不相符
-
透過
switch-role
切換至 IAM RoleDemo-EC-SysAdmin
-
至 Secrets Manager 建立 Secret,詳細請參考 Creating a Basic Secret
-
選擇
Other type of secrets
,這邊以 API Key 方式建立 Secret,因 IAM Role Policy 並沒有提供 RDS、RedShift 等權限 -
Secret key / value 配置如下圖:
- 將 Secret 命名為
Demo-EC-Secret
,並依 ABAC 要求提供相關 Tags,以下為輸入不同 Tag Value 時的動作參照:
access-team Tag value |
access-project Tag value |
Additional Tags | 動作結果 |
---|---|---|---|
N/A | N/A | N/A | Failed to create secret – Denied because tag values not atch. |
Dev | EC | N/A | Failed to create secret – Denied because the access-team tag value not match. |
Dev | CRM | N/A | Failed to create secret – Denied because the access-team and access-project tag values not match. |
Sys | EC | N/A | Secret demo successfully stored – Allowed because all required tags are match. You are also allowed to include the optional Name tags, OwnedBy , Name . |
Sys | EC | Owner =Bob |
Failed to create secret – Denied because Owner tag is not allow by policy |
Sys | EC | OwnedBy =Bob |
Secret demo successfully stored – Allowed because all required tags are match. |
Sys | CRM | N/A | Failed to create secret – Denied because access-project tag value not match |
- 若對該 Secret 做 Update 修改
access-team
或是access-project
的 Tag value,因為 Tag value unmatch 而會出現Fail to update Tags - IAM Role not authorized to perform: secretsmanager:TagResource
Access Lambda
- 建立 Lambda function
Demo-EC-Function
,並 Assign IAM RoleDemo-EC-Lambda
、創建 Lambda function
> 因應 Lambda function 在建立時並無 Assign Tags 的步驟,故無法透過 APAC Policy 限制須帶有相符 Tags 才允許執行動作
- 幫 Lambda function 打上 Tags 以辨別使用情境,Lambda funtion tags並不會在ABAC Policy底下生肖,主要仍為識別用途
Function Name | Tags |
---|---|
Demo-EC-Lambda |
access-team = Sys access-project = EC |
Demo-CRM-Lambda |
access-team = Sys access-project = CRM |
-
在 Lambda function 執行下列程式碼,與 Secret Manager 互動取得先前存放的 Secret
import json import boto3 import base64 from botocore.exceptions import ClientError
def lambda_handler(event, context):
# Set the input values secret_name = "Demo-EC-Secret" region_name = "us-east-2" # Create a Secrets Manager client session = boto3.session.Session() client = session.client( service_name='secretsmanager', region_name=region_name ) try: get_secret_value_response = client.get_secret_value( SecretId=secret_name ) print (get_secret_value_response) except ClientError as e: print (e) else: if 'SecretString' in get_secret_value_response: secret = get_secret_value_response['SecretString'] else: # Decode Secret secret = base64.b64decode(get_secret_value_response['SecretBinary']) print (secret) return { 'statusCode': 200, 'body': json.dumps(secret) }
-
執行結果如下圖,順利取得存放在 Secret Manager 之 Secret Info
以上範例為以 SystemAdmins 身份去創建 Secret 放置於 Secret Manager,透過 Lambda Function 以 IAM Role 的身份、權限去獲取 Secret。
在這流程中要注意的是 Lambda function 與 Secret Manager 互動的身份為 IAM Role,因此 APAC Policy 帶入驗證的 aws:RequestTag
為 IAM Role Tags,不是 Lambda function Tags。
Developers 身份
-
已
Demo-Alice-EC-Dev
登入至 AWS Console,透過switch-role
切換至 IAM RoleDemo-EC-Developer
可以嘗試切換至 IAM Role
Demo-EC-SysAdmin
,會因為 Tag values 不符合而被拒絕存取 -
至 Secrets Manager 訪問先前
SysAdmin
建立之 SecretDemo-EC-Secret
,會因為 Resource Tag values 與 IAM Role Tag values 不符合而被拒絕存取
- Developer 身份仍可以另外建一個符合 Tag values 要求的 Secret
Access Lambda
-
訪問先前建立之 Lambda function
Demo-EC-Function
,並執行 Test -
執行結果如下圖,順利取得存放在 Secret Manager 之 Secret Info
以上範例為以 Developers 身份去訪問先前放置於 Secret Manager 的 Secret,並透過 Lambda Function 以 IAM Role 的身份、權限去獲取 Secret。
因 Developers 身份與 SystemAdmins 不同,故無法透過 Secret Manager 訪問到由 SystemAdmins 創建的 Secret。但可以透過先前 Delegate 出來的 IAM Role 搭配 Lambda funtion 獲得,縱使無法 Access Secret,也可以取得往後做利用。
一方面讓 SystemAdmins 保有管理權限、不用分派 Credentials 出去;一方面也讓 Developers 在開發上可以滿足需求做開發。
Summary
ABAC Policy 在權限管理邏輯上與 RBAC Policy 相比便捷許多,也讓整體的設定更為的彈性、好調度。
要注意的是因目前 ABAC Policy 僅支援部分 Services,在操作、編寫 Policy 時要確認該 Service API 是否支援以 Tags 作為 Filter 條件,或者是在 Console 上面操作時有沒有區塊是可以連同 Tags 一起執行的。目前最近推出的 Services 與其 APIs,大多有支援;但反之範例中的 Lambda 便還沒有支援 ABAC Policy 的邏輯去做權限管控。
相信未來 ABAC Policy 的邏輯會在更多的 AWS Services 當中實現!讓權限管理的負擔降低,免除管理者們當不同使用場景、不同資源有所異動時,就要更新一次 Policy 的困擾!
References
Tag:ABAC, AWS Resources, AWS Tags, EC2 Instance, IAM, Lambda Function, S3 bucket