이번 포스트에서는 EKS의 보안(Security)에 대해서 알아 보겠습니다.

 

물론 쿠버네티스의 보안에는 이미지 보안, 노드 보안과 같은 영역도 있지만, 여기서는 쿠버네티스의 인증(Authentication)/인가(Authorization)가 EKS에 적용된 방식과, 워크로드(파드)의 AWS의 리소스에 대한 보안 접근이라는 두 가지 주제를 살펴보겠습니다.

 

먼저 EKS의 인증/인가의 흐름을 kubeconfig를 바탕으로 이해해보고, 두번째로 워크로드(파드)에 AWS의 리소스에 접근 권한을 부여하기 위해 파드에 IAM을 할당하는 방식에 대해서 살펴보겠습니다.

 

목차

  1. Kubernetes의 인증/인가
  2. EKS의 인증/인가
  3. AKS의 인증/인가
  4. Kubernetes의 파드 권한
  5. EKS의 파드 권한 할당
  6. AKS의 파드 권한 할당

 

1. Kubernetes의 인증/인가

쿠버네티스에서는 API를 접근을 통제하기 위해서 아래와 같은 방식이 사용됩니다.

사용자나 파드(Service Account)는 Authentication(인증) -> Authorization(인가) -> Admission Control을 단계를 지나서 비로소 쿠버네티스 API에 접근할 수 있습니다.

출처: https://kubernetes.io/docs/concepts/security/controlling-access/

 

다만 쿠버네티스 자체는 직접적으로 사용자를 저장해 인증하는 방식을 구현하지 않고 있기 때문에 다른 인증 시스템에 위임을 하여 사용자에 대한 인증을 진행할 수 있습니다.

그 이후 인가 단계에서는 인증된 주체가 쿠버네티스 리소스에 대한 적절한 접근 권한을 가진 여부를 체크하게 됩니다. 마지막으로 Admission Control에서 요청 자체에 대한 Validation이나 Mutation과 같은 추가적인 절차를 진행할수 있도록 설계되어 있습니다.

그리고 그림에서 살펴보듯이 각 단계는 퍼즐 조각처럼 여러 형태의 인증, 인가, Admission Control을 선택적으로 추가할 수 있도록 되어 있습니다.

 

이후 EKS의 인증/인가 절에서는 AWS의 인증/엑세스 관리를 담당하는 IAM을 통해서 쿠버네티스의 인증/인가를 진행하는 과정을 설명하겠습니다. 즉, AWS에서 유효한 주체(사용자)가 어떻게 쿠버네티스의 인증/인가를 거쳐 쿠버네티스를 이용할 수 있는가의 관점입니다.

 

 

2. EKS의 인증/인가

사용자의 EKS의 인증/인가 체계를 이해하기 위해서 아래 그림을 바탕으로 kubectl 명령의 실행 흐름을 따라가 보겠습니다.

출처: https://www.youtube.com/watch?v=bksogA-WXv8&t=600s

 

1) kubectl get node 명령을 수행

2) kubeconfig에 정의된 aws eks get-token 명령으로 AWS STS reigional endpoint로 Amazon EKS 클러스터에 대한 인증 토큰 요청

3) aws eks get-token의 응답으로 Token 값 수신(base64로 디코딩하면 STS(Secure Token Service)로 GetCallerIdentity를 호출하는 Pre-Signed URL 값이 들어가 있음)

<< 이 단계까지는 EKS API Endpoint로 인증 요청 전 단계 >>

4) kubectl는 Pre-Signed URL을 bearer Token으로 EKS API Cluster Endpoint로 요청

5) API 서버는 aws-iam-authenticator server(Webhook Token Authentication)로 Token Review 요청

6) aws-iam-authenticator server에서 sts GetCallerIdentity를 호출

7) AWS IAM은 토큰이 유효한지 확인 후 인증 완료하고, IAM User나 Role에 대한 ARN을 반환

8) IAM의 User/Role을 쿠버네티스의 그룹으로 맵핑한 aws-auth(ConfigMap)을 통해 쿠버네티스의 보안 주체를 확인

9) aws-iam-authenticator server(Webhook Token Authentication)에서는 TokenReview라는 데이터 타입으로 useruame과 쿠버네티스 group을 반환

<< 이 단계까지가 인증의 단계 >>

10) 이 정보를 바탕으로 Kubernetes RBAC 기반 인가 진행

<< 이 단계까지가 인가의 단계 >>

11) 인가된 경우 kubectl get node에 대한 결과 반환

 

이와 같이 kubectl를 수행하면 IAM을 통해 사용자를 인증하고, 쿠버네티스 RBAC에 따라 인가를 하게 됩니다.

 

이를 다시 요약하여 아래와 같은 4단계로 나눠보겠습니다.

 

1) kubectl 요청을 수행하면 AWS 인증 정보를 통하여 EKS 클러스터에 대한 인증 토큰 요청

2) Webhook Token Authentication을 따라 IAM을 통한 인증 진행

3) 인증 완료 후 정보로 반환된 arn을 바탕으로 쿠버네티스 그룹과의 맵핑을 확인하는데, 이 절차는 아래와 같이 두가지 방식이 있습니다.

  • aws-auth(ConfigMap) 방식 (deprecated 될 예정)
  • EKS API 방식

4) 인증된 IAM 정보를 바탕으로 쿠버네티스 RBAC을 통해 인가 진행

 

 

참고로 위의 설명에서 aws-auth(컨피그 맵)에 IAM Role/User의 arn과 쿠버네티스의 권한 그룹과 맵핑 정보를 담고 있습니다. 사용자는 eksctl create iamidentitymapping를 통해 IAM 사용자와 클러스터 그룹을 맵핑하고, 이것이 컨피그 맵에 반영됩니다.

 

다만 이 방식은 컨피그 맵이 쿠버네티스에 노출되므로, 잘못 수정하는 경우 클러스터에 이슈가 발생할 수 있는 등 여러가지 문제가 있어 최근 EKS API 방식을 도입하였습니다.

 

EKS API는 컨피그 맵이 없어지고 EKS API를 통해서 Access Entry에 IAM Role/User와 Access Policy를 맵핑하여 관리하도록 합니다. IAM을 통한 인증 완료 후 반환된 ARN 정보를 EKS API의 Access Entry 맵핑을 확인하고, 이후 쿠버네티스 RBAC 인가를 받도록 절차가 변경됩니다.

출처: https://aws.amazon.com/ko/blogs/containers/a-deep-dive-into-simplified-amazon-eks-access-management-controls/

 

웹 콘솔을 접근해 EKS에서 Access 탭에서 Authentication mode를 확인할 수 있습니다. 기본 생성된 EKS 클러스터는 EKS API 및 ConfigMap이 선택되어 있습니다. 이 옵션에서 EKS API와 ConfigMap이 중복 설정되는 경우는 EKS API가 우선적용됩니다.

 

해당 페이지의 Manage access를 통해서 아래와 같이 변경 가능한 인터페이스가 있습니다.

 

이제 실제 EKS 환경에서 동작을 확인해보겠습니다.

 

클러스터 엑세스: ConfigMap

해당 옵션의 설정 방식을 살펴보기 위해서 아래와 같이 testuser를 만들고, EKS 클러스터에 접근하기 위한 권한을 할당하는 방식을 알아보겠습니다.

# 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

# 아래 실습은 kubectl을 신규로 세팅하기 위해 기존 aws configure가 되지 않은 VM에서 진행합니다.
# testuser 자격증명 설정
aws configure
AWS Access Key ID [None]: ...
AWS Secret Access Key [None]: ....
Default region name [None]: ap-northeast-2

# get-caller-identity 확인
aws sts get-caller-identity --query Arn
"arn:aws:iam::911283464785:user/testuser"

# testuser에 대한 kubeconfig를 획득합니다.
CLUSTER_NAME=myeks
aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser

# kubectl 시도 
kubectl get node
E0315 22:46:29.480897    1795 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: the server has asked for the client to provide credentials"
E0315 22:46:30.514466    1795 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: the server has asked for the client to provide credentials"
E0315 22:46:31.629986    1795 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: the server has asked for the client to provide credentials"
E0315 22:46:32.658748    1795 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: the server has asked for the client to provide credentials"
E0315 22:46:33.649009    1795 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: the server has asked for the client to provide credentials"
error: You must be logged in to the server (the server has asked for the client to provide credentials)

# kubeconfig 확인
cat ~/.kube/config
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: ~
    server: ~
contexts:
- context:
    cluster: arn:aws:eks:ap-northeast-2:xx:cluster/myeks
    user: testuser
  name: testuser
current-context: testuser
kind: Config
preferences: {}
users:
- name: testuser
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - --region
      - ap-northeast-2
      - eks
      - get-token
      - --cluster-name
      - myeks
      - --output
      - json
      command: aws

kubectl get cm -n kube-system aws-auth -o yaml

apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::xx:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-e7CGUnBoQC96
      username: system:node:{{EC2PrivateDNSName}}
kind: ConfigMap
metadata:
  creationTimestamp: "2025-03-15T13:10:23Z"
  name: aws-auth
  namespace: kube-system
  resourceVersion: "2028"
  uid: 13151df2-4cd2-4fc2-92dc-2b0289a1be55

 

testuser도 AdministratorAccess 권한을 가지고 있지만 실제로 EKS의 API Server에 인증되는 권한은 없습니다. (단, 컨피그 맵에는 정보가 없지만 EKS를 생성한 admin 계정은 EKS API의 Access Entry에 등록되어 권한이 있음)

 

아래 iamidentitymapping 를 생성하면 aws-auth(컨피그 맵)이 업데이트 됩니다.

# Creates a mapping from IAM role or user to Kubernetes user and groups
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
ARN                                                                                     USERNAME                                GROUPS                          ACCOUNT
arn:aws:iam::xx:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-e7CGUnBoQC96 system:node:{{EC2PrivateDNSName}}       system:bootstrappers,system:nodes

# IAM Identity Mapping 생성
ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
eksctl create iamidentitymapping --cluster $CLUSTER_NAME --username testuser --group system:masters --arn arn:aws:iam::$ACCOUNT_ID:user/testuser

# 확인
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
ARN                                                                                     USERNAME                                GROUPS                          ACCOUNT
arn:aws:iam::xx:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-e7CGUnBoQC96 system:node:{{EC2PrivateDNSName}}       system:bootstrappers,system:nodes
arn:aws:iam::xx:user/testuser                                                 testuser                                system:masters

kubectl get cm -n kube-system aws-auth -o yaml
...
  mapUsers: |
    - groups:
      - system:masters
      userarn: arn:aws:iam::xx:user/testuser
      username: testuser
...

 

다만 IAM Identity Mapping 생성 후에 즉각적으로 kubectl 이 가능해지는 것은 아니며, 반영에 일부 시간이 걸릴 수 있습니다.

 

이후에 실행해보면 비로소 EKS에서 kubectl이 성공합니다.

# 시도
kubectl get node 
NAME                                               STATUS   ROLES    AGE   VERSION
ip-192-168-1-41.ap-northeast-2.compute.internal    Ready    <none>   64m   v1.31.5-eks-5d632ec
ip-192-168-2-79.ap-northeast-2.compute.internal    Ready    <none>   65m   v1.31.5-eks-5d632ec
ip-192-168-3-202.ap-northeast-2.compute.internal   Ready    <none>   65m   v1.31.5-eks-5d632ec

 

실습을 마무리하고, 다음 실습을 위해서 iamidentitymapping을 삭제하겠습니다.

# testuser IAM 맵핑 삭제
eksctl delete iamidentitymapping --cluster $CLUSTER_NAME --arn  arn:aws:iam::$ACCOUNT_ID:user/testuser

# Get IAM identity mapping(s)
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
kubectl get cm -n kube-system aws-auth -o yaml

 

 

클러스터 엑세스: EKS API

웹 콘솔의 EKS>Access>IAM access entries 를 보면 현재 할당된 권한을 확인할 수 있습니다.

현재 EKS API and ConfigMap으로 해당 클러스터를 생성한 관리자 계정은 이미 AmazoneEKSClusterAdminPolicy를 할당 받은 것으로 확인 됩니다.

먼저 아래 명령으로 EKS API 엑세스 모드로 변경합니다. 옵션을 변경하는 경우 다시 기존 옵션으로 원복은 불가한점 유의가 필요합니다.

# EKS API 액세스모드로 변경
aws eks update-cluster-config --name $CLUSTER_NAME --access-config authenticationMode=API

 

웹 콘솔에서도 변경된 것으로 확인 됩니다.

 

참고로 아래 문서를 살펴보시면 EKS를 위해 생성된 Access Policy와 어떤 권한이 할당되었는지를 확인하실 수 있으며, 현재 제공되는 Policy는 아래와 같습니다. CLI에서는 aws eks list-access-policies 를 통해서 확인 가능 합니다.

https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html

 

현재 생성된 Access Entry를 확인 할 수 있습니다.

# 현재 생성된 Access Entry 확인
aws eks list-access-entries --cluster-name $CLUSTER_NAME | jq
{
  "accessEntries": [
    "arn:aws:iam::xx:role/aws-service-role/eks.amazonaws.com/AWSServiceRoleForAmazonEKS",
    "arn:aws:iam::xx:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-e7CGUnBoQC96",
    "arn:aws:iam::xx:user/eksadmin"
  ]
}

# admin 계정의 Associated Access Policy 확인 -> AmazonEKSClusterAdminPolicy
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
{
    "associatedAccessPolicies": [
        {
            "policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy",
            "accessScope": {
                "type": "cluster",
                "namespaces": []
            },
            "associatedAt": "2025-03-15T21:56:26.361000+09:00",
            "modifiedAt": "2025-03-15T21:56:26.361000+09:00"
        }
    ],
    "clusterName": "myeks",
    "principalArn": "arn:aws:iam:xx:user/eksadmin"
}

 

앞서 생성한 testuser에 대해서 Access Entry를 생성하고 Associated Access Policy를 연결합니다.

# testuser 의 access entry 생성
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에 AmazonEKSClusterAdminPolicy 연동
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/AmazonEKSClusterAdminPolicy --access-scope type=cluster

#  Associated Access Policy 확인
aws eks list-associated-access-policies --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser
{
    "associatedAccessPolicies": [
        {
            "policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy",
            "accessScope": {
                "type": "cluster",
                "namespaces": []
            },
            "associatedAt": "2025-03-15T23:30:17.290000+09:00",
            "modifiedAt": "2025-03-15T23:30:17.290000+09:00"
        }
    ],
    "clusterName": "myeks",
    "principalArn": "arn:aws:iam::xx:user/testuser"
}

 

기존 testuser에서 EKS API를 통해서도 정상적으로 kubectl 수행이 가능합니다.

# kubectl 시도
kubectl get node 
NAME                                               STATUS   ROLES    AGE   VERSION
ip-192-168-1-41.ap-northeast-2.compute.internal    Ready    <none>   79m   v1.31.5-eks-5d632ec
ip-192-168-2-79.ap-northeast-2.compute.internal    Ready    <none>   79m   v1.31.5-eks-5d632ec
ip-192-168-3-202.ap-northeast-2.compute.internal   Ready    <none>   79m   v1.31.5-eks-5d632ec

# 현재는 AmazonEKSClusterAdminPolicy 이기 때문에 해당 작업이 가능함
kubectl auth can-i delete pods --all-namespaces
yes

# 컨피그 맵에 값이 반영되지 않는 것을 알 수 있습니다.
kubectl get cm -n kube-system aws-auth -o yaml
...
  mapUsers: |
    []
...

 

한편 Access Entry 자체를 쿠버네티스 그룹과도 맵핑해줄 수 있습니다. 먼저 앞서 생성한 Access Entry를 제거하고, 쿠버네티스 그룹과 맵핑을 해보겠습니다.

# 기존 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[]

# 확인
(testuser:N/A) [root@operator-host-2 ~]# kubectl get no
error: You must be logged in to the server (Unauthorized)

# Cluster Role 생성
cat <<EoF> ~/pod-viewer-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pod-viewer-role
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["list", "get", "watch"]
EoF

kubectl apply -f ~/pod-viewer-role.yaml

# Cluster Rolebinding 생성
kubectl create clusterrolebinding viewer-role-binding --clusterrole=pod-viewer-role --group=pod-viewer


# Access Entry 생성 (--kubernetes-group 옵션 추가)
aws eks create-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser --kubernetes-group pod-viewer

# acess policy 자체에서는 정보가 보이지 않는다.
aws eks list-associated-access-policies --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser

{
    "associatedAccessPolicies": [],
    "clusterName": "myeks",
    "principalArn": "arn:aws:iam::xx:user/testuser"
}

aws eks describe-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser | jq
{
  "accessEntry": {
    "clusterName": "myeks",
    "principalArn": "arn:aws:iam::xx:user/testuser",
    "kubernetesGroups": [
      "pod-viewer"
    ],
    "accessEntryArn": "arn:aws:eks:ap-northeast-2:xx:access-entry/myeks/user/xx/testuser/4ccacd1e-2a2e-fd71-93e2-94ead13e95e3",
    "createdAt": "2025-03-15T23:36:39.977000+09:00",
    "modifiedAt": "2025-03-15T23:36:39.977000+09:00",
    "tags": {},
    "username": "arn:aws:iam::xx:user/testuser",
    "type": "STANDARD"
  }
}

 

아래와 같이 정보를 확인해봅니다.

# kubectl 시도 (node 조회는 불가, po는 조회 가능)
(testuser:N/A) [root@operator-host-2 ~]# kubectl get no
Error from server (Forbidden): nodes is forbidden: User "arn:aws:iam::xx:user/testuser" cannot list resource "nodes" in API group "" at the cluster scope
(testuser:N/A) [root@operator-host-2 ~]# kubectl get po
No resources found in default namespace.

# can-i 로 확인
kubectl auth can-i get pods --all-namespaces
yes
kubectl auth can-i delete pods --all-namespaces
no

 

EKS의 인증/인가에 대한 실습을 마무리하겠습니다.

 

 

3. AKS의 인증/인가

Azure에서는 Microsoft Entra ID(이전 명칭: Azure AD(Azure Active Directory))라는 ID 및 엑세스 관리 서비스를 가지고 있습니다.

AKS의 인증 또한 Entra ID를 이용할 수 있으며 Azure RBAC을 함께 사용하여 다양한 옵션을 제공하고 있습니다.

애저 포탈에서 AKS의 Settings>security configuration를 확인해보면 AKS에서 선택 가능한 인증/인가 방식을 확인할 수 있습니다.

 

사실 이부분에 대해서 제대로 이 분류를 설명하고 있는 문서가 없고, 아래의 공식 문서 또한 각 용어들을 산별적으로 설명하고 있어 이해하기가 쉽지 않습니다.

https://learn.microsoft.com/en-us/azure/aks/concepts-identity

것을 설명하는 데 있어서도 어려운 부분이라 단계별로 설명을 이어나가 보겠습니다.

 

사전 지식1

이때 Microsoft Entra ID는 Azure와 M365를 포괄하는 ID 인증 관리 체계로 이해할 수 있으며,

 

Azure RBAC은 Azure 수준에 대한 권한 부여 방식이라고 간단히 이해하고 넘어가겠습니다. Azure RBAC을 이용하는 역할 할당은 각 수준별(관리그룹, 구독, 리소스 그룹, 리소스) 엑세스 제어(IAM) 메뉴에서 가능합니다.

Azure Portal의 액세스 제어(C:\Users\montauk\Desktop\STD\5. Seminar\202502_AEWS(EKS)\EKS과제6주차_20250310.assets\sub-role-assignments.png) 페이지 스크린샷.

출처: https://learn.microsoft.com/ko-kr/azure/role-based-access-control/rbac-and-directory-admin-roles

 

Azure 관점에서 각 Azure 리소스의 엑세스 제어(IAM)에 Microsoft Entra ID의 주체(사용자, 애플리케이션)와 Role을 할당하는 것으로 간단히 생각하실 수 있습니다.

 

사전 지식2

AWS와 Azure의 IAM 부분에서 용어나 관점의 차이가 있습니다.

AWS의 Role은 사용자와 같은 주체의 의미입니다. Azure에서 이러한 주체는 Service Principal(SP)이나 Managed Identity(MI)라고 합니다.

Azure의 Role은 권한(Action)들의 집합을 의미합니다. AWS에서는 이 개념을 Policy라고 합니다.

 

AWS의 IAM에서는 사용자나 Role에 Policy를 할당합니다. AWS의 IAM은 주체 관점의 RBAC(주체에 리소스+권한을 할당)을 구현하고 있습니다.

Azure의 RBAC에서는 대상(리소스)에 사용자나 주체(SP,MI)를 Role을 맵핑합니다. Azure는 리소스 관점의 RBAC(리소스에 사용자+권한을 할당)을 구현하고 있습니다.

 

예를 들어, 특정 testuser에게 가상 머신의 관리자 권한을 준다고할 때 AWS와 Azure의 방식은 아래와 같습니다.

  • AWS는 신규 Policy에 가상머신을 선택하고 관리자 권한을 부여하고, testuser에게 이 Policy를 할당합니다.
  • Azure는 가상머신에서 testuser와 관리자 Role을 맵핑하여 권한을 할당합니다.

 

사전 지식3

AKS를 위한 Azure RBAC에서는 AKS 리소스를 위한 RoleKubernetes를 위한 Role이 구분되어 있습니다.

AKS 리소스를 위한 Role이라는 것은 Azure 리소스 차원에서 AKS에 대한 CRUD(클러스터 설정 변경, 노드 풀 스케일링 등)에 대한 권한입니다. 또한 az aks get-credentials를 통한 kubeconfig를 획득하기 위한 권한도 별도로 있습니다.

Kubernetes를 위한 Role은 쿠버네티스 내부의 리소스에 대한 CRUD(Deployment 생성, confimgMap 조회 등)을 의미하며, Microsoft Entra ID authentication with Azure RBAC에서 Azure RBAC을 통해서 Kubernetes에 대한 권한을 부여할 수 있다는 의미입니다.

 

사전 지식4

그 다음은 Local accounts라는 개념으로, 활성화 된 경우 admin에 해당하는 local account가 기본적으로 생성되어 있습니다. 이는 Microsoft Entra ID를 사용하는 경우에도 존재할 수 있으며, az aks get-credentials--admin 플래그를 사용하는 경우 kubeconfig admin credentials을 획득할 수 있습니다. 이를 통해 관리자가 Entra ID인증 없이 쿠버네티스를 접근할 수 있으나, 이는 보안에 취약할 수 있어 local accounts를 비활성화 할 수 있습니다.

참고: https://learn.microsoft.com/en-us/azure/aks/manage-local-accounts-managed-azure-ad

 

사전 지식을 바탕으로 아래에 대해서 설명을 이어 나가겠습니다.

 

Local accounts with Kubernetes RBAC

Microsoft Entra ID와 인증을 연동하지 않은 모드입니다.

이 구성에서 Azure Kubernetes Service Cluster User Role을 부여 받은 사용자나 그룹은 az aks get-credentials을 통해 kubeconfig를 획득할 수 있습니다. 이 Kubeconfig는 Kubernetes의 admin 권한입니다. 이후 Kubernetes RBAC을 통해서 인가를 구성할 수 있습니다. 이 구성에서는 user credentials이 admin 권한을 가지기 때문에 --admin 플래그와 차이가 없습니다.

 

이 방식은 Microsoft Entra ID를 통한 쿠버네티스 인증이 없습니다. 단순히 kubeconfig 를 획득할 수 있는 Role을 부여하거나 부여하지 않는 방식으로 사용자를 구분할 수 있지만, 획득한 kubeconfig는 모두 동일하게 쿠버네티스에 대한 admin 권한을 가집니다.

 

Microsoft Entra ID authentication with Kubernetes RBAC

이 구성은 Microsoft Entra ID로 인증을 연동하고, 또한 Entra ID의 사용자나 그룹을 Kubernetes RBAC의 주체로 사용할 수 있습니다.

아래는 Microsoft Entra ID authentication with Kubernetes RBAC를 선택한 옵션으로 Kubernetes의 admin에 대한 ClusterRoleBinding을 지정할 Entra ID의 그룹을 지정할 수 있습니다. 해당 지정된 그룹의 사용자는 쿠버네티스의 admin 에 해당하는 권한을 할당 받습니다.

 

이 때 사용자 시나리오는 관리자 그룹에서 Kubernetes의 role/binding을 관리하고, 나머지 사용자나 그룹에 Kubernetes의 RBAC을 부여하여 사용하는 방식입니다.

 

Azure Kubernetes Service Cluster Admin Role을 가지는 사용자는 Kubernetes에 대한 admin 권한을 가진 kubeconfig를 획득할 수 있습니다. 이 때문에 local account를 비활성화 하는 옵션이 아래에 표시되어 있습니다.

 

이 구성에서도 Azure Kubernetes Service Cluster User Role을 부여 받은 사용자나 그룹만 az aks get-credentials을 통해 kubeconfig를 획득할 수 있습니다.

 

이후 획득한 kubeconfig는 Microsoft Entra ID의 인증과 연동하도록 구성되어 있으며, 또한 Kubernetes RBAC 모드에서는 RoleBinding에서 Azure Entra ID의 사용자(UPN, User Principal Name)나 그룹(Object ID)을 지정할 수 있습니다.

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: dev-user-access
  namespace: dev
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: dev-user-full-access
subjects:
- kind: Group # 일반 유저인 경우 kind: User
  namespace: dev
  name: groupObjectId # 일반 유저인 경우 user principal name (UPN) 입력

 

이 방식은 EKS의 인증/인가 방식에서 ConfigMap을 사용하는 방식(IAM의 User를 쿠버네티스의 그룹과 맵핑하는 방식)과 유사하다고 생각됩니다. 한편, EKS API의 access entry에서도 IAM의 User와 쿠버네티스 그룹을 맵핑해줄 수도 있습니다.

 

Microsoft Entra ID authentication with Azure RBAC

이 구성은 Microsoft Entra ID로 인증을 연동하고, 또한 Azure RBAC을 통하여 쿠버네티스 인가를 사용할 수 있습니다.

이 구성에서도 Azure Kubernetes Service Cluster User Role을 부여 받은 사용자나 그룹만 az aks get-credentials을 통해 kubeconfig를 획득할 수 있습니다.

이후 획득한 kubeconfig는 Microsoft Entra ID의 인증과 연동하도록 구성되어 있으며, 또한 Azure RBAC을 통해서 쿠버네티스의 인가를 구성할 수 있습니다.

 

먼저 아래와 같은 Built-in Role을 사용하거나 별도의 Custom Role을 생성할 수도 있습니다.

https://learn.microsoft.com/en-us/azure/aks/manage-azure-rbac?tabs=azure-cli#aks-built-in-roles

 

또한 Azure RBAC을 통해서 해당 Role을 사용자/그룹에 할당할 수 있습니다. 이때 --scope을 통해서 특정 네임스페이스에 할당할 수 있는 점에서 완전히 Azure RBAC을 통해 쿠버네티스 수준의 관리까지 지원을 하고 있는 것을 알 수 있습니다.

# AKS에 권한 할당
az role assignment create --role "Azure Kubernetes Service RBAC Admin" --assignee <AAD-ENTITY-ID> --scope $AKS_ID

# 특정 Namespace에 권한 할당
az role assignment create --role "Azure Kubernetes Service RBAC Reader" --assignee <AAD-ENTITY-ID> --scope $AKS_ID/namespaces/<namespace-name>

 

참고로, Azure Kubernetes Service Cluster Admin Role을 가지는 사용자는 Kubernetes에 대한 admin 권한을 가진 kubeconfig를 획득할 수 있습니다. 이 때문에 local account를 비활성화할 수 있습니다.

 

Microsoft Entra ID authentication with Azure RBAC의 접근 방식은 AKS라는 리소스에 권한을 할당하는 방식만으로 쿠버네티스 권한 관리까지 수행할 수 있기 때문에, Azure 수준에서 AKS에 할당된 모든 권한(리소스와 쿠버네티스 권한)를 확인할 수 있다는 장점이 있습니다.

 

이 방식은 EKS의 인증/인가 방식에서 EKS API를 사용하는 방식(AWS IAM의 User를 정의된 Policy와 맵핑하는 방식)과 유사한 것 같습니다. 다만 EKS API의 access entry에는 쿠버네티스 그룹을 맵핑해줄 수도 있습니다.

 

AKS의 인증/인가 요약

요약하면, AKS에서는 특정 사용자/그룹에 한해 kubeconfig를 획득하는 권한을 할당해야 합니다.

Microsoft Entra ID와 통합하지 않은 경우에는 admin 권한의 kubeconfig를 사용하는 방식으로 사용할 수 있습니다.

Kubernetes의 인증을 Microsoft Entra ID와 통합할 수 있으며, 또한 인가 방식에서 Kubernetes RBAC과 Azure RBAC를 선택할 수 있습니다.

마지막으로 Microsoft Entra ID와 통합을 한 경우 local account를 비활성화 할 수 있습니다.

 

EKS와의 차이점을 보면, kubeconfig를 획득하기 위한 권한을 별도로 가지고 있다는 점과 Microsoft Entra ID를 인증을 하지 않는 local account 방식을 제공하는 부분이 있습니다. 또한 Azure RBAC을 통해 인가를 처리해줄 수 있는 부분이 있습니다.

 

 

4. Kubernetes의 파드 권한

쿠버네티스에서 파드에 ServiceAccount를 부여하고, ServiceAccount라는 주체를 RBAC으로 구성하면 쿠버네티스의 리소스에 대한 권한을 할당 받습니다. 예를 들어, 배포를 담당하는 파드가 있고, 해당 파드 할당된 ServiceAccount에 deployments에 대한 CRUD를 허용하면, 파드에서 deployment 배포가 가능하게 됩니다.

 

다만 파드가 쿠버네티스의 리소스가 아닌 클라우드 자원 자체에 접근한다는 것은 다른 이야기입니다. 예를 들어, 파드가 AWS의 S3를 조회하거나 파일을 업로드하는 것입니다. 즉, 쿠버네티스에서 인증/인가할 수 있는 범위를 넘어서, 클라우드에서 제공하는 Identity 및 Access 관리 솔루션을 통해서 인증/인가를 받아야 합니다.

 

앞서 살펴본 EKS의 인증/인가 절에서는 AWS에서 유효한 사용자가 어떻게 쿠버네티스의 인증/인가를 이용할 수 있는가의 관점이라면, 지금 다루는 주제는 쿠버네티스에서 유효한 주체가 어떻게 AWS의 인증/인가를 이용할 수 있는가에 대한 문제입니다.

 

EKS에서는 IRSA(IAM Roles for Service Accounts)와 Pod Identity라는 방식을 제공하고 있고, 이를 다음 절 EKS의 파드 권한 할당에서 자세히 살펴보겠습니다.

 

 

5. EKS의 파드 권한 할당

아무런 권한을 부여하지 않은 파드는 노드의 권한을 가지게 됩니다. AWS에서는 인스턴스에 부여된 IAM Role을 가지게 됩니다.

아래와 같이 확인을 해볼 수 있습니다.

# awscli 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: awscli-pod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: awscli-pod
  template:
    metadata:
      labels:
        app: awscli-pod
    spec:
      containers:
      - name: awscli-pod
        image: amazon/aws-cli
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

# 파드 생성 확인
kubectl get pod -owide

# 파드 이름 변수 지정
APODNAME1=$(kubectl get pod -l app=awscli-pod -o jsonpath="{.items[0].metadata.name}")
APODNAME2=$(kubectl get pod -l app=awscli-pod -o jsonpath="{.items[1].metadata.name}")
echo $APODNAME1, $APODNAME2

# awscli 파드에서 EC2 InstanceProfile(IAM Role)의 ARN 정보가 확인됨
kubectl exec -it $APODNAME1 -- aws sts get-caller-identity --query Arn
"arn:aws:sts::xx:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-e7CGUnBoQC96/i-0e6fd9f697b0a4c2f"
kubectl exec -it $APODNAME2 -- aws sts get-caller-identity --query Arn
"arn:aws:sts::xx:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-e7CGUnBoQC96/i-04ed117980b8faf7f"

# 해당 IAM Role에 권한이 없기 때문에 실패함
kubectl exec -it $APODNAME1 -- aws s3 ls
An error occurred (AccessDenied) when calling the ListBuckets operation: User: arn:aws:sts::xx:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-e7CGUnBoQC96/i-0e6fd9f697b0a4c2f is not authorized to perform: s3:ListAllMyBuckets because no identity-based policy allows the s3:ListAllMyBuckets action
command terminated with exit code 254

 

그렇다면 인스턴스에 부여된 IAM Role을 통하여 권한을 할당하면 된다고 생각할 수 있지만, 이런 방식에서는 노드에 실행된 모든 파드에서 동일한 권한이 부여되기 때문에 최소 권한에 위배됩니다.

 

참고로 EKS를 생성하는 ClusterConfig에 관리 노드 그룹에 대해 아래와 같이 IAM을 지정할 수 있습니다.

...
managedNodeGroups:
- amiFamily: AmazonLinux2023
  desiredCapacity: 3
  iam:
    withAddonPolicies:
      autoScaler: true
      certManager: true
      externalDNS: true
  instanceType: t3.medium
...

웹 콘솔에서는 아래와 같이 인스턴스에 할당된 IAM을 따라가보면 아래와 같이 확인할 수 있습니다.

 

이러한 이유로 권한이 필요한 파드에 IAM Role을 부여하는 방식이 필요합니다. 파드 권한 할당을 위해 EKS에서 제공하는 IRSA와 Pod Identity를 확인해보겠습니다.

 

IRSA(IAM Roles for Service Accounts)

IRSA는 권한이 부여된 IAM Role을 SerivceAccount에 할당하고, 파드가 ServiceAccount를 사용하여 AWS의 인증을 통해 AWS 리소스를 접근하는 방식입니다. 이를 위해서 OIDC Issuer가 JWT를 발급해주고, 또한 IAM과 신뢰관계를 통해서 발급 여부를 확인해줍니다.

 

IRSA는 아래와 같은 절차를 통해서 이뤄집니다.

출처: https://github.com/awskrug/security-group/blob/main/files/AWSKRUG_2024_02_EKS_ROLE_MANAGEMENT.pdf

 

실습을 통해 살펴보겠습니다.

# Create an iamserviceaccount - AWS IAM role bound to a Kubernetes service account
eksctl create iamserviceaccount \
  --name my-sa \
  --namespace default \
  --cluster $CLUSTER_NAME \
  --approve \
  --attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`AmazonS3ReadOnlyAccess`].Arn' --output text)

2025-03-15 23:58:25 []  1 existing iamserviceaccount(s) (kube-system/aws-load-balancer-controller) will be excluded
2025-03-15 23:58:25 []  1 iamserviceaccount (default/my-sa) was included (based on the include/exclude rules)
2025-03-15 23:58:25 [!]  serviceaccounts that exist in Kubernetes will be excluded, use --override-existing-serviceaccounts to override
2025-03-15 23:58:25 []  1 task: {
    2 sequential sub-tasks: {
        create IAM role for serviceaccount "default/my-sa",
        create serviceaccount "default/my-sa",
    } }2025-03-15 23:58:25 []  building iamserviceaccount stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2025-03-15 23:58:25 []  deploying stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2025-03-15 23:58:25 []  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2025-03-15 23:58:56 []  waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2025-03-15 23:58:56 []  created serviceaccount "default/my-sa"

# SA 확인
kubectl get sa
NAME      SECRETS   AGE
default   0         122m
my-sa     0         4m37s

kubectl describe sa my-sa
Name:                my-sa
Namespace:           default
Labels:              app.kubernetes.io/managed-by=eksctl
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::xx:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-MYPji4gGE3x2
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>

 

쿠버네티스에 새로운 SerivceAccount가 생성되었고, 어노테이션으로 ARN이 지정된 것을 알 수 있습니다.

 

또한 위 명령을 수행하면 CloudFormation이 실행되고 IAM Role이 생성됩니다. CloudFormation을 확인해서 리소스를 보면 IAM Role이 생성된 것을 확인할 수 있습니다.

생성된 ServiceAccount를 통해 IRSA를 사용하는 파드를 생성 합니다.

# 파드 생성
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

# 파드에서 aws cli 사용 확인
NAMESPACE       NAME                            ROLE ARN
default         my-sa                           arn:aws:iam::xx:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-MYPji4gGE3x2
kube-system     aws-load-balancer-controller    arn:aws:iam::xx:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-oyUqvqXumqCT

kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn
"arn:aws:sts::xx:assumed-role/eksctl-myeks-addon-iamserviceaccount-default--Role1-MYPji4gGE3x2/botocore-session-1742051277"

# 할당된 Policy에 의해 가능한 작업 (에러 발생하지 않음)
kubectl exec -it eks-iam-test3 -- aws s3 ls

# 할당된 Policy에 의해 불가한 작업
kubectl exec -it eks-iam-test3 -- aws ec2 describe-instances --region ap-northeast-2
An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation. User: arn:aws:sts::xx:assumed-role/eksctl-myeks-addon-iamserviceaccount-default--Role1-MYPji4gGE3x2/botocore-session-1742051277 is not authorized to perform: ec2:DescribeInstances because no identity-based policy allows the ec2:DescribeInstances action
command terminated with exit code 254

 

파드 스펙에는 ServiceAccount 이름만 지정하지만, Admission Controller의 Mutation Webhook에 의해서 필요한 정보가 추가로 등록된 것을 확인할 수 있습니다.

# 해당 SA를 파드가 사용 시 mutatingwebhook으로 Env,Volume 추가함: AWS IAM 역할을 Pod에 자동으로 주입
kubectl get mutatingwebhookconfigurations pod-identity-webhook -o yaml
...
webhooks:
- admissionReviewVersions:
  - v1beta1
  clientConfig:
    caBundle: xxx
    url: https://127.0.0.1:23443/mutate
  failurePolicy: Ignore
  matchPolicy: Equivalent
  name: iam-for-pods.amazonaws.com
  namespaceSelector: {}
  objectSelector:
    matchExpressions:
    - key: eks.amazonaws.com/skip-pod-identity-webhook
      operator: DoesNotExist
  reinvocationPolicy: IfNeeded
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    resources:
    - pods
    scope: '*'
  sideEffects: None
  timeoutSeconds: 10
...

# Pod Identity Webhook은 mutating webhook을 통해 아래 Env 내용과 1개의 볼륨을 추가함
kubectl get pod eks-iam-test3
kubectl get pod eks-iam-test3 -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::xx:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-MYPji4gGE3x2
    - 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:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          audience: sts.amazonaws.com
          expirationSeconds: 86400
          path: token
...

 

마운트된 토큰을 확인해보겠습니다.

# 토큰 확인
kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token ; echo

 

JWT로 디코드 해보면 아래와 같은 정보를 확인할 수 있습니다.

{
  "aud": [
    "sts.amazonaws.com"
  ],
  "exp": 1742137620,
  "iat": 1742051220,
  "iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/EF882B...",
  "jti": "585eab74-0dd1-4047-a8d5-2181c3db9c13",
  "kubernetes.io": {
    "namespace": "default",
    "node": {
      "name": "ip-192-168-1-41.ap-northeast-2.compute.internal",
      "uid": "fb4be118-8152-452a-a0aa-eaff394022e2"
    },
    "pod": {
      "name": "eks-iam-test3",
      "uid": "784ef7a7-0acd-4394-bcd7-c4f71f5c101f"
    },
    "serviceaccount": {
      "name": "my-sa",
      "uid": "bd180c18-c38d-4974-8f34-8d9b2f7b37c8"
    }
  },
  "nbf": 1742051220,
  "sub": "system:serviceaccount:default:my-sa"
}

 

JWT 토큰의 iss를 확인해보면, 웹 콘솔의 EKS를 확인해보면 OpenID Connect provider URL와 일치하는 것을 알 수 있습니다.

 

실습을 마무리하고 IRSA 관련 리소스는 삭제하겠습니다.

# 실습 확인 후 파드 삭제 및 IRSA 제거
kubectl delete deply awscli-pod
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

 

이러한 IRSA는 관리 복잡성과, ServiceAccount를 세부적으로 지정하지 않는 경우 보안에 취약한 점 등으로 현재는Classic으로 여겨지고 이후 Pod Identity가 도입되었습니다.

 

Pod Identity

IRSA는 2019년에 도입되었다면, Pod Identity는 2023년 비교적 최근에 도입된 방식으로 보안과 사용성 측면에서 개선된 사항이 많습니다.

 

Pod Identity는 EKS Pod Identity Agent를 통해서 credentials을 발급 받고, EKS Auth API를 통해서 인증을 처리 받습니다. 아래의 처리과정을 참고 부탁드립니다.

출처: https://aws.amazon.com/ko/blogs/containers/amazon-eks-pod-identity-a-new-way-for-applications-on-eks-to-obtain-iam-credentials/

 

아래와 같이 실습을 진행하겠습니다.

Pod Identity는 애드온으로 설치가 가능합니다.

# Pod Identity 버전 확인
ADDON=eks-pod-identity-agent
aws eks describe-addon-versions \
    --addon-name $ADDON \
    --kubernetes-version 1.31 \
    --query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \
    --output text
v1.3.5-eksbuild.2
False
v1.3.4-eksbuild.1
True
v1.3.2-eksbuild.2
False
v1.3.0-eksbuild.1
False
v1.2.0-eksbuild.1
False
v1.1.0-eksbuild.1
False
v1.0.0-eksbuild.1
False

# 설치
eksctl create addon --cluster $CLUSTER_NAME --name eks-pod-identity-agent --version 1.3.5

# 확인
eksctl get addon --cluster $CLUSTER_NAME

NAME                    VERSION                 STATUS          ISSUES  IAMROLE                                                                                 UPDATE AVAILABLE CONFIGURATION VALUES            POD IDENTITY ASSOCIATION ROLES
aws-ebs-csi-driver      v1.40.1-eksbuild.1      ACTIVE          0       arn:aws:iam::xx:role/eksctl-myeks-addon-aws-ebs-csi-driver-Role1-15a6w33Xm4wR
coredns                 v1.11.4-eksbuild.2      ACTIVE          0
eks-pod-identity-agent  v1.3.5-eksbuild.2       CREATING        0
kube-proxy              v1.31.3-eksbuild.2      ACTIVE          0
metrics-server          v0.7.2-eksbuild.2       ACTIVE          0
vpc-cni                 v1.19.3-eksbuild.1      ACTIVE          0       arn:aws:iam::xx:role/eksctl-myeks-addon-vpc-cni-Role1-RS9uYpCia7T9            enableNetworkPolicy: "true"

# 데몬 셋으로 설치됨
kubectl -n kube-system get daemonset eks-pod-identity-agent

NAME                     DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
eks-pod-identity-agent   3         3         3       3            3           <none>          33s

 

아래와 같이 Pod Identity Association을 생성합니다.

# Pod Identity Association을 생성
eksctl create podidentityassociation \
--cluster $CLUSTER_NAME \
--namespace default \
--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

2025-03-16 00:22:39 [ℹ]  1 task: {
    2 sequential sub-tasks: {
        create IAM role for pod identity association for service account "default/s3-sa",
        create pod identity association for service account "default/s3-sa",
    } }2025-03-16 00:22:39 [ℹ]  deploying stack "eksctl-myeks-podidentityrole-default-s3-sa"
2025-03-16 00:22:40 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-podidentityrole-default-s3-sa"
2025-03-16 00:23:10 [ℹ]  waiting for CloudFormation stack "eksctl-myeks-podidentityrole-default-s3-sa"
2025-03-16 00:23:11 [ℹ]  created pod identity association for service account "s3-sa" in namespace "default"
2025-03-16 00:23:11 [ℹ]  all tasks were completed successfully

# 확인
kubectl get sa

NAME      SECRETS   AGE
default   0         142m

eksctl get podidentityassociation --cluster $CLUSTER_NAME
ASSOCIATION ARN                                                                                 NAMESPACE       SERVICE ACCOUNT NAME    IAM ROLE ARN            OWNER ARN
arn:aws:eks:ap-northeast-2:xx:podidentityassociation/myeks/a-8zp14caxh5ask7ed0        default         s3-sa                   arn:aws:iam::xx: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:xx:podidentityassociation/myeks/a-8zp14caxh5ask7ed0",
      "associationId": "a-8zp14caxh5ask7ed0"
    }
  ]
}

 

eksctl create podidentityassociation 또한 CloudFormation을 실행하도록 동작하며, ServiceAccount 는 별도로 생성되지 않습니다.

 

웹 콘솔을 확인해보면 Pod Association에서 정보가 확인 가능합니다. IRSA는 웹 콘솔에는 노출되지 않습니다.

 

Pod Identity를 사용하는 파드도 연관된 ServiceAccount 이름을 지정하는 것으로 사용할 수 있습니다.

# 서비스어카운트, 파드 생성
kubectl create sa s3-sa

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

# 파드 정보 확인
kubectl get pod eks-pod-identity -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_CONTAINER_CREDENTIALS_FULL_URI
      value: http://169.254.170.23/v1/credentials
    - name: AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE
      value: /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
...
    - mountPath: /var/run/secrets/pods.eks.amazonaws.com/serviceaccount
      name: eks-pod-identity-token
      readOnly: true
...
  volumes:
  - name: eks-pod-identity-token
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          audience: pods.eks.amazonaws.com
          expirationSeconds: 86400
          path: eks-pod-identity-token
...

# Pod Identity로 정보 확인
kubectl exec -it eks-pod-identity -- aws sts get-caller-identity --query Arn
"arn:aws:sts::xx:assumed-role/s3-eks-pod-identity-role/eks-myeks-eks-pod-id-0382fb7d-1b2b-45c2-84bb-d5b123292589"

# 에러 발생하지 않음
kubectl exec -it eks-pod-identity -- aws s3 ls

# 에러 발생
kubectl exec -it eks-pod-identity -- aws ec2 describe-instances --region ap-northeast-2
An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation. User: arn:aws:sts::xx:assumed-role/s3-eks-pod-identity-role/eks-myeks-eks-pod-id-0382fb7d-1b2b-45c2-84bb-d5b123292589 is not authorized to perform: ec2:DescribeInstances because no identity-based policy allows the ec2:DescribeInstances action
command terminated with exit code 254

 

마찬가지로 토큰을 확인해보겠습니다.

# 토큰 정보 확인
kubectl exec -it eks-pod-identity -- cat /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token; echo

 

aud(Audience) 가 STS에서 pods.eks.amazonaws.com 으로 다른 것을 알 수 있습니다.

{
  "aud": [
    "pods.eks.amazonaws.com"
  ],
  "exp": 1742138781,
  "iat": 1742052381,
  "iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/EF882B...",
  "jti": "50985ae4-392b-4caa-ae3c-c4de13f58e3c",
  "kubernetes.io": {
    "namespace": "default",
    "node": {
      "name": "ip-192-168-2-79.ap-northeast-2.compute.internal",
      "uid": "9e28f27b-c643-4c76-9e33-3658ca4014ed"
    },
    "pod": {
      "name": "eks-pod-identity",
      "uid": "276bf7da-851f-4eec-87d5-07488e972f2a"
    },
    "serviceaccount": {
      "name": "s3-sa",
      "uid": "7438f7de-ec70-435f-8d34-de7a239a955e"
    }
  },
  "nbf": 1742052381,
  "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

 

 

6. AKS의 파드 권한 할당

AKS에서는 파드에 Azure의 리소스에 접근하는 권한을 할당하는 방식으로 Workload Identity를 사용할 수 있습니다.

이 방식은 Azure에서 Managed Identity라는 주체를 생성하고, Azure RBAC을 통해서 권한 관리를 하며, Managed Identity를 ServiceAccount에서 사용하는 방식으로 진행됩니다.

 

또한 AKS에서 workload Identity은 --enable-oidc-issuer--enable-workload-identity 옵션을 통해 활성화 할 수 있습니다.

az aks create --resource-group "${RESOURCE_GROUP}" --name "${CLUSTER_NAME}" --enable-oidc-issuer --enable-workload-identity --generate-ssh-keys

 

AKS의 Workload Identity의 동작 과정은 아래의 문서를 살펴보실 수 있습니다.

https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview?tabs=dotnet

 

실제 클러스터에 구성하는 방식은 아래에서 설명하고 있습니다.

https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster

 

절차를 요약하면 아래와 같습니다.

1) 클러스터에 Workload Identity와 OIDC issuer 활성화

2) Managed Identity 생성

3) 쿠버네티스 ServiceAccount 생성 (Managed Identity의 Client ID 입력)

4) Federated Identity Credentials 생성

  • Managed Identity와 OIDC Issuer, 그리고 주체(ServiceAccount)를 연결
export FEDERATED_IDENTITY_CREDENTIAL_NAME="myFedIdentity$RANDOM_ID"
az identity federated-credential create --name ${FEDERATED_IDENTITY_CREDENTIAL_NAME} --identity-name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --issuer "${AKS_OIDC_ISSUER}" --subject system:serviceaccount:"${SERVICE_ACCOUNT_NAMESPACE}":"${SERVICE_ACCOUNT_NAME}" --audience api://AzureADTokenExchange

5) Managed Identity에 대한 Azure 리소스 권한 할당 (생략)

6) 애플리케이션 생성 (azure.workload.identity/use: "true" label 및 ServiceAccount 입력)

kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
    name: sample-workload-identity-key-vault
    namespace: ${SERVICE_ACCOUNT_NAMESPACE}
    labels:
        azure.workload.identity/use: "true"
spec:
    serviceAccountName: ${SERVICE_ACCOUNT_NAME}
    containers:
      - image: ghcr.io/azure/azure-workload-identity/msal-go
        name: oidc
        env:
          - name: KEYVAULT_URL
            value: ${KEYVAULT_URL}
          - name: SECRET_NAME
            value: ${KEYVAULT_SECRET_NAME}
    nodeSelector:
        kubernetes.io/os: linux
EOF

 

이러한 과정은 eksctl 을 사용하는 EKS에 비해서 다소 복잡하게 느껴지기는 합니다. 한편으로는 EKS는 신규 API를 추가하여 기능을 간단하게 제공하고, AKS는 기존 Azure의 주체를 활용하는 방식으로 기존 Azure API를 통한 처리를 하는 것으로 이해됩니다.

 

 

마무리

해당 포스트에서 EKS의 인증/인가와 파드에 IAM 권한을 할당하는 방식을 살펴봤습니다.

이 과정에서 EKS가 AWS의 IAM이라는 ID 및 엑세스 관리 서비스와 연계되는 방식을 살펴봤습니다.

 

이러한 과정은 서로 다른 시스템 간의 인증이 연동되는 방식으로 이해할 수 있으며, AWS에서 유효한 주체가 어떻게 쿠버네티스의 인증/인가를 이용할 수 있는가와 쿠버네티스에서 유효한 주체가 어떻게 AWS의 인증/인가를 이용할 수 있는가에 대한 답변이 되었으면 좋겠습니다.

 

간단히 요약하면 AWS의 사용자는 API 서버의 Token Webhook Authentication으로 IAM을 통해서 인증을 진행하고, 확인된 ARN 정보와 쿠버네티스의 그룹(혹은 Policy)의 맵핑을 확인하여 쿠버네티스 RBAC을 통해 인가가 이뤄줬습니다.

쿠버네티스의 유효한 주체는 OIDC나 혹은 Pod Identity Agent를 통해서 AWS IAM과 연계 및 인증을 통해서 유효한 토큰을 획득하고 AWS의 리소스를 접근할 수 있었습니다.

 

다음 포스트에서는 EKS에서 Fargate, Hybrid node를 사용하는 방식을 살펴보겠습니다.

'EKS' 카테고리의 다른 글

[8] EKS Upgrade  (0) 2025.04.02
[7] EKS Fargate  (0) 2025.03.23
[5-2] EKS의 오토스케일링 Part2  (0) 2025.03.07
[5-1] EKS의 오토스케일링 Part1  (0) 2025.03.07
[4] EKS의 모니터링과 로깅  (0) 2025.03.01

+ Recent posts