이번 포스팅에서는 EKS IRSA에 대해 알아보겠다.
Applications in a Pod's containers can use an AWS SDK or the AWS CLI to make API requests to AWS services using AWS Identity and Access Management (IAM) permissions.
IAM Roles for Service Accounts(IRSA) Pod 단위의 권한 관리체계로, Pod의 Service Account와 IAM을 연동하여, Pod에 권한을 부여하는 방식이다.
우선 k8s api 호출을 좀 더 디테일하게 들여다 보면,
인증/인가 요청이 들어오면, Admission Controller인 Mutating adminssion 과 Validating admission을 거치게 된다.
EKS에서 인스턴스 프로파일로 권한을 부여한다면, Pod단위가 아닌 Node 단위로 권한 부여가 되므로 디테일한 권한부여를 할 수 없다.
따라서 Pod별로 권한 부여를 하기 위해 IRSA를 제공하는 것이다. 전체적인 플로우는 아래 그림을 참조하자.
실습을 통해서 IRSA가 어떻게 적용되는 지 알아보자.
먼저, service account가 없는 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
(kimchigood:default) [root@myeks-bastion ~]# kubectl logs eks-iam-test1
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
위 Pod는 AWS CLI 기능이 동작하는 Pod로, S3를 조회하는 command를 날리게된다. 지금은 service account가 없어 요청 실패가 되었다.
cloudTrail에서도 이벤트 확인이 가능하다.
https://ap-northeast-2.console.aws.amazon.com/cloudtrail/home?region=ap-northeast-2#/events?EventName=ListBuckets
기본적으로 service acccount는 파드 생성 시 바로 만들어진다.
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 서비스 사용 시도
(kimchigood:default) [root@myeks-bastion ~]# kubectl exec -it eks-iam-test2 -- aws s3 ls
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
command terminated with exit code 254
이번에는 pod에 exec 명령어로 들어가서 's3 ls' 를 날려보았다. 역시 Access Denied가 된다.
# 서비스 어카운트 토큰 확인
SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $SA_TOKEN
jwt.io에서 토큰을 디코딩해보면 payload 항목을 조회할 수 있다.
{
"aud": [
"https://kubernetes.default.svc"
],
"exp": 1717203837,
"iat": 1685667837,
"iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/9E07815FEC4CE6828666A879B7FC5BC5",
"kubernetes.io": {
"namespace": "default",
"pod": {
"name": "eks-iam-test2",
"uid": "f52ad533-c9ad-4138-b00a-7567627a4a27"
},
"serviceaccount": {
"name": "default",
"uid": "401fa6ec-4935-408e-8478-628f49ea6df1"
},
"warnafter": 1685671444
},
"nbf": 1685667837,
"sub": "system:serviceaccount:default:default"
}
aud, exp 항목은 projectedServiceAccountToken에 의해 붙어진 것이고, iss가 EKS IDP 주소이다. 결국 EKS IDP를 통해서 인증을 받는 것이다.
그림 출처:https://aws.amazon.com/ko/blogs/containers/diving-into-iam-roles-for-service-accounts/
그럼 이제 S3에 접근할 수 있도록 IRSA를 적용해보자.
먼저, S3 읽기 권한 정책이 부여된 iamserviceaccount를 생성한다.
# 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)
그러면, cloudFormation이 돌면서 IAM Role을 생성해준다. 그리고, k8s 에는 연관된 service account가 생성되었다.
2023-06-02 10:17:57 [ℹ] 1 existing iamserviceaccount(s) (kube-system/aws-load-balancer-controller) will be excluded
2023-06-02 10:17:57 [ℹ] 1 iamserviceaccount (default/my-sa) was included (based on the include/exclude rules)
2023-06-02 10:17:57 [!] serviceaccounts that exist in Kubernetes will be excluded, use --override-existing-serviceaccounts to override
2023-06-02 10:17:57 [ℹ] 1 task: {
2 sequential sub-tasks: {
create IAM role for serviceaccount "default/my-sa",
create serviceaccount "default/my-sa",
} }2023-06-02 10:17:57 [ℹ] building iamserviceaccount stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2023-06-02 10:17:57 [ℹ] deploying stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2023-06-02 10:17:57 [ℹ] waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2023-06-02 10:18:27 [ℹ] waiting for CloudFormation stack "eksctl-myeks-addon-iamserviceaccount-default-my-sa"
2023-06-02 10:18:27 [ℹ] created serviceaccount "default/my-sa"
(kimchigood:default) [root@myeks-bastion ~]# 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::077777777666:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1WBD9ZN298JVG
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: <none>
Events: <none>
annotation을 보면, IAM Role 과 일치하는 값이 들어있다. 즉, 이 SA에 S3 조회 권한이 들어간 것이다.
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
vpc-resource-mutating-webhook 1 45m
(kimchigood:default) [root@myeks-bastion ~]# kubectl describe pod eks-iam-test3
Name: eks-iam-test3
Namespace: default
Priority: 0
Service Account: my-sa
Node: ip-192-168-1-72.ap-northeast-2.compute.internal/192.168.1.72
Start Time: Fri, 02 Jun 2023 10:22:27 +0900
Labels: <none>
Annotations: kubernetes.io/psp: eks.privileged
Status: Running
IP: 192.168.1.176
IPs:
IP: 192.168.1.176
Containers:
my-aws-cli:
Container ID: containerd://a409818229f0e35f051023681a5a66a7cd8fd4bbf0be4ba48adb8218c963d5bf
Image: amazon/aws-cli:latest
Image ID: docker.io/amazon/aws-cli@sha256:a21eb7f00ed3873b2520ee273c068d04052b06bef06b1ef7b666bb6880b28953
Port: <none>
Host Port: <none>
Command:
sleep
36000
State: Running
Started: Fri, 02 Jun 2023 10:22:37 +0900
Ready: True
Restart Count: 0
Environment:
AWS_STS_REGIONAL_ENDPOINTS: regional
AWS_DEFAULT_REGION: ap-northeast-2
AWS_REGION: ap-northeast-2
AWS_ROLE_ARN: arn:aws:iam::077777777666:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1WBD9ZN298JVG
AWS_WEB_IDENTITY_TOKEN_FILE: /var/run/secrets/eks.amazonaws.com/serviceaccount/token
Mounts:
/var/run/secrets/eks.amazonaws.com/serviceaccount from aws-iam-token (ro)
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-g8jr2 (ro)
생성된 Pod를 describe 해보면 못보던 Environment값들이 추가되어 있다. 바로 위에서 보았던 mutatingWebhook이 값을 조작해 준 것이다.
(kimchigood:default) [root@myeks-bastion ~]# kubectl get mutatingwebhookconfigurations
NAME WEBHOOKS AGE
aws-load-balancer-webhook 3 28m
pod-identity-webhook 1 45m
vpc-resource-mutating-webhook 1 45
pod-identity-webhook 을 통해서 값이 추가된 것이다.
(kimchigood:default) [root@myeks-bastion ~]# kubectl exec -it eks-iam-test3 -- aws s3 ls
2023-03-10 01:35:21 kimchigood
이제 정상적으로 S3 리스트를 읽어올 수 있게되었다.
도전과제 : IRSA - awscli 파드를 생성하면서 해당 파드에서 AWS의 모든서비스를 조회(ViewOnly)할 수 있는 권한설정 해보기
eksctl create iamserviceaccount \
--name readonly \
--namespace default \
--cluster $CLUSTER_NAME \
--approve \
--attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`ReadOnlyAccess`].Arn' --output text)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: eks-iam-test4
spec:
serviceAccountName: readonly
containers:
- name: my-aws-cli
image: amazon/aws-cli:latest
command: ['sleep', '36000']
restartPolicy: Never
EOF
(kimchigood:default) [root@myeks-bastion ~]# kubectl exec -it eks-iam-test4 -- aws ec2 describe-instance-status
{
"InstanceStatuses": [
{
"AvailabilityZone": "ap-northeast-2a",
"InstanceId": "i-0154906ab5eaf45c7",
"InstanceState": {
"Code": 16,
"Name": "running"