- Published on
AWS IAM AssumeRole AccessDenied 해결 체크리스트
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서로 다른 AWS 계정/서비스 간 권한 위임을 할 때 sts:AssumeRole은 사실상 표준 패턴입니다. 그런데 운영에서 가장 자주 마주치는 에러도 AccessDenied입니다. 이유는 단순히 “권한이 없다”가 아니라, 호출 주체 정책, 대상 Role의 신뢰 정책(Trust Policy), 세션에 걸린 조건(ExternalId, MFA, 태그, SourceIdentity), Permission Boundary, Organizations SCP, 세션 정책(Session Policy) 등 여러 레이어 중 하나라도 어긋나면 동일한 메시지로 실패하기 때문입니다.
이 글은 AssumeRole AccessDenied를 빠르게 좁혀가기 위한 실전 체크리스트와, 바로 복사해서 쓸 수 있는 CLI 진단 예제를 제공합니다.
1) 에러 메시지부터 분해하기
대표적인 실패 메시지는 다음과 같습니다.
User: arn:aws:iam::111111111111:user/alice is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::222222222222:role/TargetRoleAccessDenied: Not authorized to perform sts:AssumeRoleAccessDeniedException(서비스별 래핑)
여기서 핵심은 누가(Caller) 어떤 리소스(Role ARN)에 대해 sts:AssumeRole을 호출했는지입니다.
- Caller가 IAM User인지, Role인지, SSO 세션인지 확인
- 대상 Role ARN이 맞는지 확인(계정, 경로, 이름)
빠른 확인: 현재 호출 주체 확인
aws sts get-caller-identity
출력의 Arn이 다음 중 무엇인지 분류합니다.
arn:aws:iam::...:user/...(IAM User)arn:aws:sts::...:assumed-role/RoleName/SessionName(이미 AssumeRole된 세션)arn:aws:sts::...:federated-user/...(연동 사용자)
이 분류만으로도 다음 체크 포인트(신뢰 정책에서 Principal을 무엇으로 열어야 하는지)가 결정됩니다.
2) 체크리스트 A: Caller 측 정책에 sts:AssumeRole이 있는가
AssumeRole은 “대상 Role의 Trust Policy가 열려 있으면 끝”이 아닙니다. Caller(현재 자격 증명)의 IAM 정책이 sts:AssumeRole을 허용해야 합니다.
Caller 정책 최소 예시
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::222222222222:role/TargetRole"
}
]
}
자주 놓치는 포인트
Resource를*로 두면 되긴 하지만, 조직 정책/SCP/Boundary에서 막힐 수 있어 디버깅이 어려워집니다. 우선은 정확한 Role ARN으로 좁혀보세요.sts:AssumeRole이 아니라sts:AssumeRoleWithSAML,sts:AssumeRoleWithWebIdentity가 필요한 케이스(SSO, OIDC, EKS IRSA 등)인지 확인합니다.
정책 시뮬레이터로 Caller 권한 확인
CLI만으로도 빠르게 “Caller가 이 액션을 할 수 있는지”를 점검할 수 있습니다.
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::111111111111:user/alice \
--action-names sts:AssumeRole \
--resource-arns arn:aws:iam::222222222222:role/TargetRole
결과가 allowed가 아니면, Caller 정책/그룹/권한 경계/세션 정책 등을 더 봐야 합니다.
3) 체크리스트 B: 대상 Role의 Trust Policy(AssumeRolePolicyDocument)가 Caller를 신뢰하는가
대상 Role에는 “누가 이 Role을 Assume할 수 있는가”를 정의하는 Trust Policy가 있습니다. 여기서 Principal 또는 조건이 맞지 않으면 AccessDenied가 발생합니다.
계정 간 AssumeRole Trust Policy 예시
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:root"
},
"Action": "sts:AssumeRole"
}
]
}
위 예시는 1111 계정 내 “어떤 IAM 주체든” Caller 측 정책만 있으면 Assume 가능해집니다. 더 안전하게 하려면 root 대신 특정 Role/User ARN으로 좁힙니다.
특정 Role만 허용하는 Trust Policy 예시
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/CallerRole"
},
"Action": "sts:AssumeRole"
}
]
}
흔한 실수 패턴
- Caller가
assumed-role세션 ARN인데, Trust Policy에 그 세션 ARN을 넣는 실수- Trust Policy의
Principal은 보통arn:aws:iam::...:role/RoleName형태여야 합니다.
- Trust Policy의
- 경로가 있는 Role인데 ARN을
role/Name으로만 적어 실제 ARN과 불일치 Action이sts:AssumeRole이 아닌 다른 것으로 되어 있음
Trust Policy 확인 커맨드
aws iam get-role \
--role-name TargetRole \
--query 'Role.AssumeRolePolicyDocument'
출력된 문서에서 Principal, Action, Condition을 집중적으로 봅니다.
4) 체크리스트 C: Trust Policy의 Condition(ExternalId, MFA, SourceIdentity, 태그) 불일치
Principal이 맞아도 Condition에서 막히는 경우가 많습니다. 특히 외부 벤더 연동, CI/CD, 크로스 계정 자동화에서 빈번합니다.
ExternalId 조건이 있는 경우
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111111111111:role/CallerRole" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "my-external-id"
}
}
}
이때 호출은 반드시 --external-id를 포함해야 합니다.
aws sts assume-role \
--role-arn arn:aws:iam::222222222222:role/TargetRole \
--role-session-name debug-session \
--external-id my-external-id
MFA 조건이 있는 경우
Trust Policy 또는 Caller 정책에 MFA 조건이 걸려 있으면, MFA 없이 호출 시 실패합니다.
aws:MultiFactorAuthPresent조건 확인- 필요 시
GetSessionToken또는 MFA 기반 세션 발급 흐름 점검
Session Tag 조건이 있는 경우
최근에는 ABAC로 sts:TagSession을 활용하는데, 태그 키/값이 조건과 다르면 거절됩니다.
- Trust Policy에
aws:RequestTag/Key또는sts:TransitiveTagKeys조건이 있는지 확인 - Caller 정책에
sts:TagSession허용이 있는지 확인
5) 체크리스트 D: Permission Boundary, SCP, 세션 정책이 “허용을 덮어쓰며” 막는가
AssumeRole은 IAM의 여러 “상한선” 정책에 의해 차단될 수 있습니다.
5-1) Permission Boundary
Caller가 Role인 경우, 그 Role에 Permission Boundary가 붙어 있으면 sts:AssumeRole이 경계 밖일 수 있습니다.
확인:
aws iam get-role --role-name CallerRole \
--query 'Role.PermissionsBoundary'
Boundary 정책 문서에 sts:AssumeRole이 허용 범위에 포함되는지 확인합니다.
5-2) AWS Organizations SCP
계정에 SCP가 적용되어 sts:AssumeRole 또는 특정 Role ARN에 대한 접근이 차단될 수 있습니다.
- 관리 계정(또는 권한 있는 계정)에서 SCP 확인
- 특히
Deny는 어떤Allow보다 우선합니다
5-3) 세션 정책(Session Policy)
AssumeRole 호출 시 --policy로 세션 정책을 붙이면, 결과 권한이 더 좁아집니다. 이 자체가 AssumeRole을 막는 건 아니지만, 이후 API 호출이 AccessDenied로 이어져 “AssumeRole이 안 된 것처럼” 오해하는 케이스가 있습니다.
진단 팁: AssumeRole 결과로 받은 자격 증명으로 aws sts get-caller-identity가 되는지 먼저 확인하세요.
6) 체크리스트 E: 동일 계정인데도 AccessDenied가 나는 케이스
같은 계정 내 Role Assume인데도 실패한다면 보통 다음입니다.
- Caller 정책에
sts:AssumeRole누락 - 대상 Role Trust Policy에서 Principal을 잘못 지정
iam:PassRole과 혼동
iam:PassRole은 ECS/Lambda/Step Functions 등 서비스에 Role을 “넘겨주는” 권한이고, sts:AssumeRole과는 별개입니다. 서비스 실행 역할 문제를 AssumeRole 문제로 착각하는 경우가 많습니다.
7) CloudTrail로 “어디서 거절됐는지” 확정하기
가장 확실한 방법은 CloudTrail 이벤트에서 실패 원인을 보는 것입니다.
- 이벤트 이름:
AssumeRole - 에러 코드:
AccessDenied userIdentity와requestParameters.roleArn확인additionalEventData또는errorMessage에 조건 불일치 힌트가 나오는 경우가 있음
CloudTrail Lake나 Athena를 쓰면 더 좋지만, 콘솔에서도 이벤트 히스토리로 1차 확인이 가능합니다.
운영에서 원인 추적이 길어질 때는 “로그 기반으로 좁혀가는 루틴”이 중요합니다. 비슷한 방식으로 다른 장애도 로그와 조건을 분해해 접근하면 빨리 해결됩니다. 예를 들어 에이전트가 갑자기 연결이 끊길 때도 원인 후보를 체크리스트로 줄이는 게 효과적입니다: Jenkins 에이전트 오프라인 원인·복구 10분
8) 재현용 디버깅 시나리오: CLI로 단계별 확인
아래 순서대로 실행하면 “AssumeRole 자체가 막혔는지”와 “AssumeRole은 됐는데 이후 권한이 없는지”를 분리할 수 있습니다.
8-1) AssumeRole 시도
aws sts assume-role \
--role-arn arn:aws:iam::222222222222:role/TargetRole \
--role-session-name debug-session
여기서 AccessDenied면 Caller 정책/Trust Policy/조건/SCP/Boundary 중 하나입니다.
8-2) 성공 시 임시 자격 증명으로 신원 확인
export AWS_ACCESS_KEY_ID='ASIA...'
export AWS_SECRET_ACCESS_KEY='...'
export AWS_SESSION_TOKEN='...'
aws sts get-caller-identity
여기까지 성공하면 AssumeRole은 성공입니다. 이후 특정 API가 실패하면 대상 Role의 권한 정책(Identity Policy)을 점검해야 합니다.
8-3) 대상 Role 권한 정책 확인
aws iam list-attached-role-policies --role-name TargetRole
aws iam list-role-policies --role-name TargetRole
필요한 서비스 액션이 허용되어 있는지 확인합니다.
9) 자주 나오는 원인 TOP 10 요약 체크리스트
- Caller 정책에
sts:AssumeRole이 없다 - Caller 정책의
Resource가 대상 Role ARN과 불일치 - 대상 Role Trust Policy의
Principal이 잘못됐다(세션 ARN을 넣는 등) - Trust Policy에
ExternalId조건이 있는데 요청에--external-id가 없다/다르다 - MFA 조건이 있는데 MFA 세션이 아니다
- Session Tag 조건이 있는데 태그 키/값 또는
sts:TagSession권한이 없다 - Permission Boundary가
sts:AssumeRole을 허용하지 않는다 - Organizations SCP에서
Deny로 막고 있다 - SSO/OIDC 환경인데
AssumeRoleWithWebIdentity등 다른 플로우가 필요하다 - AssumeRole은 성공했는데 이후 API 권한 부족을 AssumeRole 실패로 오해했다
10) 안전한 권한 설계 팁(운영 관점)
- Trust Policy는 가능한 한
arn:aws:iam::...:root대신 특정 Role로 제한 - 외부 연동은
sts:ExternalId를 기본으로 사용 - ABAC를 쓴다면 태그 조건과
sts:TagSession권한을 세트로 설계 - 디버깅을 위해 일시적으로만 범위를 넓히고, 원인 확정 후 즉시 최소 권한으로 회귀
네트워크/런타임 오류처럼 보이는 문제도 실제로는 “정책 조건 불일치”인 경우가 많습니다. 호출이 실패할 때는 타임아웃/연결 끊김과 함께 나타나기도 하므로, 애플리케이션 레벨 에러와 IAM 에러를 분리해서 보는 습관이 도움이 됩니다: Node.js fetch ECONNRESET·ETIMEDOUT 해결법
부록: Trust Policy와 권한 정책을 동시에 점검하는 최소 템플릿
아래 조합을 기준으로 “Caller는 Assume 가능, Target은 S3 읽기만 가능” 같은 형태로 단계적으로 확장하면 AccessDenied를 빠르게 제거할 수 있습니다.
Caller 정책
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::222222222222:role/TargetRole"
}
]
}
Target Role Trust Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/CallerRole"
},
"Action": "sts:AssumeRole"
}
]
}
Target Role 권한 정책(예: S3 List만)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets"
],
"Resource": "*"
}
]
}
이 템플릿에서 시작해, 조건(ExternalId/MFA/태그)과 리소스 범위를 하나씩 추가하면 “어느 순간부터 깨지는지”가 명확해집니다.