บังคับให้ใส่ Tag ทุกครั้งเมื่อสร้าง Resource บน AWS

Enforce Users to Specify Tags When Creating a Resource on AWS

Nopnithi Khaokaew (Game)
4 min readMar 17, 2022

ต่อจากบทความก่อนหน้าที่ผมเขียนวิธีการใช้ 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 ได้แล้ว

จาก docs.aws.amazon.com

และถ้าดูจากรูปข้างบน เราไม่สามารถกำหนดให้เป็น 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 ถ้าไม่ใส่ tag Name
  • Statement 2 = จะ deny action ec2:RunInstances ถ้าไม่ใส่ tag DeleteProtection ที่มี 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 ทั้งหมดครับ

ถ้าคิดว่าบทความนี้มีประโยชน์ ฝากกด clap, follow และ share บทความนี้ให้ผมด้วยนะครับ ขอบคุณมากครับ ^_^

— — — — — — — — —
บทความทั้งหมดของผม
— — — — — — — — —

--

--

Nopnithi Khaokaew (Game)
Nopnithi Khaokaew (Game)

Written by Nopnithi Khaokaew (Game)

Cloud Solutions Architect & Hobbyist Developer | 6x AWS Certified, CKA, CKAD, 2x HashiCorp Certified (Terraform, Vault), etc.

No responses yet