AEWS STUDY 6주차 - Amazon EKS Security

Hanryang·2023년 6월 4일
0

AEWS 스터디

목록 보기
6/7

가시다님께서 진행하신 AEWS라는 스터디를 진행하면서 작성하는 글입니다.

AEWS= AWS EKS Workshop Study

AEWS 스터디는 4월 23일 ~ 6월 4일동안7번 진행될 예정입니다.

PKOS 스터디에서 정말 좋은 경험을 했어서 다시 이렇게 참가하게 되었습니다.

가시다님께서 진행하시는 스터디에 관심있으신 분들은 Cloudnet@Blog에 들어가시면 자세한 정보를 확인하실 수 있습니다.

인증 vs 인가

인증(Authentication)


인증은 사용자나 클라이언트가 자신의 신원을 증명하는 프로세스입니다.
즉, 시스템이 사용자가 주장하는 신원이 실제로 그 사용자의 신원인지 확인하는 것을 의미합니다. 인증은 보통 사용자의 신원을 확인하기 위해 사용되는 인증 요소를 기반으로 이루어집니다.

일반적으로 사용되는 몇 가지 인증 요소는 다음과 같습니다:

  • 사용자 이름과 비밀번호: 사용자가 등록한 사용자 이름과 비밀번호를 사용하여 인증합니다.
  • 공개키 인증서: 사용자는 개인 키와 대응하는 공개 키를 사용하여 자신의 신원을 증명합니다.

인증은 사용자의 신원을 확인하고, 이를 기반으로 사용자가 시스템에 액세스할 수 있는 권한을 얻을 수 있도록 합니다.

인가(Authorization)


인가는 인증된 사용자나 클라이언트가 특정 작업이나 자원에 대한 액세스 권한을 가지고 있는지 확인하는 프로세스입니다. 인증된 사용자의 신원을 확인한 후에도 인가를 통해 해당 사용자가 수행할 수 있는 작업의 범위를 결정합니다.

일반적으로 인가는 권한 부여를 통해 이루어집니다. 시스템은 사용자에게 역할 또는 그룹을 할당하고, 이 역할 또는 그룹에 대한 권한을 지정합니다. 권한은 특정 작업이나 자원에 대한 액세스 권한을 나타내며, 예를 들어 읽기, 쓰기, 수정, 삭제 등의 권한이 있을 수 있습니다.

인가는 보안 정책을 적용하여 시스템의 자원에 대한 접근을 제어합니다. 인증된 사용자가 시스템에 로그인하면, 인가 프로세스가 해당 사용자가 허용된 작업을 수행할 수 있는지 확인하고, 필요한 경우 해당 작업을 거부합니다.

EKS 인증/인가


EKS 인증,인가의 동작에 대해서 한줄로 설명하자면,
사용자/애플리케이션 → k8s 사용 시 ⇒ 인증은 AWS IAM, 인가는 K8S RBAC

핵심은 인증은 IAM에서 처리하고, 인가는 k8s RBAC에서 처리 하는 것이다.

인증과정을 한번 분석해보자.

  1. kubectl 명령 → aws eks get-token → EKS Service endpoint(STS)에 토큰 요청 ⇒ 응답값 디코드

    • STS(Security Token Service)
      • AWS 리소스에 대한 액세스를 제어할 수 있는 임시 보안 자격 증명(STS)을 생성하여 신뢰받는 사용자에게 제공할 수 있음
    # sts caller id의 ARN 확인
    aws sts get-caller-identity --query Arn
    => "arn:aws:iam::758651871205:user/pkosadmin"
    # kubeconfig 정보 확인
    cat ~/.kube/config | yh
    ...
    - name: admin@myeks.ap-northeast-2.eksctl.io
      user:
        exec:
          apiVersion: client.authentication.k8s.io/v1beta1
          args:
          - eks
          - get-token
          - --output
          - json
          - --cluster-name
          - myeks
          - --region
          - ap-northeast-2
          command: aws
          env:
          - name: AWS_STS_REGIONAL_ENDPOINTS
            value: regional
          interactiveMode: IfAvailable
          provideClusterInfo: false
    # 임시 보안 자격 증명(토큰)을 요청
    aws eks get-token --cluster-name $CLUSTER_NAME | jq

  2. kubectl의 Client-Go 라이브러리는 Pre-Signed URL을 Bearer Token으로 EKS API Cluster Endpoint로 요청을 보냄

    • 발급받은 토큰을 jwt 사이트에 복붙으로 디코드 정보 확인

    • PAYLOAD의 값을 URL Decode Online 에서 DECODE로 확인

      디코드 된 값들을 편하게 보자면 아래와 같습니다.

      https://sts.ap-northeast-2.amazonaws.com/?
      
      Action=GetCallerIdentity&
      
      Version=2011-06-15&
      
      X-Amz-Algorithm=AWS4-HMAC-SHA256&
      
      X-Amz-Credential=AKIA5ILF.../20230525/ap-northeast-2/sts/aws4_request&
      
      X-Amz-Date=20230525T120720Z&
      
      X-Amz-Expires=60&
      
      X-Amz-SignedHeaders=host;x-k8s-aws-id&
      
      X-Amz-Signature=6e09b846da702767f38c78831986cb558.....
    1. EKS API는 Token ReviewWebhook token authenticator에 요청 ⇒ (STS GetCallerIdentity 호출) AWS IAM 해당 호출 인증 완료 후 User/Role에 대한 ARN 반환

      • 참고로 Webhook token authenticator 는 aws-iam-authenticator 를 사용합니다. aws-iam-authenticator
        • 쿠버네티스의 RBAC 권한을 AWS IAM을 통해 제어할 수 있도록 해주는 도구
      # tokenreviews api 리소스 확인 
      kubectl api-resources | grep authentication
      
      # List the fields for supported resources.
      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.
    2. 이제 쿠버네티스 RBAC 인가를 처리합니다

      • 해당 IAM User/Role 확인이 되면 k8s aws-auth configmap에서 mapping 정보를 확인하게 됩니다.
      • aws-auth 컨피그맵에 'IAM 사용자, 역할 arm, K8S 오브젝트' 로 권한 확인 후 k8s 인가 허가가 되면 최종적으로 동작 실행을 합니다.
      • 참고로 EKS를 생성한 IAM principal은 aws-auth 와 상관없이 kubernetes-admin Username으로 system:masters 그룹에 권한을 가짐
      # Webhook api 리소스 확인 
      kubectl api-resources | grep Webhook

      # validatingwebhookconfigurations 리소스 확인
      kubectl get validatingwebhookconfigurations

      # aws-auth 컨피그맵 확인
      kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh

      # EKS 설치한 IAM User 정보 
      kubectl rbac-tool whoami

      # system:masters , system:authenticated 그룹의 정보 확인
      kubectl rbac-tool lookup system:masters
      kubectl rbac-tool lookup system:authenticated
      kubectl rolesum -k Group system:masters
      kubectl rolesum -k Group system:authenticated
      # system:masters 그룹이 사용 가능한 클러스터 롤 확인 : cluster-admin
      kubectl describe clusterrolebindings.rbac.authorization.k8s.io cluster-admin

      # cluster-admin 의 PolicyRule 확인 : 모든 리소스  사용 가능!
      kubectl describe clusterrole cluster-admin

      # system:authenticated 그룹이 사용 가능한 클러스터 롤 확인
      kubectl describe ClusterRole system:discovery
      kubectl describe ClusterRole system:public-info-viewer
      kubectl describe ClusterRole system:basic-user
      kubectl describe ClusterRole eks:podsecuritypolicy:privileged

그럼 이제 해당 작업을 직접 실습해보면서 알아가보겠습니다.

  1. testuser 사용자 생성

    # testuser 사용자 생성
    aws iam create-user --user-name testuser
    
    # 사용자에게 프로그래밍 방식 액세스 권한 부여
    aws iam create-access-key --user-name testuser
    
    # testuser 사용자에 정책을 추가
    aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --user-name testuser
    
    # get-caller-identity 확인
    aws sts get-caller-identity --query Arn
    "arn:aws:iam::911283464785:user/admin"
    
    # EC2 IP 확인 : myeks-bastion-EC2-2 PublicIPAdd 확인
    aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table
  2. testuser로 접속하여 자격증명 설정 및 확인

    # get-caller-identity
    aws sts get-caller-identity --query Arn
    => 다른 서버에 접속하여 aws configure을 먼저 진행해주어야 testuser인 것을 식별할 수 있다. 

    # 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::758651871205:user/testuser"
  3. testuser에 system:masters 그룹 부여로 EKS 관리자 수준 권한 설정

    # Creates a mapping from IAM role or user to Kubernetes user and groups
    eksctl create iamidentitymapping --cluster $CLUSTER_NAME --username testuser --group system:masters --arn arn:aws:iam::$ACCOUNT_ID:user/testuser
    # 확인
    kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh

  4. testuser kubeconfig 생성 및 kubectl 사용 확인

    # testuser kubeconfig 생성
    aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser
    # 첫번째 bastic ec2의 config와 비교해보자 => 똑같다.
    cat ~/.kube/config | yh

    # kubectl 사용 확인
    kubectl ns default
    kubectl get node -v6

    # rbac-tool 후 확인
    kubectl krew install rbac-tool && kubectl rbac-tool whoami
    {Username: "testuser",
     UID:      "aws-iam-authenticator:758651871205:AIDA3BIZM37S4WWYGD27V",
     Groups:   ["system:masters",
                "system:authenticated"],
     Extra:    {accessKeyId:  ["AKIA3BIZM37SRVL3I7GV"],
                arn:          ["arn:aws:iam::758651871205:user/testuser"],
                canonicalArn: ["arn:aws:iam::758651871205:user/testuser"],
                principalId:  ["AIDA3BIZM37S4WWYGD27V"],
                sessionName:  [""]}}
  5. testuser 의 Group 변경(system:masters → system:authenticated)으로 RBAC 동작 확인

    #testuser 의 Group 변경
    kubectl edit cm -n kube-system aws-auth

    # 시도
    kubectl get node -v6
    kubectl api-resources -v5

  6. testuser IAM 맵핑 삭제

    # 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 | yh

  7. testuser kubectl 사용 확인

    # 시도
    kubectl get node -v6
    => " Error from server (Forbidden): nodes is forbidden: User "testuser" cannot list resource "nodes" in API group "" at the cluster scope "
    kubectl api-resources -v5

EKS IRSA


IRSA에 대해서 설명하자면,

IRSA는 IAM Role for Service Account의 약자로 kubernetes의 serviceaccount를 사용하여 pod의 권한을 IAM Role로 제어할 수 있도록 하는 기능을 말합니다.

IRSA의 동작

  1. pod위에서 동작하는 application이 AWS SDK를 사용하여 S3의 리스트를 가져오려고 합니다. 이때 JWT와 IAM Role 의 ARN정보를 AWS STS에게 전달 합니다.
    1. JWT란? 
      1. JSON Web Token의 약자로, 일반적으로 HTTP API 서버를 만들 때에 인증 방법으로 사용. 서버는 사용자가 로그인할 때 고유한 Token을 생성하고, 이를 사용자에게 알려주어 사용자가 다른 API를 사용할 때에 헤더에 이 Token을 넣으면, 자신이라는 것을 증명할 수 있게 하는 것.
  2. STS는 AWS IAM에게 임시 자격증명을 줄 수 있는지 확인을 요청합니다.
  3. IAM은 IAM OIDC Provider와 통신하고, pod에 할당된 serviceaccount에 IAM Role 정보가 잘 annotate되어 있는 지 확인한 후에 IAM에게 확인 되었다는 응답을 주게 됩니다.
  4. IAM은 STS에게 권한을 줘도 된다고 응답을 주게 되고
  5. AWS STS는 pod의 AWS SDK에게 임시 자격증명을 전달합니다.
  6. 최종적으로 pod의 AWS SDK는 aws s3의 리스트를 가져올 수 있게 됩니다.

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

  • EKS IdP를 identity provider로 등록하고, 파드가 Web Identify 토큰을 통해 IAM 역할을 Assume 할 수 있게 Trust Relationship 설정이 필요합니다

여러 실습을 해보면서 작동방식을 이해해보겠습니다.

실습 1


Service Account가 없는 Pod를 실습해보겠습니다.

# 파드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**
EOF

# 로그 확인
kubectl logs eks-iam-test1

eks-iam-test1 pod에 s3에 접근할 수 있는 권한이 없어서 엑세스가 거부된 것을 확인할 수 있습니다.

실습2


Service Account가 있는 Pod를 실습해보겠습니다.

# 파드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
EOF

# aws 서비스 사용 시도
kubectl exec -it eks-iam-test2 -- aws s3 ls

Service Account를 생성해도 실습 1과 똑같이 실패한 것을 확인합니다.

Service Account 토큰을 확인하고 디코딩하여서 값을 확인해보겠습니다.

# 서비스 어카운트 토큰 확인
SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $SA_TOKEN

디코딩값

  • 헤더
    {
      "alg": "HS256",
      "kid": "652d70dd0b4ae550a54d30d79b6d1bf5f39e3243"
    }
  • 페이로드
    
    {
      "aud": [
        "https://kubernetes.default.svc"
      ],
      "exp": 1717370051,
      "iat": 1685834051,
      "iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/632F8F6D5367A8FA2BBF2D5CCE48F033",
      "kubernetes.io": {
        "namespace": "default",
        "pod": {
          "name": "eks-iam-test2",
          "uid": "331e7cab-e1be-429d-a6cb-d932e6bc0ff2"
        },
        "serviceaccount": {
          "name": "default",
          "uid": "b16b5965-4f5c-4a1d-85f5-5021da5f744b"
        },
        "warnafter": 1685837658
      },
      "nbf": 1685834051,
      "sub": "system:serviceaccount:default:default"
    }
    • 페이로드분석
      - aud
      - 토큰이 유효한 대상 (audience)으로 설정된 Kubernetes의 기본 서비스 주소인 "https://kubernetes.default.svc"를 가리킵니다.
      - exp
      - 토큰의 만료 시간을 나타내며, 이 토큰은 1717370051에 만료됩니다.
      - iat
      - 토큰이 발급된 시간을 나타냅니다.
      - iss
      - 토큰을 발급한 발급자 (issuer)로, Amazon EKS OIDC의 엔드포인트인 "https://oidc.eks.ap-northeast 2.amazonaws.com/id/632F8F6D5367A8FA2BBF2D5CCE48F033"를 가리킵니다.
      - kubernetes.io
      - Kubernetes 관련 정보를 포함하는 객체입니다. 여기에는 토큰이 발급된 네임스페이스, 파드 이름, 파드 UID, 서비스 계정 이름 및 UID 등이 포함됩니다.
      - warnafter
      - 토큰이 발급된 이후에 경고 메시지를 표시하기 시작하는 시간을 나타냅니다.
      - nbf
      - 토큰이 활성화되는 시간을 나타내며, 이 토큰은 1685834051에 활성화됩니다.
      - sub
      - 토큰의 주체 (subject)로, "system:serviceaccount:default:default"는 "default" 네임스페이스의 "default" 서비스 계정을 나타냅니다.

      호환되는 OIDC 토큰을 통해 AWS API 인증에 사용할 수 있는 토큰을 찾았지만,Kubernetes Pods에 AWS API와 함께 사용할 두 번째 토큰을 주입하기 위한 추가 구성 요소가 필요합니다.

실습3


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 \
  --attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`AmazonS3ReadOnlyAccess`].Arn' --output text)

다음을 수행합니다.
1. Kubernetes 서비스 계정
2. 지정된 IAM 정책을 가진 IAM 역할
3. 그 IAM 역할에 대한 신뢰 정책

#SA 확인 
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::758651871205:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-X2LK3SUMWLZR
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>
# IAM 역할 조회
aws iam get-role \
--role-name $(aws cloudformation describe-stacks --stack-name eksctl-myeks-addon-iamserviceaccount-default-my-sa --query 'Stacks[*].Outputs[0].OutputValue' --output text | cut -d '/' -f2)

이제 Pod를 실행해보고 서비스를 사용할 수 있는지 확인해보겠습니다.

# 파드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
EOF

# 해당 SA를 파드가 사용 시 mutatingwebhook으로 Env,Volume 추가함
kubectl get mutatingwebhookconfigurations pod-identity-webhook -o yaml | kubectl neat | yh

# 파드 생성 시 작성한 YAML에 없는 내용이 추가된 것을 확인
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::758651871205:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-X2LK3SUMWLZR
      AWS_WEB_IDENTITY_TOKEN_FILE:  /var/run/secrets/eks.amazonaws.com/serviceaccount/token

Volumes:
  aws-iam-token:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  86400
  kube-api-access-ffqs4:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true

이제 cli 사용 확인을 진행해보겠습니다.

# 파드에서 aws cli 사용 확인
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

AmazonS3ReadOnlyAccess 권한만 부여했으므로 “kubectl exec -it eks-iam-test3 -- aws s3 ls” 는 문제 없지 작동되지만 다른 명령어는 동작을 하지 않는 것을 확인할 수 있습니다.

profile
한량이 되고싶다..

0개의 댓글