IAM 정책 구조 완전 해설: Effect, Action, Resource, Condition 차이점
처음 IAM 정책 JSON을 열어봤을 때, 왜 이 권한이 작동하는지 혹은 왜 막히는지 파악하지 못해서 한참을 헤맨 경험이 있을 것이다. IAM 정책 구조의 네 가지 핵심 요소 — Effect, Action, Resource, Condition — 를 정확히 이해하지 못하면, 권한 오류는 항상 예상치 못한 곳에서 터진다.
TL;DR — IAM 정책 핵심 요소 비교
| 요소 | 역할 | 필수 여부 | 핵심 주의사항 |
|---|---|---|---|
Effect |
Allow 또는 Deny 결정 | 필수 | Explicit Deny는 모든 Allow를 재정의 |
Action |
허용/거부할 API 작업 지정 | 필수 | 서비스 네임스페이스:작업명 형식 필수 |
Resource |
정책이 적용될 AWS 리소스 범위 | 필수 | 일부 작업은 *만 허용 |
Condition |
정책 적용 조건 (선택적 필터) | 선택 | 조건 키는 서비스별로 지원 범위가 다름 |
IAM 정책이 평가되는 방식
IAM 정책은 단순한 허용 목록이 아니다. AWS는 요청이 들어올 때마다 해당 요청에 적용되는 모든 정책을 수집하고, 정해진 평가 로직에 따라 최종 Allow/Deny를 결정한다. 이 평가 순서를 모르면 왜 권한이 막히는지 절대 파악할 수 없다.
존재 여부"} B -- "Yes" --> C["즉시 거부
(Deny)"] B -- "No" --> D{"Allow 정책
존재 여부"} D -- "No" --> E["암묵적 거부
(Implicit Deny)"] D -- "Yes" --> F{"Effect + Action +
Resource 일치?"} F -- "No" --> E F -- "Yes" --> G{"Condition
존재 여부"} G -- "없음" --> H["허용 (Allow)"] G -- "있음" --> I{"Condition
충족 여부"} I -- "충족" --> H I -- "미충족" --> E style C fill:#ff4444,color:#fff style E fill:#ff8800,color:#fff style H fill:#00aa44,color:#fff
- 기본 거부(Default Deny): 명시적 허용이 없으면 모든 요청은 기본적으로 거부된다.
- Explicit Deny 우선: 어떤 정책에서든 Explicit Deny가 존재하면, Allow 정책과 무관하게 즉시 거부된다. SCP(Service Control Policy)도 동일한 원칙으로 작동한다.
- Effect + Action + Resource + Condition 모두 일치해야 Allow: 네 요소 중 하나라도 일치하지 않으면 해당 Statement는 적용되지 않는다.
- Condition은 선택적이지만 존재하면 반드시 충족: Condition 블록이 있는 경우, 조건이 충족되지 않으면 해당 Statement 전체가 무효화된다.
Effect — Allow와 Deny의 결정적 차이
Effect는 Statement가 허용인지 거부인지를 선언한다. 값은 Allow 또는 Deny 두 가지뿐이다.
많은 엔지니어가 놓치는 부분이 있다. Deny에는 두 종류가 있다 — 명시적 거부(Explicit Deny)와 암묵적 거부(Implicit Deny). 명시적 거부는 "Effect": "Deny"로 직접 선언한 것이고, 암묵적 거부는 Allow 정책이 없어서 기본값으로 거부되는 것이다. 이 둘의 차이는 권한 디버깅 시 매우 중요하다.
Explicit Deny는 교통 신호의 '절대 진입 금지' 표지판과 같다. 다른 모든 Allow 신호가 켜져 있어도 이 표지판 앞에서는 멈춰야 한다. IAM 평가 로직에서 Explicit Deny는 계층 구조 최상위에서 작동한다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::example-bucket/*"
},
{
"Effect": "Deny",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::example-bucket/confidential/*"
}
]
}
위 정책에서 example-bucket/confidential/ 하위 객체에 대한 접근은 첫 번째 Statement의 Allow와 무관하게 Deny된다. Explicit Deny가 항상 우선이기 때문이다.
Action — 어떤 API 작업을 제어할 것인가
Action은 정책이 적용될 AWS API 작업을 지정한다. 형식은 반드시 서비스네임스페이스:작업명이어야 한다.
와일드카드(*)를 사용할 수 있지만, 범위를 신중하게 설정해야 한다. s3:*는 S3의 모든 작업을 허용하고, s3:Get*는 Get으로 시작하는 모든 S3 작업을 허용한다. 최소 권한 원칙에 따라 와일드카드 사용은 최소화해야 한다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::example-bucket/*"
}
]
}
Action 목록은 배열로 여러 개를 지정할 수 있다. AWS Service Authorization Reference에서 각 서비스의 정확한 Action 이름과 리소스 레벨 지원 여부를 반드시 확인해야 한다. 잘못된 Action 이름은 정책 저장 시 오류 없이 통과되지만 실제로는 아무 효과가 없다.
Resource — 정책 적용 범위를 ARN으로 제한
Resource는 Action이 적용될 AWS 리소스를 ARN(Amazon Resource Name)으로 지정한다. 이 요소가 IAM 정책에서 가장 많은 실수가 발생하는 지점이다.
중요한 제약이 있다. 일부 IAM 작업(예: iam:ListUsers, ec2:DescribeInstances 같은 List/Describe 계열)은 리소스 레벨 권한을 지원하지 않아서 "Resource": "*"로만 설정해야 한다. 특정 ARN을 지정하면 정책이 의도대로 동작하지 않는다. AWS Service Authorization Reference에서 각 Action의 리소스 레벨 지원 여부를 확인해야 한다.
권한 지원?"} B -- "지원" --> C["특정 ARN 지정
가능"] B -- "미지원" --> D["Resource: *
필수"] C --> E["예: s3:GetObject
arn:aws:s3:::bucket/*"] D --> F["예: s3:ListAllMyBuckets
ec2:DescribeInstances"] style D fill:#ff8800,color:#fff style C fill:#0066cc,color:#fff
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListAllMyBuckets",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::example-bucket/*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example-bucket"
}
]
}
위 예시에서 s3:ListAllMyBuckets는 리소스 레벨 권한을 지원하지 않으므로 *를 사용한다. 반면 s3:GetObject는 특정 버킷 경로로 제한할 수 있다. 또한 s3:ListBucket의 Resource는 버킷 자체(arn:aws:s3:::example-bucket)이고, s3:GetObject의 Resource는 버킷 내 객체(arn:aws:s3:::example-bucket/*)임에 주의해야 한다. 이 둘을 혼동하면 권한이 작동하지 않는다.
Condition — 정책에 조건부 로직 추가
Condition은 선택적 요소지만, 보안 강화에 매우 강력한 도구다. 특정 조건이 충족될 때만 정책이 적용되도록 제한한다.
Condition 블록의 구조는 다음과 같다: 조건 연산자 → 조건 키 → 비교 값.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::example-bucket/*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "ap-northeast-2"
},
"Bool": {
"aws:SecureTransport": "true"
}
}
}
]
}
위 정책은 서울 리전(ap-northeast-2)에서 HTTPS(aws:SecureTransport: true)로 요청할 때만 S3 객체 읽기를 허용한다. 두 조건은 AND 관계로 평가된다 — 둘 다 충족해야 한다.
Condition 연산자는 데이터 타입에 따라 다르다. 자주 사용되는 연산자는 다음과 같다:
StringEquals/StringLike: 문자열 비교 (Like는 와일드카드 지원)ArnEquals/ArnLike: ARN 비교Bool: Boolean 값 비교IpAddress/NotIpAddress: IP 주소 범위 비교DateGreaterThan/DateLessThan: 날짜/시간 비교
조건 키는 전역 조건 키(aws: 접두사)와 서비스별 조건 키(s3:, ec2: 등)로 나뉜다. 서비스별 조건 키는 해당 서비스가 지원하는 경우에만 사용할 수 있으며, AWS Service Authorization Reference에서 지원 여부를 반드시 확인해야 한다.
실제 운영에서 자주 발생하는 오진 패턴
S3 버킷에 접근이 안 된다는 티켓이 들어왔다. IAM 정책을 확인하니 s3:GetObject가 Allow로 설정되어 있었다. 처음엔 IAM 정책 문제가 아닌 것 같았다.
실제 원인은 버킷 정책이었다. 버킷 정책에 "Effect": "Deny"가 설정되어 있었고, 이 Explicit Deny가 IAM의 Allow를 재정의하고 있었다. IAM 정책만 보고 권한이 올바르다고 판단한 것이 오진이었다.
IAM 권한 평가는 IAM 정책만으로 완결되지 않는다. 버킷 정책, SCP, 권한 경계(Permissions Boundary), 세션 정책까지 모두 평가 대상이다. 특히 버킷 정책의 Explicit Deny는 IAM Allow를 항상 재정의한다.
권한 문제를 빠르게 진단하려면 IAM Policy Simulator 또는 aws iam simulate-principal-policy CLI를 사용해야 한다.
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/example-user \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::example-bucket/test-object.txt
이 명령은 지정한 IAM 주체가 특정 리소스에 대해 특정 Action을 수행할 수 있는지 시뮬레이션한다. 버킷 정책은 별도로 확인해야 하며, 이 명령만으로 전체 권한 평가를 완전히 대체할 수는 없다.
Statement 여러 개를 올바르게 조합하는 방법
하나의 IAM 정책 문서는 여러 개의 Statement를 포함할 수 있다. Statement 간의 관계는 OR로 평가된다 — 어느 하나의 Statement라도 일치하면 해당 Statement의 Effect가 적용된다. 단, Explicit Deny는 항상 우선한다.
🔽 전체 정책 예시 펼치기 (Click to expand)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3ListBuckets",
"Effect": "Allow",
"Action": "s3:ListAllMyBuckets",
"Resource": "*"
},
{
"Sid": "AllowSpecificBucketAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::example-bucket",
"arn:aws:s3:::example-bucket/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "true"
}
}
},
{
"Sid": "DenyPublicFolderAccess",
"Effect": "Deny",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::example-bucket/public/*"
}
]
}
Sid(Statement ID)는 선택적 필드로, 정책 내 Statement를 식별하는 데 사용된다. 필수는 아니지만 관리 편의를 위해 의미 있는 이름을 부여하는 것이 좋다.
IAM 정책 구조 디버깅 체크리스트
권한이 예상대로 작동하지 않을 때 확인해야 할 순서다. 각 단계는 이전 단계에서 잡지 못한 레이어를 추가로 검증한다.
-
Explicit Deny 존재 여부 확인 — IAM 정책, 버킷 정책, SCP 어디에든 Explicit Deny가 있으면 Allow는 무의미하다. 가장 먼저 확인해야 할 항목이다.
aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::123456789012:role/ExampleRole \ --action-names s3:PutObject \ --resource-arns arn:aws:s3:::example-bucket/test.txt -
Action 이름 정확성 검증 — 잘못된 Action 이름은 저장 시 오류가 발생하지 않지만 실제로는 아무것도 허용하지 않는다. AWS Service Authorization Reference에서 정확한 Action 이름을 확인한다.
aws iam get-policy-version \ --policy-arn arn:aws:iam::123456789012:policy/ExamplePolicy \ --version-id v1 -
Resource ARN 형식과 범위 검증 — 버킷 자체와 버킷 내 객체의 ARN이 다르다.
s3:ListBucket은 버킷 ARN,s3:GetObject는 객체 ARN(/*포함)이 필요하다. ARN 형식 오류는 권한 평가에서 조용히 실패한다.aws iam simulate-custom-policy \ --policy-input-list file://policy.json \ --action-names s3:ListBucket \ --resource-arns arn:aws:s3:::example-bucket -
Condition 충족 여부 확인 — Condition이 있는 Statement는 조건이 충족되지 않으면 해당 Statement 전체가 무효화된다. 요청 컨텍스트(리전, IP, MFA 여부 등)가 Condition 값과 일치하는지 확인한다. Condition 블록이 있는 정책에서 권한이 간헐적으로 실패한다면 이 레이어를 의심해야 한다.
aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::123456789012:user/example-user \ --action-names s3:GetObject \ --resource-arns arn:aws:s3:::example-bucket/test.txt \ --context-entries ContextKeyName=aws:SecureTransport,ContextKeyValues=true,ContextKeyType=boolean
IAM 정책 구조 마무리 및 다음 단계
IAM 정책 구조의 네 요소를 정리하면: Effect는 허용/거부를 선언하고, Action은 대상 API 작업을 지정하며, Resource는 ARN으로 적용 범위를 제한하고, Condition은 조건부 로직을 추가한다. 이 네 요소가 모두 일치할 때만 Statement가 적용되며, Explicit Deny는 항상 최우선으로 평가된다.
IAM 정책을 더 깊이 이해하려면 AWS 공식 문서의 IAM 정책 평가 로직과 Service Authorization Reference를 참고하라. 특히 각 서비스의 Action별 리소스 레벨 지원 여부와 조건 키 목록은 정책 작성 전에 반드시 확인해야 한다.
핵심 용어 정리
| 용어 | 설명 |
|---|---|
| Explicit Deny | 정책에 명시적으로 선언된 거부. 모든 Allow보다 우선 적용된다. |
| Implicit Deny | Allow 정책이 없어서 기본값으로 거부되는 상태. 명시적 Deny와 구별된다. |
| ARN (Amazon Resource Name) | AWS 리소스를 고유하게 식별하는 이름. arn:aws:서비스:리전:계정ID:리소스 형식. |
| Statement | IAM 정책 내 개별 권한 규칙 블록. 하나의 정책에 여러 Statement를 포함할 수 있다. |
| SCP (Service Control Policy) | AWS Organizations에서 계정 레벨로 적용되는 최대 권한 경계. IAM 정책보다 상위에서 평가된다. |
댓글
댓글 쓰기