- Published on
EKS ExternalSecret 동기화 실패 - AccessDenied·InvalidIdentity 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스/컨테이너 환경에서 시크릿 동기화는 곧 서비스 가용성입니다. EKS에서 External Secrets Operator(ESO)로 AWS Secrets Manager 또는 SSM Parameter Store를 읽어 Kubernetes Secret으로 동기화할 때, 가장 흔하게 터지는 에러가 AccessDenied, InvalidIdentityToken, InvalidClientTokenId 같은 STS/IAM 계열 오류입니다.
이 글에서는 “ExternalSecret이 계속 Pending/Failed이고, controller 로그에는 AccessDenied/InvalidI…(IdentityToken/ClientToken)” 상황을 전제로, 원인별로 어디를 봐야 하는지와 재현 가능한 해결 절차를 정리합니다. IRSA(OIDC) 기반이든, 노드 IAM Role 기반이든 모두 포함합니다.
> EKS 전반 트러블슈팅이 필요하다면: Terraform apply 후 EKS 노드 NotReady - CNI·IRSA·보안그룹 점검
증상 패턴: ExternalSecret은 있는데 Secret이 안 생긴다
대표 증상은 다음과 같습니다.
ExternalSecretCR은 생성되었는데Secret이 생성되지 않음kubectl describe externalsecret ...에SecretSyncedError/ProviderError같은 이벤트external-secrets컨트롤러 로그에 다음 키워드가 반복AccessDeniedExceptionAccessDeniedInvalidIdentityTokenInvalidClientTokenIdNoCredentialProviders
먼저 상태를 빠르게 확인합니다.
kubectl -n <ns> get externalsecret
kubectl -n <ns> describe externalsecret <name>
# ESO 컨트롤러 로그(설치 네임스페이스는 보통 external-secrets)
kubectl -n external-secrets logs deploy/external-secrets -f --tail=200
또한 SecretStore/ClusterSecretStore가 어떤 인증 방식을 쓰는지 확인하세요.
kubectl -n <ns> get secretstore
kubectl -n <ns> get secretstore <store> -o yaml
kubectl get clustersecretstore
kubectl get clustersecretstore <store> -o yaml
External Secrets Operator에서 AWS 인증 흐름 이해(문제의 80%)
AWS Provider를 사용할 때 ESO는 대체로 아래 중 하나로 자격증명을 얻습니다.
- IRSA(권장): ServiceAccount ↔ IAM Role을 OIDC로 AssumeRoleWithWebIdentity
- 노드 IAM Role(비권장/레거시): EC2 인스턴스 프로파일로 호출
- 정적 키(SecretRef): Access Key/Secret Key를 Kubernetes Secret에 저장
AccessDenied는 “권한 부족”이고, InvalidIdentityToken/InvalidClientTokenId는 “토큰/자격증명 자체가 유효하지 않음”에 가깝습니다. 즉,
- AccessDenied → IAM Policy/Trust/리소스 정책/조건(Condition) 점검
- InvalidIdentityToken / InvalidClientTokenId → OIDC/IRSA 구성, 토큰 audience, 클러스터 OIDC Issuer, 시간/리전/엔드포인트 점검
1) AccessDenied: 정책은 붙였는데 왜 거부될까?
체크포인트 A: 실제로 어떤 Role로 호출하고 있나?
가장 먼저 “ESO가 어떤 IAM Role로 AWS API를 때리는지”를 확인해야 합니다. IRSA라면 ESO가 사용하는 ServiceAccount에 role annotation이 있어야 합니다.
# ESO 설치 시 사용하는 SA 이름은 차이가 있을 수 있음
kubectl -n external-secrets get sa
kubectl -n external-secrets get sa external-secrets -o yaml
정상이라면 다음이 보입니다.
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/<ESO_ROLE>
만약 annotation이 없으면, ESO는 노드 role로 떨어지거나(환경에 따라), 아예 자격증명 획득에 실패할 수 있습니다.
체크포인트 B: IAM Role 정책에 필요한 액션이 빠졌나?
Secrets Manager를 읽는 최소 권한 예시는 다음과 같습니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "arn:aws:secretsmanager:ap-northeast-2:<ACCOUNT_ID>:secret:myapp/*"
}
]
}
SSM Parameter Store라면:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath"
],
"Resource": "arn:aws:ssm:ap-northeast-2:<ACCOUNT_ID>:parameter/myapp/*"
},
{
"Effect": "Allow",
"Action": "kms:Decrypt",
"Resource": "arn:aws:kms:ap-northeast-2:<ACCOUNT_ID>:key/<KMS_KEY_ID>"
}
]
}
여기서 흔한 함정:
- SSM SecureString인데
kms:Decrypt가 없음 - Secrets Manager ARN 패턴이 정확하지 않음(특히 secret name 뒤에 랜덤 suffix가 붙는 경우)
- 리전이 달라 Resource ARN이 매칭되지 않음
체크포인트 C: 리소스 정책(Resource Policy)이나 KMS 키 정책이 막는다
권한을 role에 줬는데도 AccessDenied가 나면, Secrets Manager 리소스 정책 또는 KMS Key policy에서 거부하고 있을 수 있습니다.
- 조직 보안 정책으로 특정 Principal만 허용
- KMS 키 정책에서 role을 허용하지 않음
이 경우 IAM policy만으로는 해결되지 않습니다.
2) InvalidIdentityToken: OIDC/IRSA 토큰이 신뢰되지 않는다
InvalidIdentityToken은 IRSA에서 특히 자주 발생합니다. 핵심은 “EKS OIDC Issuer와 IAM OIDC Provider가 정확히 연결되어 있고, Trust Policy 조건이 맞는가”입니다.
체크포인트 A: 클러스터 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/XXXXXXXXXXXX
체크포인트 B: IAM에 OIDC Provider가 등록되어 있나?
aws iam list-open-id-connect-providers
aws iam get-open-id-connect-provider --open-id-connect-provider-arn <ARN>
Issuer URL의 id/XXXX가 다른 Provider를 바라보면 토큰 검증이 실패합니다(클러스터 재생성/복제/리전 혼동에서 자주 발생).
체크포인트 C: Role Trust Policy의 sub 조건이 ServiceAccount와 일치하는가?
IRSA Role의 Trust Relationship은 보통 다음 형태입니다.
{
"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:external-secrets:external-secrets",
"oidc.eks.<REGION>.amazonaws.com/id/<OIDC_ID>:aud": "sts.amazonaws.com"
}
}
}
]
}
여기서 가장 흔한 실수:
- 네임스페이스/서비스어카운트 이름이 다름
aud조건이 누락되거나 다른 값으로 설정됨- 여러 SA를 허용하려다
StringLike패턴을 잘못 씀
예: SA가 external-secrets가 아니라 external-secrets-sa면 sub가 달라져서 바로 InvalidIdentityToken이 납니다.
체크포인트 D: 컨트롤러 Pod가 SA 토큰을 제대로 마운트 받는가?
보안 설정으로 automountServiceAccountToken: false가 걸려 있으면 웹 아이덴티티 토큰 파일이 없어서 인증이 실패할 수 있습니다.
kubectl -n external-secrets get deploy external-secrets -o yaml | yq '.spec.template.spec.automountServiceAccountToken'
kubectl -n external-secrets get sa external-secrets -o yaml | yq '.automountServiceAccountToken'
3) InvalidClientTokenId: 자격증명 자체가 틀렸거나, 리전/엔드포인트가 어긋났다
InvalidClientTokenId는 보통 다음에서 발생합니다.
- 정적 키(AccessKey/SecretKey)가 잘못됨
- 키가 비활성화/삭제됨
- 다른 AWS 계정의 키를 사용 중
- IRSA가 아니라 노드 role로 호출되는데 해당 role이 STS 호출을 못 하거나, 잘못된 환경 변수가 주입됨
정적 키를 쓰는 경우(SecretRef) 점검
SecretStore에서 auth.secretRef를 쓰고 있다면, 해당 Kubernetes Secret 값이 맞는지 확인합니다.
kubectl -n <ns> get secret <aws-creds-secret> -o yaml
그리고 가능하면 정적 키 대신 IRSA로 전환하는 것을 권장합니다.
리전 불일치 점검
SecretStore에 region이 하드코딩되어 있는데 실제 시크릿이 다른 리전에 있으면, 존재하지 않는 리소스를 조회하면서 다른 형태의 오류가 섞여 나올 수 있습니다.
4) 빠른 복구 루틴: “진단 → 수정 → 재동기화” 체크리스트
1) 이벤트/로그에서 에러 문자열을 확정
kubectl -n <ns> describe externalsecret <name>
kubectl -n external-secrets logs deploy/external-secrets --tail=200 | rg -n "AccessDenied|InvalidIdentityToken|InvalidClientTokenId|NoCredential"
2) Store/SA/Role 연결 확인
kubectl -n <ns> get secretstore <store> -o yaml
kubectl -n external-secrets get sa external-secrets -o yaml
3) IAM Trust/Policy 확인
- Trust: OIDC provider + sub/aud 조건
- Policy: secretsmanager/ssm/kms 권한
4) 재동기화 트리거
ESO는 주기적으로 reconcile하지만, 즉시 확인하고 싶으면 annotation을 바꿔서 리컨실을 유도할 수 있습니다.
kubectl -n <ns> annotate externalsecret <name> reconcile-at="$(date -u +%Y-%m-%dT%H:%M:%SZ)" --overwrite
예시 구성: IRSA + ClusterSecretStore + ExternalSecret
아래는 가장 많이 쓰는 패턴(운영 권장)에 가까운 예시입니다.
ServiceAccount(IRSA)
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-secrets
namespace: external-secrets
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/eks-eso-role
ClusterSecretStore(AWS Secrets Manager)
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: aws-secretsmanager
spec:
provider:
aws:
service: SecretsManager
region: ap-northeast-2
auth:
jwt:
serviceAccountRef:
name: external-secrets
namespace: external-secrets
ExternalSecret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-secret
namespace: myapp
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secretsmanager
kind: ClusterSecretStore
target:
name: myapp-secret
creationPolicy: Owner
data:
- secretKey: DATABASE_URL
remoteRef:
key: myapp/prod/database_url
이 구성이면, 문제는 대개 IRSA Role trust/policy 또는 리소스/KMS 정책으로 좁혀집니다.
운영에서 자주 겪는 함정 6가지(실패율 높은 순)
- ServiceAccount 이름/namespace 변경 후 Trust Policy sub 미수정
- 클러스터를 새로 만들었는데 OIDC Provider가 예전 클러스터 ID를 가리킴
- SSM SecureString인데 kms:Decrypt 누락
- Secrets Manager secret ARN 매칭 실패(랜덤 suffix 고려 안 함)
- ESO 컨트롤러가 노드 IAM Role로 호출 중(의도와 다름)
- STS가 과도하게 호출되어 간헐적 실패(스로틀링)
STS 스로틀링이 보인다면 이 글도 함께 보세요: EKS IRSA는 되는데 STS 429 Throttling 해결
마무리: 에러 메시지로 분류하면 해결이 빨라진다
AccessDenied는 “권한/정책/리소스 정책/KMS 정책” 문제InvalidIdentityToken은 “OIDC/IRSA 신뢰관계(sub/aud/issuer/provider)” 문제InvalidClientTokenId는 “자격증명(키) 자체 또는 호출 주체/리전/환경” 문제
ExternalSecret 동기화 실패는 겉으로는 비슷해 보여도, AWS 인증 경로를 먼저 확정하면 원인이 급격히 좁혀집니다. 특히 EKS에서는 IRSA가 표준이므로, 문제가 생기면 가장 먼저 ServiceAccount annotation ↔ IAM Role trust ↔ OIDC provider 3점을 직선으로 연결해 확인하는 습관이 중요합니다.
EKS에서 다른 유형의 장애(노드/네트워크)로 인해 컨트롤러가 정상 동작하지 않는 경우도 있으니, Pod 상태가 이상하다면 다음 글도 참고해 보세요: EKS Pod Pending 0/XX nodes available 원인별 해결