- Published on
EKS에서 Pod는 되는데 SQS만 403 뜰 때
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버는 살아 있고 Pod도 잘 뜨며, 다른 AWS API는 되는데 SQS 호출만 403이 뜨는 상황은 생각보다 흔합니다. 특히 EKS에서는 노드 IAM, IRSA(서비스어카운트 역할), VPC Endpoint(PrivateLink), SDK 기본 리전/자격 증명 체인 같은 요소가 섞이면서 “네트워크는 되는데 권한만 막히는” 형태로 나타납니다.
이 글에서는 403을 정확히 분류하고, EKS 환경에서 재현 가능한 진단 커맨드와 함께 가장 흔한 원인(그리고 놓치기 쉬운 원인)을 우선순위대로 정리합니다.
> 참고로 DNS/HTTPS 자체가 깨진 케이스(예: DNS는 되는데 TLS만 실패)는 403이 아니라 타임아웃/핸드셰이크 에러가 많습니다. 네트워크 계층 점검이 필요하면 EKS Pod DNS는 되는데 HTTPS만 실패할 때 점검도 함께 보세요.
1) 먼저 403을 “어떤 403인지” 분류하기
SQS 403이라도 메시지가 다릅니다. 아래 중 무엇인지부터 확인하세요.
(A) AccessDenied / AuthorizationError
AccessDeniedUser: arn:aws:sts::... is not authorized to perform: sqs:SendMessage on resource: ...
→ 순수 IAM/정책 문제일 확률이 가장 큽니다(IRSA, 노드 role, 리소스 정책).
(B) InvalidSignatureException / SignatureDoesNotMatch
InvalidSignatureExceptionSignatureDoesNotMatch
→ 리전/엔드포인트/시간/프록시 문제로 서명 검증이 실패하는 케이스가 많습니다.
(C) MissingAuthenticationToken / The security token included in the request is invalid
- 자격 증명 체인이 엉켜서 엉뚱한 크리덴셜을 쓰거나, 만료/잘못된 토큰 사용.
(D) 403이지만 CloudTrail에 안 찍힘
- VPC Endpoint(Interface endpoint) 경유 시 Endpoint policy에서 차단되거나,
- 애초에 AWS까지 요청이 제대로 가지 않고(프록시/게이트웨이), 중간에서 403을 반환할 수도 있습니다.
2) “지금 Pod가 어떤 IAM으로 호출하는지”부터 확정하기
EKS에서 가장 흔한 함정은 내가 IRSA를 쓴다고 믿지만 실제로는 노드 IAM(또는 다른 자격 증명)을 쓰는 상황입니다.
2.1 Pod 안에서 STS로 호출 주체 확인
다음은 디버그용 임시 Pod에서 실행하기 좋습니다.
kubectl run -it --rm aws-debug \
--image=public.ecr.aws/aws-cli/aws-cli:2.15.0 \
--restart=Never -- sh
# Pod 내부
aws sts get-caller-identity
aws configure list
env | grep -E 'AWS_|KUBERNETES' | sort
get-caller-identity결과 ARN이 예상한 IRSA role인지 확인하세요.AWS_WEB_IDENTITY_TOKEN_FILE,AWS_ROLE_ARN이 있어야 IRSA 경로가 정상입니다.
2.2 서비스어카운트에 role-arn 주석이 붙었는지
kubectl get sa -n <namespace> <serviceaccount> -o yaml
정상 예:
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/my-sqs-role
Pod spec에서 serviceAccountName이 올바른지도 확인합니다.
kubectl get pod -n <namespace> <pod> -o jsonpath='{.spec.serviceAccountName}'; echo
3) IRSA(AssumeRoleWithWebIdentity) 신뢰 정책이 틀린 경우
IRSA는 “K8s 서비스어카운트 토큰”을 OIDC로 검증해 STS AssumeRoleWithWebIdentity를 합니다. 여기서 가장 많이 틀리는 지점은 trust policy의 subject 조건입니다.
3.1 IAM Role trust policy 예시(정상)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/oidc.eks.<REGION>.amazonaws.com/id/<OIDC_ID>"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.<REGION>.amazonaws.com/id/<OIDC_ID>:sub": "system:serviceaccount:<namespace>:<serviceaccount>",
"oidc.eks.<REGION>.amazonaws.com/id/<OIDC_ID>:aud": "sts.amazonaws.com"
}
}
}
]
}
sub가 네임스페이스/SA와 정확히 일치해야 합니다.aud가sts.amazonaws.com이어야 하는데 누락되거나 다른 값이면 STS가 거부할 수 있습니다.
3.2 증상
- Pod는 뜨지만 SQS만 403 또는 토큰 관련 에러
aws sts get-caller-identity가 노드 role로 나오거나 아예 실패
4) SQS 권한(Identity policy)과 리소스 정책(Queue policy) 동시 점검
SQS는 호출자 IAM 정책뿐 아니라, 경우에 따라 **Queue policy(리소스 기반 정책)**에서도 막힙니다.
4.1 최소 권한 예시(Identity policy)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:SendMessage",
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueUrl",
"sqs:GetQueueAttributes"
],
"Resource": "arn:aws:sqs:<REGION>:<ACCOUNT_ID>:<QUEUE_NAME>"
}
]
}
Resource에 큐 ARN이 정확한지(리전/계정/이름) 확인합니다.- FIFO라면 큐 이름이
.fifo로 끝나야 합니다.
4.2 Queue policy에서 특정 VPC Endpoint만 허용하는 경우
조직에서 PrivateLink를 강제하면서 Queue policy에 조건을 걸어두면, 같은 계정 role이라도 경로가 다르면 403이 납니다.
대표 조건:
aws:SourceVpceaws:PrincipalArn
Queue policy 예시(특정 VPCE만 허용):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowOnlyFromSpecificVPCE",
"Effect": "Allow",
"Principal": "*",
"Action": "sqs:*",
"Resource": "arn:aws:sqs:<REGION>:<ACCOUNT_ID>:<QUEUE_NAME>",
"Condition": {
"StringEquals": {
"aws:SourceVpce": "vpce-0abc123def4567890"
}
}
}
]
}
이 경우 Pod가 NAT로 퍼블릭 SQS 엔드포인트로 나가면 차단될 수 있습니다.
5) VPC Interface Endpoint(SQS PrivateLink) 정책에서 막히는 경우
EKS가 프라이빗 서브넷이고 SQS를 VPC Endpoint로 붙였다면, Endpoint policy가 “허용 목록” 방식으로 설정되어 403을 만들 수 있습니다.
5.1 확인 포인트
- SQS Interface Endpoint가 생성되어 있고, Pod 트래픽이 그쪽으로 라우팅되는지
- Endpoint policy가
sqs:SendMessage등을 허용하는지
Endpoint policy가 너무 좁으면 같은 role로도 특정 큐/액션이 거부됩니다.
6) 리전/엔드포인트 미스매치로 서명 실패(InvalidSignature)
SQS는 리전 종속 서비스입니다. 다음 실수가 자주 나옵니다.
- 큐 URL은
ap-northeast-2인데 SDK는 기본 리전을us-east-1로 잡음 AWS_REGION/AWS_DEFAULT_REGION미설정- 멀티리전 환경에서 ConfigMap/Secret이 다른 리전 값을 주입
6.1 Pod 내부에서 리전 확인
echo $AWS_REGION
echo $AWS_DEFAULT_REGION
aws configure get region
6.2 큐 URL로 리전 역추적
큐 URL 예:
https://sqs.ap-northeast-2.amazonaws.com/123456789012/my-queue
리전은 ap-northeast-2입니다. SDK 클라이언트도 동일 리전으로 생성해야 합니다.
(Python boto3) 리전 고정 예시
import boto3
sqs = boto3.client("sqs", region_name="ap-northeast-2")
resp = sqs.get_queue_attributes(
QueueUrl="https://sqs.ap-northeast-2.amazonaws.com/123456789012/my-queue",
AttributeNames=["QueueArn"]
)
print(resp)
7) 시간 드리프트로 서명 검증 실패(특히 노드/런타임 이슈)
SigV4는 요청 시각이 크게 틀리면 거부합니다. 보통은 NTP로 해결되지만, 일부 환경에서는 노드 시간 동기화가 깨져 Signature expired 류가 뜰 수 있습니다.
Pod에서 직접 시간을 바꾸긴 어렵기 때문에 노드 레벨에서 확인합니다.
# 노드에 SSM/SSH 접근이 가능하다면
chronyc tracking || true
date -u
시간 이슈는 SQS만이 아니라 다른 AWS API도 같이 흔들리는 경우가 많지만, 호출 패턴/재시도 차이로 “SQS에서만 먼저” 터져 보이기도 합니다.
8) SDK 자격 증명 체인 충돌: IRSA vs 정적 키 vs 다른 프로바이더
컨테이너 이미지나 Helm chart에 다음이 섞이면, 의도치 않게 정적 키를 우선 사용해 403이 날 수 있습니다.
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY가 남아 있음~/.aws/credentials가 이미지에 포함(안티패턴)AWS_PROFILE설정
8.1 진단
env | grep -E 'AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|AWS_SESSION_TOKEN|AWS_PROFILE'
IRSA를 쓰는 Pod라면 정적 키 환경변수는 없어야 안전합니다.
9) CloudTrail로 “누가/어떤 액션을/왜 거부당했는지” 역추적
403(AccessDenied)이면 CloudTrail에 남는 경우가 많습니다(단, 엔드포인트/중간 차단은 예외).
- Event source:
sqs.amazonaws.com - Error code:
AccessDenied - User identity: assumed role ARN
CloudTrail에서 확인할 핵심:
- 실제 호출 principal이 내가 생각한 IRSA role인지
requestParameters.queueUrl이 맞는지recipientAccountId가 다른 계정은 아닌지
10) 빠른 체크리스트(현장에서 제일 빨리 먹히는 순서)
- Pod 안에서
aws sts get-caller-identity로 호출 주체 확정 - 서비스어카운트 annotation(
eks.amazonaws.com/role-arn) 및 Pod의serviceAccountName확인 - IRSA role trust policy의
sub,aud조건 확인 - role에 붙은 IAM policy에서
sqs:*또는 필요한 액션/리소스 ARN 확인 - Queue policy(리소스 정책)에서
aws:SourceVpce/PrincipalArn조건 확인 - VPC Endpoint 사용 시 endpoint policy 확인
- 리전/큐 URL/SDK region 일치 여부 확인
- 정적 키 환경변수/프로파일 충돌 제거
부록) kubectl로 서비스어카운트/Pod 설정 한 번에 보기
# Pod가 어떤 SA를 쓰는지
kubectl get pod -n <ns> <pod> -o jsonpath='{.spec.serviceAccountName}'; echo
# SA에 role-arn이 붙었는지
kubectl get sa -n <ns> <sa> -o jsonpath='{.metadata.annotations.eks\.amazonaws\.com/role-arn}'; echo
# Pod에 AWS_* 환경변수 주입 여부(대략 확인)
kubectl exec -n <ns> <pod> -- sh -c 'env | grep -E "^AWS_" | sort'
마무리
EKS에서 “Pod는 정상인데 SQS만 403”은 대개 (1) IRSA가 실제로 적용되지 않았거나, (2) SQS 리소스/엔드포인트 정책이 경로를 제한하거나, (3) 리전/서명 조건이 어긋난 경우로 좁혀집니다. 핵심은 감으로 정책을 만지기 전에, Pod 내부에서 get-caller-identity로 실제 호출 주체를 확정하고, CloudTrail/정책(Identity + Resource + Endpoint)을 차례대로 대조하는 것입니다.
추가로 EKS 네트워크/엔드포인트 경로 이슈가 의심된다면, 같은 결의 점검 흐름을 정리한 글인 EKS Pod DNS는 되는데 HTTPS만 실패할 때 점검도 함께 보면 원인 분리가 더 빨라집니다.