서종호(가시다)님의 AWS EKS Workshop Study(AEWS) 4주차 학습 내용을 기반으로 합니다.
기초 개념 및 실습
IAM Role
Role이라는 개념은 이미 익숙할 것이다. assumeRole은 한 IAM 엔티티가 다른 Role의 임시 자격 증명(Temporary Credentials)을 발급받아 해당 Role의 권한으로 작업을 수행하는 메커니즘이다. 예를 들어 EC2 노드에는 IAM Role을 설정할 수 있는데 그 과정을 CLI 명령어로 뜯어보면 아래와 같다.
# CLI에서는 Instance Profile을 명시적으로 따로 만들어야 함
# 1. Role 생성
aws iam create-role --role-name MyRole ...
# 2. Instance Profile 별도 생성
aws iam create-instance-profile --instance-profile-name MyProfile
# 3. Profile에 Role 연결
aws iam add-role-to-instance-profile \
--instance-profile-name MyProfile \
--role-name MyRole
# 4. EC2에 Profile 연결
aws ec2 associate-iam-instance-profile \
--instance-id i-1234567890 \
--iam-instance-profile Name=MyProfile위와 같은 과정을 거쳐 해당 노드가 특정한 Role을 사용하도록 설정을 해주면 그 뒤에 EC2에서는 STS에 assumeRole 요청을 보내 실제 해당 역할을 수행할 수 있는 임시자격을 부여받는다. 실제로 콘솔에서 특정 IAM Role을 연결하고 나서 CloudTrail 이벤트를 살펴보면 아래와 같은 assumeRole 이벤트가 발생한것을 확인할 수 있다.

Service Account

Service Account는 Kubernetes에서 사람이 아닌(non-human) 계정의 한 유형으로, 클러스터 내에서 고유한 ID를 제공한다. Pod, 시스템 컴포넌트, 그리고 클러스터 내외부의 엔티티들이 특정 Service Account의 자격 증명을 사용하여 해당 Service Account로 식별될 수 있다.
쉽게 말해, 사람(User Account)이 kubectl 등을 통해 API Server에 인증하는 것과 달리, Service Account는 Pod 내부에서 실행되는 애플리케이션이 API Server와 통신할 때 사용하는 신원(identity)이다.
Service Account의 주요 특성은 다음과 같다.
- Namespaced: 각 Service Account는 특정 네임스페이스에 바인딩된다. 모든 네임스페이스는 생성 시 자동으로 default Service Account를 갖는다.
- Lightweight: 클러스터 내에 존재하며 Kubernetes API에서 정의된다. 빠르게 생성하여 특정 작업을 활성화할 수 있다.
- Portable: 복잡한 컨테이너화된 워크로드의 구성 번들에 시스템 컴포넌트에 대한 Service Account 정의를 포함할 수 있어 구성의 이식성이 높다.
Service Account와 User Account의 핵심적인 차이를 정리하면, User Account는 사람(Human)이 사용하는 외부 인증 기반의 계정이고 Kubernetes API에 객체로 존재하지 않는다. 반면 Service Account는 워크로드와 자동화를 위한 계정으로 Kubernetes API에 ServiceAccount 객체로 존재하며, Kubernetes RBAC을 통해 접근 제어가 이루어진다.
아래 그림은 Pod가 Service Account를 통해 API Server에 인증하는 전체 흐름을 보여준다. 즉, Pod라는 리소스가 SA를 통해 인증을 하고 Role과 연결된 RoleBinnding을 통해서 인가까지도 관계를 맺고 있는 리소스라고 이해할 수 있다.

실습으로 SA를 만들어보고 기본적인 인증 토큰(jwt)이 잘 생성되는지 확인해보자. 우선 각 네임스페이스와 SA를 생성한다.
# 네임스페이스(Namespace, NS) 생성 및 확인
kubectl create namespace dev-team
kubectl create ns infra-team
# 네임스페이스 확인
kubectl get ns
# 네임스페이스에 각각 서비스 어카운트 생성 : serviceaccounts 약자(=sa)
kubectl create sa dev-k8s -n dev-team
kubectl create sa infra-k8s -n infra-team
# 서비스 어카운트 정보 확인
kubectl get sa -n dev-team
kubectl get sa dev-k8s -n dev-team -o yaml
kubectl get sa -n infra-team
kubectl get sa infra-k8s -n infra-team -o yaml이후 파드를 생성하고 나서 실제 생성된 토큰을 확인하자. 파드의 경우 앞서 생성한 SA를 사용하도록 한다.
# 각각 네임스피이스에 kubectl 파드 생성 - [컨테이너이미지](https://hub.docker.com/r/bitnami/kubectl/)
# docker run --rm --name kubectl -v /path/to/your/kube/config:/.kube/config bitnami/kubectl:latest
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: dev-kubectl
namespace: dev-team
spec:
serviceAccountName: dev-k8s
containers:
- name: kubectl-pod
image: bitnami/kubectl
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: infra-kubectl
namespace: infra-team
spec:
serviceAccountName: infra-k8s
containers:
- name: kubectl-pod
image: bitnami/kubectl
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# 파드에 기본 적용되는 서비스 어카운트(토큰) 정보 확인
kubectl exec -it dev-kubectl -n dev-team -- ls /run/secrets/kubernetes.io/serviceaccount
kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/token
kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/namespace
kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/ca.crt총 3가지 파일이 마운팅 되어서 보인다. 여기서 token의 경우 jwt 토큰이고 아래와 같은 흐름으로 생성된 기본 토큰이다. 해당 토큰은 SA를 생성한다고 만들어지지 않고 아래 흐름을 거치며 생성된다.
ServiceAccount 생성
│
▼
(토큰 없음, SA만 존재)
│
▼
Pod 생성 (spec.serviceAccountName: dev-k8s)
│
▼
kubelet → TokenRequest API 호출
│
▼
API Server가 JWT 발급 (exp, pod 정보, SA 정보 포함)
│
▼
Projected Volume으로 Pod에 마운트
│
▼
kubelet이 만료 전 자동 갱신 (80% 시점)실제 토큰을 디코드 해보면 아래와 같이 페이로드 값들을 확인 가능하다. 각 두 파드에 대해서 이름, 설정한 SA 객체, 생성 시간들은 다르다. 이러한 다른 값들을 아래와 같이 디코드 할 경우 확인 할 수 있다.


RBAC

RBAC(Role-Based Access Control)는 Kubernetes에서 인가(Authorization)를 처리하는 핵심 메커니즘이다. 앞서 Service Account를 통해 Pod가 API Server에 인증(Authentication)하는 과정을 살펴보았는데, 인증만으로는 실제 리소스에 대한 접근을 제어할 수 없다. RBAC는 "누가(Subject), 어떤 리소스(Resource)에, 어떤 작업(Verb)을 할 수 있는가"를 정의하는 권한 체계로, 클러스터의 보안을 담당하는 가장 중요한 구성 요소 중 하나이다.
Kubernetes의 RBAC API는 rbac.authorization.k8s.io API 그룹에 속하며, 크게 네 가지 리소스로 구성된다. Role은 특정 네임스페이스 내에서 허용할 작업을 정의하고, ClusterRole은 클러스터 전체 범위에서 허용할 작업을 정의한다. RoleBinding은 Role을 특정 Subject(User, Group, ServiceAccount)에 바인딩하여 해당 네임스페이스 내에서 권한을 부여하며, ClusterRoleBinding은 ClusterRole을 Subject에 바인딩하여 클러스터 전체에 걸쳐 권한을 부여한다.
아래 다이어그램은 네임스페이스 범위에서의 RBAC 동작 구조를 보여준다. Subject(사용자 또는 ServiceAccount)가 RoleBinding을 통해 Role에 연결되고, Role에 정의된 리소스와 행위 조합에 따라 API 접근이 허용된다. Subject와 Role 사이는 M:N 관계로, 하나의 Subject가 여러 Role에 바인딩될 수 있고, 하나의 Role이 여러 Subject에게 부여될 수도 있다.

이제 앞서 생성한 Service Account에 실제 권한을 부여하기 위해 Role과 RoleBinding을 생성해보자. Role에는 해당 네임스페이스 내에서 허용할 apiGroups, resources, verbs를 지정한다. 이번 실습에서는 dev-team과 infra-team 네임스페이스 각각에 모든 리소스에 대한 모든 권한(["*"])을 갖는 Role을 생성한다. 실무에서는 최소 권한 원칙에 따라 필요한 권한만 부여하는 것이 권장된다.
# 각각 네임스페이스내의 모든 권한에 대한 롤 생성
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-dev-team
namespace: dev-team
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
EOF
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-infra-team
namespace: infra-team
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
EOF
# 롤 확인
kubectl get roles -n dev-team
kubectl get roles -n infra-team
kubectl get roles -n dev-team -o yaml
kubectl describe roles role-dev-team -n dev-team
...
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
*.* [] [] [*]
# 롤바인딩 생성 : '서비스어카운트 <-> 롤' 간 서로 연동
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: roleB-dev-team
namespace: dev-team
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-dev-team
subjects:
- kind: ServiceAccount
name: dev-k8s
namespace: dev-team
EOF
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: roleB-infra-team
namespace: infra-team
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-infra-team
subjects:
- kind: ServiceAccount
name: infra-k8s
namespace: infra-team
EOF
# 롤바인딩 확인
kubectl get rolebindings -n dev-team
kubectl get rolebindings -n infra-team
kubectl get rolebindings -n dev-team -o yaml
kubectl describe rolebindings roleB-dev-team -n dev-team위 코드 블록에서 수행한 작업을 정리하면, 먼저 dev-team과 infra-team 네임스페이스에 각각 role-dev-team, role-infra-team이라는 Role을 생성하였다. 이 Role들은 apiGroups, resources, verbs 모두 와일드카드(*)로 설정되어 해당 네임스페이스 내의 모든 리소스에 대한 전체 권한을 가진다. 이후 RoleBinding을 생성하여 각각의 ServiceAccount(dev-k8s, infra-k8s)를 해당 Role에 바인딩하였다. 이로써 각 Pod에서 동작하는 ServiceAccount가 자신의 네임스페이스 내에서 리소스를 자유롭게 조회, 생성, 수정, 삭제할 수 있게 된다.
아래 스크린샷은 실제로 Role과 RoleBinding이 정상적으로 생성되었는지 확인한 결과이다. kubectl describe roles 명령어로 Role의 PolicyRule을 확인하면 모든 리소스(.)에 대해 모든 동작([*])이 허용된 것을 볼 수 있고, kubectl get rolebindings 명령어로 RoleBinding의 상세 정보를 확인하면 ServiceAccount가 올바르게 연결되어 있음을 확인할 수 있다.


두 번째 스크린샷의 YAML 출력을 보면 RoleBinding 리소스의 구조를 상세히 확인할 수 있다. roleRef 필드에서 바인딩된 Role의 이름(role-dev-team)과 API 그룹(rbac.authorization.k8s.io)을, subjects 필드에서 연결된 ServiceAccount(dev-k8s)와 네임스페이스(dev-team)를 확인할 수 있다. 이처럼 RoleBinding은 Role과 Subject를 연결하는 접착제 역할을 하며, 이 세 가지 요소가 모두 올바르게 구성되어야 RBAC 인가가 정상적으로 동작한다.
정리하면, Kubernetes의 RBAC는 인증(Authentication) 이후 단계인 인가(Authorization)를 담당하며, ServiceAccount → RoleBinding → Role의 관계를 통해 Pod 내 워크로드가 API Server의 어떤 리소스에 어떤 작업을 수행할 수 있는지를 세밀하게 제어한다. 이전 섹션에서 SA를 생성하고 토큰을 확인한 것이 인증 단계라면, 이번 RBAC 실습은 그 인증된 주체에게 실제 권한을 부여하는 인가 단계에 해당한다. 클러스터 운영 시에는 네임스페이스별로 적절한 Role을 설계하고, 필요한 ServiceAccount에만 최소한의 권한을 부여하는 것이 보안의 핵심이다.
Service Account Token Volume Projection

앞서 살펴본 것처럼, Kubernetes v1.22 이후의 현재 버전에서는 Pod가 생성될 때 kubelet이 TokenRequest API를 통해 시간 제한이 있는 JWT 토큰을 발급받고, 이를 Projected Volume으로 Pod에 마운트한다. 이것이 현재 기본 동작이다. 그렇다면 Service Account Token Volume Projection이 별도로 존재하는 이유는 무엇일까?
핵심 차이를 이해하기 위해 먼저 기본 토큰의 역사를 짚어보자. Kubernetes v1.22 이전에는 ServiceAccount를 생성하면 자동으로 Secret 객체가 만들어지고, 그 안에 만료 기간이 없는 영구 토큰이 저장되었다. 이 Secret 기반 토큰은 Pod에 볼륨으로 마운트되어 사용되었는데, 토큰이 한번 발급되면 영원히 유효하기 때문에 유출 시 심각한 보안 위협이 될 수 있었다. v1.22 이후 이 방식은 Bound Service Account Token Volume 메커니즘으로 대체되었다. 이제는 kubelet이 TokenRequest API를 호출하여 기본 1시간 만료의 토큰을 받고, 이를 Projected Volume으로 Pod에 마운트하며, 만료 80% 시점에 자동으로 갱신한다.

여기서 중요한 개념 정리가 필요하다. "Projected Volume"은 PersistentVolume(PV)과는 완전히 다른 개념이다. PV는 실제 디스크 스토리지를 추상화한 것이지만, Projected Volume은 여러 볼륨 소스(serviceAccountToken, configMap, downwardAPI, secret 등)를 하나의 디렉토리에 합쳐서 마운트하는 인메모리(tmpfs) 기반의 가상 볼륨이다. 즉, Projected Volume에 마운트된 토큰은 디스크에 영구적으로 쓰이는 것이 아니라 메모리 위에 존재하며, kubelet이 API Server로부터 받은 토큰을 주기적으로 갱신하여 파일 내용을 업데이트한다. Pod가 삭제되면 이 볼륨과 토큰도 함께 사라진다. 따라서 "PV에 저장하면 디스크에 영구적으로 쓰는 것 아닌가?"라는 의문의 답은, Projected Volume은 PV가 아니고 메모리 기반이며 토큰 자체에 만료 시간(exp)이 JWT 클레임으로 포함되어 있어 API Server가 만료된 토큰을 거부한다는 것이다. 시크릿 기반 토큰이 경우 만료 시간이 JWT 클레임에 포함되어 있지 않다.

그렇다면 기본 Bound SA Token과 Service Account Token Volume Projection의 차이는 무엇인가? 기본 토큰은 audience가 kube-apiserver로 고정되어 있고, 만료 시간도 기본값(1시간)이 적용된다. 하지만 Pod 내 애플리케이션이 Vault, 외부 서비스(STS 등) kube-apiserver가 아닌 다른 시스템에 인증해야 하는 경우, 대상(audience)과 유효 기간(expiration)을 직접 지정할 수 있어야 한다. 바로 이런 경우에 Service Account Token Volume Projection을 명시적으로 설정하여 사용한다. 아래 YAML 예시에서 audience를 vault로, expirationSeconds를 7200(2시간)으로 지정하여 Vault 인증에 특화된 토큰을 Pod에 마운트하는 것을 볼 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: /var/run/secrets/tokens
name: vault-token
serviceAccountName: build-robot
volumes:
- name: vault-token
projected:
sources:
- serviceAccountToken:
path: vault-token
expirationSeconds: 7200
audience: vault코드에서 핵심은 volumes 섹션의 projected.sources.serviceAccountToken 설정이다. path는 토큰 파일이 마운트될 경로명, expirationSeconds는 토큰의 유효 기간(최소 600초, 기본 3600초), audience는 토큰의 수신 대상을 지정한다. 이렇게 생성된 토큰은 audience 클레임에 vault가 포함되어 있으므로, Vault 서버는 이 토큰을 검증할 때 자신이 의도된 수신자인지 확인할 수 있다. 공식 문서에 따르면 kubelet은 토큰의 TTL 80% 시점 또는 24시간 중 더 빠른 시점에 자동으로 토큰을 갱신하며, 애플리케이션은 토큰 파일을 주기적으로(예: 5분마다) 다시 읽어들이는 것이 권장된다. 아래 공식문서에서 더 자세한 내용을 확인할 수 있다.

생소한 개념인 Bound Service Account Token Volume, 서비스 어카운트 어드미션 컨트롤러를 다시 정리해보자.
첫째, Bound Service Account Token Volume은 Kubernetes v1.22부터 기본으로 적용되는 메커니즘으로, Pod 생성 시 서비스 어카운트 어드미션 컨트롤러가 자동으로 kube-api-access-<random> 이라는 이름의 Projected Volume을 Pod에 추가한다. 이 볼륨에는 serviceAccountToken(1시간 만료, kube-apiserver 대상), configMap(kube-root-ca.crt, CA 인증서), downwardAPI(네임스페이스 정보) 세 가지 소스가 포함된다.
둘째, 서비스 어카운트 어드미션 컨트롤러는 API Server의 일부로 동작하는 플러그인으로, Pod가 생성될 때 동기적으로 개입하여 serviceAccountName이 없으면 default로 설정하고, 해당 SA가 존재하는지 확인하며, automountServiceAccountToken이 false가 아니면 위에서 설명한 Projected Volume과 volumeMount를 자동 추가한다.
인증 및 인가
eks 클러스터를 생성하면 해당 클러스터를 생성한 IAM 계정에서만 클러스터의 정보들에 접근할 수 있다. 만약 IAM 권한으로 adminFullAccess를 가지고 있다하더라도 다른 IAM 계정에서 접근을 할 경우 콘솔 및 CLI 상에서 해당 클러스터에 접근하지 못한다.
설치 후 auth 정보를 확인해보면 아래와 같이 나온다. Cli를 통해서 eks를 설치했고 설치 시 사용한 IAM user는 terraform이라는 user를 사용했다. 해당 정보가 아래처럼 보인다.

그러면 brido-macos라는 iam user는 앞서 말한것처럼 admin 권한이 있더라도 클러스터에 대한 정보를 얻어올 수 없다. 아래처럼 해당 iam 유저에 권한을 넣어주는 2가지 방법을 알아보자.

우선 아래 명령어로 현재 클러스터가 어떤 방식을 지원하는지 파악할 수 있다.
aws eks describe-cluster --name myeks --query "cluster.accessConfig" --output json
API_AND_CONFIG_MAP→ 두 방식 병행 (마이그레이션 단계)API→ Access Entries만 사용 (완전 전환)CONFIG_MAP-> 컨피그 맵 방식만 사용 (Deprecated 예정)
참고로 모드 전환은 단방향이다. CONFIG_MAP → API_AND_CONFIG_MAP → API 방향으로만 가능하고 되돌릴 수 없다.
방식 1. aws-auth ConfigMap (레거시)

여기서 configmap을 조회해서 IAM ARN을 K8s username/groups로 변환한다. 이 후 얻어낸 사용자가 무엇을 할 수 있는지 확인해서 요청에 대한 허용/거부를 정한다.
해당 방식의 가장 큰 단점은 ConfigMap을 수동으로 편집하다 잘못된 형식이 들어가면 클러스터 접근이 완전히 차단될 수 있다. 또한 클러스터 생성자 (실습에서는 terraform IAM 유저) 의 cluster-admin 권한을 취소시킬 수 없다.
방식 2. EKS Access Entries API

컨피그 맵 방식과 가장 큰 차이는 Kubernetes API가 아닌 EKS API 레이어가 매핑을 처리한다. 컨피그 맵 방식의 단점들을 보완가능하다는 장점이 있다.
아래처럼 우선 access-entry를 생성한다.
# IAM 사용자에게 클러스터 관리자 권한 부여
aws eks create-access-entry \
--cluster-name my-cluster \
--principal-arn arn:aws:iam::123456789012:user/brido-macos \
--type STANDARD
# AWS 관리형 정책 연결 (가장 간단)
aws eks associate-access-policy \
--cluster-name my-cluster \
--principal-arn arn:aws:iam::123456789012:user/brido-macos \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \
--access-scope type=cluster이후 aws eks list-access-entries --cluster-name my-cluster 로 생성한 iam user가 적절하게 추가되어있는지 확인하고 콘솔, cli 등에서 실제 클러스터에 접근 가능한지 확인하면 된다. 아래처럼 brido-macos 라는 iam 사용자가 적절하게 추가된것을 확인할 수 있다.

IRSA
1. IRSA 개념
IRSA(IAM Roles for Service Accounts)는 Amazon EKS에서 Kubernetes Service Account에 IAM Role을 매핑하여, Pod 내 애플리케이션이 AWS 서비스에 접근할 때 필요한 자격 증명을 안전하게 제공하는 메커니즘이다. EC2 Instance Profile이 EC2 인스턴스에 자격 증명을 제공하는 것과 유사한 방식으로, Pod 단위의 세분화된 권한 관리를 가능하게 한다.
IRSA는 OIDC(OpenID Connect) 기반 페더레이션 ID 지원을 기반으로 동작한다. AWS STS의 AssumeRoleWithWebIdentity API를 통해 OIDC JWT 토큰을 IAM 임시 자격 증명으로 교환하는 구조이다.
2. 사전 구성 요소
IRSA가 동작하기 위해서는 다음 구성 요소들이 사전에 설정되어야 한다.
IAM OIDC Identity Provider
EKS 클러스터마다 하나의 OIDC Provider를 IAM에 등록해야 한다. EKS 클러스터는 고유한 OIDC Issuer URL을 가지며, 이 URL을 IAM Identity Provider로 등록하면 AWS STS가 해당 클러스터에서 발급된 JWT 토큰을 신뢰할 수 있게 된다.
# 클러스터의 OIDC Provider ID 추출
oidc_id=$(aws eks describe-cluster --name myeks \
--query "cluster.identity.oidc.issuer" --output text | cut -d '/' -f 5)
echo $oidc_id
032357E88E266F4AE7C2E8CF6F5EFEB0
# 해당 OIDC Provider가 IAM에 이미 등록되어 있는지 확인
aws iam list-open-id-connect-providers | grep $oidc_id | cut -d "/" -f4
032357E88E266F4AE7C2E8CF6F5EFEB0
출력이 반환되면 해당 클러스터의 OIDC Provider가 IAM에 이미 등록된 상태이다. 출력이 없으면 eksctl utils associate-iam-oidc-provider --cluster myeks --approve 명령으로 등록해야 한다.
OIDC Discovery 엔드포인트 확인
등록된 OIDC Provider는 공개 엔드포인트를 통해 자신의 메타데이터를 노출한다. 이 엔드포인트가 IRSA의 JWT 토큰 검증에 핵심적인 역할을 한다.
① Discovery 문서 (.well-known/openid-configuration)
IDP=$(aws eks describe-cluster --name myeks --query cluster.identity.oidc.issuer --output text)
curl -s $IDP/.well-known/openid-configuration | jq .
issuer: 이 OIDC Provider의 고유 식별자. JWT 토큰의issclaim과 일치해야 한다.jwks_uri: JWT 서명을 검증할 공개키를 가져올 수 있는 엔드포인트.claims_supported:sub(Service Account 정보)와iss(Issuer) — Trust Policy의 Condition에서 이 claim들을 검증한다.id_token_signing_alg_values_supported:RS256— RSA SHA-256 서명 알고리즘을 사용한다.
② JWKS(JSON Web Key Set) 엔드포인트
Discovery 문서의 jwks_uri로 요청하면, JWT 서명 검증에 사용하는 공개키 세트를 받는다.
curl -s $IDP/keys | jq .
각 필드의 의미:
kty: 키 타입 (RSA)kid: Key ID — JWT 헤더의kid와 매칭하여 어떤 키로 서명했는지 식별use:sig— 서명 검증 용도n,e: RSA 공개키의 Modulus와 Exponent 값
이 공개키는 EKS가 관리하며 7일마다 자동 로테이션된다. AWS STS는 JWT 토큰 수신 시 이 엔드포인트에서 공개키를 가져와 서명을 검증한다.
IAM Role + Trust Policy
Service Account가 Assume할 IAM Role을 생성하고, Trust Policy에 OIDC Provider를 Federated Principal로 지정한다. Condition 절에서 특정 네임스페이스와 Service Account만 이 Role을 Assume할 수 있도록 제한한다.
{
"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>:sub": "system:serviceaccount:<namespace>:<sa-name>",
"oidc.eks.<region>.amazonaws.com/id/<CLUSTER_ID>:aud": "sts.amazonaws.com"
}
}
}]
}IAM Policy + IRSA 생성 (eksctl 활용)
실무에서는 eksctl create iamserviceaccount 명령을 사용하여 IAM Role 생성, Trust Policy 설정, Service Account 어노테이션까지 한 번에 처리한다. 내부적으로 CloudFormation 스택을 통해 IAM Role이 생성된다.
# IAM Policy 생성 (예: AWS Load Balancer Controller용)
ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
aws iam create-policy \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://aws_lb_controller_policy.json
# IRSA 생성: IAM Role + Service Account를 한 번에 구성
eksctl create iamserviceaccount \
--cluster=myeks \
--namespace=kube-system \
--name=aws-load-balancer-controller \
--attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy \
--override-existing-serviceaccounts \
--approveeksctl이 수행하는 작업:
- CloudFormation 스택 생성 → IAM Role 생성 (OIDC Provider 기반 Trust Policy 자동 구성)
- 생성된 Role ARN을 Kubernetes Service Account의
eks.amazonaws.com/role-arn어노테이션에 설정 - Service Account가 존재하지 않으면 자동 생성
생성되었는지 확인한다.

생성한 SA에 어노테이션이 잘 설정되었는지 확인한다.

3. Pod Identity Webhook 내부 동작
IRSA의 핵심 메커니즘 중 하나는 EKS 컨트롤 플레인에서 동작하는 Amazon EKS Pod Identity Webhook이다. 이 Webhook은 Kubernetes의 Mutating Admission Controller로서, Pod 생성 요청을 가로채어 AWS 자격 증명 획득에 필요한 요소들을 자동으로 주입한다.
Webhook 등록 확인
EKS 클러스터에는 pod-identity-webhook이라는 이름의 MutatingWebhookConfiguration이 기본 등록되어 있다.

Webhook의 상세 설정을 확인하면, 웹훅 이름이 iam-for-pods.amazonaws.com이며 Pod CREATE 이벤트에 반응하도록 구성되어 있다.
kubectl describe MutatingWebhookConfiguration pod-identity-webhook
# name: iam-for-pods.amazonaws.com
# Pod CREATE 시 호출동작 트리거 조건
Pod Identity Webhook은 Pod 생성 시 해당 Pod의 serviceAccountName에 지정된 Service Account를 조회한다. 해당 Service Account에 eks.amazonaws.com/role-arn 어노테이션이 존재하면 Webhook이 Pod Spec을 변형(mutate)한다. 어노테이션이 없으면 아무 작업도 수행하지 않는다.
Mutation 증명: Deployment 원본 vs 실제 Pod Spec
Pod Identity Webhook의 동작을 가장 명확하게 확인하는 방법은, Deployment 원본에 정의되지 않은 env/volume이 실제 Pod에 존재하는지 비교하는 것이다. AWS Load Balancer Controller를 예시로 살펴본다.
Deployment 원본 확인:
kubectl get deploy -n kube-system aws-load-balancer-controller -o yamlDeployment의 Pod template에는 AWS_ROLE_ARN, AWS_WEB_IDENTITY_TOKEN_FILE 환경 변수나 aws-iam-token 볼륨이 정의되어 있지 않다.
실제 Pod 확인:
kubectl describe pod -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
Deployment 원본에는 없던 5개의 환경 변수와 aws-iam-token Volume Mount가 Pod에 존재한다. 이것이 바로 Pod Identity Webhook이 Mutating Admission 단계에서 주입한 결과이다.
주입되는 환경 변수 상세
env:
- name: AWS_STS_REGIONAL_ENDPOINTS
value: regional
- name: AWS_DEFAULT_REGION
value: ap-northeast-2
- name: AWS_REGION
value: ap-northeast-2
- name: AWS_ROLE_ARN
value: arn:aws:iam::911283464785:role/eksctl-myeks-addon-...
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: /var/run/secrets/eks.amazonaws.com/serviceaccount/tokenAWS_ROLE_ARN: Service Account 어노테이션에서 읽어온 IAM Role ARN. SDK가AssumeRoleWithWebIdentity호출 시 이 Role을 Assume한다.AWS_WEB_IDENTITY_TOKEN_FILE: JWT 토큰이 마운트된 파일 경로. SDK가 이 파일에서 토큰을 읽어 STS에 전달한다.AWS_DEFAULT_REGION/AWS_REGION: 클러스터 리전. Webhook의--aws-default-region플래그 설정에 의해 주입된다.AWS_STS_REGIONAL_ENDPOINTS:regional로 설정되면 STS 호출이 글로벌 엔드포인트(us-east-1) 대신 리전별 엔드포인트로 라우팅된다. Service Account 어노테이션eks.amazonaws.com/sts-regional-endpoints: "true"또는 Webhook의--sts-regional-endpoint플래그에 의해 주입된다.
Webhook은 이미 Pod Spec에 해당 환경 변수가 존재하는 경우 덮어쓰지 않는다.
주입되는 Volume 상세: 3개 Volume의 역할 구분
실제 LBC Pod에는 3개의 Volume이 마운트되어 있다. 각각의 역할이 다르므로 정확히 구분해야 한다.

| Volume | 주입 주체 | Audience | 용도 |
|---|---|---|---|
aws-iam-token | Pod Identity Webhook | sts.amazonaws.com | AWS STS에 제출하여 IAM 임시 자격 증명 획득 |
kube-api-access-* | kubelet (자동) | K8s API 서버 | Kubernetes API 서버 인증 |
cert | Deployment 정의 | - | LBC의 Validating/Mutating Webhook TLS 인증서 |
핵심적인 차이는 audience이다. aws-iam-token의 audience는 sts.amazonaws.com으로, 이 토큰은 Kubernetes API 서버가 아닌 AWS STS를 대상으로 발급된 것이다. 반면 kube-api-access의 토큰은 Kubernetes API 서버 인증 전용이다. 두 토큰 모두 Projected Service Account Token이지만, audience가 다르기 때문에 서로의 용도로 사용할 수 없다.
Projected Service Account Token과 Legacy Token의 차이
Kubernetes에는 두 종류의 Service Account Token이 존재하며, IRSA는 Projected Token을 사용한다. (앞서 언급된 내용 정리)
Legacy Service Account Token: Secret 리소스에 저장되는 정적 토큰으로, 만료 기간이 없고 Audience 바인딩이 없다. Kubernetes API 서버 인증 전용이다.
Projected Service Account Token (IRSA용): Kubernetes 1.12부터 지원되는 동적 토큰으로, 다음 특징을 갖는다:
- 유효 기간 설정 가능 (
expirationSeconds) — 만료 후 kubelet이 자동 갱신 - Audience 바인딩 — 특정 수신자(예:
sts.amazonaws.com)를 대상으로 발급 - OIDC JWT 형식 —
iss(Issuer),sub(Subject),aud(Audience) claim을 포함 - 서명 키 자동 로테이션 — EKS에서 관리하는 서명 키 쌍이 7일마다 자동 로테이션
Service Account 어노테이션 옵션
Pod Identity Webhook은 다음 어노테이션들을 인식한다:
eks.amazonaws.com/role-arn: (필수) Assume할 IAM Role의 ARNeks.amazonaws.com/audience: 토큰의 Audience. 기본값은sts.amazonaws.comeks.amazonaws.com/token-expiration: 토큰 만료 시간(초). 기본값은86400(24시간)eks.amazonaws.com/sts-regional-endpoints:"true"설정 시AWS_STS_REGIONAL_ENDPOINTS=regional환경 변수 주입eks.amazonaws.com/skip-containers: 특정 컨테이너에 대해 Volume/환경 변수 주입을 건너뛸 수 있는 쉼표 구분 목록
IRSA 동작 흐름 (6단계)

아래 흐름은 첨부된 아키텍처 다이어그램의 ①~⑥ 번호에 대응한다.
Step 0 (사전 단계): Pod 생성 시 Pod Identity Webhook에 의한 Mutation
kubectl apply로 Pod 생성 요청이 Kubernetes API 서버에 도달하면, Mutating Admission 단계에서 pod-identity-webhook(iam-for-pods.amazonaws.com)이 호출된다. Webhook은 Service Account의 eks.amazonaws.com/role-arn 어노테이션을 감지하고, 3절에서 설명한 환경 변수, Projected Volume, Volume Mount를 Pod Spec에 자동 주입한다. 이 모든 과정은 사용자가 인지하지 못하는 사이에 투명하게 처리된다.
Step ① Pod → AWS STS: 자격 증명 요청
Pod 내 AWS SDK가 Default Credential Chain을 따라 AWS_WEB_IDENTITY_TOKEN_FILE과 AWS_ROLE_ARN 환경 변수를 감지한다. SDK는 /var/run/secrets/eks.amazonaws.com/serviceaccount/token 파일에서 JWT 토큰을 읽고, 자동으로 AWS STS에 AssumeRoleWithWebIdentity API를 호출한다. 이때 JWT 토큰과 IAM Role ARN을 함께 전송한다.
다이어그램: "I need credentials to list S3 buckets. Here is my JWT & IAM Role ARN"
AssumeRoleWithWebIdentity 호출 시 AWS 보안 자격 증명이 필요하지 않다. 호출자의 ID는 웹 ID 공급자의 토큰으로 검증된다.
Step ② AWS STS → IAM: 토큰 검증 및 권한 확인 요청
STS는 수신한 JWT 토큰과 IAM Role ARN을 기반으로 IAM에 검증을 요청한다. IAM은 다음 항목들을 확인한다:
- 해당 IAM Role의 Trust Policy에 지정된 OIDC Provider와 토큰의
issclaim이 일치하는지 - Trust Policy의 Condition에 명시된
subclaim(예:system:serviceaccount:kube-system:aws-load-balancer-controller)이 토큰과 일치하는지 - Trust Policy의 Condition에 명시된
audclaim(sts.amazonaws.com)이 토큰과 일치하는지
다이어그램: "Hey, can you validate and let me know if I can send this POD temporary credentials to list S3 buckets? Here is the token and IAM Role ARN"
Step ③ IAM → OIDC Provider: JWT 서명 검증
IAM은 2.1절에서 확인한 OIDC Discovery 엔드포인트의 jwks_uri를 호출하여 공개키를 가져온다. 이 키의 kid와 JWT 헤더의 kid를 매칭한 뒤, 해당 RSA 공개키(n, e)로 JWT 서명을 암호학적으로 검증한다.
검증 과정:
- 토큰 수신: STS가 Pod에서 받은 JWT 토큰을 IAM에 전달
- Issuer 확인: 토큰의
iss필드에서 OIDC Provider URL 추출 - 공개키 획득:
{issuer}/.well-known/openid-configuration→jwks_uri→/keys경로에서 현재 유효한 공개키(JWKS) 수신 - 서명 검증: 공개키와 토큰의 서명(Signature)을 대조하여 위변조 여부 확인
다이어그램: "Trust — Get Public Keys from JWKS URL using /.well-known/OpenID-configuration and verify"
Step ④ IAM → STS: 검증 결과 응답
모든 검증이 통과하면, IAM은 STS에 해당 Role에 연결된 IAM Policy의 권한으로 임시 자격 증명을 발급해도 된다는 승인 응답을 보낸다.
다이어그램: "Everything looks good. Go ahead!"
Step ⑤ STS → Pod: 임시 자격 증명 발급
STS는 Pod에 임시 보안 자격 증명을 반환한다. 이 자격 증명은 다음 세 가지로 구성된다:
- Access Key ID
- Secret Access Key
- Session Token
이 자격 증명은 유효 기간이 한정된 임시 자격 증명이며, 만료되면 SDK가 자동으로 새 토큰을 사용해 재발급을 요청한다.
다이어그램: "Here are your temporary credentials"
Step ⑥ Pod → AWS Service: API 호출
Pod는 발급받은 임시 자격 증명을 사용하여 실제 AWS 서비스(예: S3)에 API 요청을 수행한다. 이때 요청은 IAM Role에 Attach된 Policy에서 허용하는 범위 내에서만 가능하다.
다이어그램: "Give me list of buckets. Here are my Temporary credentials"

OAuth2 역할 매핑 정리
Pod (AWS SDK) = Client OAuth2에서 Client가 Auth Server에 토큰을 요청하듯, Pod가 K8s OIDC Provider로부터 받은 PSAT을 들고 STS에 크레덴셜을 요청
K8s OIDC Provider = Auth Server JWT(PSAT)를 발급하고 서명해. JWKS 엔드포인트로 공개키를 외부에 제공하는 것도 Auth Server의 역할
AWS STS = Token Exchange (RFC 8693) 표준 OAuth2에는 없는 개념. PSAT(OIDC 토큰)를 받아서 AWS 전용 임시 크레덴셜로 교환해주는 중간 교환소
AWS IAM = Authorization 정책 레이어 OAuth2의 scope/권한 정의에 해당하고. Trust Policy로 "어떤 sub, aud를 가진 토큰만 허용"을 정의하고, IAM Policy로 "허용된 액션"을 정의
AWS S3 / RDS 등 = Resource Server 임시 크레덴셜을 받은 Client(Pod)가 최종적으로 접근하는 보호된 리소스
AWS S3 읽기 전용 권한이 필요한 파드에 IRSA 실습
우선 서비스 어카운트 리소스를 새롭게 하나 생성한다.
# Create an iamserviceaccount - AWS IAM role bound to a Kubernetes service account
eksctl create iamserviceaccount \
--name my-sa \
--namespace default \
--cluster $CLUSTER_NAME \
--approve \
--role-name eksctl-myeks-pod-irsa-s3-readonly-role \
--attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`AmazonS3ReadOnlyAccess`].Arn' --output text)아래처럼 생성된것을 확인한다.

# Pod Identity Webhook은 mutating webhook을 통해 아래 Env 내용과 1개의 볼륨을 추가함
kubectl get pod eks-iam-test3
kubectl get pod eks-iam-test3 -o yaml또한 웹훅에 의해서 아래 토큰 및 볼륨이 추가된것도 확인할 수 있다.

다음으로 파드 내에서 해당 Role이 잘 사용되어 s3 리소스에 접근되는지 보자.
kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn
EKS Pod Identity
개요
EKS Pod Identity는 2023년 re:Invent에서 발표된 기능으로, EKS 클러스터 내 Pod가 AWS 리소스에 접근할 때 필요한 IAM 권한을 보다 간편하게 부여하는 메커니즘이다. 기존 IRSA(IAM Roles for Service Accounts)가 OIDC Provider 기반의 복잡한 설정을 요구했다면, Pod Identity는 이를 크게 단순화하여 EKS API와 전용 Agent를 통해 Pod 수준의 IAM 자격 증명을 제공한다.
동작 원리
EKS Pod Identity의 전체 인증 흐름은 아래와 같이 5단계로 구성된다.

Step 1 — IAM Role 생성
먼저 Pod가 사용할 IAM Role을 생성한다. 이때 Trust Policy의 Principal로 OIDC Provider가 아닌 EKS 전용 서비스 pricipal pods.eks.amazonaws.com을 지정한다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}IRSA에서는 클러스터별 OIDC Issuer URL을 Trust Policy에 명시해야 했지만, Pod Identity에서는 pods.eks.amazonaws.com 하나로 모든 클러스터에서 동일한 Role을 재사용할 수 있다.
Step 2 — Pod Identity Association 생성
EKS API를 통해 IAM Role ↔ Kubernetes Service Account 간의 매핑(Association)을 생성한다. 이 매핑 정보는 EKS 컨트롤 플레인에 저장되며, 특정 Namespace의 특정 Service Account와 연결된다.
Step 3 — Pod Spec Mutation (Webhook)
Pod가 생성될 때, EKS 컨트롤 플레인의 EKS Pod Identity Webhook이 Pod Spec을 자동으로 변경(mutate)한다. 구체적으로 다음 항목들이 주입된다.
- 환경변수
AWS_CONTAINER_CREDENTIALS_FULL_URI→http://169.254.170.23/v1/credentials - 환경변수
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE→ Projected Service Account Token 경로 - Projected Volume으로 audience
pods.eks.amazonaws.com, 만료시간 24시간의 SA Token 마운트
이 Webhook 주입은 IRSA에서 eks.amazonaws.com/role-arn 어노테이션 기반으로 AWS_WEB_IDENTITY_TOKEN_FILE과 AWS_ROLE_ARN을 주입하는 동작과 대응된다. 단, Pod Identity는 Service Account에 별도 어노테이션을 달 필요가 없다.
Step 4 — 자격 증명 획득
Pod 내 AWS SDK가 AWS API를 호출할 때, Container Credential Provider 체인을 따라 로컬 주소 169.254.170.23으로 자격 증명을 요청한다.
- (4) 각 노드에 DaemonSet으로 배포된 EKS Pod Identity Agent가 이 요청을 수신한다.
- (4a) Agent는 Pod의 Projected SA Token을 포함하여 EKS Auth API에
AssumeRoleForPodIdentity요청을 보낸다. - (4b) EKS Auth API는 EKS Pod Identity API에 연결하여 해당 SA Token이 유효한 Association에 매핑되는지 검증한다.
- 검증이 완료되면 임시 자격 증명(Temporary Credentials)이 Agent를 거쳐 Pod로 반환된다.
핵심 차이점은, IRSA에서는 각 Pod의 AWS SDK가 직접 sts:AssumeRoleWithWebIdentity를 호출하던 것과 달리, Pod Identity에서는 노드당 한 번 Agent가 자격 증명을 가져와 캐싱한다는 것이다. 이로 인해 STS 호출 부하가 크게 줄어든다.
Step 5 — AWS 리소스 접근
임시 자격 증명을 획득한 Pod는 해당 IAM Role에 부여된 권한 범위 내에서 S3, DynamoDB 등 AWS 리소스에 접근한다.
실습: S3 읽기 전용 권한을 Pod에 부여하기
myeks 클러스터에서 EKS Pod Identity를 사용하여 Pod에 S3 읽기 전용 권한을 부여하는 실습을 수행했다.
1) EKS Pod Identity Agent 애드온 확인

eks-pod-identity-agent 애드온이 ACTIVE 상태로 정상 설치되어 있다. 이 애드온은 각 워커 노드에 DaemonSet으로 배포되며, Pod로부터의 자격 증명 요청을 처리하는 Agent 역할을 한다.
2) Pod Identity Association 생성 (eksctl)
eksctl create podidentityassociation \
--cluster $CLUSTER_NAME \
--namespace default \
--create-service-account \
--service-account-name s3-sa \
--role-name s3-eks-pod-identity-role \
--permission-policy-arns arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
--region ap-northeast-2이 단일 명령으로 다음이 한 번에 수행된다.
- IAM Role
s3-eks-pod-identity-role생성 (Trust Policy에pods.eks.amazonaws.com자동 설정) AmazonS3ReadOnlyAccessPermission Policy 연결- Kubernetes Service Account
s3-sa생성 - Pod Identity Association (IAM Role ↔ SA 매핑) 등록
여기서 역할을 정확히 구분하면, Pod Identity Association 자체는 "어떤 SA가 어떤 IAM Role을 사용하는가"라는 매핑만 담당한다. Pod가 실제로 어떤 AWS 리소스에 어떤 작업을 할 수 있는지는 해당 IAM Role에 연결된 Permission Policy(이 경우 AmazonS3ReadOnlyAccess)가 결정한다.
eksctl이 이 두 관심사를 하나의 명령으로 묶어주기 때문에 한 번에 처리되는 것처럼 보이지만, aws eks create-pod-identity-association CLI를 직접 사용할 경우에는 IAM Role을 먼저 생성하고 --role-arn으로 기존 Role을 지정하는 2단계 구성이 필요하다.
IRSA에서는 OIDC Provider 설정 → IAM Role + Trust Policy 작성 → SA 생성 + 어노테이션 추가 등 여러 단계가 필요했던 것과 비교하면 현저히 간소화된 워크플로우다.
3) Service Account 생성 확인

s3-sa Service Account가 default 네임스페이스에 자동 생성되었다. IRSA와 달리 eks.amazonaws.com/role-arn 어노테이션이 필요 없다. Association 정보는 Kubernetes 오브젝트가 아닌 EKS API 측에 저장되기 때문이다.
4) Pod Identity Association 조회

EKS API에 Association이 정상 등록되어 있으며, default 네임스페이스의 s3-sa Service Account에 매핑된 것을 확인할 수 있다.
5) CloudTrail 이벤트 확인
AWS CloudTrail에서 CreatePodIdentityAssociation 이벤트가 기록된 것을 확인할 수 있다. User name이 terraform으로 표시되어 있으며, Event source는 eks.amazonaws.com이다. 이처럼 Pod Identity Association의 생성/변경/삭제는 모두 CloudTrail에 감사 로그로 남기 때문에 보안 추적이 용이하다.

IRSA와 EKS Pod Identity 비교
| 구분 | IRSA | EKS Pod Identity |
|---|---|---|
| 인증 방식 | OIDC Provider + AssumeRoleWithWebIdentity | EKS Auth API + AssumeRoleForPodIdentity |
| Trust Policy Principal | 클러스터별 OIDC Provider ARN (Federated) | pods.eks.amazonaws.com (Service) |
| OIDC Provider 필요 여부 | 필수 (클러스터당 1개) | 불필요 |
| SA 어노테이션 필요 여부 | 필수 (eks.amazonaws.com/role-arn) | 불필요 (EKS API에서 관리) |
| IAM Role 재사용성 | 클러스터마다 Trust Policy에 OIDC Issuer 추가 필요 | 동일 Role을 여러 클러스터에서 Trust Policy 수정 없이 재사용 |
| STS 호출 주체 | 각 Pod의 AWS SDK가 직접 호출 | 노드의 Pod Identity Agent가 대리 호출 후 캐싱 |
| Session Tags 지원 | 미지원 | 지원 (클러스터명, 네임스페이스, SA명 등 자동 태깅) |
| 지원 환경 | EKS, EKS Anywhere, Self-managed K8s on EC2, ROSA | EKS on Cloud 전용 |
| 설정 복잡도 | 높음 (OIDC + Trust Policy + SA 어노테이션) | 낮음 (Association 생성 1회) |
| Trust Policy 크기 제한 이슈 | 클러스터 수 증가 시 Trust Policy 크기 제한(2,048자) 도달 가능 | 해당 없음 |
| 교차 계정 | OIDC Trust + 수동 Role Chaining 또는 Resource-based Policy | 2025년 6월부터 targetRoleArn 파라미터로 자동 Role Chaining 지원 |
Cross-Account 접근이란?
실무 환경에서는 AWS 계정을 용도별로 분리하여 운영하는 멀티 어카운트 전략이 일반적이다. 예를 들어 EKS 클러스터는 개발 계정(Account A)에서 운영하지만, 접근해야 하는 S3 버킷이나 DynamoDB 테이블은 데이터 계정(Account B)에 존재하는 상황이 빈번하다.
┌─────────────────────────┐ ┌─────────────────────────┐
│ Account A (개발) │ │ Account B (데이터) │
│ │ │ │
│ EKS Cluster │ │ S3 Bucket │
│ └─ Pod (s3-sa) │ ──────> │ DynamoDB Table │
│ │ │ Route53 Hosted Zone │
│ IAM Role A (Source) │ │ IAM Role B (Target) │
└─────────────────────────┘ └─────────────────────────┘이처럼 다른 AWS 계정의 리소스에 접근하는 것이 교차 계정 접근이며, 이를 위해서는 계정 간 신뢰 관계를 기반으로 한 IAM Role Chaining이 필요하다.
IRSA에서의 교차 계정 접근
IRSA 환경에서 교차 계정 접근(A가 B assume)을 구성하려면 다음과 같은 수동 작업이 필요하다.
- Account A에 OIDC Provider 기반 IAM Role A를 생성한다.
- Account B에 Target IAM Role B를 생성하고, Trust Policy에서 Role A의
sts:AssumeRole을 허용한다. - Role A의 Permission Policy에
sts:AssumeRole로 Role B를 Assume할 수 있는 권한을 추가한다. - 애플리케이션 코드에서 직접 Role Chaining 로직을 구현하거나, AWS SDK Config 파일에
source_profile+role_arn을 설정해야 한다.
즉 IRSA에서는 Role Chaining을 애플리케이션 또는 SDK 설정 레벨에서 처리해야 하며, 이는 코드 수정을 수반한다.
EKS Pod Identity에서의 교차 계정 접근
2025년 6월에 출시된 Pod Identity의 교차 계정 기능은 이 과정을 크게 단순화한다. Association 생성 시 roleArn(Source Role)과 targetRoleArn(Target Role) 두 개를 지정하면, EKS Auth API가 Role Chaining을 자동으로 수행한다.
aws eks create-pod-identity-association \
--cluster-name myeks \
--namespace default \
--service-account-name s3-sa \
--role-arn arn:aws:iam::111111111111:role/source-role \ # Account A (EKS 계정)
--target-role-arn arn:aws:iam::222222222222:role/target-role # Account B (리소스 계정)Pod가 자격 증명을 요청하면, EKS Pod Identity Agent → EKS Auth API 경로에서 다음이 자동으로 일어난다.
source-role을 Assume (sts:AssumeRolebypods.eks.amazonaws.com)source-role의 자격 증명으로target-role을 Assume (Role Chaining)- 최종적으로
target-role의 임시 자격 증명이 Pod에 전달됨
애플리케이션 코드 수정 없이 교차 계정 접근이 가능하며, Session Tags도 그대로 유지되어 ABAC 패턴도 계정 간에 적용할 수 있다. 이때 source-role은 반드시 EKS 클러스터와 동일한 계정에 있어야 하고, target-role의 Trust Policy에서 source-role의 sts:AssumeRole을 허용해야 한다.



