- K8S(EKS) Identity and Access Management
- K8S 인증/인가 기초 실습
- K8S(EKS) Pod(SA) with IAM Role → AWS 리소스 사용
- EKS Pod Identity
- 도전과제) IRSA 와 Pod Identity 를 비교 정리해보기
▫️ 1.1.1 접근 제어 방식
▫️ 1.1.2 권한 부여 방식
▫️ Pod → AWS 권한은 IAM Role 기반
▫️ 방식:
▫️ 1.3.1 관리자가 kubectl get node 실행 시 과정

# 임시 보안 자격 증명(토큰)을 요청 : expirationTimestamp 시간경과 시 토큰 재발급 필요.
## kubectl이 expirationTimestamp를 보고 알아서 새 토큰을 받아오기 때문에 사용자는 인지하지 못한 채 계속 작업할 수 있습니다.
export CLUSTER_NAME=myeks
aws eks get-token --cluster-name $CLUSTER_NAME | jq
aws eks get-token --cluster-name $CLUSTER_NAME | jq -r '.status.token'
aws eks get-token --cluster-name $CLUSTER_NAME --debug | jq
# EKS 인증 토큰 가져오기
## 각 토큰은 k8s-aws-v1로 시작하여 base64 인코딩 문자열로 이어집니다.
TOKEN_DATA=$(aws eks get-token --cluster-name myeks | jq -r '.status.token')
echo $TOKEN_DATA

▫️1.3.2 클라이언트(kubectl)가 K8S(EKS) API 서버(Endpoint)에 Action 요청 : 일반적인 k8s api 요청 호출 + Bearer Token 포함

kubectl의 Client-Go 라이브러리는 Pre-Signed URL을 Bearer Token으로 k8s action 과 함께 EKS API Cluster Endpoint로 요청을 보냄
kubectl은 보안 민감 정보(Authorization 헤더 등)를 일반적인 로그 레벨에서 마스킹(Masking) 처리하여 출력하지 않도록 설계되어 있습니다.
curl 명령어를 흉내 내어 출력할 때 Authorization 헤더는 고의적으로 제외
# 2. curl 호출 : 로그에 찍힌 URL과 헤더에 토큰 추가하여 실행 => 15분 후 토큰 만료되니, 15분 이후에는 토큰 재발급해서 사용 필요!
## Authorization: Bearer 확인!
"arn:aws:iam::102011327768:user/yenajo"curl -k -s -XGET \
-H "Authorization: Bearer $TOKEN_DATA" \
-H "Accept: application/json" \
'https://477EA305223C6CF560322DABBFBF429C.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500'
(주소는 EKS 클러스터의 Kubernetes API Server 엔드포인트 주소로 변경 해서 테스트)
▫️ 1.3.2 k8s(eks) api 는 ‘웹 토큰 인증’ 방식을 통한 인증을 위해서, Token Review 요청 ⇒ AWS 인증 확인 후 응답 시 : 유저, K8S Subject(group) 등

# tokenreviews api 리소스 확인
kubectl api-resources | grep authentication

TokenReview는 사용자에 대한 토큰 인증을 시도(확인)합니다.

직접 TokenReview 요청 해보기
TOKEN_DATA=$(aws eks get-token --cluster-name myeks | jq -r '.status.token')
cat > token-review.yaml << EOF
apiVersion: authentication.k8s.io/v1
kind: TokenReview
metadata:
name: mytoken
spec:
token: ${TOKEN_DATA}
EOF
cat token-review.yaml

▫️1.3.3 Server side (aws-iam-authenticator server) 는 AWS STS GetCallerIdentity 호출(제출)을 통해 서명 검증(인증!) 후 사용자 정보 반환

fields @timestamp, @message, @logStream, @log,stsendpoint
| filter @logStream like /authenticator/
| filter @message like /stsendpoint/
| sort @timestamp desc
| limit 10000


▫️1.3.4 AWS IAM 주체 → K8S Subject 매칭 + 인가! k8s Authz : 방안1(EKS API - Docs) , 방안2(aws-auth ConfigMap is deprecated - Docs)


▫️1.3.5 EKS API - Docs : AWS IAM 주체 → K8S Subject 매칭 + k8s Authz ‘Webhook’ 인가! - SubjectAccessReview

Access Entry : AWS IAM 주체 → K8S Subject 매칭(Authorization Mapping) + k8s Authz ‘Webhook’ 인가! ⇒ k8s action 실행 후 리턴!

EKS authorizer : 인가 모드 지원 - Node, (k8S) RBAC, Webhook


# 현재 Access Entry 에 IAM User admin
aws eks list-access-entries --cluster-name myeks | jq

# SubjectAccessReviews는 사용자/그룹이 K8S Action이(인가) 가능한지 확인
kubectl explain subjectaccessreviews

# Shows the subject for the current context with which one authenticates with the cluster
kubectl rbac-tool -h
kubectl rbac-tool whoami
(kubecrew, rbac-tool 설치되어있어야함)

▪️ 직접 SubjectAccessReviews 요청 확인 해보기
ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
cat > sar-request.yaml << EOF
apiVersion: authorization.k8s.io/v1
kind: SubjectAccessReview
spec:
user: "arn:aws:iam::${ACCOUNT_ID}:user/admin" # 확인하고 싶은 IAM ARN 또는 K8s User
groups:
- system:masters
resourceAttributes:
namespace: "kube-system"
verb: "get"
resource: "pods"
EOF
cat sar-request.yaml
▪️ EKS에서 제공하는 EKA API Access Entry 관리형 정책 확인
# 맵핑된 정책 확인 : macOS - 뒤에 admin 은 자신의 IAM User로 변경해서 실행 할 것!
export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
aws eks list-associated-access-policies --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID\:user/admin | jq # macOS
# 맵핑된 정책 확인 : Linux (운영서버, WSL2 등) - 뒤에 admin 은 자신의 IAM User로 변경해서 실행 할 것!
export ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
# EKS에서 제공하는 관리형 정책 확인
aws eks list-access-policies --output table
‼️ 참고: 일반 K8S system:masters 그룹에 속한 사용자 특징 : 인증 후 → 인가 우회. 즉, 인증 시 바로 슈퍼유저 권한
▫️1.3.6 EC2에서 Node IAM Role(EC2 Instance Profile)을 사용(Assume)하여 K8S(EKS) API를 호출 시 과정

# 노드에서 실행
aws ssm start-session --target $NODE1
sudo su -
# EC2 에서 별도 aws configure 없이 sts 실행 확인 : IAM Role!
aws sts get-caller-identity --query Arn

assume 되어있는것을 확인 가능하다

권한자체에 s3 에 관련된 권한은 없으므로 불가능
▪️ 클라이언트(kubectl)가 K8S(EKS) API 서버(Endpoint)에 Action 요청 : 일반적인 k8s api 요청 호출 + Bearer Token 포함
# kubectl 설치(node)
# https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html
curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.35.2/2026-02-27/bin/linux/amd64/kubectl
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version

거부

자기 자신은 조회 가능한것을 확인할 수 있습니다
▫️1.3.7 k8s(eks) api 는 ‘웹 토큰 인증’ 방식을 통한 인증을 위해서, Token Review 요청 ⇒ AWS 인증 확인 후 응답 시 : 유저, K8S Subject(group) 등
▪️ EKS 토큰 인증 흐름 정리
kubectl 요청
↓
Bearer Token 포함 → EKS API Server
↓
TokenReview 요청 → AWS STS
↓
IAM Role 확인 → K8s Identity 반환
↓
RBAC 권한 확인 → 허용/거부
토큰 발급
aws eks get-token 으로 STS 기반 토큰 생성
k8s-aws-v1. 로 시작하는 base64 인코딩된 STS URL
API 서버가 토큰 검증 (TokenReview)
EKS API Server가 AWS IAM Authenticator를 통해 STS에 "이 토큰 누구꺼야?" 질문
STS 응답으로 IAM Identity 확인
K8s Identity로 매핑
IAM Role: myeks-ng-1
↓
username: system:node:ip-192-168-21-21...
groups: system:nodes, system:authenticated
IAM Role → K8s username/group으로 변환
▪️ TokenReview 응답에서 핵심 필드

▫️ aws cli 환경에서, 신입 Devops 엔지어를 위한 eks 자격 증명 설정
# testuser 사용자 생성
aws iam create-user --user-name testuser
# 사용자에게 프로그래밍 방식 액세스 권한 부여
aws iam create-access-key --user-name testuser
# testuser 사용자에 정책을 추가
aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --user-name testuser
# get-caller-identity 확인
aws sts get-caller-identity --query Arn

# aws-cli 컨테이너 실행
docker run -it --name aws-cli --entrypoint /bin/sh amazon/aws-cli
# testuser 자격증명 설정
aws configure
AWS Access Key ID [None]: AKIA5ILF2F...
AWS Secret Access Key [None]: ePpXdhA3cP....
Default region name [None]: ap-northeast-2
# get-caller-identity 확인
aws sts get-caller-identity --query Arn
"arn:aws:iam::911283464785:user/testuser"
# aws cli 명령 시도
aws s3 ls


근데 저는 왜 실패하는거같지가 않죠..
무튼 eks는 eks를 생성한 사람 외에 user는 iam 액세스 항목에서 권한설정을 해줘야합니다(아니면 권한을 부여받던가)
▪️ testuser의 access entry 생성
export CLUSTER_NAME=myeks
export ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
aws eks create-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser
aws eks list-access-entries --cluster-name $CLUSTER_NAME | jq -r .accessEntries[]

아직 그룹이나 권한은 없다
▪️ testuser에 AmazonEKSAdminPolicy 연동
aws eks associate-access-policy --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminPolicy --access-scope type=cluster

사고칠지도 모르는 신입사원에게 AmazonEKSClusterAdminPolicy권한보다 살짝 부족한 권한을 부여했다
확인
aws eks list-associated-access-policies --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser | jq
aws eks describe-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser | jq

kube-apiserver로 들어오는 모든 요청은 먼저 인증을 거칩니다

EKS는 aws-iam-authenticator(또는 aws eks get-token)를 통해 IAM 토큰을 Webhook으로 검증합니다. 토큰 TTL은 15분입니다.

인증 이후 요청이 허용되는지 판단합니다. K8s는 여러 인가 모드를 지원하며, EKS는 기본적으로 RBAC을 사용합니다.

▫️ 2.2.1 RBAC 핵심 리소스
Role / ClusterRole → 무엇을(리소스) 어떻게(verb) 할 수 있는지 정의
RoleBinding / ClusterRoleBinding → 누구에게 Role을 부여할지 연결

kubectl 요청
↓
[인증] IAM Token → Webhook(aws-iam-authenticator) → IAM 검증
↓
[매핑] aws-auth ConfigMap or Access Entry → K8s User/Group 결정
↓
[인가] RBAC → Role/ClusterRole 확인 → 허용 or 거부

① Pod가 뜰 때 SA(ServiceAccount)에 묶인 JWT 토큰을 자동으로 받음
② Pod가 AWS 리소스를 쓰려 하면, 그 JWT로 STS에 AssumeRoleWithWebIdentity를 요청
③ STS가 IAM Role의 신뢰 정책을 확인하고 임시 자격증명(AccessKey + SecretKey + SessionToken)을 돌려줌
④ 이 임시 자격증명으로 S3, DynamoDB 등 AWS 리소스를 호출
OIDC : 사용자를 인증해 사용자에게 액세스 권한을 부여할 수 있게 해주는 프로토콜
(IRSA 는 OIDC 씀)

▪️ 동작 과정
Pod ↔ IAM Role을 직접 매핑, Node 전체 권한 없이 Pod별 최소 권한

automountServiceAccountToken: falsePod를 생성할 때 Pod Spec에 토큰을 전달하지 않도록 설정할 수 있습니다.# 파드1 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: eks-iam-test1
spec:
containers:
- name: my-aws-cli
image: amazon/aws-cli:latest
args: ['s3', 'ls']
restartPolicy: Never
automountServiceAccountToken: false
terminationGracePeriodSeconds: 0
EOF
# 확인
kubectl get pod
kubectl describe pod
# 로그 확인
kubectl logs eks-iam-test1
# 파드1 삭제
kubectl delete pod eks-iam-test1
automountServiceAccountToken: false 로 해놔서

권한이 없어 pod생성이 막힘
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: eks-iam-test2
spec:
containers:
- name: my-aws-cli
image: amazon/aws-cli:latest
command: ['sleep', '36000']
restartPolicy: Never
terminationGracePeriodSeconds: 0
EOF
# 확인
kubectl get pod
kubectl describe pod
kubectl get pod eks-iam-test2 -o yaml



이것이 되어있는이유는 terraform 으로 코드 배포를 할 때 irsa 를 사용하기위해
enable_irsa = true 를 넣어놨기 때문
▪️ Kubernetes 서비스 계정이 IAM 역할을 통해 사용할 IAM Policy 생성
# IAM Policy json 파일 다운로드 : Download an IAM policy for the AWS Load Balancer Controller that allows it to make calls to AWS APIs on your behalf.
curl -o aws_lb_controller_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/refs/heads/main/docs/install/iam_policy.json
cat aws_lb_controller_policy.json | jq
# AWSLoadBalancerControllerIAMPolicy 생성 : Create an IAM policy using the policy downloaded in the previous step.
aws iam create-policy \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://aws_lb_controller_policy.json
# 확인
ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text)
aws iam get-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy | jq

이것을 사용할 수 있게 IRSA 설정을 해보자
# IRSA 생성 : cloudforamtion 를 통해 IAM Role 생성
eksctl create iamserviceaccount \
--cluster=$CLUSTER_NAME \
--namespace=kube-system \
--name=aws-load-balancer-controller \
--attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy \
--override-existing-serviceaccounts \
--approve
# 확인 : 아래 출력되는 Role ARN을 관리콘솔에서 확인!
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
iam 롤을 만들어 주고,
k8s sa를 만들어주고, sa에 annotation을 주입


중요한것은 신뢰관계

▪️ AWS LBC 설치
# Helm Chart Repository 추가
helm repo add eks https://aws.github.io/eks-charts
helm repo update
# Helm Chart - AWS Load Balancer Controller 설치
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --version 3.1.0 \
--set clusterName=$CLUSTER_NAME \
--set serviceAccount.name=aws-load-balancer-controller \
--set serviceAccount.create=false \
--set enableCertManager=true


▪️ 상세 확인 : Pod Spec 주입 확인, aud 가 aws sts 확인
AWS_ROLE_ARN: arn:aws:iam::102011327768:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-Zev8Eh5cR6Zw
AWS_WEB_IDENTITY_TOKEN_FILE: /var/run/secrets/eks.amazonaws.com/serviceaccount/token
/var/run/secrets/eks.amazonaws.com/serviceaccount ← IRSA 토큰
IRSA가 작동하는 이유 → ServiceAccount 자체에 annotation이 달려있기 때문
Deployment에는 IRSA 설정이 안 보이는데 Pod에는 있다
↓
EKS의 Pod Identity Webhook이 ServiceAccount annotation을 보고
Pod 생성 시점에 자동으로 환경변수 + 볼륨을 주입해주는 것
Deployment YAML엔 없었는데 Pod에 있는 것들 = 웹훅이 주입한 것
▪️ (AWS LBC 동작 확인) kube-ops-view 배포 + ALB Ingress(MyDomain, HTTPS → HTTP)
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
alb.ingress.kubernetes.io/group.name: study
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/ssl-redirect: "443"
alb.ingress.kubernetes.io/success-codes: 200-399
alb.ingress.kubernetes.io/target-type: ip
labels:
app.kubernetes.io/name: kubeopsview
name: kubeopsview
namespace: kube-system
spec:
ingressClassName: alb
rules:
- host: kubeopsview.$MyDomain
http:
paths:
- backend:
service:
name: kube-ops-view
port:
number: 8080
path: /
pathType: Prefix
EOF

▪️ 목표 2 : 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)
# 확인 >> 웹 관리 콘솔에서 CloudFormation Stack >> IAM Role 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
kubectl get sa
kubectl describe sa my-sa

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: eks-iam-test3
spec:
serviceAccountName: my-sa
containers:
- name: my-aws-cli
image: amazon/aws-cli:latest
command: ['sleep', '36000']
restartPolicy: Never
terminationGracePeriodSeconds: 0
EOF
# Pod Identity Webhook은 mutating webhook을 통해 아래 Env 내용과 1개의 볼륨을 추가함
kubectl get pod eks-iam-test3
kubectl get pod eks-iam-test3 -o yaml


SA_TOKEN=$(kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token)
echo $SA_TOKEN


확인해보니 aud 주소가 변경되었다
(아까는 k8s내부의 api 주소로 되어있었음)
토큰의 사용처가 sts가 받는다는 것
-> aws 의 서비스를 사용하기위해 요청
-> 원천은 oidc provider에게 요청할거긴 함
kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn

됨
kubectl exec -it eks-iam-test3 -- aws s3 ls
kubectl exec -it eks-iam-test3 -- aws ec2 describe-instances --region ap-northeast-2
kubectl exec -it eks-iam-test3 -- aws ec2 describe-vpcs --region ap-northeast-2

▪️ AWS CloudTrail 이벤트 중 AssumeRoleWithWebIdentity - Link


EKS Pod Identity는 AWS EKS에서 Pod가 AWS 서비스에 접근할 때 사용하는 IAM 권한 관리 방식입니다. 기존의 IRSA(IAM Roles for Service Accounts) 방식을 개선한 것
OIDC Provider 불필요
pods.eks.amazonaws.com 신뢰 정책에서 서비스 주체로 지정합니다.

terraform 소스로 배포 완료
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
# 확인
kubectl get sa
kubectl get sa s3-sa -oyaml
eksctl get podidentityassociation --cluster $CLUSTER_NAME
ASSOCIATION ARN NAMESPACE SERVICE ACCOUNT NAME IAM ROLE ARN
arn:aws:eks:ap-northeast-2:911283464785:podidentityassociation/myeks/a-blaanudo8dc1dbddw default s3-sa arn:aws:iam::911283464785:role/s3-eks-pod-identity-role
aws eks list-pod-identity-associations --cluster-name $CLUSTER_NAME | jq
# IAM Role 확인 : ABAC 지원을 위해 sts:Tagsession 추가
aws iam get-role --query 'Role.AssumeRolePolicyDocument' --role-name s3-eks-pod-identity-role | jq .


▪️ 테스트용 파드 생성 및 AWS 사용 확인 : AssumeRoleForPodIdentity
# 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: eks-pod-identity
spec:
serviceAccountName: s3-sa
containers:
- name: my-aws-cli
image: amazon/aws-cli:latest
command: ['sleep', '36000']
restartPolicy: Never
terminationGracePeriodSeconds: 0
EOF
# 확인
## Amazon EKS가 EKS Pod Identity 연결이 있는 서비스 계정을 사용하는 새 Pod를 시작하면 EKS Pod Identity 웹훅이 실행됩니다
## 두 개의 환경 변수를 추가하여 pod 사양을 변경 합니다 : AWS_CONTAINER_CREDENTIALS_FULL_URI , AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE
kubectl get pod eks-pod-identity -o yaml
kubectl exec -it eks-pod-identity -- aws sts get-caller-identity --query Arn
kubectl exec -it eks-pod-identity -- aws s3 ls
kubectl exec -it eks-pod-identity -- env | grep AWS
# 토큰 정보 확인
kubectl exec -it eks-pod-identity -- ls /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/
kubectl exec -it eks-pod-identity -- cat /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token

aud정보가 pod 인것을 확인가능하다. 토큰을 보내는 주체가 달라짐
▪️ AWS CloudTrail 중 AssumeRoleForPodIdentity - Link
OIDC(OpenID Connect) identity provider와 IAM trust policy를 기반으로 Kubernetes Service Account에 IAM 역할을 매핑합니다.
▫️ 5.1.1 동작 방식
▫️ 5.1.2 한계점
IRSA의 한계를 해소하기 위해 EKS 전용으로 설계된 새로운 방식입니다.
▫️ 5.2.1 동작 방식
OIDC Provider가 필요 없어 IAM 관리자 권한 없이도 설정 가능하고, trust policy의 Principal이 항상 pods.eks.amazonaws.com으로 동일하기 때문에 여러 클러스터에서 동일한 IAM Role을 재사용할 수 있습니다
💡 두 방식은 공존 가능하며, 같은 Pod에 둘 다 설정된 경우 Pod Identity가 우선 적용된다고 합니다.