[AWS] IAM
AWS IAM
"누가, 무엇을, 어디에" 할 수 있는지를 결정하는 AWS의 심장부. 정책 JSON 구조부터 평가 로직, Role·AssumeRole·STS 동작 원리, Permission Boundary·SCP, Federation까지 — 어디에도 없는 완전판.
| Category | Level | Reads | Status |
|---|---|---|---|
| Cloud Computing · Security | 중급 ~ 고급 | 약 28분 | VERIFIED |
IAM(Identity and Access Management)이란?
IAM은 "누가(Identity) 무엇을(Action) 어떤 리소스에(Resource) 할 수 있는지"를 정의하고 통제하는 AWS의 인증·인가 서비스다.
AWS의 모든 API 호출 — EC2를 켜는 것도, S3 버킷을 읽는 것도, 심지어 IAM 정책 자체를 바꾸는 것도 — 은 예외 없이 IAM을 거쳐 "이 요청을 허용할 것인가"를 판정받는다. IAM을 정확히 이해하지 못하면 AWS의 다른 모든 서비스를 아무리 잘 알아도 보안 사고 한 번에 무너질 수 있다 — 실제 클라우드 침해 사고의 상당수가 기술 취약점이 아니라 잘못 설정된 IAM 권한에서 시작된다.
왜 필요한가
IAM이 없다면 계정에 접근하는 모든 사람·서비스가 루트 계정 수준의 무제한 권한을 갖거나, 반대로 권한을 아예 나눌 방법이 없다. IAM은 이 극단 사이에서 최소 권한 원칙(Principle of Least Privilege) — 딱 필요한 만큼만, 필요한 기간만 권한을 부여하는 것을 실현하는 도구다.
개발자는 개발 환경만, 재무팀은 결제 정보만, 운영팀은 프로덕션 인프라만 — 조직 내 역할에 맞게 권한을 세분화한다.
EC2가 S3를 읽어야 한다면, 그 EC2에게 딱 그 권한만 부여한다. 자격증명을 코드에 하드코딩할 필요가 없어진다.
핵심 구성요소 4가지
고정된 자격증명(비밀번호, Access Key)을 갖는 영구적인 신원. 사람이나 애플리케이션이 직접 로그인·API 호출에 사용한다.
User들을 묶는 컨테이너. Group에 Policy를 붙이면 소속된 모든 User가 그 권한을 상속받는다. Group 자체는 로그인할 수 없다.
고정 자격증명이 없는 "임시로 맡아 쓰는" 신원. 사람, 서비스, 다른 계정 누구나 조건을 만족하면 이 Role을 맡아(Assume) 그 권한을 임시로 빌려 쓸 수 있다.
"무엇을 허용/거부할지"를 정의한 JSON 문서. User·Group·Role 어디에도 붙일 수 있는 권한의 실제 내용물이다.
Policy 구조 완전 분해
모든 IAM Policy는 JSON 문서이며, 구조는 항상 같은 골격을 따른다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3ReadOnly",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"IpAddress": { "aws:SourceIp": "203.0.113.0/24" }
}
}
]
}
Allow 또는 Deny 딱 둘뿐이다. 이 값이 없는 "허용도 거부도 아닌" 상태는 존재하지 않는다.
대상 API 동작. s3:GetObject처럼 서비스:동작 형식이며, 와일드카드(s3:Get*)도 가능하다.
대상 리소스의 ARN. Identity-based Policy에서는 생략 불가하며, 어떤 리소스에 적용될지 명시해야 한다.
추가 조건. IP 대역, MFA 사용 여부, 시간대 등으로 권한을 더 세밀하게 제한한다. 실무 보안 강화의 핵심 도구다.
"누구에게"를 명시하는 필드. Identity-based Policy에는 없고(붙는 대상 자체가 Principal이므로), Resource-based Policy와 Trust Policy에서만 등장한다.
Policy 유형
| 구분 | AWS Managed | Customer Managed | Inline |
|---|---|---|---|
| 관리 주체 | AWS | 내 계정 | 내 계정 |
| 재사용 | 여러 신원에 재사용 | 여러 신원에 재사용 | 단 하나의 신원 전용 |
| 버전 관리 | AWS가 업데이트 | 직접 버전 관리(최대 5개) | 없음(즉시 반영) |
| 권장 상황 | 빠른 시작, 표준 권한 | 조직 특화 재사용 정책 | 이 신원에만 필요한 예외적 권한 |
또한 정책이 어디에 붙느냐로도 나뉜다 — User·Group·Role에 붙는 Identity-based Policy와, S3 버킷·SQS 큐 등 리소스 자체에 붙는 Resource-based Policy(대표적으로 S3 Bucket Policy)다. Resource-based Policy는 유일하게 다른 계정의 Principal을 직접 지정할 수 있어, 크로스 계정 접근의 또 다른 축이 된다.
정책 평가 로직 — 가장 중요한데 아무도 제대로 안 알려주는 것
한 사람에게 여러 정책이 동시에 적용되는 건 흔한 일이다. 여러 정책이 서로 다른 결론을 내릴 때 AWS는 정확히 다음 순서로 판정한다.
어떤 정책이든 하나라도 명시적으로 Deny하면, 다른 모든 정책이 Allow여도 최종 결과는 무조건 거부다. Deny는 항상 이긴다.
Deny가 하나도 없다면, 적용되는 모든 정책 중 하나라도 Allow하면 허용된다. 여러 정책의 Allow는 합집합으로 누적된다.
어떤 정책도 명시적으로 언급하지 않은 Action은 기본적으로 항상 거부다. AWS IAM은 "화이트리스트" 모델이다 — 허용을 명시해야만 열린다.
Allow여도, Permission Boundary나 SCP에서 Deny하면 최종 결과는 거부다. 즉 평가는 "붙어있는 모든 정책 종류(Identity-based + Resource-based + Boundary + SCP)를 통틀어" 이뤄지며, 그 안에서 Deny 최우선 원칙이 관통한다. 이 부분은 뒤(10~11장)에서 Boundary·SCP를 다룬 뒤 다시 정리한다.
Role과 AssumeRole 동작 방식
Role은 두 개의 서로 다른 정책을 동시에 갖는다는 점이 User와 결정적으로 다르다.
"누가 이 Role을 맡을 수 있는가"를 정의. Role 자체에 붙는 Resource-based Policy의 일종이며, Principal 필드로 허용할 대상(계정, 서비스, 사용자 등)을 명시한다.
"이 Role을 맡으면 무엇을 할 수 있는가"를 정의. User·Group에 붙이는 것과 동일한 형태의 Identity-based Policy다.
// Trust Policy 예시: 특정 계정의 특정 Role만 이 Role을 맡을 수 있음 { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:role/CI-Deployer" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "unique-shared-secret" } } }] }
임시 자격증명을 발급하는 AWS 서비스. AssumeRole, Federation 로그인, EC2 Instance Profile 모두 내부적으로 STS가 임시 자격증명을 찍어낸다.
크로스 계정 접근 패턴
여러 AWS 계정을 운영하는 조직(대부분의 엔터프라이즈)에서 A 계정 사용자가 B 계정 리소스에 접근하는 대표 패턴이다.
B 계정에 Role을 만들고 Trust Policy에 A 계정을 Principal로 등록. A 계정 사용자가 sts:AssumeRole로 B 계정 Role을 맡아 임시 자격증명으로 작업한다. 가장 표준적인 방식이다.
S3 Bucket Policy처럼 리소스 자체의 정책에 A 계정 Principal을 직접 허용. Role을 거치지 않고 바로 접근 가능하지만, 지원하는 리소스 타입이 제한적이다.
제3자(다른 회사 등)에게 Role을 내줄 때, Trust Policy 조건에 추가하는 공유 비밀값. "혼동된 대리인(Confused Deputy)" 공격 — 제3자가 의도치 않게 다른 고객의 리소스에 접근하게 되는 상황 — 을 막기 위한 표준 대응책이다.
EC2가 Role을 쓰는 방식 — Instance Profile
EC2에 Access Key를 박아 넣지 않고도 AWS API를 호출할 수 있는 이유가 Instance Profile이다. EC2에 Role을 연결하면, 인스턴스 내부에서 메타데이터 서비스(IMDS)를 통해 임시 자격증명을 자동으로 받아 쓴다.
// EC2 내부에서 실행하면 자동 발급된 임시 자격증명 확인 가능
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME
이 자격증명은 자동으로 주기적으로 회전(Rotate)되며, SDK·CLI가 별도 설정 없이 이를 자동으로 사용한다 — 코드에 자격증명을 하드코딩할 이유가 없어지는 근본적인 이유다.
Permission Boundary — 권한의 상한선
Permission Boundary는 "이 신원이 최대로 가질 수 있는 권한의 천장"을 정의하는 특수한 정책이다. 일반 Permission Policy와 절대 혼동하면 안 된다 — 이 둘은 AND 연산으로 결합된다.
"이 신원에게 무엇을 허용할지"를 직접 부여하는 정책.
"Permission Policy가 아무리 관대해도, 절대 이 선을 넘을 수 없다"는 상한선.
실제 유효 권한은 Permission Policy ∩ Permission Boundary(교집합)다. 예를 들어 Boundary가 S3만 허용한다면, Permission Policy에 EC2 전체 권한이 있어도 EC2 호출은 여전히 거부된다.
Permission Boundary의 대표 활용 사례. 팀 리더에게 "팀원의 IAM Role을 만들 권한"을 주되, Boundary로 "관리자 권한까지 부여하는 Role은 못 만들게" 제한해 권한 상승(Privilege Escalation)을 원천 차단한다.
SCP (Service Control Policy) — 조직 전체 가드레일
SCP는 AWS Organizations 레벨에서 계정 전체(또는 OU 단위)에 적용되는 최상위 가드레일이다. Permission Boundary가 "신원 하나"의 상한선이라면, SCP는 "계정 전체"의 상한선이다.
| 구분 | Permission Boundary | SCP |
|---|---|---|
| 적용 범위 | 개별 User/Role | 계정 전체 / OU |
| 관리 주체 | 계정 내에서 설정 | Organizations 관리 계정에서 설정 |
| 루트 계정에도 적용? | 아니오 | 예 (루트 계정도 못 벗어남) |
| Allow 자체를 부여? | 아니오(상한선만) | 아니오(상한선만) |
// SCP 예시: 특정 리전 외 모든 리전 사용 차단 { "Effect": "Deny", "Action": "*", "Resource": "*", "Condition": { "StringNotEquals": { "aws:RequestedRegion": ["ap-northeast-2"] } } }
Identity Federation
모든 사람에게 IAM User를 만들어주는 대신, 이미 있는 신원 체계(사내 AD, 구글, 소셜 로그인 등)를 그대로 활용해 AWS 권한을 위임하는 방식이다. 내부적으로는 전부 STS의 AssumeRoleWithSAML / AssumeRoleWithWebIdentity를 거친다.
사내 Active Directory, Okta 같은 기업용 IdP와 연동. 임직원이 사내 계정 그대로 로그인해 매핑된 Role로 AWS 콘솔에 접근한다.
구글, Amazon 같은 OIDC 제공자로 로그인한 사용자에게 Role을 매핑. 모바일·웹 앱에서 최종 사용자에게 임시 AWS 권한을 주는 Amazon Cognito Identity Pool이 대표적 구현체다.
구 AWS SSO. 여러 AWS 계정과 외부 SaaS 앱까지 하나의 로그인으로 통합 관리하는 AWS의 사내 SSO 서비스. Organizations와 결합해 조직 전체 접근을 중앙화한다.
MFA, Access Key, 루트 계정 보안
- 루트 계정은 MFA를 반드시 활성화하고, 일상 작업에는 절대 사용하지 않음
- 사람의 콘솔 접근은 Federation/IAM Identity Center로, User+비밀번호 최소화
- 불가피하게 Access Key를 쓴다면 정기적으로 교체(Rotate)
- 민감한 작업(삭제, 정책 변경 등)에는 Condition으로 MFA 필수화
- Access Key를 코드/저장소에 하드코딩
- 모든 사람에게
AdministratorAccess관리형 정책을 붙임 - 루트 계정으로 일상 업무 수행
- 오래된 미사용 자격증명을 방치
최소 권한을 실제로 실천하는 법
원칙은 쉽지만 "정확히 필요한 권한"을 처음부터 알기는 어렵다. AWS는 이를 돕는 도구를 제공한다.
실제 CloudTrail 활동 로그를 분석해 "이 Role이 지난 N일간 실제로 쓴 Action"만으로 최소 권한 정책 초안을 자동 생성해준다.
정책을 실제로 적용하기 전에 "이 요청이 허용될지 거부될지"를 시뮬레이션해서 미리 검증할 수 있다.
실무 순서: 처음엔 넓은 권한으로 시작 → Access Analyzer로 실제 사용 패턴 수집 → 사용된 Action만으로 정책을 좁혀 재배포 → 반복. "처음부터 완벽한 최소 권한"을 손으로 짜려 하기보다, 이렇게 점진적으로 좁혀가는 방식이 실무에서 훨씬 현실적이다.
실습 가이드 — EC2용 Role 생성부터 검증까지
"EC2가 특정 S3 버킷만 읽을 수 있게" 만드는 가장 흔한 시나리오로 처음부터 끝까지 따라가 본다.
Customer Managed Policy 생성
IAM 콘솔 → Policies → Create policy → JSON 탭에서 04장 구조를 참고해 s3:GetObject, s3:ListBucket만 특정 버킷 ARN으로 한정한 정책을 작성하고 저장(예: S3ReadOnly-MyBucket).
Role 생성 & 신뢰 대상 지정
IAM 콘솔 → Roles → Create role → Trusted entity type은 AWS service → Use case는 EC2 선택. 이 과정에서 AWS가 ec2.amazonaws.com을 Principal로 하는 Trust Policy를 자동 생성해준다.
Permission Policy 연결
1단계에서 만든 S3ReadOnly-MyBucket 정책을 검색해 체크 → Role 이름 지정(예: EC2-S3Reader-Role) → Create role.
EC2에 Role 연결
EC2 콘솔에서 인스턴스 선택 → Actions → Security → Modify IAM role → 3단계 Role 선택. 기존 실행 중인 인스턴스에도 재부팅 없이 즉시 적용된다.
검증
해당 EC2에 접속해 aws s3 ls s3://my-bucket/ 실행 — 성공해야 한다. 이어서 aws s3 rm s3://my-bucket/some-file 시도 — AccessDenied가 떠야 정상이다(쓰기 권한을 주지 않았으므로, 06장의 묵시적 Deny가 여기서 실제로 작동하는 것을 확인하는 것).
자주 하는 실수
"Action": "*", "Resource": "*"를 습관적으로 붙이는 것- User에 정책을 직접 붙이고 Group을 아예 안 쓰는 것
- Trust Policy의 Principal을
"*"(모든 계정)로 열어두는 것 - Permission Boundary·SCP 존재를 모른 채 Identity-based Policy만 붙였다 왜 안 되는지 못 찾는 것
- 장기 Access Key를 발급받아 로컬 PC나 코드에 계속 방치
- 필요한 Action·Resource를 구체적으로 명시
- Group 또는 Role 기반으로 권한을 부여
- Trust Policy는 항상 구체적인 Principal + Condition으로 좁히기
- Boundary·SCP까지 포함한 전체 평가 그림을 항상 염두에 두기
- 가능하면 항상 Role(임시 자격증명)로 대체