본문 바로가기

DevOps

[AWS EKS] EKS Security (IRSA)

CloudNet@팀의 EKS 스터디 AEWS 2기에 작성된 자료를 토대로 작성합니다.

 

쿠버네티스 클러스터에서 실행되는 파드가 AWS 서비스에 어떻게 액세스 권한을 부여 받을수 있을까?

쿠버네티스 클러스터는 AWS의 자원이 아닌데 어떻게 IAM Role을 할당 받을 수 있을까?

먼저, POD가 AWS API 에 요청과 응답을 어떤 방식으로 보내고 받는지 확인해보자.

 IRSA                                                                                                                           

  • IAM Roles for Service Accounts
  • 쿠버네티스 파드가 AWS 서비스 권한을 부여받는 방법

 OIDC(OpenID Connect) 란?                                                                                   

  • 사용자가 Role을 Assume 받는 과정과 동일한 방식으로 pod도 Role을 Assume 받아야 한다.
  • 그러기 위해서는 인증 절차가 필요한데 이를 EKS OIDC Provider가 위임하여 처리한다.

 IRSA Workflow                                                                                                                          

  • pod 내부에서 AWS SDK를 통해 s3를 검색한다면 어떤 방식으로 권한을 부여받을까?JWT와 IAM Role의 ARN 정보를 AWS STS에 전달한다.

  Volume Projection                                                                                                   

  • Pod Volume에 계속 마운트 되는 aws-iam-token은 뭘까?

Service Account Token Volume Projection은 Kubernetes의 보안 기능 중 하나로, Pod에 Service Account Token을 안전하게 제공하는 방법입니다. 이 기능은 파드가 실행될 때 자동으로 마운트되는 볼륨을 통해 JWT를 Pod에 주입합니다.

 

  • 유효기간 (expiration), 대상 (audience)을 통해 더욱 강화된 보안을 사용합니다.
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /var/run/secrets/tokens
      name: vault-token
  serviceAccountName: build-robot
  volumes:
  - name: vault-token
    projected:
      sources:
      - serviceAccountToken:
          path: vault-token
          expirationSeconds: 7200    # 만료시간
          audience: vault            # 대상

 

  • k8s 1.22 버전부터 Bound Service Account Token Volume 

  • 실습으로 projection volume 사용해보기
# Create the Secrets:
## Create files containing the username and password:
echo -n "admin" > ./username.txt
echo -n "1f2d1e2e67df" > ./password.txt

## Package these files into secrets:
kubectl create secret generic user --from-file=./username.txt
kubectl create secret generic pass --from-file=./password.txt

# 파드 생성
kubectl apply -f <https://k8s.io/examples/pods/storage/projected.yaml>

# 파드 확인
kubectl get pod test-projected-volume -o yaml | kubectl neat | yh
...
volumes:
  - name: all-in-one
    projected:
      defaultMode: 420
      sources:
      - secret:
          name: user
      - secret:
          name: pass
  - name: kube-api-access-n6n9v
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace

# 시크릿 확인
kubectl exec -it test-projected-volume -- ls /projected-volume/
password.txt  username.txt

kubectl exec -it test-projected-volume -- cat /projected-volume/username.txt ;echo
admin

kubectl exec -it test-projected-volume -- cat /projected-volume/password.txt ;echo
1f2d1e2e67df

# 삭제
kubectl delete pod test-projected-volume && kubectl delete secret user pass

 

  IRSA 실습 1번 : automount  = false                                                                                                                

  • 먼저, automount 옵션을 false로 설정한뒤 권한이 없는 상태에서 AWS SDK를 사용 
# 파드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

 

CloudTrail 이벤트 ListBuckets 확인 → 기록 표시까지 약간의 시간 필요

autoMount 옵션을 false로 지정했을때                       

해당 요청을 Issue (발행) 한 주체는 - NodeinstanceRole

  IRSA 실습 2번                                                                                                            

  • Pods는 Service Account를 통해 자격증명을 받습니다. 이를 통해 aws sdk를 사용할수 있습니다.
# 파드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
  • describe 해보면 환경변수와 볼륨 마운트 부분을 확인해봐라 실습 1에서 automount false일때와는 다르게 aws iam token과 aws_role_arn이 추가되었다.
  • 하지만 aws s3를 조회하는 sdk 권한은 없는것으로 보인다. 실습 3번에서 서비스 어카운트에 적합한 권한을 부여하는것을 실습

  IRSA 실습 3번                                                                                                            

  • Service Account에 권한을 부여하는것은 eksctl create iamserviceaccount
  • 이는 Webhook 을 통해서 파드에 Token을 주입하며, 서비스 어카운트를 만든뒤 annotation을 통해 IAM Role의 ARN을 명시함으로 인해 IAM Role의 신뢰관계가 형성된다.
  • Cloudformation으로 생성된 IAM Role은 oidc-provider와 신뢰관계

  • IRSA 생성 -> 서비스 어카운트 확인 -> Annotation에 aws iam role arn 확인 
# 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)

# 확인 >> 웹 관리 콘솔에서 CloudFormation Stack >> IAM Role 확인
# aws-load-balancer-controller IRSA는 어떤 동작을 수행할 것 인지 생각해보자!
eksctl get iamserviceaccount --cluster $CLUSTER_NAME

# Inspecting the newly created Kubernetes Service Account, we can see the role we want it to assume in our pod.
kubectl get sa
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::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1MJUYW59O6QGH
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>
  • pod 생성 -> 오토 마운트
  • 오토 마운트 된 iam token과 환경 변수를 확인할것
# 파드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


kubectl get pod eks-iam-test3 -o yaml | kubectl neat | yh
...
    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 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::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-GE2DZKJYWCEN
      AWS_WEB_IDENTITY_TOKEN_FILE:  /var/run/secrets/eks.amazonaws.com/serviceaccount/token
    Mounts:
      /var/run/secrets/eks.amazonaws.com/serviceaccount from aws-iam-token (ro)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-69rh8 (ro)
...
Volumes:
  aws-iam-token:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  86400
  kube-api-access-sn467:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
...
  • The mutating webhook  파드에 단지 마운트만 해줄 뿐 아니라 환경변수 주입도 해준다.
# api 리소스 확인
kubectl api-resources |grep hook
mutatingwebhookconfigurations                  admissionregistration.k8s.io/v1        false        MutatingWebhookConfiguration
validatingwebhookconfigurations                admissionregistration.k8s.io/v1        false        ValidatingWebhookConfiguration

#
kubectl explain mutatingwebhookconfigurations
kubectl get MutatingWebhookConfiguration

# pod-identity-webhook 확인
kubectl describe MutatingWebhookConfiguration pod-identity-webhook 
kubectl get MutatingWebhookConfiguration pod-identity-webhook -o yaml | yh
  • 실습 확인 후 파드 삭제 및 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

'DevOps' 카테고리의 다른 글

[AWS EKS] EKS CICD  (0) 2024.04.15
[AWS EKS] EKS Security (Kyverno)  (0) 2024.04.13
[AWS EKS] EKS Security ( IAM Authenticator)  (0) 2024.04.08
[AWS EKS] Autoscaling ( Karpenter)  (0) 2024.04.03
[AWS EKS] EKS Autoscaling ( Cluster )  (0) 2024.04.02