CloudNet@팀의 EKS 스터디 AEWS 2기에 작성된 자료를 토대로 작성합니다.
쿠버네티스 클러스터에서 실행되는 파드가 AWS 서비스에 어떻게 액세스 권한을 부여 받을수 있을까?
쿠버네티스 클러스터는 AWS의 자원이 아닌데 어떻게 IAM Role을 할당 받을 수 있을까?
먼저, POD가 AWS API 에 요청과 응답을 어떤 방식으로 보내고 받는지 확인해보자.
✅ IRSA
- IAM Roles for Service Accounts
- 쿠버네티스 파드가 AWS 서비스 권한을 부여받는 방법
✅ OIDC(OpenID Connect) 란?
- 사용자가 Role을 Assume 받는 과정과 동일한 방식으로 pod도 Role을 Assume 받아야 한다.
- 그러기 위해서는 인증 절차가 필요한데 이를 EKS OIDC Provider가 위임하여 처리한다.
✅ IRSA Workflow
- pod 내부에서 AWS SDK를 통해 s3를 검색한다면 어떤 방식으로 권한을 부여받을까?JWT와 IAM Role의 ARN 정보를 AWS STS에 전달한다.
✅ Volume Projection
- Pod Volume에 계속 마운트 되는 aws-iam-token은 뭘까?
Service Account Token Volume Projection은 Kubernetes의 보안 기능 중 하나로, Pod에 Service Account Token을 안전하게 제공하는 방법입니다. 이 기능은 파드가 실행될 때 자동으로 마운트되는 볼륨을 통해 JWT를 Pod에 주입합니다.
- 유효기간 (expiration), 대상 (audience)을 통해 더욱 강화된 보안을 사용합니다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: /var/run/secrets/tokens
name: vault-token
serviceAccountName: build-robot
volumes:
- name: vault-token
projected:
sources:
- serviceAccountToken:
path: vault-token
expirationSeconds: 7200 # 만료시간
audience: vault # 대상
- k8s 1.22 버전부터 Bound Service Account Token Volume
- 실습으로 projection volume 사용해보기
# Create the Secrets:
## Create files containing the username and password:
echo -n "admin" > ./username.txt
echo -n "1f2d1e2e67df" > ./password.txt
## Package these files into secrets:
kubectl create secret generic user --from-file=./username.txt
kubectl create secret generic pass --from-file=./password.txt
# 파드 생성
kubectl apply -f <https://k8s.io/examples/pods/storage/projected.yaml>
# 파드 확인
kubectl get pod test-projected-volume -o yaml | kubectl neat | yh
...
volumes:
- name: all-in-one
projected:
defaultMode: 420
sources:
- secret:
name: user
- secret:
name: pass
- name: kube-api-access-n6n9v
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
# 시크릿 확인
kubectl exec -it test-projected-volume -- ls /projected-volume/
password.txt username.txt
kubectl exec -it test-projected-volume -- cat /projected-volume/username.txt ;echo
admin
kubectl exec -it test-projected-volume -- cat /projected-volume/password.txt ;echo
1f2d1e2e67df
# 삭제
kubectl delete pod test-projected-volume && kubectl delete secret user pass
✅ IRSA 실습 1번 : automount = false
- 먼저, automount 옵션을 false로 설정한뒤 권한이 없는 상태에서 AWS SDK를 사용
# 파드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
terminationGracePeriodSeconds: 0
EOF
# 확인
kubectl get pod
kubectl describe pod
# 로그 확인
kubectl logs eks-iam-test1
# 파드1 삭제
kubectl delete pod eks-iam-test1
CloudTrail 이벤트 ListBuckets 확인 → 기록 표시까지 약간의 시간 필요
autoMount 옵션을 false로 지정했을때
해당 요청을 Issue (발행) 한 주체는 - NodeinstanceRole
✅ IRSA 실습 2번
- Pods는 Service Account를 통해 자격증명을 받습니다. 이를 통해 aws sdk를 사용할수 있습니다.
# 파드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
terminationGracePeriodSeconds: 0
EOF
# 확인
kubectl get pod
kubectl describe pod
- describe 해보면 환경변수와 볼륨 마운트 부분을 확인해봐라 실습 1에서 automount false일때와는 다르게 aws iam token과 aws_role_arn이 추가되었다.
- 하지만 aws s3를 조회하는 sdk 권한은 없는것으로 보인다. 실습 3번에서 서비스 어카운트에 적합한 권한을 부여하는것을 실습
✅ IRSA 실습 3번
- Service Account에 권한을 부여하는것은 eksctl create iamserviceaccount
- 이는 Webhook 을 통해서 파드에 Token을 주입하며, 서비스 어카운트를 만든뒤 annotation을 통해 IAM Role의 ARN을 명시함으로 인해 IAM Role의 신뢰관계가 형성된다.
- Cloudformation으로 생성된 IAM Role은 oidc-provider와 신뢰관계
- IRSA 생성 -> 서비스 어카운트 확인 -> Annotation에 aws iam role arn 확인
# 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 Stack >> IAM Role 확인
# aws-load-balancer-controller IRSA는 어떤 동작을 수행할 것 인지 생각해보자!
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
# Inspecting the newly created Kubernetes Service Account, we can see the role we want it to assume in our pod.
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::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1MJUYW59O6QGH
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: <none>
Events: <none>
- pod 생성 -> 오토 마운트
- 오토 마운트 된 iam token과 환경 변수를 확인할것
# 파드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
terminationGracePeriodSeconds: 0
EOF
kubectl get pod eks-iam-test3 -o yaml | kubectl neat | yh
...
volumeMounts:
- mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount
name: aws-iam-token
readOnly: true
...
volumes:
- name: aws-iam-token
projected:
sources:
- serviceAccountToken:
audience: sts.amazonaws.com
expirationSeconds: 86400
path: token
...
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::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-GE2DZKJYWCEN
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-69rh8 (ro)
...
Volumes:
aws-iam-token:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 86400
kube-api-access-sn467:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
...
- The mutating webhook 파드에 단지 마운트만 해줄 뿐 아니라 환경변수 주입도 해준다.
# api 리소스 확인
kubectl api-resources |grep hook
mutatingwebhookconfigurations admissionregistration.k8s.io/v1 false MutatingWebhookConfiguration
validatingwebhookconfigurations admissionregistration.k8s.io/v1 false ValidatingWebhookConfiguration
#
kubectl explain mutatingwebhookconfigurations
kubectl get MutatingWebhookConfiguration
# pod-identity-webhook 확인
kubectl describe MutatingWebhookConfiguration pod-identity-webhook
kubectl get MutatingWebhookConfiguration pod-identity-webhook -o yaml | yh
- 실습 확인 후 파드 삭제 및 IRSA 제거
#
kubectl delete pod eks-iam-test3
eksctl delete iamserviceaccount --cluster $CLUSTER_NAME --name my-sa --namespace default
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
kubectl get sa
'DevOps' 카테고리의 다른 글
[AWS EKS] EKS CICD (0) | 2024.04.15 |
---|---|
[AWS EKS] EKS Security (Kyverno) (0) | 2024.04.13 |
[AWS EKS] EKS Security ( IAM Authenticator) (0) | 2024.04.08 |
[AWS EKS] Autoscaling ( Karpenter) (0) | 2024.04.03 |
[AWS EKS] EKS Autoscaling ( Cluster ) (0) | 2024.04.02 |