บังคับให้ใส่ Tag ทุกครั้งเมื่อสร้าง Resource บน AWS
Enforce Users to Specify Tags When Creating a Resource on AWS
ต่อจากบทความก่อนหน้าที่ผมเขียนวิธีการใช้ IAM policy และ tag เพื่อป้องกัน user ที่มีสิทธิ์ลบเผลอลบ resource บน AWS โดยไม่ตั้งใจ ดังนั้นบทความนี้ผมเลยจะต่อยอดวิธี enforce ให้ user ต้องใส่ tag ทุกครั้งที่มีสร้าง resource ขึ้นมาครับ
— — — — — — — — —
บทความทั้งหมดของผม
— — — — — — — — —
อธิบาย IAM Policy Condition (เท่าที่ใช้)
โครงสร้าง Condition Block
"Condition": {
"<condition-operator>": {
"<condition-key>": "<condition-value>"
}
}
Condition Operator
- StringEquals = ค่าของ condition key ที่ pass มากับ request จะต้องตรงกับ condition value
- StringLike = ค่าของ condition key ที่ pass มากับ request จะต้องตรงกับ pattern ใน condition value (ใช้ wildcard เช่น * หรือ ?)
- StringNotEquals = ตรงข้ามกับ StringEquals
- StringNotLike = ตรงข้ามกับ StringLike
Set Operator
- ForAllValues = ค่าของ condition key ทุกตัวจะต้องตรงตามเงื่อนไขของ condition operator
- ForAnyValue = ค่าของ condition key ตัวใดตัวหนึ่งจะต้องตรงตามเงื่อนไขของ condition operator
Condition Key
- aws:RequestTag = ใช้สำหรับเช็ค tag key และ value (ในส่วนของ tag key จะไม่สน case-sensitive)
- aws:TagKeys = ใช้สำหรับเช็ค tag key เท่านั้น
ตัวอย่างการบังคับใส่ Tag เมื่อสร้าง Resource
สมมุติให้ user นี้เป็น AdministratorAccess ละกัน ซึ่งมี permission ที่ allow action สำหรับสร้าง EC2 instance ได้อยู่แล้ว ดังนั้นจึงต้องทำ policy เพิ่มเป็น explicit deny
แล้วไส้ในเป็นยังไงบ้างลองมาดูตัวอย่างกัน
ตัวอย่าง 1 เมื่อสร้าง EC2 Instance จะต้องใส่ Tag ดังนี้
- Name (ไม่สน case-sensitive) และ value จะเป็นอะไรก็ได้
- สามารถใส่ tag อื่น ๆ ที่ไม่ใช่ Name เข้ามาด้วยได้
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyCreateEC2InstancesWhenNotTags",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotLike": {
"aws:RequestTag/Name": [
"*"
]
}
}
}
]
}
Policy นี้จะ deny request ในการสร้าง EC2 instance นี้ ถ้าหากค่าของ contidion key (aws:RequestTag/Name) ที่ใส่มาไม่ตรงกับ condition value (*)
แปลเป็นภาษาคนได้ว่า ถ้าไม่ใส่ tag (Name หรือ name) ที่มี value เป็นอะไรก็ได้มา ก็จะไม่สามารถสร้าง EC2 instance ได้นั่นเอง แต่ถ้าใส่มาจะสร้างได้ แม้จะใส่ tag อื่นที่ไม่ใช่ Name มาด้วยก็ไม่เป็นไร
ตัวอย่าง 2 เมื่อสร้าง EC2 Instance จะต้องใส่ Tag ดังนี้
- DeleteProtection (ไม่สน case-sensitive) และ value จะต้องเป็น true หรือ false เท่านั้น (สน case-sensitive)
- สามารถใส่ tag อื่น ๆ ที่ไม่ใช่ DeleteProtection เข้ามาด้วยได้
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyCreateEC2InstanceWhenNotTag",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotEquals": {
"aws:RequestTag/DeleteProtection": [
"true",
"false"
]
}
}
}
]
}
ตัวอย่าง 3 เมื่อสร้าง EC2 Instance จะต้องใส่ Tag ดังนี้
- Name (ไม่สน case-sensitive) และ value จะเป็นอะไรก็ได้
- DeleteProtection (ไม่สน case-sensitive) และ value จะต้องเป็น true หรือ false เท่านั้น (สน case-sensitive)
- สามารถใส่ tag อื่น ๆ ที่ไม่ใช่ Name และ DeleteProtection เข้ามาด้วยได้
งั้นเขียนแบบด้านล่างนี้ถูกมั้ย?
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyCreateEC2InstancesWhenNotTags",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotLike": {
"aws:RequestTag/Name": [
"*"
]
},
"StringNotEquals": {
"aws:RequestTag/DeleteProtection": [
"true",
"false"
]
}
}ห
}
]
}
ไม่ถูกครับ การใส่ condition มากกว่าหนึ่งตัวมันจะ AND กัน ทำให้ policy จะ deny ก็ต่อคุณไม่ใส่ tag ทั้งสองตัวมา นั่นแปลว่าถ้า user ใส่ tag มาแค่ตัวใดตัวนึงเช่น Name ก็ทำให้สามารถสร้าง EC2 instance ได้แล้ว
และถ้าดูจากรูปข้างบน เราไม่สามารถกำหนดให้เป็น OR ได้ ซึ่งจริง ๆ มีวิธี workaround หลายทางเลย แต่ผมเลือกใช้การเพิ่ม statement แบบนี้
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyCreateEC2InstancesWhenNotTag1",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotLike": {
"aws:RequestTag/Name": [
"*"
]
}
}
},
{
"Sid": "DenyCreateEC2InstancesWhenNotTag2",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotEquals": {
"aws:RequestTag/DeleteProtection": [
"true",
"false"
]
}
}
}
]
}
หรือถ้า user ไม่มี permission เลย ก็สามารถเปลี่ยนเป็น allow และกลับ condition operator ไม่เป็น Not ได้ แบบนี้จะทำให้สามารถใส่ condition ทั้งสองตัวไว้ด้วยกันได้ แต่มันจะวุ่นตรงที่ต้องมาไล่ allow action ทุกอย่างให้ครบ ยกเว้น action เดียวที่เป็นการสร้าง instance นั่นแหละ ประมาณนี้
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowRunInstancesWhenTag",
"Effect": "Allow",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringLike": {
"aws:RequestTag/Name": [
"*"
]
},
"StringEquals": {
"aws:RequestTag/DeleteProtection": [
"true",
"false"
]
}
}
},
{
"Sid": "AllowRunOthers",
"Effect": "Allow",
"Action": "ec2:RunInstances",
"Resource": [
"arn:aws:ec2:*::image/*",
"arn:aws:ec2:*:*:security-group/*",
"arn:aws:ec2:*:*:network-interface/*",
"arn:aws:ec2:*:*:subnet/*",
"arn:aws:ec2:*:*:key-pair/*",
"arn:aws:ec2:*:*:volume/*"
]
},
{
"Sid": "AllowCreateTags",
"Effect": "Allow",
"Action": "ec2:CreateTags",
"Resource": "*"
}
]
}
ตัวอย่าง 4 เมื่อสร้าง EC2 Instance จะต้องใส่ Tag ดังนี้
- Name (สน case-sensitive) และ value จะเป็นอะไรก็ได้
- DeleteProtection (สน case-sensitive) และ value จะต้องเป็น true หรือ false เท่านั้น (สน case-sensitive)
- ไม่สามารถใส่ tag อื่นได้
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyCreateEC2InstancesWhenNotTags1",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotLike": {
"aws:RequestTag/Name": [
"*"
]
}
}
},
{
"Sid": "DenyCreateEC2InstancesWhenNotTags2",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotEquals": {
"aws:RequestTag/DeleteProtection": [
"true",
"false"
]
}
}
},
{
"Sid": "DenyCreateEC2InstancesWhenNotTags3",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"ForAnyValue:StringNotEquals": {
"aws:TagKeys": [
"Name",
"DeleteProtection"
]
}
}
}
]
}
- Statement 1 = จะ deny action
ec2:RunInstances
ถ้าไม่ใส่ tagName
- Statement 2 = จะ deny action
ec2:RunInstances
ถ้าไม่ใส่ tagDeleteProtection
ที่มี value เป็นtrue
หรือfalse
- Statement 3 = จะ deny action
ec2:RunInstances
ถ้ามี tag แม้แต่ตัวเดียวที่ไม่ใช่Name
หรือDeleteProtection
ที่ statement 3 ถ้าใช้ set operation ForAllValues
มันจะเช็คว่า tag ที่ใส่มาทุกตัวต้องไม่ใช่ Name หรือ DeleteProtection ถึงจะ deny ดังนั้นถ้า user ใส่ tag มาแบบนี้
- Name = test
- DeleteProtect = true
- Environment = dev
ก็ยังสามารถสร้าง EC2 instance ได้อยู่ดี
ตัวอย่าง 5 ใส่ Tag ได้เฉพาะตอนสร้าง EC2 Instance ครั้งแรก
อันนี้แถมให้ครับ จาก policy นี้ user จะใส่ tag ให้กับ EC2 instance ได้เฉพาะตอนสร้างครั้งแรกเท่านั้น ส่วน instance ที่สร้างขึ้นมาแล้วจะไม่สามารถใส่ tag ได้
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowTaggingOnlyFirstCreation",
"Effect": "Deny",
"Action": "ec2:CreateTags",
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringNotEquals": {
"ec2:CreateAction": "RunInstances"
}
}
}
]
}
เพราะอะไร? ก็ condition กำหนดไว้ว่า action ec2:CreateTags
จะถูก deny ถ้าหาก ec2:CreateAction
ไม่ใช่ RunInstances (request ที่เป็นการสร้าง)
ดังนั้นต่อให้ user มี permission เป็น AdministratorAccess ที่ทำได้ทุกอย่างก็ไม่สามารถใส่ tag ให้ existing EC2 instance ได้ เพราะถูก explicit deny ทับ
อ้างอิง
นี่คือ document ที่ต้องไปดูประกอบการเขียน policy ทั้งหมดครับ
- https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_multi-value-conditions.html
- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/supported-iam-actions-tagging.html
- https://docs.aws.amazon.com/service-authorization/latest/reference/reference_policies_actions-resources-contextkeys.html
ถ้าคิดว่าบทความนี้มีประโยชน์ ฝากกด clap, follow และ share บทความนี้ให้ผมด้วยนะครับ ขอบคุณมากครับ ^_^
— — — — — — — — —
บทความทั้งหมดของผม
— — — — — — — — —