이번주도 어김없이 스터디 공유를 시작하겠습니다
주제는 EKS AuthN/AuthZ입니다
관리자가 kubectl get node 실행 시 과정

[1] 클라이언트(aws cli)에서 토큰 생성 : Client side (aws-iam-authenticator token) - Github


# sts caller id의 ARN 확인
aws sts get-caller-identity --query Arn
"arn:aws:iam::<자신의 Account ID>:user/admin"
# kubeconfig 정보 확인
cat ~/.kube/config
...
users:
- name: arn:aws:eks:ap-northeast-2:911283464785:cluster/myeks
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- --region
- ap-northeast-2
- eks
- get-token
- --cluster-name
- myeks
- --output
- json
command: aws
env: null
# Get a token for authentication with an Amazon EKS cluster.
# This can be used as an alternative to the aws-iam-authenticator.
aws eks get-token help
# 임시 보안 자격 증명(토큰)을 요청 : 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
...
2026-03-31 23:41:10,583 - MainThread - botocore.regions - DEBUG - Calling endpoint provider with parameters: {'Region': 'ap-northeast-2', 'UseDualStack': False, 'UseFIPS': False, 'UseGlobalEndpoint': False}
2026-03-31 23:41:10,583 - MainThread - botocore.regions - DEBUG - Endpoint provider result: https://sts.ap-northeast-2.amazonaws.com
...
# EKS 인증 토큰 가져오기
## 각 토큰은 k8s-aws-v1로 시작하여 base64 인코딩 문자열로 이어집니다.
TOKEN_DATA=$(aws eks get-token --cluster-name myeks | jq -r '.status.token')
echo $TOKEN_DATA
k8s-aws-v1.aHR0cHM6Ly9zdHMuYXAtbm9ydGhlYXN0LTIuYW1hem9uYXdzLmNvbS8_QWN0aW9uPUdldENhbGxlcklkZW50aXR5JlZlcnNpb249MjAxMS0wNi0xNSZYLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUE1SUxGMkZKSTNFR0tOR05ZJTJGMjAyNjAzMzElMkZhcC1ub3J0aGVhc3QtMiUyRnN0cyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwMzMxVDEzNTAxNVomWC1BbXotRXhwaXJlcz02MCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QlM0J4LWs4cy1hd3MtaWQmWC1BbXotU2lnbmF0dXJlPTM1YzhkZmZiMWFiYzYwYmQ0Njg2ZmRhMWU0YWU1ZTgzN2Y2Y2NhNDczMjdhMWVjY2M5YTQ3Y2U5NDY2MDE2OTQ
# JWT 구조 분리 (Header, Payload, Signature)
## IFS='.': 내부 필드 구분자(Internal Field Separator)를 마침표로 설정
## read ...: 토큰을 세 부분으로 나누어 각각 header, payload, signature 변수에 담기
IFS='.' read header payload signature <<< "$TOKEN_DATA"
# Payload 디코딩 및 데이터 추출 => Pre-signed URL
## fold -w 4: 페이로드 문자열을 4글자씩 끊어서 여러 줄로 만듭니다. Base64 패딩 처리를 위한 준비
## sed '$ d': 마지막 줄을 삭제. AWS EKS 토큰의 페이로드 끝에는 간혹 URL-Safe 변환 과정에서 생긴 불필요한 문자나 패딩 이슈가 있을 수 있는데, 이를 정제하는 트릭
## tr -d '\n': 다시 모든 줄바꿈을 제거하여 하나의 문자열로 합침
## base64 --decode: 최종적으로 인코딩된 문자열을 복호화하여 실제 JSON 데이터를 출력
echo "$header"
echo "$signature"
echo "$payload"
echo "$payload" | fold -w 4 | sed '$ d' | tr -d '\n' | base64 --decode
https://sts.ap-northeast-2.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA5ILF2FJI3EGKNGNY%2F20260331%2Fap-northeast-2%2Fsts%2Faws4_request&X-Amz-Date=20260331T135015Z&X-Amz-Expires=60&X-Amz-SignedHeaders=host%3Bx-k8s-aws-id&X-Amz-Signature=35c8dffb1abc60bd4686fda1e4ae5e837f6cca47327a1eccc9a47ce9466016
https://sts.ap-northeast-2.amazonaws.com/?Action=GetCallerIdentity&
Version=2011-06-15&
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=AKIA5ILF2FJI3EGKNGNY%2F20260331%2Fap-northeast-2%2Fsts%2Faws4_request& # aws access key id
X-Amz-Date=20260331T135015Z&X-Amz-Expires=60&
X-Amz-SignedHeaders=host%3Bx-k8s-aws-id&
X-Amz-Signature=35c8dffb1abc60bd4686fda1e4ae5e837f6cca47327a1eccc9a47ce9466016

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

# kubectl 실행 시, 출력 로그 확인
# https://18041FBFE166FC9F18E70E63241C796E.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500
kubectl get node -v=10
...
I0401 15:48:58.414931 87026 helper.go:113] "Request Body" body=""
## kubectl은 보안 민감 정보(Authorization 헤더 등)를 일반적인 로그 레벨에서 마스킹(Masking) 처리하여 출력하지 않도록 설계되어 있습니다.
## curl 명령어를 흉내 내어 출력할 때 Authorization 헤더는 고의적으로 제외
I0401 15:48:58.414961 87026 round_trippers.go:527] "Request" curlCommand=<
curl -v -XGET -H "Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json;as=Table;v=v1beta1;g=meta.k8s.io,application/json" -H "User-Agent: kubectl/v1.35.3 (darwin/arm64) kubernetes/6c1cd99" 'https://18041FBFE166FC9F18E70E63241C796E.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500'
# kubectl 대신, 직접 토큰을 사용해서 k8s api 호출 해보기!
# 1. 토큰 확보
TOKEN_DATA=$(aws eks get-token --cluster-name myeks | jq -r '.status.token')
echo $TOKEN_DATA
# 2. curl 호출 : 로그에 찍힌 URL과 헤더에 토큰 추가하여 실행 => 15분 후 토큰 만료되니, 15분 이후에는 토큰 재발급해서 사용 필요!
## Authorization: Bearer 확인!
curl -k -v -XGET \
-H "Authorization: Bearer $TOKEN_DATA" \
-H "Accept: application/json" \
'https://D101CFA79EB3A774D39A52E2425B034F.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500'
...
> Authorization: Bearer k8s-aws-v1.aHR0cHM6Ly9zdHMuYXAtbm9ydGhlYXN0LTIuYW1hem9uYXdzLmNvbS8_QWN0aW9uPUdldENhbGxlcklkZW50aXR5JlZlcnNpb249MjAxMS0wNi0xNSZYLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUE1SUxGMkZKSTNFR0tOR05ZJTJGMjAyNjA0MDElMkZhcC1ub3J0aGVhc3QtMiUyRnN0cyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNDAxVDA4NTYyMFomWC1BbXotRXhwaXJlcz02MCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QlM0J4LWs4cy1hd3MtaWQmWC1BbXotU2lnbmF0dXJlPThkZGE5M2Y1MzEyMGM3Y2FjOGU0MWIxY2EzNTliOGZmMWY0MDEyNDMyNTIzNDgyZGE4NzUwZjhlYTI4OTVmNjg
...
curl -k -s -XGET \
-H "Authorization: Bearer $TOKEN_DATA" \
-H "Accept: application/json" \
'https://D101CFA79EB3A774D39A52E2425B034F.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500' | jq


[3] k8s(eks) api 는 ‘웹 토큰 인증’ 방식을 통한 인증을 위해서, Token Review 요청 ⇒ AWS 인증 확인 후 응답 시 : 유저, K8S Subject(group) 등

웹훅 인증 전략 webhook authentication strategy 은 bearer tokens을 검증하는 웹훅을 호출합니다. Amazon EKS에서 이러한 베어러 토큰 bearer tokens 은 kubectl 명령을 실행할 때 AWS CLI 또는 aws-iam-authenticator client 에 의해 생성됩니다. 명령을 실행하면 토큰이 kube-apiserver로 전달되어 인증 웹훅으로 전달됩니다. 요청이 잘 구성된 경우, 웹훅은 토큰 본문에 포함된 사전 서명된 URL을 호출합니다. 이 URL은 요청의 서명을 검증하고 사용자 계정, Arn, UserId와 같은 사용자에 대한 정보를 kube-apiserver로 반환합니다.
# tokenreviews api 리소스 확인
kubectl api-resources | grep authentication
tokenreviews authentication.k8s.io/v1 false TokenReview
# TokenReview는 사용자에 대한 토큰 인증을 시도(확인)합니다.
kubectl explain tokenreviews.spec
kubectl explain tokenreviews.status
kubectl explain tokenreviews.status.user
kubectl explain tokenreviews
...
DESCRIPTION:
TokenReview attempts to authenticate a token to a known user. Note:
TokenReview requests may be cached by the webhook token authenticator
plugin in the kube-apiserver.
# 직접 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
# AWS 인증 확인 후 응답 시 : 유저, K8S Subject(group) 등
## => Amazon EKS 클러스터를 생성할 경우, 클러스터를 생성하는 IAM 보안 주체에게는 Amazon EKS 제어 영역의 클러스터 역할 기반 액세스 제어(RBAC) 구성에 system:masters 권한이 자동으로 부여됩니다.
## => 이 보안 주체는 표시되는 구성에 나타나지 않음!
## groups=[system:authenticated] => 즉, system:masters 가 생략되어 있다고 보면 됨!
kubectl create -f token-review.yaml -v=9
...
# 가장 마지막 출력 확인해보자
I0331 23:50:19.504430 13262 helper.go:246] "Response Body" body="{\"kind\":\"TokenReview\",\"apiVersion\":\"authentication.k8s.io/v1\",\"metadata\":{\"managedFields\":[{\"manager\":\"kubectl-create\",\"operation\":\"Update\",\"apiVersion\":\"authentication.k8s.io/v1\",\"time\":\"2026-03-31T14:50:19Z\",\"fieldsType\":\"FieldsV1\",\"fieldsV1\":{\"f:spec\":{\"f:token\":{}}}}]},\"spec\":{\"token\":\"k8s-aws-v1.aHR0cHM6Ly9zdHMuYXAtbm9ydGhlYXN0LTIuYW1hem9uYXdzLmNvbS8_QWN0aW9uPUdldENhbGxlcklkZW50aXR5JlZlcnNpb249MjAxMS0wNi0xNSZYLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUE1SUxGMkZKSTNFR0tOR05ZJTJGMjAyNjAzMzElMkZhcC1ub3J0aGVhc3QtMiUyRnN0cyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwMzMxVDE0NDgzN1omWC1BbXotRXhwaXJlcz02MCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QlM0J4LWs4cy1hd3MtaWQmWC1BbXotU2lnbmF0dXJlPTYyM2I1MDA0ODA2MWI2YjA1YWExMzdkZTlhNmYxMjJlODJjYWQyYTMzZTM5NmNhYzdlNjU3NjNiNzM5YmU1YTQ\"},\"status\":{\"authenticated\":true,\"user\":{\"username\":\"arn:aws:iam::911283464785:user/admin\",\"uid\":\"aws-iam-authenticator:911283464785:AIDA5ILF2FJIR2CFPVMG7\",\"groups\":[\"system:authenticated\"],\"extra\":{\"accessKeyId\":[\"AKIA5ILF2FJ [truncated 299 chars]"
...
"status": {
"authenticated": true,
"user": {
"username": "arn:aws:iam::911283464785:user/admin",
"uid": "aws-iam-authenticator:911283464785:AIDA5ILF2FJIR2CFPVMG7",
"groups": [
"system:authenticated"
],
"extra": {
"accessKeyId": [
"AKIA5ILF2FJI3EGKNGNY"
],
"arn": [
"arn:aws:iam::911283464785:user/admin"
],
"canonicalArn": [
"arn:aws:iam::911283464785:user/admin"
],
"principalId": [
"AIDA5ILF2FJIR2CFPVMG7"
],
"sessionName": [
""
],
"sigs.k8s.io/aws-iam-authenticator/principalId": [
"AIDA5ILF2FJIR2CFPVMG7"
]
}
},
"audiences": [
"https://kubernetes.default.svc"
]
}
}
tokenreview.authentication.k8s.io/mytoken created


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

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

# aws-auth 컨피그맵 확인 : 현재는 IAM access entry 방식 우선 적용으로 아래 출력 내용과 다를 수 있습니다.
kubectl get cm -n kube-system aws-auth -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::911283464785:role/myeks-ng-1
groups:
- system:bootstrappers
- system:nodes
username: system:node:{{EC2PrivateDNSName}}
#---<아래 생략(추정), ARN은 EKS를 설치한 IAM User , 여기 있었을경우 만약 실수로 삭제 시 복구가 가능했을까?---
mapUsers: |
- groups:
- system:masters
userarn: arn:aws:iam::111122223333:user/admin
username: kubernetes-admin
# AWS IAM 주체를 → K8S Subject 매칭 방안2 사용 시 eksctl 명령어를 통한 확인
eksctl get iamidentitymapping --cluster myeks
ARN USERNAME GROUPS ACCOUNT
arn:aws:iam::911283464785:role/myeks-ng-1 system:node:{{EC2PrivateDNSName}} system:bootstrappers,system:nodes

K8S(EKS) API가 TokenReview 응답을 받고 나서, K8S RBAC 인가 확인 후, 허용(Allow)시 K8S Action 실행 후 최종 사용자에게 응답!

[5] 방안1 EKS API - Docs : AWS IAM 주체 → K8S Subject 매칭 + k8s Authz ‘Webhook’ 인가! - SubjectAccessReview
k8s(eks) api 서버 로그 : 인증(authentication-token-webhook), 인가(authorization-webhook) → 즉 AWS측에서 ‘인증/인가’ 가능!


K8S 대신 AWS 측에서 인가 처리 동작(Webhook, SubjectAccessReviews) 확인

# 현재 Access Entry 에 IAM User admin
aws eks list-access-entries --cluster-name myeks | jq
{
"accessEntries": [
"arn:aws:iam::911283464785:role/aws-service-role/eks.amazonaws.com/AWSServiceRoleForAmazonEKS",
"arn:aws:iam::911283464785:role/myeks-ng-1",
"arn:aws:iam::911283464785:user/admin"
]
}
# api 리소스 확인
kubectl api-resources | grep subject
...
subjectaccessreviews authorization.k8s.io/v1 false SubjectAccessReview
# SubjectAccessReviews는 사용자/그룹이 K8S Action이(인가) 가능한지 확인
kubectl explain subjectaccessreviews.spec
kubectl explain subjectaccessreviews.status
kubectl explain subjectaccessreviews
...
DESCRIPTION:
SubjectAccessReview checks whether or not a user or group can perform an
action.
# Shows the subject for the current context with which one authenticates with the cluster
kubectl rbac-tool -h
kubectl rbac-tool whoami
{Username: "arn:aws:iam::911283464785:user/admin",
UID: "aws-iam-authenticator:911283464785:AIDA5ILF2FJIR2CFPVMG7",
Groups: ["system:authenticated"], # system:masters 생략!
Extra: {accessKeyId: ["AKIA5ILF2FJI3EGKNGNY"],
arn: ["arn:aws:iam::911283464785:user/admin"],
canonicalArn: ["arn:aws:iam::911283464785:user/admin"],
principalId: ["AIDA5ILF2FJIR2CFPVMG7"],
sessionName: [""],
sigs.k8s.io/aws-iam-authenticator/principalId: ["AIDA5ILF2FJIR2CFPVMG7"]}}
# 직접 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
kubectl create -f sar-request.yaml -v=8
# AWS 인증 확인 후 응답 시 : 유저, K8S Subject(group) 등
## => Amazon EKS 클러스터를 생성할 경우, 클러스터를 생성하는 IAM 보안 주체에게는 Amazon EKS 제어 영역의 클러스터 역할 기반 액세스 제어(RBAC) 구성에 system:masters 권한이 자동으로 부여됩니다.
## => 이 보안 주체는 표시되는 구성에 나타나지 않음!
## groups=[system:authenticated] => 즉, system:masters 가 생략되어 있다고 보면 됨!
kubectl create -f token-review.yaml -v=9
...
# 가장 마지막 출력 확인해보자
I0402 00:34:22.205722 93399 helper.go:246] "Response Body" body=<
{"kind":"SubjectAccessReview","apiVersion":"authorization.k8s.io/v1","metadata":{"managedFields":[{"manager":"kubectl-create","operation":"Update","apiVersion":"authorization.k8s.io/v1","time":"2026-04-01T15:34:22Z","fieldsType":"FieldsV1","fieldsV1":{"f:spec":{"f:groups":{},"f:resourceAttributes":{".":{},"f:namespace":{},"f:resource":{},"f:verb":{}},"f:user":{}}}}]},"spec":{"resourceAttributes":{"namespace":"kube-system","verb":"get","resource":"pods"},"user":"arn:aws:iam::911283464785:user/admin","groups":["system:masters"]},"status":{"allowed":true}}
...
{
"kind": "SubjectAccessReview",
"apiVersion": "authorization.k8s.io/v1",
...
"spec": {
"resourceAttributes": {
"namespace": "kube-system",
"verb": "get",
"resource": "pods"
},
"user": "arn:aws:iam::911283464785:user/admin",
"groups": [
"system:masters"
]
},
"status": {
"allowed": true

EKS에서 제공하는 EKA API Access Entry 관리형 정책 확인 - Docs
# 맵핑된 정책 확인 : 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
...
"associatedAccessPolicies": [
{
"policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy",
"accessScope": {
"type": "cluster",
...
# 맵핑된 정책 확인 : Linux (운영서버, WSL2 등) - 뒤에 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 # Linux
# EKS에서 제공하는 관리형 정책 확인
aws eks list-access-policies
aws eks list-access-policies --output table
|+--------------------------------------------------------------------------------------+------------------------------------------------+|
|| arn:aws:eks::aws:cluster-access-policy/AmazonAIOpsAssistantPolicy | AmazonAIOpsAssistantPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonARCRegionSwitchScalingPolicy | AmazonARCRegionSwitchScalingPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSACKPolicy | AmazonEKSACKPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminPolicy | AmazonEKSAdminPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminViewPolicy | AmazonEKSAdminViewPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSArgoCDClusterPolicy | AmazonEKSArgoCDClusterPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSArgoCDPolicy | AmazonEKSArgoCDPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSAutoNodePolicy | AmazonEKSAutoNodePolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSBlockStorageClusterPolicy | AmazonEKSBlockStorageClusterPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSBlockStoragePolicy | AmazonEKSBlockStoragePolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy | AmazonEKSClusterAdminPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSComputeClusterPolicy | AmazonEKSComputeClusterPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSComputePolicy | AmazonEKSComputePolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy | AmazonEKSEditPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSEventPolicy | AmazonEKSEventPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSHybridPolicy | AmazonEKSHybridPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSKROPolicy | AmazonEKSKROPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSLoadBalancingClusterPolicy | AmazonEKSLoadBalancingClusterPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSLoadBalancingPolicy | AmazonEKSLoadBalancingPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSNetworkingClusterPolicy | AmazonEKSNetworkingClusterPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSNetworkingPolicy | AmazonEKSNetworkingPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSSecretAdminPolicy | AmazonEKSSecretAdminPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSSecretReaderPolicy | AmazonEKSSecretReaderPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy | AmazonEKSViewPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonEMRJobPolicy | AmazonEMRJobPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonSagemakerHyperpodClusterPolicy | AmazonSagemakerHyperpodClusterPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonSagemakerHyperpodControllerPolicy | AmazonSagemakerHyperpodControllerPolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonSagemakerHyperpodSpacePolicy | AmazonSagemakerHyperpodSpacePolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonSagemakerHyperpodSpaceTemplatePolicy | AmazonSagemakerHyperpodSpaceTemplatePolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonSagemakerHyperpodSystemNamespacePolicy | AmazonSagemakerHyperpodSystemNamespacePolicy ||
|| arn:aws:eks::aws:cluster-access-policy/AmazonSagemakerHyperpodUserClusterPolicy | AmazonSagemakerHyperpodUserClusterPolicy ||
|+--------------------------------------------------------------------------------------+------------------------------------------------+|

[6] 방안1 EKS API 을 통한 인증/인가를 마친 후, K8S(EKS) API는 K8S Action 실행 후 클라이언트에 최종 응답!
[사전 준비 1] testuser 사용자 생성
# testuser 사용자 생성
aws iam create-user --user-name testuser
# 사용자에게 프로그래밍 방식 액세스 권한 부여
aws iam create-access-key --user-name testuser
{
"AccessKey": {
"UserName": "testuser",
"AccessKeyId": "AKIA5ILF2##",
"Status": "Active",
"SecretAccessKey": "TxhhwsU8##",
"CreateDate": "2023-05-23T07:40:09+00:00"
}
}
# 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
"arn:aws:iam::911283464785:user/admin"

[사전 준비 2] aws-cli 컨테이너 실행 후 aws iam 자격증명 설정 및 확인
# aws-cli 컨테이너 실행
docker run -it --name aws-cli --entrypoint /bin/sh amazon/aws-cli
-------------------------------------------------
# aws cli 명령 시도
aws s3 ls
# 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

[1] testuser kubeconfig 생성 및 kubectl 사용 확인
# [사전 준비 2] 에서 컨테이너 sh 진입 상태에서 아래 실행
docker run -it --name aws-cli --entrypoint /bin/sh amazon/aws-cli
-----------------------------------------------------------------
# testuser kubeconfig 생성 >> aws eks update-kubeconfig 실행이 가능한 이유는?, 3번 설정 후 약간의 적용 시간 필요
CLUSTER_NAME=myeks
aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser
Added new context testuser to /root/.kube/config
# kubeconfig 확인
cat ~/.kube/config
# kubectl 설치
# 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/arm64/kubectl # macOS
curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.35.2/2026-02-27/bin/linux/amd64/kubectl # windows (WSL2)
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client=true
# kubectl 사용 확인 : 실패!
kubectl get node -v6
# rbac-tool 설치 : https://github.com/alcideio/rbac-tool
yum install tar gzip -y
curl https://raw.githubusercontent.com/alcideio/rbac-tool/master/download.sh | bash
# Shows the subject for the current context with which one authenticates with the cluster
./bin/rbac-tool whoami
Error: Failed to create kubernetes client - the server has asked for the client to provide credentials

[2] 방안1 EKS API 을 통한 Access 설정 : testuser에 AmazonEKSAdminPolicy 정책 부여로 EKS 관리자 수준 권한 설정
# [사전 준비 2] 에서 컨테이너 sh 진입 상태에서 아래 실행 -> 혹은 자신의 PC에서 실행해도됨(aws 관리자 수준 자격증명 상태에서)
docker run -it --name aws-cli --entrypoint /bin/sh amazon/aws-cli
-----------------------------------------------------------------
# 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
# 확인
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

[3] 최종 kubectl 사용 확인!
# [사전 준비 2] 에서 컨테이너 sh 진입 상태에서 아래 실행
docker run -it --name aws-cli --entrypoint /bin/sh amazon/aws-cli
-----------------------------------------------------------------
# kubectl 시도
kubectl get node -v6
kubectl get deploy -A
kubectl auth can-i delete pods --all-namespaces

EKS에서 제공하는 관리형 정책이 아닌, 직접 만든 커스텀 RBAC을 사용해보자!~~
[0] testuser에 Access Entry 제거
# [사전 준비 2] 에서 컨테이너 sh 진입 상태에서 아래 실행 -> 혹은 자신의 PC에서 실행해도됨(aws 관리자 수준 자격증명 상태에서)
docker run -it --name aws-cli --entrypoint /bin/sh amazon/aws-cli
-----------------------------------------------------------------
# 기존 testuser access entry 제거
aws eks delete-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[]
[1] K8S 에 ClusterRole 과 ClusterRoleBinding 생성
# 자신의 PC에서 실행
# ClusterRole 생성
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-viewer-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list", "get", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-admin-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["*"]
EOF
kubectl get clusterrole | grep ^pod
pod-admin-role 2026-04-01T17:53:14Z
pod-viewer-role 2026-04-01T17:53:14Z
# ClusterRoleBinding 생성
kubectl create clusterrolebinding viewer-role-binding --clusterrole=pod-viewer-role --group=pod-viewer
kubectl create clusterrolebinding admin-role-binding --clusterrole=pod-admin-role --group=pod-admin
kubectl get clusterrolebinding | grep -E 'admin-role|viewer-role'
# 확인
kubectl rbac-tool lookup pod-viewer
SUBJECT | SUBJECT TYPE | SCOPE | NAMESPACE | ROLE | BINDING
-------------+--------------+-------------+-----------+-----------------+----------------------
pod-viewer | Group | ClusterRole | | pod-viewer-role | viewer-role-binding
kubectl rbac-tool lookup pod-admin
SUBJECT | SUBJECT TYPE | SCOPE | NAMESPACE | ROLE | BINDING
------------+--------------+-------------+-----------+----------------+---------------------
pod-admin | Group | ClusterRole | | pod-admin-role | admin-role-binding
kubectl rolesum -k Group pod-viewer
Group: pod-viewer
Policies:
• [CRB] */viewer-role-binding ⟶ [CR] */pod-viewer-role
Resource Name Exclude Verbs G L W C U P D DC
pods [*] [-] [-] ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖
kubectl rolesum -k Group pod-admin
Group: pod-admin
Policies:
• [CRB] */admin-role-binding ⟶ [CR] */pod-admin-role
Resource Name Exclude Verbs G L W C U P D DC
pods [*] [-] [-] ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔

[2] Access Entry 설정 : pod-viewer 그룹
# [사전 준비 2] 에서 컨테이너 sh 진입 상태에서 아래 실행 -> 혹은 자신의 PC에서 실행해도됨(aws 관리자 수준 자격증명 상태에서)
docker run -it --name aws-cli --entrypoint /bin/sh amazon/aws-cli
-----------------------------------------------------------------
# Access Entry 설정 : 먼저 k8s group pod-viewer
aws eks create-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser --kubernetes-group pod-viewer
...
"accessEntry": {
"clusterName": "myeks",
"principalArn": "arn:aws:iam::91128...:user/testuser",
"kubernetesGroups": [
"pod-viewer"
],
# Access Entry 확인
aws eks list-associated-access-policies --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser
aws eks describe-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser | jq

[3] kubectl 사용 확인!
# [사전 준비 2] 에서 컨테이너 sh 진입 상태에서 아래 실행
docker run -it --name aws-cli --entrypoint /bin/sh amazon/aws-cli
-----------------------------------------------------------------
# kubectl 시도
kubectl get node -A
kubectl get pod -A
kubectl auth can-i get pods --all-namespaces
kubectl auth can-i delete pods --all-namespaces
[4] Access Entry 설정 업데이트 : pod-admin그룹
# [사전 준비 2] 에서 컨테이너 sh 진입 상태에서 아래 실행
docker run -it --name aws-cli --entrypoint /bin/sh amazon/aws-cli
-----------------------------------------------------------------
# Access Entry 설정 업데이트
aws eks update-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser --kubernetes-group pod-admin | jq -r .accessEntry
...
"kubernetesGroups": [
"pod-admin"
...
# 확인
aws eks describe-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser | jq
...
"kubernetesGroups": [
"pod-admin"
],
...

[5] kubectl 사용 확인!
# [사전 준비 2] 에서 컨테이너 sh 진입 상태에서 아래 실행
docker run -it --name aws-cli --entrypoint /bin/sh amazon/aws-cli
-----------------------------------------------------------------
# kubectl 시도
kubectl get node -A
kubectl get pod -A
kubectl auth can-i get pods --all-namespaces
kubectl auth can-i delete pods --all-namespaces

IRSA 소개 : K8S 에서 발급한 토큰 검증을 위해서, AWS가 ODIC 프로바이더(예. 구글 인증)인 K8S(토큰 발급 주체)를 통해 검증 후, IAM Role Assume 전달

IRSA 동작 과정

AWS SDK는 AWS_ROLE_ARN 및 AWS_WEB_IDENTITY_TOKEN_FILE 이름의 환경변수를 읽어들여 Web Identity 토큰으로 AssumeRoleWithWebIdentify를 호출함으로써 Assume Role을 시도하여 임시 자격 증명을 획득하고, 특정 IAM Role 역할을 사용할 수 있게 됩니다.
이때 Assume Role 동작을 위한 인증은 AWS가 아닌 외부 Web IdP(EKS IdP)에 위임하여 처리합니다.


실습1 : 파드에 서비스 어카운트 없이 생성해보기
# 파드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

실습2 : 기본 Kubernetes API 접근용 ServiceAccount 첫번째 토큰 확인 aud : https://kubernetes.default.svc - Docs
# 파드2 생성
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
...
volumes:
- name: kube-api-access-g9shf
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
kubectl get pod eks-iam-test2 -o yaml | grep serviceAccount
serviceAccount: default
serviceAccountName: default
kubectl get sa default
kubectl exec -it eks-iam-test2 -- ls -al /var/run/secrets/kubernetes.io/serviceaccount
kubectl exec -it eks-iam-test2 -- ls -al /var/run/secrets/kubernetes.io/serviceaccount/..data
kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token ;echo
# aws 서비스 사용 시도
kubectl exec -it eks-iam-test2 -- aws s3 ls
# 서비스 어카운트 토큰 확인
SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $SA_TOKEN
# jwt 혹은 아래 JWT 웹 사이트 이용 https://jwt.io/
jwt decode $SA_TOKEN --json --iso8601
...
#헤더
{
"alg": "RS256",
"kid": "1559d9b7d1fc97f5da6354aba03386bf67988a3d",
"typ": "JWT"
}
# 페이로드 확인
# OAuth2에서 쓰이는 aud, exp 속성 확인! > projectedServiceAccountToken 기능으로 토큰에 audience 등 항목을 덧붙힘
# iss : 토큰 발급자(OIDC 제공업체)를 나타냅니다 https://oidc.eks.us-east-1.amazonaws.com/id/80D562ED8026E91294D52E09BEA261D4. 이 OIDC 제공업체 URL은 토큰 검증 과정에서 사용됩니다.
# aud : 토큰의 대상 주소를 나타냅니다 https://kubernetes.default.svc. 이는 Kubernetes API 서버에 접근하는 데 사용되는 클러스터 내부의 주소입니다. 즉, 이 토큰은 API 서버에서는 승인되지만 다른 서비스에서는 거부됩니다.
# exp 와 iat : 이는 토큰의 만료 시간을 나타내며, 기본적으로 시간 제한 토큰을 가능하게 합니다.
{
"aud": [
"https://kubernetes.default.svc" # 해당 주소는 k8s api의 ClusterIP 서비스 주소 도메인명, kubectl get svc kubernetes
],
"exp": 1716619848, # 만료 시간
"iat": 1685083848, # 발급 시간
"iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/F6A7523462E8E6CDADEE5D41DF2E71F6", # 토큰 발급자(OIDC 제공업체)
"jti": "ee823c34-f020-4f77-90f3-61fab4de244a", # 토큰 고유 ID - replay attack 방지 / 추적용
"kubernetes.io": {
"namespace": "default",
"node": {
"name": "ip-192-168-1-70.ap-northeast-2.compute.internal",
"uid": "f4fabf42-4a9c-43f0-8a1e-edeb2a8fbb42"
},
"pod": {
"name": "eks-iam-test2",
"uid": "10dcccc8-a16c-4fc7-9663-13c9448e107a"
},
"serviceaccount": {
"name": "default",
"uid": "acb6c60d-0c5f-4583-b83b-1b629b0bdd87"
},
"warnafter": 1685087455 # Kubernetes 내부 로직에서 사용 - 이 시간 이후에는 토큰 갱신 권장
},
"nbf": 1685083848, # 이 사간 이후 유효
"sub": "system:serviceaccount:default:default" # 이 토큰의 주인. default 네임스페이스의 default 서비스 계정.
}
# 파드2 삭제
kubectl delete pod eks-iam-test2

실습3 : IRSA 설정 및 SA 두번째 토큰 확인 aud : sts.amazonaws.com - Blog , Github

# 클러스터에 기존 IAM OIDC 공급자가 있는지 확인합니다. 클러스터의 OIDC 공급자 ID를 검색하여 변수에 저장합니다.
oidc_id=$(aws eks describe-cluster --name myeks --query "cluster.identity.oidc.issuer" --output text | cut -d '/' -f 5)
echo $oidc_id
032357E88E266F4AE7C2E8CF6F5EFEB0
# 클러스터 ID를 가진 IAM OIDC 공급자가 계정에 이미 있는지 확인합니다.
# aws iam list-open-id-connect-providers | grep $oidc_id | cut -d "/" -f4
aws iam list-open-id-connect-providers | grep $oidc_id | cut -d "/" -f4
032357E88E266F4AE7C2E8CF6F5EFEB0
IDP=$(aws eks describe-cluster --name myeks --query cluster.identity.oidc.issuer --output text)
curl -s $IDP/.well-known/openid-configuration | jq -r '.'
curl -s $IDP/keys | jq -r '.'
curl -s https://oidc.eks.ap-northeast-2.amazonaws.com/id/D101CFA79EB3A774D39A52E2425B034F/.well-known/openid-configuration | jq .
# Discovery 엔드포인트 호출 (.well-known) : OIDC issuer 주소 뒤에 /.well-known/openid-configuration을 붙이면 됩
curl -s https://oidc.eks.ap-northeast-2.amazonaws.com/id/032357E88E266F4AE7C2E8CF6F5EFEB0/.well-known/openid-configuration | jq .
{
"issuer": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/032357E88E266F4AE7C2E8CF6F5EFEB0",
"jwks_uri": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/032357E88E266F4AE7C2E8CF6F5EFEB0/keys",
"authorization_endpoint": "urn:kubernetes:programmatic_authorization",
"response_types_supported": [
"id_token"
],
"subject_types_supported": [
"public"
],
"claims_supported": [
"sub",
"iss"
],
"id_token_signing_alg_values_supported": [
"RS256"
]
}
# JWKS(JSON Web Key Set) 호출 : 위에서 찾은 jwks_uri (보통 끝에 /keys가 붙음)로 다시 요청을 보냅니다.
## EKS가 발행한 JWT 토큰을 검증할 때 사용하는 공개키 세트
curl -s https://oidc.eks.ap-northeast-2.amazonaws.com/id/032357E88E266F4AE7C2E8CF6F5EFEB0/keys | jq .
{
"keys": [
{
"kty": "RSA",
"kid": "1559d9b7d1fc97f5da6354aba03386bf67988a3d",
"use": "sig",
"alg": "RS256",
"n": "7FP6H4lhMYbuTq8orkcDw5zJcbYHhP4tD0KhiR3Tk8nLklaRsWZLXaqcbouFkDDCKV07mU6o3Dv-3HZSst9clyuc2nHzriIECUiLN_z-iPDaMgA5UyO78qS-OErXz_Nz3FwQNrjdrkQ54a0HBOwAadewOigYENR6paGjg2Nu7XEmWSQA5lT5cvXLHwm4NOKXDVafT7gvLL2oGKsQreTxVa7XI47XQ5Se5Up8O0FqWd5WvQ1Lia5d2C-BNxXCjDsickX--ZNm3qPgIY5M4Ao98hbfoIA5PCAMazTq-jGjWCjey17wlKRJfmsbHjXOTkcP6DILHCbfHHhCf2m8BsaWhw",
"e": "AQAB"
}
]
}
# 왜 이 과정이 중요한가요? (IRSA 검증 원리)
## AWS STS나 다른 검증 주체는 여러분이 뽑은 JWT 토큰의 서명이 유효한지 확인하기 위해 위 과정을 거칩니다.
## 토큰 수신: Pod에서 보낸 JWT 토큰을 받음.
## Issuer 확인: 토큰 내부의 iss 필드를 보고 위 URL로 접속.
## 공개키 획득: /keys 경로에서 현재 유효한 공개키(JWKS)를 가져옴.
## 서명 검증: 가져온 공개키와 토큰의 서명(Signature)을 대조하여 변조 여부를 확인.

Kubernetes 서비스 계정이 IAM 역할을 통해 사용할 IAM Policy 생성 - Docs
# 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

Pod가 Kubernetes 서비스 계정을 사용하도록 구성합니다 : IAM Role 생성, K8S SA 생성, K8S SA Annonation 추가
# IRSA 생성 : cloudforamtion 를 통해 IAM Role 생성
CLUSTER_NAME=myeks
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
kubectl get serviceaccounts -n kube-system aws-load-balancer-controller
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
NAMESPACE NAME ROLE ARN
kube-system aws-load-balancer-controller arn:aws:iam::911283464785:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-n7mN9x019mGE
# k8s 에 SA 확인
# Inspecting the newly created Kubernetes Service Account, we can see the role we want it to assume in our pod.
kubectl get serviceaccounts -n kube-system aws-load-balancer-controller -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::911283464785:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-cNSJN97wewS7
...


AWS LBC 설치 : 현재 Node IAM Role에 ELB 권한 없는 상태 - Link , Docs , values
# 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
# 확인
helm list -n kube-system
kubectl get pod -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
kubectl logs -n kube-system deployment/aws-load-balancer-controller -f
# 인증서 관련 정보 확인
## LBC는 단순히 AWS ALB/NLB를 만들기만 하는 것이 아니라, 사용자가 작성한 Ingress나 Service 설정이 올바른지 검토하고 수정하는 역할도 수행
## 이를 위해 API 서버는 LBC의 9443 포트로 HTTPS 요청을 보내는데, 이 통신을 위해 aws-load-balancer-tls 인증서가 필요함.
## 이때 해당 인증서의 만료 전 Cert-manager 이 자동 갱신 처리해줌
## 발급된 인증서 데이터를 aws-load-balancer-tls라는 이름의 Secret에 저장
kubectl get certificaterequests,issuers,certificates -n kube-system
kubectl get secret -n kube-system
kubectl get secret aws-load-balancer-tls -n kube-system -o yaml
# cert-manager 류의 인증서 자동 갱신 미사용 시?
## LBC의 웹훅 인증서는 보통 유효기간이 짧습니다. cert-manager가 없으면 인증서가 만료되는 순간 ,
# kubectl apply 명령이 모두 에러(Internal error: failed calling webhook...)가 나며 클러스터 관리가 불가능해집니다.
# AWS Load Balancer Controller 확인
kubectl get serviceaccounts -n kube-system aws-load-balancer-controller -o yaml
kubectl rolesum -n kube-system aws-load-balancer-controller
kubectl get deployment -n kube-system aws-load-balancer-controller
kubectl describe deploy -n kube-system aws-load-balancer-controller
kubectl describe deploy -n kube-system aws-load-balancer-controller | grep 'Service Account'
Service Account: aws-load-balancer-controller
상세 확인 : Pod Spec 주입 확인, aud 가 aws sts 확인!
# 아래 ENV 와 두번쨰 볼륨은 어떻게 pod spec 에 설정이 주입되었을까요?
k get deploy -n kube-system aws-load-balancer-controller -o yaml # 비교해보자!
kubectl describe pod -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller
...
Environment:
AWS_STS_REGIONAL_ENDPOINTS: regional
AWS_DEFAULT_REGION: ap-northeast-2
AWS_REGION: ap-northeast-2
AWS_ROLE_ARN: arn:aws:iam::911283464785:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-OUFLQbVkFHFn
AWS_WEB_IDENTITY_TOKEN_FILE: /var/run/secrets/eks.amazonaws.com/serviceaccount/token
Mounts:
/tmp/k8s-webhook-server/serving-certs from cert (ro)
/var/run/secrets/eks.amazonaws.com/serviceaccount from aws-iam-token (ro)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-t6zct (ro)
...
Volumes:
aws-iam-token:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 86400
cert:
Type: Secret (a volume populated by a Secret)
SecretName: aws-load-balancer-tls
Optional: false
kube-api-access-t6zct:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
Optional: false
DownwardAPI: true
# aud 가 k8s api 가 아닌 AWS STS 이다! 즉, 해당 토큰 검증은 AWS STS 요청!
kubectl get pod -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller -o yaml
...
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-iamserviceaccount-kube-sys-Role1-OUFLQbVkFHFn
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: /var/run/secrets/eks.amazonaws.com/serviceaccount/token
...
volumeMounts:
- mountPath: /tmp/k8s-webhook-server/serving-certs
name: cert
readOnly: true
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-t6zct
readOnly: true
- mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount
name: aws-iam-token
readOnly: true
...
volumes:
- name: aws-iam-token
projected:
defaultMode: 420
sources:
- serviceAccountToken:
audience: sts.amazonaws.com
expirationSeconds: 86400
path: token
# 해당 SA를 사용하는 파드가 생성 시 mutatingwebhook으로 Env,Volume 주입!
kubectl get MutatingWebhookConfiguration
NAME WEBHOOKS AGE
aws-load-balancer-webhook 6 19m
cert-manager-webhook 1 23m
pod-identity-webhook 1 4h38m
vpc-resource-mutating-webhook 1 4h38m
# pod-identity-webhook 확인
kubectl describe MutatingWebhookConfiguration pod-identity-webhook
kubectl get MutatingWebhookConfiguration pod-identity-webhook -o yaml
...
name: iam-for-pods.amazonaws.com
# iam-for-pods.amazonaws.com은 AWS EKS에서 Pod Identity Webhook의 Mutating Webhook으로, 다음과 같은 작업을 수행합니다:
# Pod 생성 시 호출되어 ServiceAccount의 IAM 역할 정보를 확인.
# Pod에 환경 변수(AWS_ROLE_ARN, AWS_WEB_IDENTITY_TOKEN_FILE)와 토큰 볼륨을 주입.
# Pod이 AWS 리소스에 안전하고 세밀하게 접근할 수 있도록 인증 메커니즘 제공.

(AWS LBC 동작 확인) kube-ops-view 배포 + ALB Ingress(MyDomain, HTTPS → HTTP)
# kube-ops-view : NodePort 나 LoadBalancer Type 필요 없음!
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=ClusterIP --set env.TZ="Asia/Seoul" --namespace kube-system
# 확인
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
# 사용 리전의 인증서 ARN 변수 지정 : 정상 상태 확인(만료 상태면 에러 발생!)
CERT_ARN=$(aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text)
echo $CERT_ARN
# 자신의 공인 도메인 변수 지정
MyDomain=<자신의 공인 도메인>
echo $MyDomain
MyDomain=gasida.link
echo $MyDomain
# kubeopsview 용 Ingress 설정 : group 설정으로 1대의 ALB를 여러개의 ingress 에서 공용 사용
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
# service, ep, ingress 확인
kubectl get ingress,svc,ep -n kube-system
# Kube Ops View 접속 정보 확인
echo -e "Kube Ops View URL = https://kubeopsview.$MyDomain/#scale=1.5"
open "https://kubeopsview.$MyDomain/#scale=1.5" # macOS
# (참고) 삭제 시
kubectl delete ingress -n kube-system kubeopsview
목표 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
...
Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::911283464785:role/eksctl-myeks-pod-irsa-s3-readonly-role
...
# 파드 3번 생성
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
...
- name: AWS_ROLE_ARN
value: arn:aws:iam::911283464785:role/eksctl-myeks-pod-irsa-s3-readonly-role
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: /var/run/secrets/eks.amazonaws.com/serviceaccount/token
...
volumeMounts:
- mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount
name: aws-iam-token
readOnly: true
...
volumes:
- name: aws-iam-token
projected:
sources:
- serviceAccountToken:
audience: sts.amazonaws.com
expirationSeconds: 86400
path: token
...
kubectl exec -it eks-iam-test3 -- ls /var/run/secrets/eks.amazonaws.com/serviceaccount
token
kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token ; echo
...
# 서비스 어카운트 두 번째 토큰 확인
SA_TOKEN=$(kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token)
echo $SA_TOKEN
# jwt 혹은 아래 JWT 웹 사이트 이용 https://jwt.io/
jwt decode $SA_TOKEN --json --iso8601
...
#헤더
{
"alg": "RS256",
"kid": "1559d9b7d1fc97f5da6354aba03386bf67988a3d",
"typ": "JWT"
}
# 페이로드 확인 : 해당 토큰은 “EKS Pod가 AWS IAM Role을 Assume하기 위한 IRSA 전용 JWT”
{
"aud": [
"sts.amazonaws.com" # AWS STS 전용, 즉 IRSA 토큰
],
"exp": 1775208011,
"iat": 1775121611,
"iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/032357E88E266F4AE7C2E8CF6F5EFEB0", # 이 토큰을 발급한 주체 = EKS OIDC Provider
"jti": "3c491844-9bb0-45a3-87f7-91027503f233",
"kubernetes.io": {
"namespace": "default",
"node": {
"name": "ip-192-168-12-7.ap-northeast-2.compute.internal",
"uid": "f42ea459-d9d0-45ea-b891-e16b79903cdd"
},
"pod": {
"name": "eks-iam-test3",
"uid": "fa9be901-b9e9-4bbd-9f89-4ca876ef2315"
},
"serviceaccount": {
"name": "my-sa",
"uid": "491bbb33-01ef-4646-b184-97e32e9519ae"
}
},
"nbf": 1775121611,
"sub": "system:serviceaccount:default:my-sa"
}
# 파드에서 aws cli 사용 확인
kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn
"arn:aws:sts::911283464785:assumed-role/eksctl-myeks-pod-irsa-s3-readonly-role/botocore-session-1775122119"
# 되는 것고 안되는 것은 왜그런가?
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


목표 2 실습 후 관련 리소스 삭제
kubectl delete pod eks-iam-test3
eksctl delete iamserviceaccount --cluster $CLUSTER_NAME --name my-sa --namespace default
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
kubectl get sa



pod-identity-agent 설치 확인 : 노드 네트워크 정보 확인
# 확인
eksctl get addon --cluster $CLUSTER_NAME
kubectl -n kube-system get daemonset eks-pod-identity-agent
kubectl -n kube-system get pods -l app.kubernetes.io/name=eks-pod-identity-agent
kubectl get ds -n kube-system eks-pod-identity-agent -o yaml
...
containers:
- args:
- --port
- "80"
- --cluster-name
- myeks
- --probe-port
- "2703"
command:
- /go-runner
- /eks-pod-identity-agent
- server
....
ports:
- containerPort: 80
name: proxy
protocol: TCP
- containerPort: 2703
name: probes-port
protocol: TCP
...
securityContext:
capabilities:
add:
- CAP_NET_BIND_SERVICE
...
hostNetwork: true
...
# 네트워크 정보 확인 : SSM 을 통해 노드 접속 후 아래 확인
## EKS Pod Identity Agent uses the hostNetwork of the node and it uses port 80 and port 2703 on a link-local address on the node.
## This address is 169.254.170.23 for IPv4 and [fd00:ec2::23] for IPv6 clusters.
sudo ss -tnlp | grep eks-pod-identit
LISTEN 0 4096 169.254.170.23:80 0.0.0.0:* users:(("eks-pod-identit",pid=4446,fd=6))
LISTEN 0 4096 127.0.0.1:2703 0.0.0.0:* users:(("eks-pod-identit",pid=4446,fd=8))
LISTEN 0 4096 [fd00:ec2::23]:80 [::]:* users:(("eks-pod-identit",pid=4446,fd=3))
LISTEN 0 4096 *:2705 *:* users:(("eks-pod-identit",pid=4446,fd=7))
sudo ip -c route
sudo ip -c -br -4 addr
sudo ip -c addr
...
6: pod-id-link0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether e2:0e:05:d1:1f:06 brd ff:ff:ff:ff:ff:ff
inet 169.254.170.23/32 scope global pod-id-link0
valid_lft forever preferred_lft forever
...

eksctl 로 podidentityassociation 설정 : IAM Role 생성, K8S SA 생성
# eksctl 로 podidentityassociation 설정 : AWS CloudFormation 확인
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
{
"associations": [
{
"clusterName": "myeks",
"namespace": "default",
"serviceAccount": "s3-sa",
"associationArn": "arn:aws:eks:ap-northeast-2:911283464785:podidentityassociation/myeks/a-pm07a3bg79bqa3p24",
"associationId": "a-pm07a3bg79bqa3p24"
}
]
}
# IAM Role 확인 : ABAC 지원을 위해 sts:Tagsession 추가
aws iam get-role --query 'Role.AssumeRolePolicyDocument' --role-name s3-eks-pod-identity-role | jq .
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}

테스트용 파드 생성 및 AWS 사용 확인 : AssumeRoleForPodIdentity - Link

# 파드 생성
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
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE=/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
AWS_STS_REGIONAL_ENDPOINTS=regional
AWS_DEFAULT_REGION=ap-northeast-2
AWS_REGION=ap-northeast-2
AWS_CONTAINER_CREDENTIALS_FULL_URI=http://169.254.170.23/v1/credentials
# 토큰 정보 확인
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
# https://www.jwt.io/
## iss : 토큰 발급자(OIDC 제공업체)를 나타냅니다 https://oidc.eks.us-west-2.amazonaws.com/id/8BA4A70AA33A68D27898EB4903D8A6E7. 이 OIDC 제공업체 URL은 토큰 검증 과정에서 사용됩니다.
## aud : 이는 토큰의 수신자를 나타냅니다 pods.eks.amazonaws.com. 즉, EKS Pod Identity Service, 다시 말해 EKS Auth 서비스에서만 토큰이 허용되고 다른 서비스에서는 거부됩니다.
## exp 와 iat : 이는 토큰의 만료 시간을 나타내며, 기본적으로 시간 제한 토큰을 가능하게 합니다.
## kubernetes.io : 이 토큰은 네임스페이스 내의 app1 서비스 계정을 가진 특정 파드에 바인딩되어 있음을 나타냅니다 . 즉, 동일한 서비스 토큰과 동일한 네임스페이스를 가진 다른 파드에서도 이 토큰을 사용할 수 없습니다.sa1ns-a
{
"aud": [
"pods.eks.amazonaws.com"
],
"exp": 1775206063,
"iat": 1775124814,
"iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/032357E88E266F4AE7C2E8CF6F5EFEB0",
"jti": "f68ab71b-6360-459e-b20a-7277000ff0da",
"kubernetes.io": {
"namespace": "default",
"node": {
"name": "ip-192-168-12-7.ap-northeast-2.compute.internal",
"uid": "f42ea459-d9d0-45ea-b891-e16b79903cdd"
},
"pod": {
"name": "eks-pod-identity",
"uid": "c15f3022-1f8c-4748-87e1-51532d4da001"
},
"serviceaccount": {
"name": "s3-sa",
"uid": "a5ae3504-7237-461c-9027-3eb58288e1b9"
}
},
"nbf": 1775124814,
"sub": "system:serviceaccount:default:s3-sa"
}

실습 후 관련 리소스 삭제
eksctl delete podidentityassociation --cluster $CLUSTER_NAME --namespace default --service-account-name s3-sa
kubectl delete pod eks-pod-identity
kubectl delete sa s3-sa