ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [AWS] EKS - Security(스터디 6주차)
    Infra/Cloud 2023. 5. 30. 19:08

    드디어 가장 친근한 주제 Security이다!

     

    kubernetes를 공부하면서 가장헷갈리고 조금은 복잡하다고 느낄 수 있었던 인증,인가이다.

     

    인증과 인가에 대한 구분은 굉장히 중요하다고 생각하고 이번주차도 굉장히 알찬 스터디였다:)

     

     


     

    EKS의 인증과 인가

    관리형 kubernetes인 EKS에서  인증과 인가는 하나의 플랫폼에서 진행되지 않는다는 특징이 있다. 

    바로 인증은 AWS의 IAM이 인가는 Kubernetes의 RBAC가 담당한다. 

     

     

     

    k8s 인증 완벽이해 #1 - X.509 Client Certs

    쿠버네티스를 지금까지 사용해 오면서 어렴풋이만 인증서와 토큰을 이용하여 사용자 인증을 하는지는 알고 있엇지만 그 이상 다른 방법에 대해서는 자세히 몰랐었습니다. 쿠버네티스 공인 자

    coffeewhale.com

    On-premise Kubernetes를 공부할 때, 커피고래님 블로그에서 kubernete는 유저의 자체적인 인증정보를 별도로 저장하지 않는다는 것을 본 적이 있다. 사용자 인증 체계의 대부분을 외부 시스템이나 메커니즘을 이용하는데 이로인해 인증체계를 쉽게 확장할 수 있다고 한다.

     

     

     

    - RBAC 관련 krew plugin install

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

    왼쪽에서부터 rbac-tool  /  access-matrix  /  rolesum
     rbac-view 사용 및 페이지 접속(echo -e "RBAC View Web http://$(curl -s ipinfo.io/ip):8800")

    RBAC관련 krew plugin들이다. 현재 자신의 인증정보를 보여주거나 특정 serviceaccount의 권한을 한눈에 보여주는 등 굉장히 편리한 plugin들이다. 참고로 rbac-view의 경우, 실행시킨 localhost의 8800 port로 서비스를 제공한다.

     

     

     

    특히 개인적으로 rolesum 어떤 clusterrole에 binding되었는지까지 빠르게 보여주기 때문에 기존에 kubectl get으로 많은 clusterrole을 헤집어본사람이라면 너무나 편리하다고 느낄만한 기능이다.

     

     


     

    EKS 인증/인가 Flow

    EKS에서 사용자 / Application이 Kubernetes를 사용할 때, 인증은 AWS IAM 사용하며 인가는 Kubernetes RBAC를 사용한다.

    AEWS 스터디원 박준희님이 공유해주신 Flow 그림이다.

     

     

    0. Get Json Web Token(JWT는 X509 Certificate의 lightwight JSON 버전)

    API=$(kubectl config view -o jsonpath="{.clusters[].cluster.server}")
    TOKEN=$(aws eks get-token --cluster-name=$CLUSTER_NAME | jq -r '.status.token')
    echo $API $TOKEN

    Security Token Services Endpoints
    kubeconfig(Path: .kube/config)

    편의를 위해 .kube/config 파일을 kubeconfig라고 하겠다. 우리가 kubectl 명령을 진행하면 kubeconfig file을 이용AWS STS(Security Token Service)에 Token을 요청하게된다. 

     

    aws eks get-token --output json --cluster-name myeks --region ap-northeast-2

    이 요청은 aws-cli의 get-token 명령이며 해당 내용은 kubeconfig file에 명시되어있다. 때문에 명시된 그대로 직접 aws-cli로 요청을 보내면 Json Web Token을 Response 받는 것도 당연히 가능하다.

     

     

    IAM User의 Permission Policy와 STS에게 받은 Token의 Payload 정보

    현재 kubernetes의 관리자 인스턴스에는 aws-cli를 사용을 위해 생성한 IAM User(k8s-ho)의 Credential이 존재한다(환경변수로 등록). 또한 이 k8s-ho에는 AdministratorAccess라는 권한정책이 등록되어있으며 이 계정을 이용해 우리는 aws-cli로 token을 요청하고 받는 것이다.


    응답받은 Token을 Base64 decode해보면 GetCallerIdentity를 호출하는 Pre-signed URL이라는 것을 알 수 있다(Token Payload를 URL Decoding해서 보면 더 잘보인다).

     

    * Pre-signed URL:

    AWS 자원에 대한 접근 권한을 제공하기 위해서 사용되는 이름 그대로 사전에 적절한 권한을 가진 자격증명에 의하여 Signed 된 URL

     

    [참고] 예전에 사용했던 명령어다!!

     

    Environment variables to configure the AWS CLI - AWS Command Line Interface

    This environment variable only applies to an assumed role with web identity provider it does not apply to the general assume role provider configuration.

    docs.aws.amazon.com

    아마도 GetCallerIdentity는 낯이 익을 것이다. 바로 aws-cli를 사용했다면 한번쯤 사용해봤을 명령어와 관련이 있다. aws configure 명령이후 잘 등록되었는지 확인할 때 사용했었다(이 EKS는 aws configure가 아닌 환경변수를 통해 IAM Credential을 등록했다)

     

     

     

    1. Request EKS APIserver Endpoint with Bearer Token

    curl -k $API -H "Authorization: Bearer $(echo $TOKEN)"

    token을 받은 이후 kubectl의 Client-Go Library는 EKS Apiserver Enpoint에 Request를 보내는데, Header의 Authorization 필드에 Bearer type으로 이 Token을 담아 전송하므로써 kubernetes에 User 인증을 시도하게된다.

     

    우리가 사용하는 kubectl 명령은 EKS APIserver에 request를 보낼 때 이 Token을 이용하여 인증을 받는 것이기 때문에 Token이 탈취당하면 kubernetes가 위험해질 수 있다.

     

    참고로 위의 명령을 통해 직접 전송해보면 사용자 입장에서는 바로 응답만 받기 때문에 중간 과정에 대한 내용은 확인 할 수 없다. 그러므로 우선 다음과정을 보자.


    잠깐!! On-premise Kubernetes의 인증과 비교를 해보자(.kube/config)

    kubectl config view --raw  -o jsonpath="{.users[].user.client-certificate-data}" | base64 -d > client-cert.pem
    kubectl config view --raw  -o jsonpath="{.users[].user.client-key-data}" | base64 -d > client-key.pem
    curl $(kubectl config view --raw  -o jsonpath="{.clusters[].cluster.server}") --cert client-cert.pem --key client-key.pem -k

    => On-premise Kubernetes에는 users아래 get-token 명령이 아닌 client-cert와 client-private key가 존재한다. 해당 계정이 kube-Apiserver와 통신하기 위해서는 cert 파일과 key 파일이 모두 필요한데 그 이유는 MTLS를 진행하기 때문이다.

     

     

    웹브라우저의 경고 / curl를 통해 볼 수 있는 경고

    kubectl config view --raw  -o jsonpath="{.clusters[].cluster.certificate-authority-data}" | base64 -d > ca.pem
    curl $(kubectl config view --raw  -o jsonpath="{.clusters[].cluster.server}") --cert client-cert.pem --key client-key.pem --cacert ca.pem

    => 우리가 평소에 사용하는 curl 명령어의 -k 옵션은 웹 브라우저로 비교해서 생각해볼 수있다. 가끔 특정 웹 페이지에 접속할 때, 신뢰할 수 없는 연결 또는 인증서라는 경고를 본적이 있을 것이다. 이 경고가 나오는 이유는 여러가지인데 대표적으로 사용하는 웹 브라우저에 해당 인증서가 존재하지 않을 때 발생한다. 여기서 신뢰할 수 없는데도 진행할 것이냐는 물음에 "네!"를 누르고 접속하는 것이 바로 curl -k와 비슷하다고 보면된다. 또한 TLS 통신을 이해하고 있다면 일반적인 웹을 통한 TLS 접속은 서버를 신뢰할 수 있는지를 확인하는 것이 목적이라고 볼 수 있다. curl에서 이런 경고는 --cacert 옵션으로 해결이 가능하다.

     

     

    [참고] 서버 내의 CA 인증서 리스트 파일 경로

    Debian 계열 (예: Ubuntu): cat /etc/ssl/certs/ca-certificates.crt
    Red Hat 계열 (예: CentOS, Fedora): cat /etc/pki/tls/certs/ca-bundle.crt

    CA 인증서 리스트 존재시(왼쪽) / CA 인증서 리스트 없을시(오른쪽)

    => 해당 이름의 파일이 없으면 curl 요청시 에러가 난다. 위의 사진은 해당 파일내의 인증서 내용을 삭제하고 curl을 진행해본 것이다. 이를 통해 네이버에서 사용하는 인증서는 CA에서 발급된 인증서라는 것도 확인할 수 있었다. 때문에 .kube/config에 있는 kubernetes CA 인증서 파일을 ca-bundle.crt나 ca-certificates.crt 파일 내에 등록하면 curl을 사용할 때 굳이 -k나 --cacert를 하지 않아도 된다.

     

     

     

    SSL and TLS Protocols - OpenSSLWiki

    SSL stands for Secure Sockets Layer and was originally created by Netscape. SSLv2 and SSLv3 are the 2 versions of this protocol (SSLv1 was never publicly released). After SSLv3, SSL was renamed to TLS. TLS stands for Transport Layer Security and started wi

    wiki.openssl.org

    client-certificate 파일 상세보기 / kubernetes-admin의 권한

    system:masters kubernetes의 최고권한을 가진 그룹
    system:authenticated 사용자 인증이 완료된 그룹

    => 일반적인 웹 통신과 다르게 Kubernetes에서는 Client와 Server가 상호 확인하는 MTLS를 사용한다. 때문에 curl 옵션으로 client를 신뢰할 수 있도록---cert 옵션으로 인증서를, --key 옵션으로 private key를 전송하는 것이다. 하지만 이 때 실제 private key를 전송하는 것이 아닌 private key로 signing한 데이터를 전송한다고한다(--key는 signing에 사용될 private key를 지정하는 것). private key는 client만 가지고 있을 것이므로 signing한 데이터를 함께 전송한 인증서 내부의 공개키로 복호화가 된다면 이 요청을 client를 신뢰할 수 있다는 것을 입증한 것이기 때문이다. 

     

    이러한 과정으로 MTLS를 진행하며 위와 같이 curl 명령을 통해 kube-apiserver에 인증 및 request가 가능하다. client 인증서를 확인해보면 .kube/config 파일에 있는 kubernetes-admin이 system:master group이며 해당 group은 cluster-admin이라는 막강한 clusterrole에 binding되어있는 것을 알 수 있다. kubernetes에서는 RBAC로 중요한 / 많은 권한을 계정에 binding 했다면, 인증 관련 관리를 굉장히 신경써야한다. 때문에 private key가 존재하는 .kube/config 파일은 잘 관리되어야한다. 이는 EKS도 포함되는 말이다.

    (참고로 /etc/kubernetes/admin.conf도 같이 잘 관리하자^^)

     

    요약하자면 On-premise Kubernetes에서는 Cluster 계정에 대한 인증을 Client Cert와 Client Private Key로 진행하고 EKS에서는 STS를 통해 Token을 발급받고 해당 Token에 대한 TokenReview 요청 -> IAM Service의 사용자 검증을 통해 인증한다는 것을 알 수 있었다.

     


     

    2. AWS-IAM-Authenticator Server(Webhook Token Authentication) & IAM Service

    TokenReview 권한 확인
    STS GetCallerIdentity 호출 확인

    요청을 받은 EKS APIserver는 TokenReviewAWS-IAM-Authenticator Server(Webhook Token Authentication)에 요청하여 사용자 Token 인증을 진행하게 된다.
    (api-resource 명령을 통해 TokenReview Resourcer type이 존재한다는 것은 알 수 있으나 권한이 없어 관련 Listing은 불가하다).

     

     

     

    * AWS-IAM-Authenticator Server(Webhook Token Authentication) 정보

    # kubectl로 내용 확인
    kubectl describe validatingwebhookconfigurations.admissionregistration.k8s.io eks-aws-auth-configmap-validation-webhook
    
    # curl로 내용 확인
    curl -k $API/apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations/eks-aws-auth-configmap-validation-webhook \
    -H "Authorization: Bearer $(echo $TOKEN)"

    Describe validatingwebhookconfigurations eks-aws-auth-configmap-validation-webhook

    => 결과적으로 여기서 사용된 Webhook의 경우, Mutatation이 아닌 Validation Webhook으로, EKS APIserver로부터 요청이 들어왔을 때,  해당 요청에 대해 유효성을 검사한 뒤, 허용/거부를 반환하게된다.

     

    => 간략하게 webhook 관련 설정 내용을 살펴보자. 요청이 들어오면 위에 명시된 URL로 요청을 전달한 뒤, 응답을 받아처리한다. namespaceSelector를 통해 해당 Label과 일치하는 namespace의 Resource에 적용하고 있으며 rule을 통해 ConfigMap resource에 대해 UPDATE를 작업을 대상으로 한다는 것을 확인할 수 있었다.

     

    kubectl get cm -n $(kubectl get ns -l kubernetes.io/metadata.name=kube-system -o jsonpath="{.items[].metadata.name}")

    aws-auth 정보(왼쪽) / IAM role의 ARN(오른쪽)

    => Label을 통해 살펴보면 UPDATE되는 ConfigMap Resource는 aws-auth라는 것도 알 수 있었다.

     

     

    aws sts get-caller-identity 명령 / token의 payload로 돌려받은 GetCallerIdentity

    AWS-IAM-Authenticator Server(Webhook Token Authenticatior)는 전달받은 TokenReview를 정보를 가지고 AWS IAM Service에 Token Payload 내용의 STS GetCallerIdentity를 요청(사용자 검증)한다. 이후 신원확인이 성공하게되면 User, Role에 대한 ARN을 응답받게 된다! 

     

    위의 사진에서 aws sts get-caller-identity 명령 / token의 payload로 돌려받은 GetCallerIdentity 결과가 같은 이유는 생각해보면 당연하다. 중간의 과정이 어쨌던 aws sts 명령을 진행한 credential도 k8s-ho이고 kubectl 요청 과정에서 aws get-token으로 발급받아 사용한 token 역시도,  payload에 k8s-ho의 credential(Access-key ID가 들어있음)이 들어있기 때문이다(결국 둘다 그 과정중  IAM 서비스에서 사용자 검증을 받았을 것이다) 

     

     

    * Webhook 관련 페이지

     

    195. [Kubernetes] AWS IAM Authenticator 기초 사용 방법 및 EKS에서의 인증 원리

    이번 포스트에서는 aws-iam-authenticator의 기초적인 사용 방법과, 이를 이용해 EKS에서 인증하는 방법...

    blog.naver.com

     

    Authenticating

    This page provides an overview of authenticating. Users in Kubernetes All Kubernetes clusters have two categories of users: service accounts managed by Kubernetes, and normal users. It is assumed that a cluster-independent service manages normal users in t

    kubernetes.io

     

     

    3. Kubernetes ConfigMap  Update

    # aws-auth ConfigMap
    kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh
    apiVersion: v1
    kind: ConfigMap
    metadata: 
      name: aws-auth
      namespace: kube-system
    data: 
      mapRoles: |
        - groups:
          - system:bootstrappers
          - system:nodes
          rolearn: arn:aws:iam::91128.....:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-1OS1WSTV0YB9X
          username: system:node:{{EC2PrivateDNSName}}
          
    #---<아래 생략(추정), ARN은 EKS를 설치한 IAM User , 여기 있었을경우 만약 실수로 삭제 시 복구가 가능했을까?---
      mapUsers: |
        - groups:
          - system:masters
          userarn: arn:aws:iam::111122223333:user/admin
          username: kubernetes-admin

    AWS-IAM-Authenticator Server(Webhook Token Authenticatior)는 전달받은 USER, Role ARN정보를 Kubernetes configMap인 aws-auth에 Update하여 Kubernetes의 RBAC로 사용하기 위한 준비를 마친다.

     

    실제 aws-auth configMap을 보면 이전에 On-premise에서도 존재한 system:masters에 대한 내용이 없는데 이는 누군가 실수로 삭제 시, 복구가 불가할 수 있어 숨긴 것이 아닌가라는 추측이있다.

     

    마지막으로 TokenReview(mapping된 Username, group 등의 정보 + 인증 결과(허용/거부))를 APIserver에 반환하게 되며 여기까지가 인증이다.

     

    이 포스팅 시작에서 말했듯 Kubernetes는 자체적인 인증체계보다는 외부의 인증 시스템 또는 메커니즘을 사용한다고 했다. 여기서는 IAM Service나 webhook 등이 그 역할을 했다고 볼 수 있을 것 같다.

     

    4. RBAC(인가)

    kubectl rbac-tool whoami
    kubectl rbac-tool lookup system:masters
    kubectl rbac-tool lookup system:authenticated

    현재 kubernetes cluster에서 사용중인 계정 정보와 binding된 role 정보
    현재 해당 계정에 Binding된 ClusterRole과 권한정보

    이후로는 On-premise Kubernetes와 동일하다. 계정이 속해있는 group이나 계정 자체에 Roled이나 ClusterRole을 binding해서 사용하면 된다.

     

     


    직접 IAM User를 생성해서 진행해보기

    - 현재 환경에서는 관리형 인스턴스가 2개라 두 번째 관리형 인스턴스에서 진행했다.

    # 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

    => IAM 계정을 생성하고 access key와 권한부여를 마쳤다.

     

    새로운 계정의 Credential 등록하기

    vi /etc/profile
    source /etc/profile
    aws sts get-caller-identity --query Arn

    /etc/profile

    - 현재는 해당 계정을 사용할 때만든 기존계정의 credential이 등록되어있기 때문에 바꿔주여야한다. aws configure을 사용해도 되지만 이번에는 환경변수로 진행해보았다. EKS 생성 시, 해당 경로에 환경변수를 등록해두었기 때문에 기존 계정의 credential을 testuser의 것으로 바꾸어주고 확인하면 된다. 이제 kubernetes cluster를 testuser 계정으로 사용할 수 있게 된 것이다.

     

     

    Kubectl을 통해 kubernetes 제어

    에러발생..

    testuser 역시 IAM에서 AdministratorAccess 권한을 가지고 있으나 실패했다. 이유는 뭘까?? 바로 .kube/config 즉, kubeconfig 파일이 없다. 하지만 kubeconfig 파일을 기존의 인스턴스에서 그대로 가져와서 사용한다고 하더라도 접속이 불가하다. 

     

     

     

     

    Enabling IAM principal access to your cluster - Amazon EKS

    Enabling IAM principal access to your cluster Access to your cluster using IAM principals is enabled by the AWS IAM Authenticator for Kubernetes, which runs on the Amazon EKS control plane. The authenticator gets its configuration information from the aws-

    docs.aws.amazon.com

     

     

    해결 방법 1

    eksctl create iamidentitymapping --cluster $CLUSTER_NAME --username testuser --group system:masters --arn arn:aws:iam::$ACCOUNT_ID:user/testuser

    IAM role 또는 User와 Kubernetes user and Group간의 mapping이 필요하기 때문이다. 위의 명령어로 mapping을 진행해주자. 위의 명령어는 기존 계정으로 진행해야한다.

     

    aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser
    kubectl krew install rbac-too && kubectl rbac-tool whoami
    eksctl get iamidentitymapping --cluster $CLUSTER_NAME

    이후 두 번째 instance에서 위의 명령을 통해 kubeconfig상 update를 하면 두 번째 인스턴스에도 .kube/config 파일이 생성되고 정상적으로 kubectl 명령을 사용할 수 있게 된다.

     

     

     

     

    해결 방법 2

    kubectl edit -n kube-system cm aws-auth
    
    # configMap 수정
    apiVersion: v1
    data:
      mapRoles: |
        - groups:
          - system:bootstrappers
          - system:nodes
          rolearn: arn:aws:iam::123123123123:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-V46QLRI9C1IQ
          username: system:node:{{EC2PrivateDNSName}}
          
    # 아래추가 ------------------------------    
      mapUsers: |
        - groups:
          - system:masters
          userarn: arn:aws:iam::123123123123:user/testuser
          username: testuser
    # ------------------------------    
    
    kind: ConfigMap
    metadata:
      creationTimestamp: "2023-06-03T03:07:44Z"
      name: aws-auth
      namespace: kube-system

    인증 실패에서 -> 정상적으로 동작하는 것을 확인
    에러로그상 "인가 실패아닌가??"하고 착각할 수 있지만 우리는 과정을 이해하고 있고 response를 자세히보면 401 반환이다.

    두번째 인스턴스에는 .kube/config 파일이 없으니 첫번째 instance에서 .kube/config 파일을 복사해오고 내용을 testuser에 맞게 수정해주자. 이후 첫번재 인스턴스에 접속하여 aws-auth configMap을 수정해주어 kubernetes subject와 rolearn을 mapping을 해주는 것이다. 그렇게하면 처음 인증 실패하던 것이 정상적으로 동작하는 것을 볼 수 있다.

     

    IAM과 Kubernetes간의 연결고리가 되는  aws-auth configMap 관리가 상당히 중요하다는 것도 알 수 있다. 때문에 변조나 내용확인이 불가하도록 RBAC를 잘 설정해서 관리해야할 것같다.

     

     


     

    IRSA(IAM Roles Service Accounts)

    IRSA는 Kubernetes ServiceAccount를 위한 IAM Service라고 할 수 있다. IRSA는Pod에서 AWS Resouce 이용에 대한 권한을 부여할 때 사용된다. IRSA외에도 EC2 Instance Profile이라는 편리한 기능도 존재하지만 이 기능은 하나의 Node에서 구동되는 여러 Pod가 동일한 권한을 부여한다는 점에서 치명적일 수 있다(최소 권한 부여를 따르지 않으므로 비추천한다.) 

     

    => 이러한 EC2 Instance Profile의 한계를 해결하는, Pod에서 사용되는 ServiceAccount 별로 서로 IAM role을 부여하기위해 사용되는 것이 IRSA이다.

     

     

    Pod를 생성했을 때  내부적으로 어떤일이 일어날까?

    kubectl run test --image=nginx

    Pod Describe와 Pod내에 존재하는 serviceaccount의 token

    Pod를 생성하게되면 serviceaccount에 대한 명시가 없는 경우 Default로 default serviceaccount로 적용이된다. 또한 serviceaccount마다 token 값이 생성되어 volume형태로 pod에 mount된다. 이 token을 이용하면 kubernetes apiserver에 해당 serviceaccount가 가진 권한만큼 request를 사용할 수 있게된다.

     

    때문에 위에서 말했듯 serviceaccount의 role은 최소권한이 부여되어야하며 굳이 pod가 다른 resource와의 연동 등이 필요없다면 serviceaccount token이 자동 mount되지 않도록 spec. automountServiceAccountToken: false를 설정해주는 것이 좋다.

     

     

     

    * Projected Volumes

     

    Projected Volumes

    This document describes projected volumes in Kubernetes. Familiarity with volumes is suggested. Introduction A projected volume maps several existing volume sources into the same directory. Currently, the following types of volume sources can be projected:

    kubernetes.io

      volumes:
      - name: kube-api-access-5wt2b
        projected:
          defaultMode: 420
          sources:
          - serviceAccountToken:
              expirationSeconds: 3607
              path: token 
              audience: nginx

    기본적으로 serviceaccount token mount에 사용하던 것과 달리 Volumes에 projected라는 것 작성되어있다. 기본과 projected volume의 기능적인 차이는 projected를 사용하면 token의 유효기간(expiration), 대상(audience) 등 Token의 속성을 설정할 수 있게 된다는 것이다.

     

     

     

     

    * AWS S3를 사용하려는 Pod 예시

    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
    
    # Pod log 확인
    kubectl logs eks-iam-test1
    
    # Pod 삭제
    kubectl delete pod eks-iam-test1

    CloudTrail로도 확인이 가능함

    => Pod container가 내부적으로 aws-cli로 ListBucket 명령을 진행하지만 해당 serviceaccount에는 AWS에 대한 IAM role이 없으므로 AccessDenied 한 것을 확인할 수 있었다. 

     

     

    다시 Pod를 생성하여 serviceaccount Token 정보 확인해보자.

    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
    
    # default:default serviceaccount token 확인
    kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token

    token 정보 / IdP

    Pod에 적용되어있는 default serviceaccount token의 payload를 확인해보니 projected volume으로 인해 OAuth2에서 사용되는 aud, exp 속성이 확인되었다. iss 속성은 EKS Open ID Connection Provider (EKS IdP) 주소로 EKS IdP를 통해 kubernetes가 발급한 token이 유효한지 검증할 때 사용되는 정보이다.

     

     

     

    그럼이제 serviceaccount에 IAM role을 부여해보자!! IRSA

    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

    => IRSA를 부여하는 명령어이다. serviceaccount가 없다면 생성하며 IAM role을 IAM policy에 맞게 만들어서 사용할 수 있게 attach 해준다.

     

    좀더 자세히 말해서, IRSA를 생성하게되면 CloudFormation Stack이 생성된다. 그 Stack이 생성하는 Resource를 확인해보면 IAM role이라는 것을 알 수 있다. 그리고 해당 IAM role을 확인해보면 위의 명령어에 작성한대로 AmazonS3ReadOnlyAccess policy를 attach한 것을 볼 수 있다.

     

    IAM role에서 Trusted entities의 Federated 값은 AWS 입장에서는 다른 인증기관이며 그 기관의 값이 바로 EKS의 OpenID Connect 공급자 URL이다. 즉, AWS IAM이 해당 주소(EKS OIDC)를 통해 인증을 요청하는 것이다.

     

    마지막으로 잘생성되었는지 명령어로 확인해보자. 해당 명령어를 통해 my-sa serviceaccount가 해당 AWS Arn role을 사용할 수 있게 적용되었고 해당 role에는 s3를 읽기로 접근할 수 있는 policy가 적용되어있는 것을 볼 수 있었다.

     

    describe serviceaccount my-sa

    그리고 my-sa의 annotations에서 해당 role이 적용된 것도 확인이 가능하다.

     

     

     

    IRSA가 적용된 Serviceaccount로 Pod 생성

    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
    
    # ListBuckets 진행
    kubectl exec -it eks-iam-test3 -- s3 ls

    Env와 token mount가 자동으로 생성됨

    생성된 Pod를 자세히 살펴보면 ENV와 aws-iam-token mount가 적용된 것을 확인할 수 있다. 이게 갑자기 왜 생긴 것일까?? 그 이유는 바로 Mutating Webhook(Mutating Admission 과정에서 진행됨) 때문이다.

     

     

    - Admission Control 

    kubernetes에서 인증과 인가 다음에 진행되는 과정중하나로 앞어 인증에서 봤던 Validating Admission과 Mutating Admission이 존재한다. Mutating이 먼저 진행되는데, 인증과 인가 이후 Mutating webhook을 통해 관리자가 요청한 request를 임의로 변경할 수 있다.이후 object schema validation(문법이나 요청이 정상적인지 확인), Validating Admission 과정을 지다. ETCD에 기록되는 것이다.

     

     

    생성된 aws-iam-token 값도 확인해보자

    IAM_TOKEN=$(kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token)
    echo $IAM_TOKEN

    => serviceaccount 정보와 token관련 속성 값들이 정리되어있는 것을 확인할 수 있다.

     

     

     

     인증 관련 IDP(IDentity Provider) URL 확인방법

    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 '.'

     

     

     

    마지막으로 원래하려고 했던 Pod에서 aws 명령어로 S3 bucket listing을 진행해보면 IRSA를 진행한 serviceaccount의 권한으로인해 성공적으로 응답을 받은 것을 볼 수 있었다.

     

    반응형

    댓글

Designed by Tistory.