Published on

EKS IRSA AccessDenied 권한 꼬임 12가지 해결

Authors

서론

EKS에서 IRSA(IAM Roles for Service Accounts)는 kube2iam/kiam 같은 노드 단위 권한 공유를 피하고, 파드 단위로 최소 권한을 부여할 수 있게 해줍니다. 그런데 막상 운영에 들어가면 AccessDenied, InvalidIdentityToken, AccessDeniedException 같은 오류가 “권한이 없어서”가 아니라 “권한이 꼬여서” 발생하는 경우가 많습니다.

이 글은 IRSA에서 자주 발생하는 권한 꼬임을 12가지로 분류하고, 증상 로그, 원인, 해결 순서(무엇을 먼저 확인해야 하는지)를 실전 관점에서 정리합니다.

관련해서 S3만 403이 나는 케이스는 아래 글이 더 깊습니다.

또한 파드가 재시작하며 로그가 휘발되는 상황이라면 CrashLoop 진단을 먼저 해두면 좋습니다.

먼저: 10분 내 IRSA 상태 확인 체크리스트

IRSA 문제는 “파드가 어떤 자격 증명을 쓰고 있는가”를 먼저 고정해야 합니다.

1) 파드 환경 변수와 토큰 파일 확인

kubectl -n $NS exec -it $POD -- sh -lc 'env | egrep "AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE|AWS_REGION|AWS_DEFAULT_REGION"'
kubectl -n $NS exec -it $POD -- sh -lc 'ls -l $AWS_WEB_IDENTITY_TOKEN_FILE && head -c 80 $AWS_WEB_IDENTITY_TOKEN_FILE && echo'

AWS_ROLE_ARNAWS_WEB_IDENTITY_TOKEN_FILE 이 없다면 IRSA가 애초에 주입되지 않은 상태입니다.

2) 현재 호출자가 누구인지 확인

AWS CLI가 들어있다면 가장 빠릅니다.

aws sts get-caller-identity

CLI가 없다면 앱 로그에서 STS 호출 실패 메시지를 확인하거나, 디버그용 컨테이너를 임시로 붙이는 방식(kubectl debug)을 고려하세요.

3) ServiceAccount 어노테이션 확인

kubectl -n $NS get sa $SA -o yaml | sed -n '1,120p'

아래 어노테이션이 핵심입니다.

  • eks.amazonaws.com/role-arn: arn:aws:iam::...:role/...

1) OIDC Provider 미등록 또는 다른 클러스터 OIDC를 참조

증상

  • InvalidIdentityToken
  • No OpenIDConnect provider found in your account for ...

원인

클러스터의 OIDC issuer URL에 대응하는 IAM OIDC Provider가 계정에 등록되어 있지 않거나, 다른 EKS 클러스터에서 만든 Provider를 재사용하다가 issuer가 불일치합니다.

해결

  1. 클러스터 issuer 확인
aws eks describe-cluster --name $CLUSTER --query 'cluster.identity.oidc.issuer' --output text
  1. IAM OIDC provider 목록에서 일치 여부 확인
aws iam list-open-id-connect-providers
aws iam get-open-id-connect-provider --open-id-connect-provider-arn $ARN
  1. 없으면 생성(대표적으로 eksctl 사용)
eksctl utils associate-iam-oidc-provider --cluster $CLUSTER --approve

2) Trust Policy의 sub 조건이 ServiceAccount와 불일치

증상

  • AccessDenied (AssumeRoleWithWebIdentity 단계에서)
  • 앱 로그에 Not authorized to perform sts:AssumeRoleWithWebIdentity

원인

IRSA는 STS가 토큰의 sub 클레임을 검사합니다. 보통 system:serviceaccount:네임스페이스:서비스어카운트명 형태인데, Trust Policy의 Condition이 다른 값을 가리키면 거부됩니다.

해결

  1. ServiceAccount의 실제 네임스페이스/이름 확인
kubectl get sa -A | grep -E "^$NS\s+$SA\b"
  1. IAM Role trust policy 점검(예시)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/ABCDEFG"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.ap-northeast-2.amazonaws.com/id/ABCDEFG:sub": "system:serviceaccount:my-ns:my-sa",
          "oidc.eks.ap-northeast-2.amazonaws.com/id/ABCDEFG:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}

sub가 정확히 일치하도록 수정합니다.

3) Trust Policy에서 issuer key를 잘못 적음(경로/아이디 불일치)

증상

  • 조건이 있어 보이는데도 계속 AccessDenied

원인

StringEquals의 key는 issuer host/path가 정확히 들어가야 합니다. 흔한 실수는 아래처럼 id/XXXX가 빠지거나, 리전이 다르거나, 중간 경로가 잘못된 경우입니다.

해결

클러스터 issuer를 그대로 복사해서 ...:sub, ...:aud 키를 구성하세요.

ISSUER=$(aws eks describe-cluster --name $CLUSTER --query 'cluster.identity.oidc.issuer' --output text)
echo $ISSUER
# 예: https://oidc.eks.ap-northeast-2.amazonaws.com/id/ABCDEFG

# trust policy key에는 https:// 를 뺀 나머지가 들어갑니다.
# oidc.eks.ap-northeast-2.amazonaws.com/id/ABCDEFG:sub

4) ServiceAccount 어노테이션 누락 또는 잘못된 Role ARN

증상

  • 파드에 AWS_ROLE_ARN 이 없음
  • 혹은 존재하지만 다른 역할을 가리킴

원인

ServiceAccount에 eks.amazonaws.com/role-arn 어노테이션이 없거나 오타가 있습니다. 또는 Helm values에서 SA를 새로 만들지 않고 기존 SA를 참조하는데, 실제로는 다른 SA가 사용되는 경우도 많습니다.

해결

  1. SA에 어노테이션 추가
kubectl -n $NS annotate sa $SA eks.amazonaws.com/role-arn=arn:aws:iam::123456789012:role/my-irsa-role --overwrite
  1. Deployment가 어떤 SA를 쓰는지 확인
kubectl -n $NS get deploy $DEPLOY -o jsonpath='{.spec.template.spec.serviceAccountName}{"\n"}'
  1. 변경 후에는 파드를 재생성해야 반영됩니다.
kubectl -n $NS rollout restart deploy $DEPLOY

5) 파드가 기본 ServiceAccount를 사용 중(Helm/매니페스트 불일치)

증상

  • SA는 잘 만들어졌는데 파드는 default SA로 뜸

원인

spec.template.spec.serviceAccountName 설정 누락, Helm chart에서 serviceAccount.create/serviceAccount.name 조합 실수 등으로 파드가 의도치 않게 default SA를 사용합니다.

해결

Deployment/StatefulSet 템플릿에 SA를 명시하세요.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      serviceAccountName: my-sa
      containers:
        - name: app
          image: my-image

6) STS 엔드포인트/리전 설정 꼬임(특히 중국/정부 리전, VPC 엔드포인트)

증상

  • InvalidIdentityToken 또는 AccessDenied가 특정 네트워크/서브넷에서만 발생
  • S3는 되는데 STS만 실패하거나, 반대로 STS는 되는데 S3만 403

원인

  • AWS_REGION/AWS_DEFAULT_REGION 값이 실제 리전과 불일치
  • 프라이빗 클러스터에서 STS VPC 엔드포인트가 없거나 DNS가 꼬임
  • SDK가 글로벌 STS를 치는 설정과 리전 STS 정책이 충돌

해결

  1. 파드에서 리전 환경 변수를 명시적으로 확인/고정
kubectl -n $NS exec -it $POD -- sh -lc 'echo $AWS_REGION; echo $AWS_DEFAULT_REGION'
  1. 필요 시 STS VPC 엔드포인트 추가 및 Private DNS 확인
  • com.amazonaws.${region}.sts 인터페이스 엔드포인트
  1. S3 403만 나는 심화 케이스는 아래 글 참고

7) IAM Policy는 맞는데 KMS/암호화 키 정책에서 막힘

증상

  • S3 AccessDenied 또는 KMS.AccessDeniedException
  • CloudWatch Logs, Secrets Manager 등에서 “키로 복호화 불가”류 오류

원인

S3 SSE-KMS, Secrets Manager, EBS 암호화 등은 IAM Policy만으로 끝나지 않고 KMS Key Policy가 추가로 허용해야 합니다.

해결

  • 앱이 사용하는 역할 ARN을 KMS Key Policy에 kms:Decrypt, kms:Encrypt, kms:GenerateDataKey 등으로 허용
  • 리소스 정책이 있는 서비스(S3 bucket policy, SNS topic policy 등)도 함께 점검

예시(키 정책의 Statement 일부)

{
  "Sid": "AllowUseOfKeyFromIrsaRole",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::123456789012:role/my-irsa-role"
  },
  "Action": [
    "kms:Decrypt",
    "kms:Encrypt",
    "kms:GenerateDataKey"
  ],
  "Resource": "*"
}

8) Bucket Policy/Resource Policy에서 aws:PrincipalArn 조건으로 차단

증상

  • IAM Role에 S3 권한이 있는데도 AccessDenied

원인

S3 bucket policy가 aws:PrincipalArn, aws:PrincipalAccount, aws:SourceVpce 같은 조건으로 제한되어 있고, IRSA 역할이 조건에 포함되지 않습니다.

해결

  1. 버킷 정책에서 조건을 확인
aws s3api get-bucket-policy --bucket $BUCKET --query Policy --output text
  1. IRSA 역할 ARN을 허용 목록에 추가하거나 조건을 재설계합니다.

9) Permission Boundary 또는 SCP에 의해 허용이 잘려 나감

증상

  • 정책상 Allow인데도 계속 AccessDenied
  • CloudTrail에 explicitDeny 또는 Organizations 관련 힌트

원인

  • IAM Role에 Permission Boundary가 걸려 실제 유효 권한이 축소
  • AWS Organizations SCP가 해당 액션을 금지

해결

  1. Role의 permission boundary 확인
aws iam get-role --role-name my-irsa-role --query 'Role.PermissionsBoundary'
  1. Organizations SCP 확인은 계정/조직 권한이 필요합니다. 보안/플랫폼 팀과 함께 Deny 규칙을 점검하세요.

10) 토큰 파일 마운트/권한 문제(PSA, fsGroup, readOnlyRootFilesystem)

증상

  • AWS_WEB_IDENTITY_TOKEN_FILE 은 있는데 읽기 실패
  • 앱 로그에 permission denied 또는 no such file or directory

원인

  • automountServiceAccountToken: false
  • Pod Security 설정으로 projected volume 마운트가 제한
  • 컨테이너가 비루트로 동작하면서 파일 접근이 꼬임(드물지만 보안 강화 환경에서 발생)

해결

  1. SA 또는 Pod에 automountServiceAccountToken 확인
kubectl -n $NS get sa $SA -o jsonpath='{.automountServiceAccountToken}{"\n"}'
kubectl -n $NS get pod $POD -o jsonpath='{.spec.automountServiceAccountToken}{"\n"}'
  1. 명시적으로 true 설정(필요한 경우)
spec:
  automountServiceAccountToken: true
  1. 파일 권한/마운트 경로를 파드 내부에서 확인
kubectl -n $NS exec -it $POD -- sh -lc 'id; ls -al /var/run/secrets/eks.amazonaws.com/serviceaccount || true'

11) SDK가 IRSA를 안 타고 IMDS(노드 역할)로 빠짐

증상

  • sts get-caller-identity 결과가 노드 인스턴스 프로파일 역할로 나옴
  • 노드 역할에는 권한이 없어서 AccessDenied 발생

원인

  • 오래된 SDK/라이브러리가 web identity provider를 제대로 지원하지 않음
  • 환경 변수 우선순위가 꼬여 다른 credential provider chain이 먼저 선택됨
  • IMDS 접근이 열려 있어(기본) 노드 크레덴셜로 흘러감

해결

  1. 파드에서 호출자 확인
aws sts get-caller-identity
  1. SDK 버전 업그레이드(특히 Java v1 구버전, 오래된 boto3/credential_process 조합 등)

  2. 가능하면 IMDS 차단을 고려

  • EKS는 AWS_EC2_METADATA_DISABLED=true 환경 변수로 SDK의 IMDS 접근을 끌 수 있습니다.
env:
  - name: AWS_EC2_METADATA_DISABLED
    value: "true"

단, 워크로드가 진짜로 EC2 메타데이터를 필요로 한다면 신중히 적용하세요.

12) 역할 체인/교차 계정 AssumeRole에서 sts:AssumeRole 누락

증상

  • 1차 IRSA role assume은 되는데, 이후 다른 role로 전환하다 AccessDenied
  • 예: 앱이 AssumeRole로 운영/데이터 계정 role을 추가로 assume

원인

IRSA role의 권한 정책에 sts:AssumeRole이 없거나, 대상 role의 trust policy가 IRSA role을 신뢰하지 않습니다.

해결

  1. IRSA role에 sts:AssumeRole 부여(대상 role ARN으로 제한)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::999999999999:role/target-role"
    }
  ]
}
  1. 대상 role trust policy에 IRSA role을 Principal로 허용
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/my-irsa-role"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

실전 디버깅 팁: CloudTrail로 “거부된 지점”을 고정하기

IRSA 문제는 “어떤 API가 거부됐는지”를 분리하면 빨라집니다.

  • AssumeRoleWithWebIdentity 자체가 거부되면 Trust Policy/OIDC/sub/aud 문제일 확률이 큽니다.
  • AssumeRoleWithWebIdentity는 성공했는데 서비스 API에서 거부되면 IAM Policy, 리소스 정책, KMS, Permission Boundary/SCP를 의심하세요.

CloudTrail 이벤트에서 아래 필드를 특히 봅니다.

  • eventNameAssumeRoleWithWebIdentity 인지
  • errorCodeAccessDenied 인지
  • userIdentity.sessionContext.sessionIssuer.arn 값이 기대한 role인지

마무리: IRSA AccessDenied를 가장 빨리 푸는 순서

  1. 파드가 IRSA를 “주입받았는지” 확인(AWS_ROLE_ARN, 토큰 파일)
  2. sts get-caller-identity로 “누구로 호출 중인지” 고정
  3. STS assume 단계 실패면 OIDC/Trust Policy의 sub/aud/issuer key를 집중 점검
  4. assume 성공 후 서비스 API 실패면 IAM Policy 뿐 아니라 리소스 정책(S3/KMS 등), Permission Boundary/SCP까지 확장

IRSA는 한 번 안정화하면 운영 효율이 매우 좋지만, 초기에는 여러 레이어(쿠버네티스 SA, OIDC, IAM trust, IAM permission, 리소스 정책)가 동시에 얽혀 “권한 꼬임”이 발생합니다. 위 12가지를 체크리스트처럼 순서대로 밟으면, 대부분의 AccessDenied는 30분 내에 원인을 특정할 수 있습니다.