사전 준비
위와 같이 사전 준비가 필요합니다. 저번 글에서 설명했듯이, EKS를 배포하기 위한 VPC를 생성하고, Public Subnet, Private Subnet을 생성합니다. 그 후 EKS Cluster에 접근하기 위한 bastion EC2를 미리 생성합니다.
추가로 지난번 실습 때 진행했었던 ExternalDNS와 AWS LB Controller, EBS csi driver 설치, gp3 스토리지 클래스 생성까지 해주시고, Prometheus와 Grafana까지 설치합니다.
Service Account Token Volume Projection
ChatGPT가 알려주는 Service Account Token Volume Projection
Service Account Token Volume Projection은 Kubernetes의 보안 기능 중 하나로, Pod에 Service Account Token을 안전하게 제공하는 방법입니다. 이 기능은 파드가 실행될 때 자동으로 마운트되는 볼륨을 통해 서비스 계정 JWT (JSON Web Tokens)를 파드에 주입합니다.
기존에는 유효기간이 없는 Secret 기반의 Volume을 사용하여 Token을 관리했었습니다. 하지만 이 방법으로 Token을 관리하기에는 보안과 관리에 부족함이 있어 kubernetes에서는 ServiceAccountToken을 통해 사용하는 대상(audience), 유효 기간(expiration)등 설정을 통해 Token의 속성을 지정하여 관리하는 기능을 제공했지만 이또한 부족했습니다.
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 # 대상
Bound Service Account Token Volume
Bound Service Account Token Volume는 ServiceAccountToken을 Secret Volume 형태로 mount하는 대신 projected volume을 사용하여 Token을 관리하는 kubernetes의 기능입니다.
- name: kube-api-access-<random-suffix>
projected:
defaultMode: 420 # 420은 rw- 로 소유자는 읽고쓰기 권한과 그룹내 사용자는 읽기만, 보통 0644는 소유자는 읽고쓰고실행 권한과 나머지는 읽고쓰기 권한
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
projected volume에는 3가지로 구분됩니다.
serviceAccountToken
kube-apiserver로부터 TokenRequest API를 통해 얻은 Token으로 Pod에 연결되며 기본적으로 1시간 뒤에 만료되고, 파드가 삭제될 때 만료됩니다.
configMap
kube-apiserver에 대한 연결을 확인하는 데 사용되는 CA 번들을 포함합니다.
downwardAPI
파드의 네임스페이스를 참조합니다.
Configure a Pod to Use a Projected Volume for Storage
Configure a Pod to Use a Projected Volume for Storage란 위에서 설명한 3가지의 설정(serviceAccountToken, configMap, downwardAPI)을 하나의 디렉터리에 통합하여 Storage 형태로 Mount하는 것을 의미합니다.
apiVersion: v1
kind: Pod
metadata:
name: test-projected-volume
spec:
containers:
- name: test-projected-volume
image: busybox:1.28
args:
- sleep
- "86400"
volumeMounts:
- name: all-in-one
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: user
- secret:
name: pass
이처럼 spec.volumeMounts에 MountPath로 directory 경로를 설정하여 Pod를 배포합니다.
...
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
...
그 후 pod의 정보를 확인해보면 위와 같이 projected 정보가 설정된 것을 확인하실 수 있고, Token 값을 volume로 mount되어 사용하실 수 있습니다.
EKS IRSA
일반적으로 EKS Worker Node는 EC2 인스턴스 기반으로 동작하며, AWS ALB Controller 관련 IAM 정책, EKS Worker Node 관련 IAM 정책등 다양한 IAM 정책들을 EC2 인스턴스가 할당받습니다. 그렇기에 EC2 인스턴스(Worker Node)에 배포되어있는 Pod가 탈취당하면 해당 EC2 인스턴스에 설정된 IAM 권한을 악용할 수 있게 됩니다.
이렇듯 EC2 전체에 IAM 권한을 부여해서 사용한다면 사용하기에는 쉽지만 최소 권한 부여 원칙에 어긋나기에 보안상 사용을 권고하지 않습니다.
ChatGPT가 알려주는 IRSA?
IRSA (IAM Roles for Service Accounts)는 AWS에서 제공하는 기능으로, Kubernetes의 Serivce Account을 AWS IAM역할에 연결할 수 있게 해줍니다. 이를 통해 Kubernetes 파드가 AWS 리소스에 접근할 때 필요한 권한을 안전하게 관리할 수 있습니다.
kubernetes Pod가 AWS 서비스를 사용할 시에 AWS STS를 통해 임시 자격증명을 받으며 IAM OIDC Identity Provider를 통해 인증/인가를 받아 Kubernetes의 Pod가 AWS 서비스를 사용할 수 있게 됩니다.
즉, IRSA는 Kubernetes의 Service Account에 IAM 역할을 연결해주어 Pod가 AWS 리소스를 사용할 수 있게 해주는 기능입니다.
실습1 . ServiceAccount 생성하지 않고 Pod 배포 후 IAM 정책 접근 시도
eks-iam-test1 Pod 생성
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
위의 명세로 pod를 하나 생성합니다.
eks-iam-test1 Pod 확인
Error 상태인 것을 확인하실 수 있습니다.
kubectl describe pod eks-iam-test1
describe 명령어로 자세히 살펴보겠습니다.
Events에도 별다른 이유가 적혀있지 않습니다.
kubectl logs eks-iam-test1
그렇다면 Pod의 log를 확인해보겠습니다.
ListBucket 정책에서 AccessDenied가 발생하고 있습니다.
실습2. Service Accounts 생성하고 Pod 배포 후 IAM 정책 접근
eks-iam-test2 Pod 생성
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
위의 명세로 eks-iam-test2 pod를 배포해보겠습니다.
eks-iam-test2 Pod 확인
Running 상태로 잘 띄어져 있습니다.
kubectl get pod eks-iam-test2 -o yaml | kubectl neat | yh
위의 명령어로 배포된 Pod의 yaml 명세를 살펴보겠습니다.
serviceAccount가 자동으로 연결되었고, 위에서 설명한대로 secret과 token의 값들이 projected volume 형태로 mount 된것을 확인하실 수 있습니다.
kubectl exec -it eks-iam-test2 -- ls /var/run/secrets/kubernetes.io/serviceaccount
exec 명령어를 통해 직접 Pod에 접근하여 해당 경로에 token과 secret값들이 있는지 확인해보겠습니다.
aws 서비스 사용 시도
kubectl exec -it eks-iam-test2 -- aws s3 ls
serviceAccount 설정도 되었고, Token도 있는 것을 확인했으니 이제 aws 서비스인 s3에 접근해보도록 하겠습니다.
여전히 ListBuckets 정책에서 Access Denied이 발생하고 있습니다.
ServiceAccountToken 확인
kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
위 명령어를 통해 ServiceAccountToken을 확인합니다.
JWT 웹 사이트 이용하여 Decode
JWT 웹 사이트를 이용하여 Decode해보겠습니다.
왼쪽에 해당 SecretAccountToken 값을 넣고 알고리즘을 HS256으로 설정 후 Decode 진행해주시면 Decode된 Payload 값을 확인하실 수 있습니다.
실습3. IRSA를 생성하고 Pod 생성 후 IAM 접근
iamserviceaccount 생성
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)
kubernetes의 serviceaccount와 AmazonS3ReadOnlyAccess의 정책을 가진 IAM을 연결하는 iamserviceaccount를 생성합니다.
iamserviceaccount 확인
AWS Managed Console의 CloudFormation에서 해당 iamserviceaccount가 생성되었는지 확인이 가능합니다.
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
위의 명령어를 통해서도 확인 가능합니다.
kubernetes service account 확인
kubectl get sa
kubernetes에 생성되어 있는 service account를 확인해보겠습니다.
kubectl describe sa my-sa
describe 명령어를 통해 자세히 살펴보겠습니다.
Annotations 값으로 IAM Role이 설정되어 있는 것을 확인하실 수 있습니다.
eks-iam-test3 Pod 생성
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
sericeAccountName을 위에서 생성해놓은 my-sa로 설정한 eks-iam-test3 Pod를 생성합니다.
eks-iam-test3 Pod 확인
kubectl get pod eks-iam-test3 -o yaml | kubectl neat | yh
위의 명령어를 통해 eks-iam-test3 Pod의 yaml 명세를 살펴보겠습니다.
그러면 추가하지 않았던 env내용과 1개의 volume이 추가된 것을 확인하실 수 있습니다. (Pod Identity Webhook이 mutating webhook을 통해 추가함)
kubectl exec -it eks-iam-test3 -- ls /var/run/secrets/eks.amazonaws.com/serviceaccount
exec 명령어를 통해 eks-iam-test3에 mount한 경로에 token이 있는지 확인해보겠습니다.
aws 서비스 사용 시도
kubectl exec -it eks-iam-test3 -- aws s3 ls
모든 설정이 완료되었고, 이제 exec 명령어를 통해서 해당 Pod에서 AWS S3 조회 명령어를 실행해보겠습니다.
정상적으로 eks-irsa-test bucket이 보여지는 것을 확인하실 수 있습니다!!
하지만 AWS는 JWT Token의 유효성만 확인하고 Token 파일과 ServiceAccount에 지정된 실제 역할 간의 일관성을 보장하지 않습니다.
즉, Condition을 잘못 설정할 시에는 Token과 역할 ARN만 있다면 악용할 수 있습니다.
( 신뢰 정책에 "*"를 부여한다면...? 매우매우 보안에 취약해집니다.)
EKS Pod Identity
ChatGPT가 알려주는 EKS Pod Identity
EKS Pod Identity는 EKS 클러스터에서 실행되는 Pod에 AWS 리소스에 대한 접근 권한을 부여하는 새로운 기능입니다. 기존에는 IAM 역할과 서비스 계정을 사용하여 Pod에 권한을 부여했지만, 이 방법은 복잡하고 관리하기 어려웠습니다. EKS Pod Identity는 Pod의 태그를 기반으로 역할을 할당하여 이러한 문제를 해결합니다.
즉, OIDC Provider를 사용하지 않고 AWS 자체적으로 Pod에 AWS 리소스에 대한 접근 권한을 부여합니다.
실습
Add-on 추가
aws eks create-addon --cluster-name $CLUSTER_NAME --addon-name eks-pod-identity-agent
위의 명령어를 통해 eks-pod-identity-agent를 설치합니다.
Add-on 확인
eksctl get addon --cluster $CLUSTER_NAME
eksctl get addon 명령어를 통해서 현재 cluster에 설치된 add-on 목록을 출력합니다.
kubectl -n kube-system get pods -l app.kubernetes.io/name=eks-pod-identity-agent
기본적으로 add-on은 kube-system namespace에 설치되다보니 kube-system namespace에서 app.kubernetes.io/name=eks-pod-identity-agent label을 가진 pod 목록을 출력하겠습니다.
3개의 Pod가 띄어져 있는데, 이는 worker node가 3개이기때문입니다.
kubectl get ds -n kube-system eks-pod-identity-agent -o yaml | kubectl neat | yh
위의 명령어를 통해 eks-pod-identity-agent DeamonSets의 상세 정보를 살펴보겠습니다.
...
containers:
- args:
- --port # (1)
- "80"
- --cluster-name
- myeks
- --probe-port # (2)
- "2703"
command:
- /go-runner
- /eks-pod-identity-agent
- server
....
ports:
- containerPort: 80
name: proxy
protocol: TCP
- containerPort: 2703
name: probes-port
protocol: TCP
...
securityContext: # (3)
capabilities:
add:
- CAP_NET_BIND_SERVICE
...
hostNetwork: true # (4)
...
(1) container의 80 port로 연결할 수 있게 되어있습니다.
(2) probe port는 2703으로 설정되어 있습니다.
(3) securityContext중 network binding 기능이 필요하기에 CAP_NET_BIND_SERVICE가 추가되어있습니다.
(4) podNetwork를 쓰는게 아닌 hostNetwork 를 사용하기에 Pod의 IP와 Node의 IP가 같습니다.
podidentityassociation 설정
eksctl create podidentityassociation \
--cluster $CLUSTER_NAME \
--namespace default \
--service-account-name s3-sa \
--role-name s3-eks-pod-identity-role \
--permission-policy-arns arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
--region $AWS_REGION
podidentityassociation를 생성합니다.
확인
생성 후 CloudFormation에서 Stack을 확인해보겠습니다.
eksctl get podidentityassociation --cluster $CLUSTER_NAME | jq
get podidentityassociatoin 명령어를 통해서도 확인하실 수 있습니다.
aws iam get-role --query 'Role.AssumeRolePolicyDocument' --role-name s3-eks-pod-identity-role | jq .
신뢰관계도 명령어로 확인하실 수 있습니다.
ServiceAccount 생성
kubectl create sa s3-sa
podIdentity는 ServiceAccount를 따로 생성해주지 않기 때문에 설정해놓았던 이름으로 직접 생성해주셔야 합니다.
eks-pod-identity Pod 생성
apiVersion: v1
kind: Pod
metadata:
name: eks-pod-identity
spec:
serviceAccountName: s3-sa
containers:
- name: my-aws-cli
image: amazon/aws-cli:latest
command: ['sleep', '36000']
restartPolicy: Never
terminationGracePeriodSeconds: 0
serviceAccountName을 위에서 설정한 s3-sa로 연결시키는 Pod를 생성합니다.
확인
kubectl get pod eks-pod-identity -o yaml | kubectl neat| yh
eks-pod-identity Pod의 yaml 명세를 살펴보겠습니다.
AWS 설정들이 지정된 env가 추가되었고, volume으로 serviceAccount가 mount되어있습니다. 또한, projected Volume이 mount되어 token값이 연결되어있는 것을 확인하실 수 있습니다.
aws 서비스 사용 시도
kubectl exec -it eks-pod-identity -- aws s3 ls
eks-pod-identity Pod에 접근하여 AWS S3 buckect 목록을 출력하는 명령어를 실행해보겠습니다.
정상적으로 출력되는것을 확인하실 수 있습니다!
또한, AWS Managed Console에 접근하여서 EKS에서 엑세스 탭에 접근하시면 아래와 같이 Pod Identity 연결에 service account 값이 등록된 것을 확인하실 수 있습니다.
Reference
- https://aws.amazon.com/ko/blogs/containers/diving-into-iam-roles-for-service-accounts/
'스터디 이야기 > AWS EKS' 카테고리의 다른 글
[AEWS] 7-1. Amazon EKS - CI/CD (Jenkins) (0) | 2024.04.16 |
---|---|
[AEWS] 6-3. Amazon EKS - Security (Kyverno) (0) | 2024.04.11 |
[AEWS] 6-1. Amazon EKS - Security (EKS 인증/인가) (0) | 2024.04.09 |
[AEWS] 5-3. Amazon EKS - AutoScaling (Karpenter) (0) | 2024.04.02 |