쿠버네티스의 기본적인 인증/인가는 Service Account와 RBAC(Role/RoleBinding)으로 이루어졌다면
EKS에서 인증/인가는 AWS IAM과 K8S RBAC으로 이루어져 있다.
그래서 사용자가 kubectl 명령을 실행하면 내부적으로 EKS 인증과 K8S 인가 과정을 거쳐서 사용자에게 최종적으로 값을 반환하게 된다.
Amazon EKS 마이그레이션 요점정리 - 강인호 솔루션즈 아키텍트, AWS :: AWS Summit Korea 2022
위 영상을 11분부터 참고하였다.
eks get-token
명령을 통해 EKS Service Endpoint에 Request를 보내고 토큰 값을 Response 받게 된다. 이 때 이 토큰 값은 GetCallerIdentity
를 호출하는 Pre-Signed URL이다.IAM-Authentication Server
에서 Token Authentication Webhook이 호출되고STS GetCallerIdentity
를 호출해서 AWS IAM에서 해당 호출에 대한 인증을 거치게 된다. aws-auth
에서 매핑되는 값을 찾고전체적인 그림은 위와 같고 하나씩 실습으로 진행해 볼 것이다.
kubectl 명령을 실행했을 때, AWS IAM Authenticator Server를 통해 인증 과정을 거치게 된다. 기존에는 클러스터 안에 aws-iam-authenticator
를 설치했어야 했는데, aws cli 버전 1.16.156 이상부터는 aws-iam-authenticator
를 포함하고 있어서 별도 설치 없이 aws eks get-token
을 사용할 수 있다고 한다.
eks get-token
실행 → EKS Service Endpoint에 토큰 요청kubeconfig를 열어 보면 aws eks get-token
command를 실행하는 부분이 보인다.
이를 직접 커맨드로 실행해 보면
expirationTimestamp
과 token
값을 얻을 수 있다.
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~~"
}
}
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에 잘 나와 있다.
서명된 AWS API 요청 생성 - AWS Identity and Access Management
Presigned URL에서 CanonicalRequest를 생성한다.
UNSIGNED-PAYLOAD
를 사용할 수 있다.X-Amz-Signature
는 제외한다.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
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))]
Signing Key
이제 순서대로 해시함수 SHA256과 비밀키로 암호화하는 과정을 거친다
DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>")
DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>")
DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>")
SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")
Signature
3번에서 얻어낸 SigningKey와 2번에서 얻어낸 StringToSign를 HMAC-SHA256를 사용하여 암호화하고 Base16으로 인코딩된 값으로 X-Amz-Signature
에 사용할 수 있다.
암호화는 아래 사이트 참조하였다.
Free HMAC-SHA256 Online Generator Tool | Devglan
EKS API는 Token Review를 Webhook token authenticator에 요청하고 STS GetCallerIdentity를 호출하게 된다.
api resources를 확인해 보면 TokenReview가 보인다.
aws-auth
에서 IAM User/Role을 K8S User/Group으로 반환Token Authentication Webhook에서 Configmap인 aws-auth
에서 매핑되는 값을 찾기 때문에 Webhook 리소스를 확인해 보았다.
aws-auth
는 아래와 같이 되어 있다.
저 rolearn
에 따라 매핑된 groups
와 username
을 반환한다.
지금 EKS를 설치한 IAM User는 system:authenticated
그룹에 속해 있는데 아래 나타나진 않지만 system:masters
권한을 기본적으로 갖고 있다.
system:masters
이 사용 가능한 클러스터 롤은 cluster-admin
이다.
모든 리소스에 대한 권한이 있기 때문에 인가 단계를 통과했다.
aws iam create-user --user-name testuser
aws iam create-access-key --user-name testuser
{
"AccessKey": {
"UserName": "testuser",
"AccessKeyId": "**",
"Status": "Active",
"SecretAccessKey": "**",
"CreateDate": "2024-04-13T06:02:15+00:00"
}
}
aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/**AdministratorAccess** --user-name **testuser**
# aws configure
AccessKey & SecretKey 입력
# aws sts get-caller-identity --query Arn
"arn:aws:iam::**:user/testuser"
# 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
..............
현재 클러스터에 권한을 확인한다.
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
testuser에게 system:masters
그룹을 부여해 준다.
eksctl create **iamidentitymapping** --cluster $**CLUSTER_NAME** --username **testuser** --group **system:masters** --arn arn:aws:iam::$ACCOUNT_ID:user/**testuser**
aws-auth
를 확인해 보면 mapUsers
에 testuser 정보가 들어가 있다.
이는 eks-aws-auth-configmap-validation-webhook이라는 validatingwebhookconfigurations이 aws-auth를 업데이트할 수 있는 권한을 가지고 있기 때문에 iamidentitymapping
명령어를 통해 eks-aws-auth-configmap-validation-webhook가 aws-auth 내용을 업데이트해 준 것이다.
다시 클러스터에 권한을 확인해 보면 testuser도 같이 보인다.
# aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser
Added new context testuser to /root/.kube/config
eksctl **delete** iamidentitymapping --cluster $CLUSTER_NAME --arn arn:aws:iam::$ACCOUNT_ID:user/testuser
aws-auth에서 mapUsers를 실수로 삭제해 버린다면 사용자는 모든 권한을 잃어버린다. 근데 클러스터를 처음 생성한 User는 mapUsers에 들어가 있지 않았는데 예전에는 들어가 있었다고 한다. 그래서 한번 삭제되면 장애가 발생할 수도 있기 때문…
특히 system:nodes를 삭제하면 모든 노드가 NotReady 상태로 빠지게 되었다고 한다,.
장애 방지로 aws-auth에 immutable: true 값을 설정해 주면 수정이 불가한다고 한다.
하지만 좀더 쿠버네티스 네이티브한 방식으로 변경하기 위해 인증 모드를 EKS API 방식을 도입한 건데, 그래서 mapUsers에 admin(클러스터 생성한 유저)가 없어도 EKS 클러스터 IAM 액세스 항목에 추가되어 있기에 admin 사용자는 admin 권한을 사용할 수 있던 것이다.
다음 실습에선 인증 모드를 EKS API 방식으로 바꿔 진행해 보겠다.
스터디에서 RBAC 관련 krew 플러그인을 추천해 주셨는데 사용해 보면 유용할 것 같다.
kubectl krew install access-matrix rbac-tool rbac-view rolesum whoami
kubectl rbac-view
로 실행하고 공인IP:8800
으로 웹 접속하면 웹에서 RBAC 검색이 가능하다.