Published on

EKS OIDC Provider 삭제로 IRSA 전부 실패했을 때 복구

Authors

서로 다른 팀/파이프라인이 같은 EKS 클러스터를 쓰는 환경에서 IAM OIDC Provider를 삭제해버리면, 그 순간부터 IRSA(IAM Roles for Service Accounts) 기반의 모든 Pod가 AWS API 호출에 실패하기 시작합니다. 증상은 대개 "어제까지 되던 S3/STS 호출이 갑자기 전부 AccessDenied"처럼 보이지만, 실제로는 OIDC 연동 자체가 끊겨 STS가 웹 아이덴티티 토큰을 신뢰하지 못하는 상태가 됩니다.

이 글은 “OIDC Provider 삭제 → IRSA 전부 실패” 상황에서 정확히 무엇이 망가졌는지를 빠르게 확인하고, OIDC Provider를 재생성한 뒤, 각 IAM Role의 Trust Policy와 Kubernetes ServiceAccount 설정을 복구해서 정상화하는 절차를 단계별로 다룹니다.

관련해서 IRSA에서 STS AccessDenied를 더 넓은 관점으로 점검하는 체크리스트는 아래 글도 함께 보면 좋습니다.

1) 장애 시그널: 어떤 에러가 뜨나

OIDC Provider가 사라지면 IRSA는 다음 패턴으로 터집니다.

  • 앱 로그/SDK 에러
    • AccessDenied / InvalidIdentityToken
    • No OpenIDConnect provider found in your account for https://oidc.eks.<region>.amazonaws.com/id/<CLUSTER_ID>
    • AssumeRoleWithWebIdentity 호출 실패
  • Kubernetes 이벤트는 대체로 조용합니다(컨테이너는 뜨지만 AWS 호출이 실패).

즉, Pod는 Running인데 AWS API만 실패하는 형태가 흔합니다.

2) 왜 전부 실패하나: IRSA 동작 원리에서의 “끊긴 고리”

IRSA는 요약하면 다음 흐름입니다.

  1. Pod 안에서 AWS_WEB_IDENTITY_TOKEN_FILE(서비스어카운트 토큰)을 읽음
  2. SDK가 sts:AssumeRoleWithWebIdentity 호출
  3. STS는 토큰의 iss(issuer)가 가리키는 IAM OIDC Provider를 찾아 신뢰 여부 확인
  4. IAM Role의 Trust Policy(Condition의 sub, aud)가 토큰 클레임과 일치하면 임시 자격증명 발급

여기서 3번의 OIDC Provider가 삭제되면 STS가 issuer를 검증할 수 없어서, 클러스터 내 모든 IRSA가 동시에 무너집니다.

3) 10분 내 원인 확정: OIDC Provider 존재 여부 확인

3.1 클러스터 OIDC Issuer 확인

aws eks describe-cluster \
  --name <cluster-name> \
  --region <region> \
  --query "cluster.identity.oidc.issuer" \
  --output text

출력 예:

https://oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E

3.2 IAM에 OIDC Provider가 남아있는지 확인

aws iam list-open-id-connect-providers --query "OpenIDConnectProviderList[].Arn" --output text

각 Provider의 URL은 아래로 확인합니다.

aws iam get-open-id-connect-provider \
  --open-id-connect-provider-arn <provider-arn>

Url 목록에 위에서 확인한 oidc.eks.../id/<ID>가 없다면, 삭제된 것이 확정입니다.

4) 복구 핵심: OIDC Provider를 “클러스터 Issuer와 동일하게” 재생성

OIDC Provider는 EKS 클러스터의 issuer URLthumbprint, 그리고 **client ID(aud)**가 정확해야 합니다. EKS IRSA의 client ID는 일반적으로 sts.amazonaws.com입니다.

가장 안전한 방법은 eksctl로 재연결(재생성)하는 것입니다.

4.1 eksctl로 OIDC Provider 재생성

eksctl utils associate-iam-oidc-provider \
  --cluster <cluster-name> \
  --region <region> \
  --approve

성공하면 IAM에 OIDC Provider가 다시 생성됩니다.

> 운영 환경에서는 이 단계가 가장 중요합니다. OIDC Provider를 콘솔에서 수동 생성할 수도 있지만, thumbprint/URL 오타로 시간이 크게 늘어납니다.

4.2 (검증) 생성 후 issuer가 매칭되는지 확인

ISSUER=$(aws eks describe-cluster --name <cluster-name> --region <region> \
  --query "cluster.identity.oidc.issuer" --output text)

aws iam list-open-id-connect-providers --query "OpenIDConnectProviderList[].Arn" --output text | \
tr '\t' '\n' | while read -r arn; do
  url=$(aws iam get-open-id-connect-provider --open-id-connect-provider-arn "$arn" --query Url --output text)
  if [ "https://$url" = "$ISSUER" ]; then
    echo "MATCH: $arn -> $url"
  fi
done

MATCH가 찍히면 “OIDC Provider 복구”는 완료입니다.

5) 두 번째 복구 포인트: IAM Role Trust Policy가 여전히 올바른가

OIDC Provider를 다시 만들었는데도 일부 워크로드가 계속 실패한다면, 다음을 의심해야 합니다.

  • Role의 Trust Policy가 이전 OIDC Provider ARN을 Principal로 잡고 있었고, 재생성 후 ARN이 달라짐
  • Conditionsub(serviceaccount) 또는 aud가 실제 토큰과 불일치

5.1 Trust Policy에서 가장 흔히 망가지는 부분

IRSA Trust Policy 예시는 아래와 같습니다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/oidc.eks.<region>.amazonaws.com/id/<CLUSTER_ID>"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.<region>.amazonaws.com/id/<CLUSTER_ID>:aud": "sts.amazonaws.com",
          "oidc.eks.<region>.amazonaws.com/id/<CLUSTER_ID>:sub": "system:serviceaccount:<namespace>:<serviceaccount>"
        }
      }
    }
  ]
}

여기서 Federated ARN이 현재 계정에 존재하는 OIDC Provider ARN과 일치해야 합니다.

5.2 OIDC Provider ARN을 기준으로 Trust Policy 일괄 점검

  1. 현재 클러스터 issuer에 해당하는 OIDC Provider ARN 찾기
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=<region>
CLUSTER_ID=<cluster-id-from-issuer>

OIDC_ARN=$(aws iam list-open-id-connect-providers --query "OpenIDConnectProviderList[].Arn" --output text | \
tr '\t' '\n' | grep "oidc-provider/oidc.eks.${REGION}.amazonaws.com/id/${CLUSTER_ID}" | head -n 1)

echo "$OIDC_ARN"
  1. 특정 Role의 trust policy 확인
aws iam get-role --role-name <role-name> --query "Role.AssumeRolePolicyDocument" --output json

Federated가 위 OIDC_ARN과 다르면, 정상적으로 STS가 신뢰하지 않습니다.

5.3 Trust Policy 수정(예: aws cli)

cat > trust.json <<'JSON'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "REPLACE_WITH_OIDC_ARN"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "REPLACE_WITH_ISSUER_HOSTPATH:aud": "sts.amazonaws.com",
          "REPLACE_WITH_ISSUER_HOSTPATH:sub": "system:serviceaccount:default:my-sa"
        }
      }
    }
  ]
}
JSON

# 편의상 sed로 치환(환경에 맞게 수정)
OIDC_ARN='<oidc-provider-arn>'
ISSUER_HOSTPATH='oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLE...'

sed -i.bak "s|REPLACE_WITH_OIDC_ARN|$OIDC_ARN|g" trust.json
sed -i.bak "s|REPLACE_WITH_ISSUER_HOSTPATH|$ISSUER_HOSTPATH|g" trust.json

aws iam update-assume-role-policy --role-name <role-name> --policy-document file://trust.json

> ISSUER_HOSTPATHhttps://를 뺀 host/path 형태로 들어갑니다. (Trust policy key는 oidc.eks...:sub 형태)

6) 세 번째 복구 포인트: Kubernetes ServiceAccount annotation 확인

IRSA는 ServiceAccount에 아래 annotation이 있어야 합니다.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-sa
  namespace: default
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/<role-name>

확인:

kubectl get sa my-sa -n default -o yaml

annotation이 없다면, 해당 Pod는 IRSA를 쓰지 못하고(또는 노드 IAM으로 떨어져) 다른 형태의 실패를 유발할 수 있습니다.

7) 실제로 “복구됐는지” 확인하는 가장 빠른 방법

7.1 Pod 안에서 현재 자격증명 확인

AWS CLI가 들어있는 디버그 Pod로 확인하는 게 빠릅니다.

kubectl run -n default irsa-debug \
  --rm -it --restart=Never \
  --image public.ecr.aws/aws-cli/aws-cli:2.15.0 \
  --serviceaccount my-sa \
  --command -- sh

Pod 내부에서:

aws sts get-caller-identity
  • 기대 결과: Arnassumed-role/<role-name>/... 형태로 나와야 합니다.
  • 실패한다면 에러 메시지에 No OpenIDConnect provider found / InvalidIdentityToken / AccessDenied가 섞여 나옵니다.

7.2 토큰 클레임(sub/aud) 확인(필요 시)

cat $AWS_WEB_IDENTITY_TOKEN_FILE | awk -F. '{print $2}' | base64 -d 2>/dev/null | jq

여기서 aud, sub, iss를 보고 Trust Policy의 Condition과 정확히 맞는지 확인합니다.

8) 운영에서 자주 겪는 함정 4가지

8.1 OIDC Provider를 재생성하면 ARN이 바뀔 수 있다

OIDC Provider는 “URL이 같아도” 리소스 ARN은 새로 만들어질 수 있습니다. Trust Policy의 Principal.Federated옛 ARN을 가리키면 계속 실패합니다.

8.2 Trust Policy의 Condition key 오타

oidc.eks.<region>.amazonaws.com/id/<CLUSTER_ID>:sub에서 <CLUSTER_ID>가 틀리거나, region이 다르면 무조건 실패합니다.

8.3 ServiceAccount 교체/롤링이 안 되어 토큰이 갱신되지 않는다고 착각

대부분은 OIDC/Trust 문제지만, 워크로드가 오래 살아있고 토큰/환경변수 상태가 애매하면 Deployment 롤링 재시작으로 증상이 정리되는 경우가 있습니다.

kubectl rollout restart deploy/<deploy-name> -n <namespace>

8.4 IRSA와 노드 IAM 권한이 섞여 “일부만” 되는 것처럼 보임

어떤 Pod는 노드 IAM으로도 동작(예: 읽기 권한은 노드에 있음)하고, 어떤 Pod는 IRSA 전용 권한이 필요해 실패하면서 장애가 더 헷갈립니다. sts get-caller-identity로 “누구로 호출하는지”부터 고정하세요.

9) 재발 방지: 삭제 사고를 막는 실전 가드레일

  1. IAM OIDC Provider 삭제 권한 제한
  • 최소한 iam:DeleteOpenIDConnectProvider는 운영 계정에서 강하게 제한
  1. Terraform/CloudFormation으로 OIDC Provider를 코드로 관리
  • 수동 생성/삭제를 줄이고, drift를 감지
  1. IRSA 핵심 Role에 대한 자동 점검
  • 정기적으로 assume role with web identity 성공 여부를 canary job으로 확인
  1. 변경 작업 전 체크리스트화
  • “클러스터 OIDC issuer 확인 → IAM OIDC Provider 존재 확인 → 주요 Role trust principal 일치 확인”을 릴리즈 체크에 포함

10) 복구 요약(런북 형태)

  • (A) 클러스터 issuer 확인: aws eks describe-cluster ... identity.oidc.issuer
  • (B) IAM OIDC Provider 존재 확인: aws iam list-open-id-connect-providers
  • (C) 없으면 재생성: eksctl utils associate-iam-oidc-provider --approve
  • (D) 주요 Role trust policy의 Principal.Federated가 새 OIDC Provider ARN인지 확인/수정
  • (E) ServiceAccount annotation 확인: eks.amazonaws.com/role-arn
  • (F) 디버그 Pod로 aws sts get-caller-identity 확인

이 순서대로 하면 “OIDC Provider 삭제로 IRSA 전부 실패” 상황은 대개 30~60분 내에 안정적으로 복구됩니다. 핵심은 OIDC Provider 복구만으로 끝나지 않고, Trust Policy의 Federated ARN/Condition까지 함께 맞춰야 한다는 점입니다.