Cloud

EKS Security

Hanhorang31 2025. 3. 16. 02:04
 
본 블로그 글은 CloudNet@ 가시다님이 진행하는 스터디, AEWS3기에서 참고하였습니다.

 

AWS 의 보안 모델은 고객 공동 책임 모델로 AWS는 클라우드의 보안을, 고객은 클라우드에서의 보안 을 담당합니다.

공동 책임 모델에 따라 고객은 클라우드에서의 보안을 관리해야 합니다.

아래 EKS에서의 보안 영역을 참고하면 EKS 노드의 보안은 고객의 영역으로 애플리케이션과 접근을 위한 권한에 대해 보안 관리가 필요합니다.

 

EKS 보안을 이해하기 위해 쿠버네티스의 접근 제어 과정을 확인하겠습니다.

먼저, 쿠버네티스는 API를 통해 클러스터 내 모든 리소스의 상태를 관리합니다.

kubectl api-resources -v 6 
I0316 01:11:45.942538    9369 loader.go:402] Config loaded from file:  /Users/hanseungho/.kube/config
I0316 01:11:45.944526    9369 envvar.go:172] "Feature gate default state" feature="ClientsAllowCBOR" enabled=false
I0316 01:11:45.944537    9369 envvar.go:172] "Feature gate default state" feature="ClientsPreferCBOR" enabled=false
I0316 01:11:45.944540    9369 envvar.go:172] "Feature gate default state" feature="InformerResourceVersion" enabled=false
I0316 01:11:45.944542    9369 envvar.go:172] "Feature gate default state" feature="WatchListClient" enabled=false
I0316 01:11:46.678071    9369 round_trippers.go:560] GET https://412E75B86CFB737FB6FC350828183F99.gr7.ap-northeast-2.eks.amazonaws.com/api?timeout=32s 200 OK in 732 milliseconds
I0316 01:11:46.719881    9369 round_trippers.go:560] GET https://412E75B86CFB737FB6FC350828183F99.gr7.ap-northeast-2.eks.amazonaws.com/apis?timeout=32s 200 OK in 38 milliseconds
NAME                                SHORTNAMES   APIVERSION                        NAMESPACED   KIND
bindings                                         v1                                true         Binding
componentstatuses                   cs           v1                                false        ComponentStatus
configmaps                          cm           v1                                true         ConfigMap
endpoints                           ep           v1                                true         Endpoints
events                              ev           v1                                true         Event
limitranges                         limits       v1                                true         LimitRange
namespaces                          ns           v1                                false        Namespace
nodes                               no           v1                                false        Node
persistentvolumeclaims              pvc          v1                                true         PersistentVolumeClaim
persistentvolumes                   pv           v1                           false        PersistentVolume
..
..

# api 조회
kubectl get --raw /api
{"kind":"APIVersions","versions":["v1"],"serverAddressByClientCIDRs":[{"clientCIDR":"0.0.0.0/0","serverAddress":"ip-172-16-98-9.ap-northeast-2.compute.internal:443"}]}

# 조회 
kubectl get --raw /api/v1/namespaces | jq 
{
  "kind": "NamespaceList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "111807"
  },
  "items": [
    {
      "metadata": {
        "name": "amazon-cloudwatch",
        "uid": "65294f2e-e971-403b-99cd-b4243ea37184",
        "resourceVersion": "1633",
        "creationTimestamp": "2025-03-15T09:47:12Z",
        "labels": {
          "kubernetes.io/metadata.name": "amazon-cloudwatch",
          "name": "amazon-cloudwatch"
        },
...
https://iximiuz.com/en/series/working-with-kubernetes-api/

 

쿠버네티스 API 서버(Kube-API Server) 를 호출하는 과정에서 접근 제어(Controlling Access to the Kubernetes API) 가 수행됩니다.

 

위 kubectl 의 결과들은 ~/.kube/config 에 저장된 정보를 토대로 API 서버를 다음 과정으로 호출합니다.

  • certificate-authority-data 는 API 서버의 CA (Certificate Authority) 인증서를 Base64 인코딩한 값으로 API 서버 신뢰된 서버인지 검증합니다.(추가 TLS 서명을 제공하여 서명된 TLS 인증서가 일치하는 지비교)
  • aws-iam-authenticator 를 통해 STS를 거쳐 AWS IAM과 연동하여 인증을 수행합니다
sudo vi ~/.kube/config 
----
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tL../horang1
    server: https://85CC1C62E40F411FBCA3C0A3C98A0F85.gr7.ap-northeast-2.eks.amazonaws.com
  name: arn:aws:eks:ap-northeast-2:17..:cluster/horang1
  ..
contexts:
- context:
    cluster: cluster-1.ap-northeast-2.eksctl.io
    user: admin@cluster-1.ap-northeast-2.eksctl.io
  name: admin@cluster-1.ap-northeast-2.eksctl.io
current-context: arn:aws:eks:ap-northeast-2:170698194833:cluster/hsh-eks
kind: Config
preferences: {}
...
- name: hsh@hsh-kubeflow-cluster2.ap-northeast-2.eksctl.io
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - token
      - -i
      - hsh-kubeflow-cluster2
      command: aws-iam-authenticator
      env:
      - name: AWS_STS_REGIONAL_ENDPOINTS
        value: regional
      - name: AWS_DEFAULT_REGION
        value: ap-northeast-2
      - name: AWS_PROFILE
        value: hsh
      interactiveMode: IfAvailable
      provideClusterInfo: false

 

 

EKS Security

보안 구성 방식에 따라 다음의 표로 분류됩니다.

구분
설명
예시
aws-auth
IAM 사용자·역할과 EKS 클러스터 접근 권한을 연결하는 방식.
IAM 사용자나 역할의 클러스터 접근 시 권한 부여
Cluster Access Management
클러스터 접근 권한 관리(보통 aws-auth 통해 구현).
특정 사용자 또는 역할만 EKS 클러스터 접근 가능하도록 설정
IRSA
Kubernetes의 Pod가 IAM 역할을 통해 AWS 리소스 접근하는 방식.
Pod에서 AWS 리소스(S3, DynamoDB 등)에 접근할 때 서비스 계정에 IAM 역할 연결
Pod Identity
IRSA의 업그레이드 버전으로 Pod에 더 세밀하게 IAM 권한 부여 가능.
서비스 계정과 IAM 역할을 연결하고 Pod에 직접 지정하여 세부적 권한 관리 가능

 

Aws-auth ConfigMap (Deprecated 예정)

인증을 AWS IAM, 인가를 k8s RBAC를 통해 진행하여 사용자 또는 역할(Role)의 클러스터 접근 권한을 세부적으로 관리하는 방식입니다.

기본적으로 EKS에서 사용되는 인증/인가였지만 후술할 단점으로 Deprecated 예정인 방식입니다.

aws-auth 방식은 aws-auth configmap을 통해 aws iam 과 k8s을 매핑하여 관리합니다.

  • EC2 노드 그룹은 k8s의 "system:bootstrappers", "system:nodes" 권한을 가집니다.
  • 클러스터를 구성한 IAM은 aws-auth 와 상관없이 kubernetes-admin Username으로 system:masters 그룹에 권한을 가집니다.
kubectl describe cm aws-auth -n kube-system

Name:         aws-auth
Namespace:    kube-system
Labels:       <none>
Annotations:  <none>

Data
====
mapAccounts:
----
[]


mapRoles:
----
- "groups":
  - "system:bootstrappers"
  - "system:nodes"
  "rolearn": "arn:aws:iam::1..:role/core-node-group-eks-node-group-20250315093607877900000004"
  "username": "system:node:{{EC2PrivateDNSName}}"

각 권한을 확인하기 위해 krew 툴을 설치합니다.

kubectl krew install access-matrix rbac-tool rbac-view rolesum whoami

위 권한을 확인하면 다음과 같습니다.

# 현재 Context의 보안 정보 확인 
kubectl rbac-tool whoami
{Username: "kubernetes-admin",
 UID:      "aws-iam-authenticator:170698194833:AIDASPPTHX6I4EKOHX7EH",
 Groups:   ["system:masters",
            "system:authenticated"],
 Extra:    {accessKeyId:                                   ["AKIASPPTHX6I3KJYKZCN"],
            arn:                                           ["arn:aws:iam::17,,:user/hsh@.."],
            canonicalArn:                                  ["arn:aws:iam::17..:user/hsh@..],
            principalId:                                   [".."],
            sessionName:                                   [""],
            sigs.k8s.io/aws-iam-authenticator/principalId: [".."]}}
            
# system:masters 권한 확인            
kubectl rbac-tool lookup system:masters
  SUBJECT        | SUBJECT TYPE | SCOPE       | NAMESPACE | ROLE          | BINDING        
-----------------+--------------+-------------+-----------+---------------+----------------
  system:masters | Group        | ClusterRole |           | cluster-admin | cluster-admin 

# 노드 그룹 권한 확인
kubectl rbac-tool lookup system:bootstrappers 
  SUBJECT              | SUBJECT TYPE | SCOPE       | NAMESPACE | ROLE                  | BINDING                
-----------------------+--------------+-------------+-----------+-----------------------+------------------------
  system:bootstrappers | Group        | ClusterRole |           | eks:node-bootstrapper | eks:node-bootstrapper  
 
# 노드 그룹 권한 확인 
kubectl rbac-tool lookup system:nodes
  SUBJECT      | SUBJECT TYPE | SCOPE       | NAMESPACE | ROLE                  | BINDING                
---------------+--------------+-------------+-----------+-----------------------+------------------------
  system:nodes | Group        | ClusterRole |           | eks:node-bootstrapper | eks:node-bootstrapper  

위 aws-auth 매핑 정보는 eksctl을 통해 매핑이 가능합니다.

# auth 매핑 예제 
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
eksctl create iamidentitymapping --cluster $CLUSTER_NAME --username csi-user01 --group system:masters --arn arn:aws:iam::$ACCOUNT_ID:user/csi-user01

# 매핑 확인
kubectl describe cm aws-auth -n kube-system
----
...
mapUsers:
----
- groups:
  - system:masters
  userarn: arn:aws:iam::17...:user/csi-user01
  username: csi-user01
  

이 방식은 IAM, K8s RBAC 둘 다 세팅해야 하는 복잡함과 aws-auth 컨피그맵을 잘못 수정 시 발생할 수 있는 장애가 있어 Deprecated 예정입니다.

 

 

 

Cluster Access

AWS IAM의 역할(Role/User)을 EKS 클러스터 내 Access EntryAccess Policy라는 Kubernetes 네이티브 객체로 별도 관리하는 방식입니다.

기존 aws-auth ConfigMap 직접 편집이 필요 없으며 Kubernetes API로 관리됩니다.

구성 변경은 AWS 콘솔을 통해 변경이 가능합니다.

EKS API 및 configmap 옵션을 선택하면 업데이트 후 변경이 완료됩니다.

이전 testuser 를 마이그레이션하고자 한다면 Access Entry 생성과 Access Policy 연결이 필요합니다.

aws eks create-access-entry \
  --cluster-name $CLUSTER_NAME \
  --principal-arn arn:aws:iam::17...:user/csi-user01 \
  --type STANDARD 
  
aws eks associate-access-policy \
--cluster-name $CLUSTER_NAME \
--principal-arn arn:aws:iam::17..:user/csi-user01 \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \
--access-scope type=cluster
 
 
# csi-user01 로 변경 
aws configure 
aws eks update-kubeonfig --name $CLUSTER_NAME

# 권한 확인
kubectl get pods -A 

 

 

 

IRSA (IAM Roles for Service Account)

Kubernetes의 Pod가 IAM 역할을 얻어 AWS 서비스를 관리하는 방식입니다.

EKS 파드가 S3 객체를 접근할 때의 보안 접근 방식을 예로 들면 다음과 같이 접근할 수 있습니다.

  1. IAM 역할 검증 : AWS STS가 JWT 토큰을 검증하고, 해당 Pod가 IAM 역할을 사용할 수 있는지 확인 요청
  2. OIDC 신뢰 검증 : AWS IAM이 OIDC Provider의 공개키를 가져와 JWT 서명을 검증하여 신뢰 관계 확인
  3. IAM 권한 체크 완료 : IAM이 STS에게 Pod가 해당 역할을 사용할 권한이 있음을 알림
  4. 임시 자격 발급 : STS는 Pod에게 사용할 수 있는 임시 자격증명(Temporary Credentials)을 발급
  5. AWS 리소스 접근 : Pod는 발급받은 임시 자격증명을 사용해 AWS 서비스(S3 등)에 접근

위 S3 접근을 위한 IRSA을 설정하고 과정을 확인하겠습니다.

# ServiceAccount에 IAM Policy 연결 
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)

# 구성 연결 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
NAMESPACE        NAME    ROLE ARN
default         my-sa   arn:aws:iam::17..:role/eksctl-hsh-eks-addon-iamserviceaccount-defaul-Role1-kXIxehnWIfsV


# SA Annotation 확인, Role 정보 
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::17..:role/eksctl-hsh-eks-addon-iamserviceaccount-defaul-Role1-kXIxehnWIfsV
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>

생성한 IAM Role을 확인하면 S3 READ 정책을 확인할 수 있고 신뢰 관계는 구성한 EKS 에 설정된 것을 확인할 수 있습니다.

설정한 SA 를 통해 신규 파드를 생성하여 세부 정보를 확인하겠습니다.

핵심은 파드 생성시 볼륨이 추가되는 것이며 볼륨안 토큰이 AWS STS를 통해 IAM 인증하는 단계입니다. (위 과정 2번)

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

# 해당 SA를 파드가 사용 시 mutatingwebhook으로 Env,Volume 추가함 : AWS IAM 역할을 Pod에 자동으로 주입
kubectl get mutatingwebhookconfigurations pod-identity-webhook -o yaml
...
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


# Pod yaml 확인시 볼륨이 추가됨 (이 볼륨이 AWS STS를 통해 IAM 인증) 
kubectl get pod eks-iam-test3 -o yaml
..
  volumes:
  - name: aws-iam-token
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          audience: sts.amazonaws.com
          expirationSeconds: 86400
          path: token
  
# 발급 토큰 확인
kubectl describe pod eks-iam-test3 
...
Environment:
  AWS_STS_REGIONAL_ENDPOINTS:   regional
  AWS_DEFAULT_REGION:           ap-northeast-2
  AWS_REGION:                   ap-northeast-2
  AWS_ROLE_ARN:                 arn:aws:iam::17..:role/eksctl-hsh-eks-addon-iamserviceaccount-defaul-Role1-kXIxehnWIfsV
  AWS_WEB_IDENTITY_TOKEN_FILE:  /var/run/secrets/eks.amazonaws.com/serviceaccount/token
  

# 권한 확인
kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn
"arn:aws:sts::17...:assumed-role/eksctl-hsh-eks-addon-iamserviceaccount-defaul-Role1-kXIxehnWIfsV/botocore-session-1742047747"

# S3 접근 확인 
kubectl exec -it eks-iam-test3 -- aws s3 ls
2024-11-09 08:44:58 cf-template..

또한, IRSA는 EKS addon 사용으로 AWS 서비스를 관리할때 사용합니다.

AWS 로드밸런서 컨트롤러나 EBS CSI 드라이버를 사용한다면 명령어와 AWS 콘솔을 통해 확인할 수 있습니다.

kubectl describe sa aws-load-balancer-controller-sa -n kube-system

다만 IRSA 도 단점이 있습니다.

  • 사용자에 따라 권한 (*)를 부여하여 전체 접근이 가능 할 수 있음
  • Eleavated Permissions : IAM 관리자가 Identity Provider (IdP) 및 IAM 역할 구성을 직접 설정해야 함
  • Cluster scoped : 클러스터 단위 관리 필요, EKS 신규 구성시 기존 Role에 신규 신뢰관계 추가 필요
  • Bounded : IAM 역할 신뢰 정책(Trust Policy)의 크기 제한이 존재함

위 단점을 보안한 것이 아래 EKS Pod Identity 입니다.

실습 전 생성한 자원을 삭제합니다.

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

 

 

 

EKS Pod Identity

보완된 점은 신뢰 관계를 OIDC가 아닌 addon(EKS Pod Identity)에서 처리한다는 것입니다.

EKS Pod Identity 가 파드 내 AWS SDK 호출을 인증을 대신하여 AWS 인증 및 검증을 수행합니다.

  1. IAM 역할 생성 : AWS IAM에서 EKS Pod Identity용 IAM 역할을 생성
  2. PodIdentityAssociation 생성 : Pod Identity API를 통해 Pod과 IAM 역할을 연결
  3. Pod Webhook 적용 : Webhook이 Pod 생성 요청을 감지하고 IAM 역할을 주입
  4. Pod AWS 요청 처리 : Pod 내부 AWS SDK 요청을 EKS Pod Identity Agent가 인증 4a. IAM 역할 인증 : EKS Auth API가 AWS AssumeRoleForPodIdentity 호출 4b. IAM 역할 검증 : Pod Identity API가 IAM 역할 연결을 검증
  5. AWS 리소스 접근 : Pod가 EKS Pod Identity Agent를 통해 AWS 서비스 사용

eks-pod-identity-agent 을 구성하여 실습하겠습니다.

# 호환 버전 확인
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

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

# 구성 확인
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   2         2         2       2            2           <none>          89s


kubectl get ds -n kube-system eks-pod-identity-agent -o yaml
...
      containers:
      - args:
        - --port
        - "80"
        - --cluster-name
        - hsh-eks
        - --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
...

# 노드 내 접근하여 포트 확인 
kubectl node-shell <node-1> 
ss -tnlp | grep  eks-pod-identit; echo "-----";
ip -c route
ip -c -br - 4 addr 
  • CAP_NET_BIND_SERVICE : Pod 내부 컨테이너가 80번(HTTP)이나 443번(HTTPS) 같은 낮은 포트(Privileged Port)를 사용할 수 있도록 설정되어 있습니다.
  • 169.254.170.23는 AWS IMDS (Instance Metadata Service) 및 EKS Pod Identity에서 IAM 역할을 관리하는 로컬 주소입니다. 이는 AWS STS (Security Token Service)와 연동하여 IAM 역할을 Pod에 제공하는 데 사용됩니다.
  • 노드 EC2 Profile에 2023년 기능 출시 이후 Policy에 AssumeRoleForPodIdentity 권한이 업데이트되어 호출이 가능합니다.

IRSA 예제와 같이 파드에서 S3 접근을 위한 예제를 구성하겠습니다.

콘솔에서 확인하면 신뢰 관계에 OIDC 의존성은 사라진 것을 확인할 수 있습니다.

 

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


# 구성 확인
eksctl get podidentityassociation --cluster $CLUSTER_NAME
ASSOCIATION ARN                                                                                        NAMESPACE       SERVICE ACCOUNT NAME    IAM ROLE ARN                                            OWNER ARN
arn:aws:eks:ap-northeast-2:17..:podidentityassociation/hsh-eks/a-5nnty2k2s6abbzblo      default         s3-sa                   arn:aws:iam::17..:role/s3-eks-pod-identity-role 

예제 파드를 배포하여 기능 동작을 확인하겠습니다.

# sa 생성 
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 exec -it eks-pod-identity -- aws sts get-caller-identity --query Arn
kubectl exec -it eks-pod-identity -- aws s3 ls
2024-11-09 08:44:58 cf-template..

# 접근 토큰 확인
kubectl exec -it eks-pod-identity -- cat /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token

실습 데이터는 다음과 같이 삭제합니다.

eksctl delete podidentityassociation --cluster $CLUSTER_NAME --namespace default --service-account-name s3-sa
kubectl delete pod eks-pod-identity
kubectl delete sa s3-sa



'Cloud' 카테고리의 다른 글

EKS Version Upgrade  (0) 2025.04.02
EKS Fargate & AutoMode  (0) 2025.03.23
EKS Karpenter  (0) 2025.03.09
EKS Autoscaling  (0) 2025.03.09
Grafana Observability구성  (0) 2025.03.01