가시다님께서 진행하신 AEWS라는 스터디를 진행하면서 작성하는 글입니다.
AEWS= AWS EKS Workshop Study
AEWS 스터디는 4월 23일 ~ 6월 4일동안 총 7번 진행될 예정입니다.
PKOS 스터디에서 정말 좋은 경험을 했어서 다시 이렇게 참가하게 되었습니다.
가시다님께서 진행하시는 스터디에 관심있으신 분들은 Cloudnet@Blog에 들어가시면 자세한 정보를 확인하실 수 있습니다.
인증은 사용자나 클라이언트가 자신의 신원을 증명하는 프로세스입니다.
즉, 시스템이 사용자가 주장하는 신원이 실제로 그 사용자의 신원인지 확인하는 것을 의미합니다. 인증은 보통 사용자의 신원을 확인하기 위해 사용되는 인증 요소를 기반으로 이루어집니다.
일반적으로 사용되는 몇 가지 인증 요소는 다음과 같습니다:
인증은 사용자의 신원을 확인하고, 이를 기반으로 사용자가 시스템에 액세스할 수 있는 권한을 얻을 수 있도록 합니다.
인가는 인증된 사용자나 클라이언트가 특정 작업이나 자원에 대한 액세스 권한을 가지고 있는지 확인하는 프로세스입니다. 인증된 사용자의 신원을 확인한 후에도 인가를 통해 해당 사용자가 수행할 수 있는 작업의 범위를 결정합니다.
일반적으로 인가는 권한 부여를 통해 이루어집니다. 시스템은 사용자에게 역할 또는 그룹을 할당하고, 이 역할 또는 그룹에 대한 권한을 지정합니다. 권한은 특정 작업이나 자원에 대한 액세스 권한을 나타내며, 예를 들어 읽기, 쓰기, 수정, 삭제 등의 권한이 있을 수 있습니다.
인가는 보안 정책을 적용하여 시스템의 자원에 대한 접근을 제어합니다. 인증된 사용자가 시스템에 로그인하면, 인가 프로세스가 해당 사용자가 허용된 작업을 수행할 수 있는지 확인하고, 필요한 경우 해당 작업을 거부합니다.
EKS 인증,인가의 동작에 대해서 한줄로 설명하자면,
⇒ 사용자/애플리케이션 → k8s 사용 시 ⇒ 인증은 AWS IAM, 인가는 K8S RBAC
핵심은 인증은 IAM에서 처리하고, 인가는 k8s RBAC에서 처리 하는 것이다.
인증과정을 한번 분석해보자.
kubectl 명령 → aws eks get-token → EKS Service endpoint(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
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.....
EKS API는 Token Review 를 Webhook token authenticator에 요청 ⇒ (STS GetCallerIdentity 호출) AWS IAM 해당 호출 인증 완료 후 User/Role에 대한 ARN 반환
# 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.
이제 쿠버네티스 RBAC 인가를 처리합니다
# 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
그럼 이제 해당 작업을 직접 실습해보면서 알아가보겠습니다.
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
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"
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
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: [""]}}
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
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
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
IRSA에 대해서 설명하자면,
IRSA는 IAM Role for Service Account의 약자로 kubernetes의 serviceaccount를 사용하여 pod의 권한을 IAM Role로 제어할 수 있도록 하는 기능을 말합니다.
IRSA의 동작
여러 실습을 해보면서 작동방식을 이해해보겠습니다.
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에 접근할 수 있는 권한이 없어서 엑세스가 거부된 것을 확인할 수 있습니다.
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와 함께 사용할 두 번째 토큰을 주입하기 위한 추가 구성 요소가 필요합니다.
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” 는 문제 없지 작동되지만 다른 명령어는 동작을 하지 않는 것을 확인할 수 있습니다.