쿠버네티스의 기본적인 인증/인가는 Service Account와 RBAC(Role/RoleBinding)으로 이루어졌다면

EKS에서 인증/인가는 AWS IAM과 K8S RBAC으로 이루어져 있다.

그래서 사용자가 kubectl 명령을 실행하면 내부적으로 EKS 인증과 K8S 인가 과정을 거쳐서 사용자에게 최종적으로 값을 반환하게 된다.

Untitled

Amazon EKS 마이그레이션 요점정리 - 강인호 솔루션즈 아키텍트, AWS :: AWS Summit Korea 2022

위 영상을 11분부터 참고하였다.

  1. 사용자가 kubectl 명령어를 실행한다.
  2. kubeconfig에 정의된 eks get-token 명령을 통해 EKS Service Endpoint에 Request를 보내고 토큰 값을 Response 받게 된다. 이 때 이 토큰 값은 GetCallerIdentity를 호출하는 Pre-Signed URL이다.
  3. kubectl 클라이언트 Go 라이브러리는 Pre-Signed URL을 Bearer Token으로 EKS API Cluster Endpoint에 요청을 보내면
  4. IAM-Authentication Server에서 Token Authentication Webhook이 호출되고
  5. STS GetCallerIdentity를 호출해서 AWS IAM에서 해당 호출에 대한 인증을 거치게 된다.
  6. 이 때 User나 Role에 대한 ARN을 반환한다.
  7. IAM User/Role을 Configmap인 aws-auth에서 매핑되는 값을 찾고
  8. 쿠버네티스 User/Group으로 리턴한다.
  9. Authentication에서는 Token Review라는 데이터 타입으로 User/Group을 반환하고
  10. 이는 쿠버네티스 RBAC에 따라 Authorization 과정을 진행하게 된다.
  11. 최종적으로 사용자는 권한에 따른 Allow/Deny 응답을 받게 된다.

전체적인 그림은 위와 같고 하나씩 실습으로 진행해 볼 것이다.

IAM Authenticator 인증 과정

kubectl 명령을 실행했을 때, AWS IAM Authenticator Server를 통해 인증 과정을 거치게 된다. 기존에는 클러스터 안에 aws-iam-authenticator를 설치했어야 했는데, aws cli 버전 1.16.156 이상부터는 aws-iam-authenticator를 포함하고 있어서 별도 설치 없이 aws eks get-token을 사용할 수 있다고 한다.

1&2. kubectl 명령 실행 → kubeconfig에 정의된 eks get-token 실행 → EKS Service Endpoint에 토큰 요청

kubeconfig를 열어 보면 aws eks get-token command를 실행하는 부분이 보인다.

Untitled 1

이를 직접 커맨드로 실행해 보면

expirationTimestamptoken 값을 얻을 수 있다.

expirationTimestamp 시간이 지나면 토큰은 만료되어 재발급이 필요하다.

# aws eks get-token --output json --cluster-name myeks --region ap-northeast-2
{
    "kind": "ExecCredential",
    "apiVersion": "client.authentication.k8s.io/v1beta1",
    "spec": {},
    "status": {
        "expirationTimestamp": "2024-04-13T01:23:03Z",
        "token": "k8s-aws-v1.token~~"
    }
}

3. Pre-Signed URL을 Bearer Token으로 EKS API Cluster Endpoint에 요청보내

eks get-token으로 받은 토큰 값을 디코딩하고 해당 PAYLOAD 값을 또 URL Decode해 보면 일반적인 API 호출과 유사하다.

파라미터 값으로는 Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders, X-Amz-Signature가 들어가 있다.

X-Amz-Credential 에는 나의 AccessKey 값이 포함되어 있고 X-Amz-Signature에는 SecretKey로 암호화를 포함한 16진수가 들어가 있었다.

GetCallerIdentity를 호출하는 것도 알 수 있다.

https://sts.ap-northeast-2.amazonaws.com/?
Action=GetCallerIdentity&
Version=2011-06-15&
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=[AccessKey]/20240413/ap-northeast-2/sts/aws4_request&
X-Amz-Date=20240413T010903Z&
X-Amz-Expires=60&
X-Amz-SignedHeaders=host;x-k8s-aws-id&
X-Amz-Signature=[signature value]

Signature을 계산하는 방식은 AWS Docs에 잘 나와 있다.

Untitled 2

서명된 AWS API 요청 생성 - AWS Identity and Access Management

Authenticating Requests: Using Query Parameters (AWS Signature Version 4) - Amazon Simple Storage Service

  1. Presigned URL에서 CanonicalRequest를 생성한다.
    1. HTTP method, URI, 쿼리스트링, 헤더, 페이로드를 포함한다.
    2. hash 암호화된 PAYLOAD가 포함되는데 요청 본문의 내용이 중요하지 않는 경우에는 UNSIGNED-PAYLOAD를 사용할 수 있다.
    3. Presigned URL에 포함된 쿼리스트링 중에 X-Amz-Signature는 제외한다.
    4. Canonical Headers에는 HTTP host 헤더를 포함해야 하는데 여러 개의 헤더를 포함할 수 있다.

    아마 아래와 같은 형식이 생성될 거라 생각한다 (예제 코드를 보고 예상해 본 값이다)

     POST
     /
     X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=[accesskey]%2F20240413%2Fap-northeast-2%2Fsts%2Faws4_request&X-Amz-Date=20240413T010903Z&X-Amz-Expires=60&X-Amz-SignedHeaders=host;x-k8s-aws-id
     host:sts.ap-northeast-2.amazonaws.com
        
     host;x-k8s-aws-id
     e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
    
  2. StringToSign

    각 쿼리스트링(X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date)에 해당하는 값이다.

    마지막으로 1번에서 생성한 CanonicalRequest를 SHA256으로 암호화하고 Base16으로 인코딩한 값이다.

     AWS4-HMAC-SHA256
     20240413T010903Z
     20240413/ap-northeast-2/sts/aws4_request
     [Hex(SHA256Hash(Canonical Request))]
    
  3. Signing Key

    이제 순서대로 해시함수 SHA256과 비밀키로 암호화하는 과정을 거친다

    • HMAC-SHA256를 사용하고
    • 처음에 Secret Key를 같이 암호화한다.
     DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>")
     DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>")
     DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>")
     SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")
    
  4. Signature

    3번에서 얻어낸 SigningKey와 2번에서 얻어낸 StringToSign를 HMAC-SHA256를 사용하여 암호화하고 Base16으로 인코딩된 값으로 X-Amz-Signature에 사용할 수 있다.

Untitled 3

암호화는 아래 사이트 참조하였다.

SHA256

[Free HMAC-SHA256 Online Generator Tool Devglan](https://devglan.com/online-tools/hmac-sha256-online)

4&5&6. IAM-Authentication Server에서 Token Authentication Webhook 호출 → STS GetCallerIdentity 호출 → User/Role 반환

EKS API는 Token Review를 Webhook token authenticator에 요청하고 STS GetCallerIdentity를 호출하게 된다.

Untitled 4

api resources를 확인해 보면 TokenReview가 보인다.

Untitled 5

7&8. aws-auth에서 IAM User/Role을 K8S User/Group으로 반환

Token Authentication Webhook에서 Configmap인 aws-auth에서 매핑되는 값을 찾기 때문에 Webhook 리소스를 확인해 보았다.

Untitled 6

Untitled 7

aws-auth는 아래와 같이 되어 있다.

Untitled 8

rolearn에 따라 매핑된 groupsusername을 반환한다.

9&10&11. K8S 인가 단계

지금 EKS를 설치한 IAM User는 system:authenticated 그룹에 속해 있는데 아래 나타나진 않지만 system:masters 권한을 기본적으로 갖고 있다.

Untitled 9

system:masters이 사용 가능한 클러스터 롤은 cluster-admin이다.

Untitled 10

모든 리소스에 대한 권한이 있기 때문에 인가 단계를 통과했다.

Untitled 11

신규 사용자 추가 시

클러스터 관리자 콘솔

  1. testuser 사용자 생성

     aws iam create-user --user-name testuser
    
  2. AccessKey 발급

     aws iam create-access-key --user-name testuser
     {
         "AccessKey": {
             "UserName": "testuser",
             "AccessKeyId": "**",
             "Status": "Active",
             "SecretAccessKey": "**",
             "CreateDate": "2024-04-13T06:02:15+00:00"
         }
     }
    
  3. 사용자에게 admin 권한 부여하고 사용자 계정을 전달해 준다.

     aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --user-name testuser
    

신규 사용자 콘솔

  1. testuser를 받은 사용자는 자격 증명을 설정하고 확인한다.

     # aws configure
     	AccessKey & SecretKey 입력
     # aws sts get-caller-identity --query Arn
     "arn:aws:iam::**:user/testuser"
    
  2. 하지만 admin 권한을 가졌는데도 불구하고 kubectl 에러가 발생한다.

    이유는 K8S에 매핑되는 User/Group 권한은 없기 때문이다.

     # kubectl get node -v6
     I0413 15:10:55.861572    5930 round_trippers.go:553] GET http://localhost:8080/api?timeout=32s  in 0 milliseconds
     E0413 15:10:55.861674    5930 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
     ..............
    

다시 관리자 콘솔

  1. 현재 클러스터에 권한을 확인한다.

     eksctl get iamidentitymapping --cluster $CLUSTER_NAME
    

    Untitled 12

  2. testuser에게 system:masters 그룹을 부여해 준다.

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

    Untitled 13

  3. aws-auth를 확인해 보면 mapUsers에 testuser 정보가 들어가 있다.

    이는 eks-aws-auth-configmap-validation-webhook이라는 validatingwebhookconfigurations이 aws-auth를 업데이트할 수 있는 권한을 가지고 있기 때문에 iamidentitymapping 명령어를 통해 eks-aws-auth-configmap-validation-webhookaws-auth 내용을 업데이트해 준 것이다.

    Untitled 14

  4. 다시 클러스터에 권한을 확인해 보면 testuser도 같이 보인다.

    Untitled 15

다시 사용자 콘솔

  1. kubeconfig를 생성한다.

     # aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser
     Added new context testuser to /root/.kube/config
    
  2. kubectl를 실행해 본다. 이제 요청을 잘 받아 온다.

    Untitled 16

  3. 현재 testuser가 가지고 있는 권한을 확인해 보면

    제대로 권한이 설정되어 있는 것을 확인할 수 있다.

    Untitled 17

aws-auth에서 삭제되면?

  1. testuser 삭제

     eksctl **delete** iamidentitymapping --cluster $CLUSTER_NAME --arn  arn:aws:iam::$ACCOUNT_ID:user/testuser
    

    Untitled 18

  2. 권한 오류 발생한다.

    Untitled 19

aws-auth에서 mapUsers를 실수로 삭제해 버린다면 사용자는 모든 권한을 잃어버린다. 근데 클러스터를 처음 생성한 User는 mapUsers에 들어가 있지 않았는데 예전에는 들어가 있었다고 한다. 그래서 한번 삭제되면 장애가 발생할 수도 있기 때문…

특히 system:nodes를 삭제하면 모든 노드가 NotReady 상태로 빠지게 되었다고 한다,.

장애 방지로 aws-auth에 immutable: true 값을 설정해 주면 수정이 불가한다고 한다.

하지만 좀더 쿠버네티스 네이티브한 방식으로 변경하기 위해 인증 모드를 EKS API 방식을 도입한 건데, 그래서 mapUsers에 admin(클러스터 생성한 유저)가 없어도 EKS 클러스터 IAM 액세스 항목에 추가되어 있기에 admin 사용자는 admin 권한을 사용할 수 있던 것이다.

Untitled 20

다음 실습에선 인증 모드를 EKS API 방식으로 바꿔 진행해 보겠다.

참고) RBAC 플러그인

스터디에서 RBAC 관련 krew 플러그인을 추천해 주셨는데 사용해 보면 유용할 것 같다.

kubectl krew install access-matrix rbac-tool rbac-view rolesum whoami
  • access-matrix: 클러스터 또는 네임스페이스가 가지는 권한을 보여 준다

    Untitled 21

  • rbac-tool: 상세한 RBAC 설정을 한 눈에 보여 준다.

    Untitled 22

  • rbac-view: kubectl rbac-view로 실행하고 공인IP:8800으로 웹 접속하면 웹에서 RBAC 검색이 가능하다.

    Untitled 23

  • rbac-tool whoami: 현재 context의 인증 정보를 보여 준다.

    Untitled 24

  • rolesum: RBAC 정보를 요약해 준다. 이 플러그인이 가시성이 제일 좋다!

    Untitled 25

    Untitled 26

Categories:

Updated:

Leave a comment