IAM은 AWS 보안의 척추다. 어제 본 6개 도메인 어디를 가든, 결국 "이 주체(principal)가 이 리소스에 이 행위를 할 수 있는가"라는 질문으로 환원된다. KMS 복호화도, S3 GetObject도, cross-account 배포도 전부 IAM 평가 로직이라는 같은 엔진 위에서 결정된다. 그래서 IAM을 "정책 JSON 외우기"가 아니라 평가 알고리즘을 이해하는 것으로 접근해야 SCS-C03의 미묘한 시나리오를 푼다.
오늘은 IAM의 네 가지 빌딩블록(사용자·그룹·역할·정책)이 정확히 무엇인지, 그리고 한 요청이 들어왔을 때 AWS가 Allow/Deny를 결정하는 평가 흐름을 단계별로 따라간다. 평가 순서의 핵심 한 문장을 미리 던지면: "명시적 Deny가 모든 것을 이긴다. 그 다음 명시적 Allow가 있어야 허용된다. 둘 다 없으면 암묵적 Deny."
| 요소 | 정의 | 자격 증명 | 핵심 용도 |
|---|---|---|---|
| User(사용자) | 영구적 신원, 사람 또는 앱 1개에 대응 | 장기 access key, 비밀번호 | 가급적 피한다(키 노출 위험) |
| Group(그룹) | 사용자 묶음, 정책 부착용 컨테이너 | 없음(자격증명 없음) | 사용자에게 정책을 일괄 부여 |
| Role(역할) | 누구든 일시적으로 "맡을 수 있는" 신원 | STS 임시 자격증명(만료됨) | EC2/Lambda, cross-account, 페더레이션 |
| Policy(정책) | 권한을 기술한 JSON 문서 | 해당 없음 | 위 요소에 부착되어 권한 정의 |
여기서 시험이 반복적으로 노리는 통찰이 있다. 장기 자격증명(IAM User access key)은 가능한 한 쓰지 말고, Role의 임시 자격증명을 쓰라는 것이다. EC2가 S3에 접근해야 하면 User access key를 인스턴스에 넣는 게 아니라 **인스턴스 프로파일(IAM Role)**을 붙인다. Lambda가 DynamoDB에 접근하면 **실행 역할(execution role)**을 쓴다. 사람도 가급적 IAM User 대신 **IAM Identity Center(SSO)**로 로그인해 임시 자격증명을 받는다.
💡 관련 이론: Group은 "그릇"일 뿐 그 자체로는 자격증명이 없어서 Group을 Principal로 지정할 수 없다. 신입이 자주 틀리는 부분 — Role의 trust policy에
"Principal": {"AWS": "arn:...:group/Devs"}를 쓰면 동작하지 않는다. 그룹은 정책을 사용자에게 전달하는 통로일 뿐, AssumeRole의 대상이 될 수 없다.
🔍 더 깊이: Role을 "맡는다(assume)"는 것의 실체는 STS(Security Token Service)가 임시 자격증명 3종(AccessKeyId, SecretAccessKey, SessionToken)을 발급하는 것이다. 이 자격증명은 만료 시간(기본 1시간, 최대 12시간)이 있어서, 노출돼도 영향이 시간적으로 제한된다. 이것이 장기 키보다 안전한 근본 이유이며, Day 4에서 STS를 깊이 다룬다.
정책은 "어디에 붙느냐"로 두 종류로 나뉜다. 오늘은 개념만 잡고 Day 3에서 깊이 들어간다.
리소스 기반 정책의 결정적 효용은 cross-account 접근을 가능하게 한다는 점이다. 다른 계정의 주체에게 내 S3 버킷을 열어주려면 그 버킷의 리소스 기반 정책에 상대 계정을 명시한다.
선택지를 클릭하면 정답·해설이 펼쳐집니다.
문제 1
한 IAM 사용자가 `AdministratorAccess` 관리형 정책을 가지고 있다. 동시에 그 사용자가 속한 계정의 SCP에 `s3:*`에 대한 명시적 `Deny`가 있다. 이 사용자가 S3 객체를 읽으려 하면?
문제 2
계정 A의 IAM 역할이 계정 B의 S3 버킷에 객체를 쓰려고 한다. 접근이 성공하려면 반드시 충족돼야 하는 조건은?
문제 3
IAM Group에 대한 설명으로 옳은 것은?
문제 4
한 개발자에게 `AmazonS3FullAccess`가 부여돼 있지만, 어떤 정책도 명시적으로 허용하지 않은 `dynamodb:GetItem`을 호출하려 한다. 결과와 그 이유로 옳은 것은?
문제 5
보안팀이 모든 멤버 계정에서 누구도(admin·root 포함) CloudTrail을 비활성화하지 못하게 강제하려 한다. 가장 적절한 방법은?
모든 정책은 Statement의 배열이고, 각 Statement는 다음 요소를 갖는다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReadSpecificBucket",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-secure-bucket",
"arn:aws:s3:::my-secure-bucket/*"
],
"Condition": {
"Bool": {"aws:SecureTransport": "true"}
}
}
]
}Allow 또는 Denys3:GetObject). 와일드카드(s3:*) 가능*(정책이 이미 그 리소스에 붙어 있음)⚠️ 함정:
"Resource": "*"와"Action": "*"를 함께 쓰면 사실상 admin이다. 시험에서 "최소 권한"을 물으면 와일드카드를 좁힌 보기가 정답이다. 특히s3:ListBucket은 버킷 ARN(arn:aws:s3:::bucket)에,s3:GetObject는 객체 ARN(.../*)에 적용된다 — 두 ARN 레벨을 섞으면 동작하지 않는 게 흔한 실수다.
이것이 오늘의 핵심이다. 단일 계정 내에서 한 요청이 들어왔을 때 AWS가 결정하는 순서는 다음과 같다.
[ IAM 정책 평가 흐름 (단일 계정) ]
요청(Principal + Action + Resource + Context)
|
1. 적용 가능한 모든 정책 수집
(Identity-based, Resource-based, SCP, Permissions Boundary, Session policy)
|
2. 명시적 Deny가 하나라도 있는가? ── 예 ──▶ DENY (최우선, 무조건)
| 아니오
3. SCP가 해당 Action을 허용하는가? ── 아니오 ──▶ DENY
| 예
4. Permissions Boundary가 허용하는가? ── 아니오 ──▶ DENY
| 예
5. 명시적 Allow가 하나라도 있는가? ── 아니오 ──▶ DENY (암묵적 거부)
| 예
▼
ALLOW
세 가지 원칙으로 압축된다.
💡 관련 이론: 이 평가 순서를 외울 때 핵심은 **"가드레일(SCP, Permissions Boundary)은 권한을 주지 않고 상한선만 긋는다"**는 점이다. SCP가
s3:*를 허용해도 그것만으로는 아무 권한도 안 생긴다 — 신원 기반 정책에 명시적 Allow가 있어야 한다. SCP는 "최대 허용 범위"를 정의할 뿐이다. 그래서 SCP에서 흔히 쓰는 패턴은 Allow가 아니라 Deny 가드레일이다.
🔍 더 깊이: cross-account의 경우 평가가 양쪽 계정에서 각각 일어난다. 계정 A의 주체가 계정 B의 버킷에 접근하려면 ① A 계정의 신원 기반 정책이 Allow 그리고 ② B 계정의 리소스 기반 정책이 A를 Allow — 둘 다 필요하다(같은 계정 내에서는 둘 중 하나만 있어도 됨). 이 "양쪽 모두" 규칙이 cross-account 시나리오의 단골 함정이다.
추상적인 알고리즘을 구체적 상황에 대보자.
상황 1: 개발자에게 admin 권한이 있는데 특정 S3 버킷만 막고 싶다.
신원 기반 정책은 AdministratorAccess라 모든 것을 Allow한다. 여기에 명시적 Deny statement를 추가하면(예: 특정 버킷 ARN에 "Effect": "Deny") 명시적 Deny가 Allow를 이겨서 그 버킷만 차단된다. SCP에도 같은 Deny를 넣으면 계정 전체에 강제된다.
{
"Effect": "Deny",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::sensitive-prod-bucket",
"arn:aws:s3:::sensitive-prod-bucket/*"
]
}상황 2: SCP에서 ec2:*를 명시적으로 Deny했는데 사용자에게 AmazonEC2FullAccess가 있다.
결과는 Deny. SCP의 명시적 Deny가 모든 것을 이긴다. 사용자 정책의 Allow는 무력화된다. 이것이 SCP를 거버넌스 가드레일로 쓰는 이유다.
🎯 시나리오: "한 보안팀이 모든 계정에서 CloudTrail 비활성화를 막고 싶다"는 요구. 정답은 사용자별 정책 수정이 아니라 SCP에
cloudtrail:StopLogging·cloudtrail:DeleteTrailDeny를 OU에 적용하는 것. 이러면 그 계정의 admin이나 root조차 CloudTrail을 끌 수 없다(management 계정 root는 SCP 예외). 명시적 Deny가 모든 Allow를 이기는 원리의 거버넌스 응용이다.
평가 로직을 머리로만 따라가면 실수한다. AWS는 검증 도구를 제공한다.
# IAM Policy Simulator: 특정 주체가 특정 행위를 할 수 있는지 시뮬레이션
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::111122223333:user/alice \
--action-names s3:GetObject s3:DeleteObject \
--resource-arns arn:aws:s3:::my-bucket/secret.txt
# 현재 호출자가 누구인지 (역할 디버깅의 시작점)
aws sts get-caller-identity
# 사용자에게 붙은 정책 목록 확인
aws iam list-attached-user-policies --user-name alice
aws iam list-user-policies --user-name alice # 인라인 정책📚 사례: 운영 환경에서 "왜 접근이 거부되는가"의 80%는 ① 암묵적 Deny(아무도 Allow 안 함) ② SCP의 숨은 Deny ③ Permissions Boundary 초과 ④ cross-account에서 한쪽만 허용 — 이 네 가지다.
aws sts get-caller-identity로 "내가 지금 어떤 역할인지" 먼저 확인하는 습관이 디버깅 시간을 절반으로 줄인다. CloudTrail의AccessDenied이벤트에는 어떤 정책 때문인지 단서가 남는다.
IAM Access Analyzer는 리소스 정책을 분석해 계정·조직 경계 밖으로 접근을 허용하는 리소스를 자동으로 찾아낸다. S3 버킷, IAM 역할, KMS 키, Lambda 함수 등이 외부 principal에게 열려 있으면 finding을 생성한다.
이것은 도메인 2(탐지)와 도메인 4(IAM)의 교차점이다. 수백 개의 버킷·역할을 사람이 일일이 검토할 수 없으니, "신뢰 경계를 넘는 접근"을 자동으로 탐지하는 것이다. 정책 생성(policy generation) 기능은 CloudTrail 로그를 분석해 실제 사용된 권한만으로 최소 권한 정책을 만들어주기도 한다.
💡 관련 이론: Access Analyzer의 핵심은 외부(external) vs 신뢰(trusted) 구분이다. 같은 계정·조직 내 접근은 정상으로 보고, 그 경계를 넘는 것만 finding으로 올린다. "조직 영역(zone of trust)을 organization으로 설정"하면 조직 내 cross-account는 정상 처리되고 진짜 외부 노출만 잡힌다.
오늘의 핵심 세 가지. 첫째, IAM의 빌딩블록 중 Role(임시 자격증명)을 기본으로 쓰고 User(장기 키)는 피한다 — 이것이 모든 모범 사례의 출발점이다. 둘째, 정책 평가는 암묵적 Deny → 명시적 Allow가 뒤집음 → 명시적 Deny가 모든 것을 이김이라는 3원칙의 알고리즘이고, SCP·Permissions Boundary는 권한을 주지 않고 상한선만 긋는다. 셋째, cross-account는 양쪽 계정 모두의 허용이 필요하다.
내일은 정책을 더 깊이 판다. Identity vs Resource 정책의 미묘한 상호작용, Condition 키를 이용한 정밀 통제, 그리고 Permissions Boundary로 최소 권한을 설계하는 실전 패턴을 다룬다. 오늘 배운 평가 알고리즘이 그 모든 것의 토대가 된다.