IAM 정책과 평가 로직 (사용자, 역할, 정책 평가 순서)
IAM 사용자와 역할, 자격 증명 기반 정책과 리소스 기반 정책, 권한 경계와 세션 정책, SCP 와 RCP 가 한 요청에 어떤 순서로 적용되는지 그림으로 정리하고, 최소 권한을 만드는 실무 도구(Access Analyzer, IAM Policy Autopilot)를 소개합니다.
IAM 의 등장인물
IAM(Identity and Access Management)은 "누가(주체) 무엇을(액션) 어디에(리소스) 어떤 조건에서" 할 수 있는지를 정합니다. 모든 AWS API 호출은 IAM 평가를 거칩니다.
| 주체 | 자격 증명 | 언제 쓰나 |
|---|---|---|
| 루트 사용자 | 이메일과 비밀번호 | 계정 해지, 일부 결제 설정 같은 소수 작업만. 평소엔 잠가 둠 |
| IAM 사용자 | 장기 액세스 키, 비밀번호 | 되도록 쓰지 않음. 외부 시스템이 키만 받을 수 있을 때 예외적으로 |
| IAM 역할 | STS 가 주는 임시 자격 증명 | EC2, Lambda, ECS 태스크, 다른 계정, 외부 IdP 연동, 사람의 로그인 |
| 페더레이션 사용자 | IdP 토큰을 역할로 교환 | IAM Identity Center, SAML, OIDC(GitHub Actions 등) |
팁 사람은 IAM Identity Center로, 워크로드는 역할로 들어오게 하면 장기 액세스 키가 거의 필요 없습니다. 키가 없으면 유출될 키도 없습니다.
정책의 종류
| 정책 | 어디에 붙나 | 하는 일 |
|---|---|---|
| 자격 증명 기반 정책 | 사용자, 그룹, 역할 | 주체가 할 수 있는 일을 허용 |
| 리소스 기반 정책 | S3 버킷, KMS 키, SQS, Lambda 등 | 리소스에 누가 접근할 수 있는지 허용. 다른 계정 주체를 지정 가능 |
| 권한 경계 (permissions boundary) | 사용자, 역할 | 자격 증명 정책이 줄 수 있는 최대치 |
| 세션 정책 | AssumeRole 호출 때 넘김 | 그 세션에서만 더 좁힘 |
| SCP | Organizations 의 OU, 계정 | 계정 안 주체가 받을 수 있는 최대치 |
| RCP | Organizations 의 OU, 계정 | 계정 안 리소스에 허용될 수 있는 최대치 |
SCP 와 RCP 는 권한을 주지 않습니다. 상한선만 정합니다. SCP 에서 s3:* 를 허용해도 역할에 정책이 없으면 아무것도 못 합니다.
정책 평가 순서
한 요청이 들어오면 IAM 은 관련된 모든 정책을 모아 다음 순서로 판단합니다.
- 어느 정책이든 명시적 Deny 가 맞으면 끝입니다. 거부.
- RCP 와 SCP 가 이 요청을 허용하지 않으면 거부. (관리 계정의 주체에는 SCP 가 적용되지 않습니다.)
- 같은 계정 안이면 리소스 기반 정책 또는 자격 증명 기반 정책 중 하나가 허용하면 다음 단계로 갑니다.
- 권한 경계와 세션 정책이 있으면 그 범위 안이어야 합니다.
- 아무것도 허용하지 않았다면 암묵적 거부입니다.
다른 계정의 리소스에 접근할 때는 조건이 더 엄격합니다. 호출하는 쪽의 자격 증명 정책과 리소스 쪽의 리소스 기반 정책이 둘 다 허용해야 합니다. 다른 계정 S3 에 쓸 때 버킷 정책만 열고 역할 정책을 빼먹어서 403 이 나는 일이 흔합니다. 자세한 추적법은 AccessDenied 트러블슈팅에 있습니다.
정책 문법 핵심
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadAppBucket",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::app-data-prod", "arn:aws:s3:::app-data-prod/*"],
"Condition": { "StringEquals": { "aws:PrincipalOrgID": "o-abc123example" } }
},
{
"Sid": "DenyWithoutTLS",
"Effect": "Deny",
"Action": "s3:*",
"Resource": ["arn:aws:s3:::app-data-prod", "arn:aws:s3:::app-data-prod/*"],
"Condition": { "Bool": { "aws:SecureTransport": "false" } }
}
]
}ListBucket은 버킷 ARN,GetObject는버킷/*객체 ARN 에 걸립니다. 둘을 섞으면 조용히 안 맞습니다.- 자주 쓰는 전역 조건 키:
aws:PrincipalOrgID(우리 조직 주체만),aws:SourceVpce(특정 VPC 엔드포인트 경유만),aws:RequestedRegion(리전 제한),aws:PrincipalTag/...(ABAC),aws:SecureTransport. NotAction,NotResource는 Allow 와 함께 쓰면 생각보다 넓게 열립니다. Deny 와 짝지어 쓰는 것이 안전합니다.
역할 위임 (AssumeRole)
역할에는 두 개의 정책이 붙습니다. 신뢰 정책(누가 이 역할을 맡을 수 있나)과 권한 정책(맡은 뒤 무엇을 할 수 있나)입니다.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::111122223333:oidc-provider/token.actions.githubusercontent.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" },
"StringLike": { "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main" }
}
}]
}GitHub Actions 같은 CI 는 이렇게 OIDC 로 역할을 맡게 하면 저장소에 액세스 키를 넣을 필요가 없습니다. sub 조건을 빼면 아무 저장소나 이 역할을 맡을 수 있으니 반드시 저장소와 브랜치까지 좁힙니다.
다른 회사(SaaS 벤더)에 역할을 내줄 때는 sts:ExternalId 조건으로 혼동된 대리인(confused deputy) 문제를 막습니다. 반대로 AWS 의 신원을 외부 서비스로 내보내야 할 때는 2025-11 에 나온 IAM 아웃바운드 아이덴티티 페더레이션으로 AWS 가 서명한 JWT 를 받아 외부 서비스에 제시할 수 있습니다.
최소 권한 만들기
처음부터 완벽한 최소 권한 정책을 쓰기는 어렵습니다. 넓게 시작해 좁히는 도구를 씁니다.
| 도구 | 하는 일 |
|---|---|
| IAM Access Analyzer 외부 접근 분석 | 조직 밖에서 접근 가능한 버킷, 역할, 키를 찾음 |
| Access Analyzer 미사용 접근 분석 | 90 일 동안 안 쓴 역할, 키, 권한을 찾음 (유료) |
| Access Analyzer 정책 생성 | CloudTrail 기록으로 실제 쓴 액션만 담은 정책 초안 생성 |
| 정책 검증, 사용자 지정 정책 검사 | 문법 오류, 너무 넓은 권한, 금지한 액션 포함 여부를 CI 에서 검사 |
| 마지막 액세스 정보 | 서비스별로 마지막 사용 시각을 보여 줘서 안 쓰는 서비스 권한을 지움 |
| IAM Policy Autopilot | re:Invent 2025 에 공개된 오픈 소스 MCP 서버. 코드를 읽고 필요한 IAM 정책 초안을 만들어 줌 |
할당량과 숫자
- 관리형 정책 문서 크기는 6,144 자, 역할 하나에 붙일 수 있는 관리형 정책은 기본 10 개(최대 20 개로 늘릴 수 있음)입니다.
- 역할 세션의 최대 시간은 기본 1 시간이고 역할 설정에서 최대 12 시간까지 늘릴 수 있습니다. 역할 체이닝(역할로 다른 역할 맡기)은 1 시간이 한계입니다.
- IAM 변경은 전 세계로 퍼지는 데 짧은 지연이 있습니다. 정책을 고친 직후의 AccessDenied 는 몇 초 뒤 다시 시도해 봅니다.
자주 하는 실수
"Resource": "*"와"Action": "*"를 개발 편의로 붙이고 잊음. Access Analyzer 의 미사용 접근 결과를 주기적으로 봅니다.- 루트 액세스 키 생성. 만들 이유가 거의 없습니다. 조직 단위로는 루트 자격 증명 중앙 관리로 멤버 계정의 루트 비밀번호 자체를 없앨 수 있습니다.
- 권한 경계를 위임 없이 씀. 경계는 "개발자가 스스로 역할을 만들되 정해진 범위를 넘지 못하게" 할 때 진가가 있습니다.
iam:CreateRole에iam:PermissionsBoundary조건을 걸어야 의미가 있습니다. - Deny 문에
aws:PrincipalArn예외를 넣지 않음. 파이프라인 역할까지 막혀서 배포가 멈춥니다.