1. K8S 인증/인가
K8S 인증/인가
쿠버네티스 API 서버에 대한 인증 및 인가에 대해 설명합니다.
인증(Authentication)
인증은 접근하려는 사용자의 신분을 확인하는 과정입니다.
쿠버네티스에서는 대표적으로 아래 3가지 방식의 인증을 지원합니다.
- X509 Client Certs
- kubectl
- Service Account
X509 Client Certs
kubeconfig에 CA crt(발급 기관 인증서) , Client crt(클라이언트 인증서) , Client key(클라이언트 개인키)를 통해 인증합니다.
kubectl
러 클러스터(kubeconfig)를 관리 가능 - contexts 에 클러스터와 유저 및 인증서/키 참고합니다.
Service Account
서비스 계정에 부여된 API 토큰(시크릿)을 사용하여 인증하는 방식
인가(Authorization)
인가는 인증된 사용자에 대한 권한을 부여하는 작업입니다.
쿠버네티스에서는 대표적으로 RBAC 을 통해서 관리합니다.
- Namespace/Cluster - Role/ClusterRole, RoleBinding/ClusterRoleBinding, Service Account
- Role(롤) - (RoleBinding 롤 바인딩) - Service Account(서비스 어카운트) : 롤 바인딩은 롤과 서비스 어카운트를 연결
- Role(네임스페이스 내 자원의 권한) vs ClusterRole(클러스터 수준의 자원의 권한)
k8s api 접근 단계
- AuthN → AuthZ → Admisstion Control 권한이 있는 사용자에 한해서 관리자(Admin)가 특정 행동을 제한(validate) 혹은 변경(mutate) - 링크 Slack
- AuthN & AuthZ - MutatingWebhook - Object schema validation - ValidatingWebhook → etcd
- Admission Control도 Webhook으로 사용자에게 API가 열려있고, 사용자는 자신만의 Admission Controller를 구현할 수 있으며, 이를 Dynamic Admission Controller라고 부르고, 크게 MutatingWebhook과 ValidatingWebhook로 나뉩니다.
- MutatingWebhook은 사용자가 요청한 request에 대해서 관리자가 임의로 값을 변경하는 작업입니다.
- ValidatingWebhook은 사용자가 요청한 request에 대해서 관리 자기 허용을 막는 작업입니다.
kubectl get validatingwebhookconfigurations
kubectl get mutatingwebhookconfigurations
k8s 인증 / 인가 실습
개발팀과, 인프라팀이 있다고 가정하고, dev-team, infra-team이라는 각각 네임스페이스와 서비스 어카운트를 생성하여 인증 및 인가를 테스트합니다.
dev-team, infra-team이라는 각각 네임스페이스와 서비스 어카운트를 를 생성합니다.
# 네임스페이스(Namespace, NS) 생성 및 확인
kubectl create namespace dev-team
kubectl create ns infra-team
# 네임스페이스 확인
kubectl get ns
# 네임스페이스에 각각 서비스 어카운트 생성 : serviceaccounts 약자(=sa)
kubectl create sa dev-k8s -n dev-team
kubectl create sa infra-k8s -n infra-team
# 서비스 어카운트 정보 확인
kubectl get sa -n dev-team
kubectl get sa dev-k8s -n dev-team -o yaml | yh
kubectl get sa -n infra-team
kubectl get sa infra-k8s -n infra-team -o yaml | yh
bitnami의 kubectl 이미지의 파드를 각각의 서비스 어카운트를 부여하여 생성합니다.
# 각각 네임스피이스에 kubectl 파드 생성 - 컨테이너이미지
# docker run --rm --name kubectl -v /path/to/your/kube/config:/.kube/config bitnami/kubectl:latest
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: dev-kubectl
namespace: dev-team
spec:
serviceAccountName: dev-k8s
containers:
- name: kubectl-pod
image: bitnami/kubectl:1.28.5
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: infra-kubectl
namespace: infra-team
spec:
serviceAccountName: infra-k8s
containers:
- name: kubectl-pod
image: bitnami/kubectl:1.28.5
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# 확인
kubectl get pod -A
kubectl get pod -o dev-kubectl -n dev-team -o yaml
serviceAccount: dev-k8s
...
kubectl get pod -o infra-kubectl -n infra-team -o yaml
serviceAccount: infra-k8s
...
# 파드에 기본 적용되는 서비스 어카운트(토큰) 정보 확인
kubectl exec -it dev-kubectl -n dev-team -- ls /run/secrets/kubernetes.io/serviceaccount
kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/token
kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/namespace
kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/ca.crt
해당 서비스 어카운트의 권한으로 kubectl 명령어를 입력해 보면, 권한 에러가 발생합니다.
- auth can-i 명령어를 이용하여 권한을 확인합니다.
# 각각 파드로 Shell 접속하여 정보 확인 : 단축 명령어(alias) 사용
alias k1='kubectl exec -it dev-kubectl -n dev-team -- kubectl'
alias k2='kubectl exec -it infra-kubectl -n infra-team -- kubectl'
# 권한 테스트
k1 get pods # kubectl exec -it dev-kubectl -n dev-team -- kubectl get pods 와 동일한 실행 명령이다!
k1 run nginx --image nginx:1.20-alpine
k1 get pods -n kube-system
# 권한애러
# Error from server (Forbidden): pods is forbidden: User #"system:serviceaccount:dev-team:dev-k8s" cannot list resource "pods" in API # group "" in the namespace "dev-team"
command terminated with exit code 1
k2 get pods # kubectl exec -it infra-kubectl -n infra-team -- kubectl get pods 와 동일한 실행 명령이다!
k2 run nginx --image nginx:1.20-alpine
k2 get pods -n kube-system
# (옵션) kubectl auth can-i 로 kubectl 실행 사용자가 특정 권한을 가졌는지 확인
k1 auth can-i get pods
no
해당 네임스페이스에 대한 모든 권한을 각각의 네임스페이스의 부여하여 다시 kubectl 명령어가 동작하는지 확인합니다.
- Role을 통해 네임스페이스에 대한 모든 권한을 가진 역할을 생성합니다.
- Rolebinding을 통해 특정 사용자와 권한을 부여한 역할을 매핑시킵니다.
# 각각 네임스페이스내의 모든 권한에 대한 롤 생성
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-dev-team
namespace: dev-team
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
EOF
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-infra-team
namespace: infra-team
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
EOF
# 롤 확인
kubectl get roles -n dev-team
kubectl get roles -n infra-team
kubectl get roles -n dev-team -o yaml
kubectl describe roles role-dev-team -n dev-team
...
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
*.* [] [] [*]
# 롤바인딩 생성 : '서비스어카운트 <-> 롤' 간 서로 연동
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: roleB-dev-team
namespace: dev-team
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-dev-team
subjects:
- kind: ServiceAccount
name: dev-k8s
namespace: dev-team
EOF
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: roleB-infra-team
namespace: infra-team
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-infra-team
subjects:
- kind: ServiceAccount
name: infra-k8s
namespace: infra-team
EOF
# 롤바인딩 확인
kubectl get rolebindings -n dev-team
kubectl get rolebindings -n infra-team
kubectl get rolebindings -n dev-team -o yaml
kubectl describe rolebindings roleB-dev-team -n dev-team
...
Role:
Kind: Role
Name: role-dev-team
Subjects:
Kind Name Namespace
---- ---- ---------
ServiceAccount dev-k8s dev-team
2. EKS 인증/인가
EKS는 사용자나 애플리케이션이 EKS 클러스터에 접근 시, AWS IAM을 통해 인증하고, 내부 리소스에 대한 인가는 K8S RBAC을 통해 관리하도록 동작합니다.
EKS 인증/인가 동작과정
EKS 클러스터에서 kubectl 명령어 시, 대략적인 통신흐름은 다음과 같습니다.
1. kubectl 명령 → aws eks get-token → EKS Service endpoint(STS)에 토큰 요청
2. Pre-Signed URL을 Bearer Token으로 EKS API Cluster Endpoint로 요청을 보냄
3. EKS API는 Token Review를 를 Webhook token authenticator에 요청 ⇒ (STS GetCallerIdentity 호출)
4. 쿠버네티스 RBAC 인가를 처리
실습 : 데브옵스 신입 사원을 위한 myeks-bastion-2에 설정해보기
데브옵스 신입사원이 들어왔다고 가정하여, 새로운 계정을 생성하여 EKS를 사용할 수 있도록 설정해 봅니다.
1. testuser 사용자를 생성합니다.
- testuser에 AdministratorAccess 정책을 추가합니다.
# testuser 사용자 생성
aws iam create-user --user-name testuser
# 사용자에게 프로그래밍 방식 액세스 권한 부여
aws iam create-access-key --user-name testuser
{
"AccessKey": {
"UserName": "testuser",
"AccessKeyId": "AKIARJRRSX5KPXSOVDFY",
"Status": "Active",
"SecretAccessKey": "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",
"CreateDate": "2024-04-13T17:29:12+00:00"
}
}
# 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::089224560468:user/admin"
kubectl whoami
2. 새 작업용 EC2 인스턴스에 접속하여, 생성한 계정의 자격증명을 설정합니다.
# testuser 자격증명 설정
aws configure
AWS Access Key ID [None]: AKIARJRRSX5KPXSOVDFY
AWS Secret Access Key [None]: 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
Default region name [None]: ap-northeast-2
# get-caller-identity 확인
aws sts get-caller-identity --query Arn
"arn:aws:iam::089224560468:user/testuser"
3. testuser에 system:masters 그룹 부여로 EKS 관리자 수준 권한 설정합니다.
# 방안1 : eksctl 사용 >> iamidentitymapping 실행 시 aws-auth 컨피그맵 작성해줌
# Creates a mapping from IAM role or user to Kubernetes user and groups
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
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
...
kubectl get validatingwebhookconfigurations eks-aws-auth-configmap-validation-webhook -o yaml | kubectl neat | yh
# 방안2 : 아래 edit로 mapUsers 내용 직접 추가!
kubectl edit cm -n kube-system aws-auth
---
apiVersion: v1
data:
mapRoles: |
- groups:
- system:bootstrappers
- system:nodes
rolearn: arn:aws:iam::089224560468:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-LHQ7DWHQQRZJ
username: system:node:{{EC2PrivateDNSName}}
mapUsers: |
- groups:
- system:masters
userarn: arn:aws:iam::089224560468:user/testuser
username: testuser
...
# 확인 : 기존에 있는 role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-YYYYY 는 어떤 역할/동작을 하는 걸까요?
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
ARN USERNAME GROUPS ACCOUNT
arn:aws:iam::089224560468:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-LHQ7DWHQQRZJ system:node:{{EC2PrivateDNSName}} system:bootstrappers,system:nodes
arn:aws:iam::089224560468:user/testuser testuser system:masters
aws-auth라는 컨피그 맵을 통해서 EKS의 권한을 관리하기 때문에, 컨피그맵의 mapRoles,mapUsers 필드의 내용에 따라 권한이 부여된다. eksctl 명령어를 통해서 iamidentitymapping를 생성하면 validatingwebhookconfigurations에 의해서 아래처럼 컨피그맵에 자동으로 내용이 반영되는 것을 확인할 수 있다. 따라서, 해당 컨피그맵을 수정하거나 삭제하면, EKS 전반적인 동작에 영향을 미치기 때문에, 아주 치명적인 문제를 발생한다.
4. testuser kubeconfig 생성 및 kubectl 사용 확인
# testuser kubeconfig 생성 >> aws eks update-kubeconfig 실행이 가능한 이유는?, 3번 설정 후 약간의 적용 시간 필요
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
I0414 03:10:06.744108 2503 loader.go:395] Config loaded from file: /root/.kube/config
I0414 03:10:07.604858 2503 round_trippers.go:553] GET https://662552ED4B213E8BB31575D8CF6F9EF7.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500 200 OK in 849 milliseconds
NAME STATUS ROLES AGE VERSION
ip-192-168-1-86.ap-northeast-2.compute.internal Ready 164m v1.28.5-eks-5e0fdde
ip-192-168-2-131.ap-northeast-2.compute.internal Ready 164m v1.28.5-eks-5e0fdde
ip-192-168-3-225.ap-northeast-2.compute.internal Ready 164m v1.28.5-eks-5e0fdde
EKS access management controls
기존 configmap을 통한 인증방식은 동작 방식이 너무 복잡하고, aws-auth 컨피그맵을 잘못 수정 시 장애를 발생하는 치명적인 단점을 가지고 있습니다. 따라서 EKS access management controls는 그러한 단점을 보완하고 간단한 구성과 쉬운 사용을 위해 만들어진 신기능입니다. AWS 콘솔에서 액세스 항목 탭을 통해 확인할 수 있으며, aws-auth 컨피그맵에 등록되어있지 않아도 EKS API 액세스 항목을 통해서 권한을 관리할 수 있습니다.
액세스 정책 변경
기본적인 인증 모드는 EKS API 및 ConfigMap입니다. ConfigMap 방식을 사용하지 않기 위해, EKS API모드로 변경합니다.
aws eks update-cluster-config --name $CLUSTER_NAME --access-config authenticationMode=API
testuser 재설정
액세스 모드를 변경하여 이제는 Configmap을 사용할 수 없기 때문에 위의 실습에서 적용한 testuser의 권한이 적용되지 않으므로,
변경한 새로운 방식으로 권한을 다시 부여합니다.
# testuser 의 access entry 생성
aws eks create-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser
aws eks list-access-entries --cluster-name $CLUSTER_NAME | jq -r .accessEntries[]
# testuser에 AmazonEKSClusterAdminPolicy 연동
aws eks associate-access-policy --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy --access-scope type=cluster
#
aws eks list-associated-access-policies --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser | jq
aws eks describe-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser | jq
Configmap 방식보다도 훨씬 간단하게 권한을 설정하고 확인할 수 있습니다. AWS에서도 EKS access management controls를 사용하는 것을 권장하고 있습니다.
Access entries and Kubernetes groups
액세스 항목 생성 시, --kubernetes-group 옵션을 통해서 쿠버네티스 롤을 직접 생성하고 그룹에 바인딩하여 손쉽게 사용할 수 있습니다.
pod-viewer(읽기 전용)와 pod-admin(모든 권한)의 나누어 클러스터 롤을 생성하고 바인딩합니다. 먼저, pod-viewer를 액세스 항목으로 생성합니다.
# 기존 testuser access entry 제거
aws eks delete-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser
aws eks list-access-entries --cluster-name $CLUSTER_NAME | jq -r .accessEntries[]
#
cat <<EoF> ~/pod-viewer-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-viewer-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list", "get", "watch"]
EoF
cat <<EoF> ~/pod-admin-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-admin-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["*"]
EoF
kubectl apply -f ~/pod-viewer-role.yaml
kubectl apply -f ~/pod-admin-role.yaml
#
kubectl create clusterrolebinding viewer-role-binding --clusterrole=pod-viewer-role --group=pod-viewer
kubectl create clusterrolebinding admin-role-binding --clusterrole=pod-admin-role --group=pod-admin
#
aws eks create-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser --kubernetes-group pod-viewer
...
"accessEntry": {
"clusterName": "myeks",
"principalArn": "arn:aws:iam::91128...:user/testuser",
"kubernetesGroups": [
"pod-viewer"
],
#
aws eks list-associated-access-policies --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser
aws eks describe-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser | jq
...
"kubernetesGroups": [
"pod-viewer"
],
...
pod-viewer의 역할을 부여받았기 때문에, get 명령어는 잘 동작하지만, delete 명령어는 권한이 허용되지 않음을 확인할 수 있습니다.
kubectl whoami
# kubectl 시도
kubectl get pod -v6
kubectl api-resources -v5
kubectl auth can-i get pods --all-namespace
yes
kubectl auth can-i delete pods --all-namespaces
no
pod-admin으로 kubernetesGroups 업데이트를 적용하고 다시 확인합니다.
#
aws eks update-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser --kubernetes-group pod-admin | jq -r .accessEntry
...
"kubernetesGroups": [
"pod-admin"
...
aws eks describe-access-entry --cluster-name $CLUSTER_NAME --principal-arn arn:aws:iam::$ACCOUNT_ID:user/testuser | jq
...
"kubernetesGroups": [
"pod-admin"
],
...
모든 권한을 가진 pod-admin 역할을 부여받아 delete 명령어도 잘 동작함을 확인할 수 있습니다.
# testuser 정보 확인
aws sts get-caller-identity --query Arn
kubectl whoami
# kubectl 시도
kubectl get pod -v6
kubectl api-resources -v5
kubectl auth can-i get pods --all-namespaces
yes
kubectl auth can-i delete pods --all-namespaces
yes
3. EKS IRSA & Pod Identity
EC2 Instance Profile
EC2 Instance Profiles는 워커 노드 인스턴스에 역할을 부여하는 방식으로 사용하기는 편하지만, 최소 권한 부여 원칙에 위배하며 보안상 사용을 권고하지 않습니다. 왜냐하면 파드 하나가 문제가 생기면 해당 파드에서 노드에 권한 된 모든 권한을 사용할 수 있기 때문입니다.
IRSA를 사용하여 단점을 보완할 수 있습니다.
사전지식
IRSA를 알아보기 전 몇 가지 사전지식이 필요합니다.
1. Service Account Token Volume Projection
- 기존 default SA의 토큰 유효기간 등의 단점을 보완
- 파드가 시크릿을 안전하게 사용하는 기법으로, 토큰을 사용하는 대상(audience), 유효 기간(expiration) 등 토큰의 속성을 지정
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
2. Bound Service Account Token Volume 바인딩된 서비스 어카운트 토큰 볼륨
- 서비스 어카운트 어드미션 컨트롤러는 토큰 컨트롤러에서 생성한 만료되지 않은 서비스 계정 토큰에 시크릿 기반 볼륨 대신 다음과 같은 프로젝티드 볼륨을 추가합니다.
- 프로젝티드 볼륨은 세 가지로 구성된다.
- kube-apiserver로부터 TokenRequest API를 통해 얻은 서비스어카운트토큰(ServiceAccountToken). 서비스어카운트토큰은 기본적으로 1시간 뒤에, 또는 파드가 삭제될 때 만료된다. 서비스어카운트토큰은 파드에 연결되며 kube-apiserver를 위해 존재한다.
- kube-apiserver에 대한 연결을 확인하는 데 사용되는 CA 번들을 포함하는 컨피그맵(ConfigMap).
- 파드의 네임스페이스를 참조하는 DownwardA
- 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
3. Configure a Pod to Use a Projected Volume for Storage
- 시크릿 컨피그맵 downwardAPI serviceAccountToken의 볼륨 마운트를 하나의 디렉터리에 통합
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
실제로 projected 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 소개
WS IRSA (IAM Roles for Service Accounts)는 Amazon EKS (Elastic Kubernetes Service) 클러스터 내에서 특정 서비스 계정에 IAM 역할을 자동으로 할당할 수 있게 해주는 기능입니다. 이를 통해 Pod 또는 워크로드가 AWS 서비스에 안전하게 접근할 수 있습니다.
IRSA를 사용하면 각 Pod 또는 워크로드에 대해 생성된 서비스 계정에 IAM 역할이 자동으로 매핑되므로, 개별 Pod가 필요한 권한을 가지고 AWS 리소스에 접근할 수 있습니다.
IRSA 동작과정
- AWS SDK는 AWS_ROLE_ARN 및 AWS_WEB_IDENTITY_TOKEN_FILE 이름의 환경변수를 읽어 들여 Web Identity 토큰으로 AssumeRoleWithWebIdentify를 호출함으로써 Assume Role을 시도하여 임시 자격 증명을 획득하고, 특정 IAM Role 역할을 사용할 수 있게 됩니다.
- 이때 Assume Role 동작을 위한 인증은 AWS가 아닌 외부 Web IdP(EKS IdP)에 위임하여 처리합니다
IRSA 실습
1. 서비스 어카운트를 생성하지 않고 파드 생성
aws s3 ls 명령어 시, 권한이 없다는 에러가 발생합니다.
# 파드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
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
# 파드1 삭제
kubectl delete pod eks-iam-test1
2. IRSA 생성하여 파드 생성
- eksctl create iamserviceaccount 명령어를 통해 생성
- 쿠버네티스 서비스어카운트와 AWS IAM Role ARN을 매핑(iamidentity와 다르니 주의 )
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)
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
NAMESPACE NAME ROLE ARN
default my-sa arn:aws:iam::089224560468:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-3OmrLSbtoqC8
kube-system aws-load-balancer-controller arn:aws:iam::089224560468:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-655KiJzxZvLC
IRSA 생성 시, CloudFormation과 서비스 어카운트가 생성됨을 확인할 수 있습니다.
IRSA 통해 생성한 서비스 어카운트로 파드를 신규 파드를 생성합니다.
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
pod의 필드를 확인하면 Env, Volume 추가되었음을 확인할 수 있습니다. 이는 IRSA의 mutating webhook을 통해서 변조됨을 알 수 있습니다.
# 해당 SA를 파드가 사용 시 mutatingwebhook으로 Env,Volume 추가함
kubectl get mutatingwebhookconfigurations pod-identity-webhook -o yaml | kubectl neat | yh
# 파드 생성 yaml에 없던 내용이 추가됨!!!!!
# Pod Identity Webhook은 mutating webhook을 통해 아래 Env 내용과 1개의 볼륨을 추가함
kubectl get pod eks-iam-test3
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 exec -it eks-iam-test3 -- ls /var/run/secrets/eks.amazonaws.com/serviceaccount
token
kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token ; echo
...
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
aws 커맨드를 통해 s3 조회도 잘 동작하는 것을 확인할 수 있습니다.
# 파드에서 aws cli 사용 확인
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn
"arn:aws:sts::911283464785:assumed-role/eksctl-myeks-addon-iamserviceaccount-default-Role1-GE2DZKJYWCEN/botocore-session-1685179271"
# 되는 것고 안되는 것은 왜그런가?
kubectl exec -it eks-iam-test3 -- aws s3 ls
2024-04-14 04:33:02 [ℹ] created serviceaccount "default/my-sa"
(admin-eks@myeks:N/A) [root@myeks-bastion ~]# eksctl get iamserviceaccount --cluster $CLUSTER_NAME
NAMESPACE NAME ROLE ARN
default my-sa arn:aws:iam::089224560468:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-3OmrLSbtoqC8
kube-system aws-load-balancer-controller arn:aws:iam::089224560468:role/eksctl-myeks-addon-iamserviceaccount-kube-sys-Role1-655KiJzxZvLC
(admin-eks@myeks:N/A) [root@myeks-bastion ~]# k describe sa
default my-sa
(admin-eks@myeks:N/A) [root@myeks-bastion ~]# k describe sa
default my-sa
(admin-eks@myeks:N/A) [root@myeks-bastion ~]# k 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::089224560468:role/eksctl-myeks-addon-iamserviceaccount-default--Role1-3OmrLSbtoqC8
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: <none>
Events: <none>
Pod Identity
IRSA의 문제점
IRSA는 아래와 같은 취약점과 한계점이 존재합니다.
Pod Identity는 IRSA의 단점을 보완하는 신기능입니다.
Pod Identity 설치
addon을 통해 손쉽게 설치할 수 있습니다.
#
ADDON=eks-pod-identity-agent
aws eks describe-addon-versions \
--addon-name $ADDON \
--kubernetes-version 1.28 \
--query "addons[].addonVersions[].[addonVersion, compatibilities[].defaultVersion]" \
--output text
v1.2.0-eksbuild.1
True
v1.1.0-eksbuild.1
False
v1.0.0-eksbuild.1
False
# 모니터링
watch -d kubectl get pod -A
# 설치
aws eks create-addon --cluster-name $CLUSTER_NAME --addon-name eks-pod-identity-agent
혹은
eksctl create addon --cluster $CLUSTER_NAME --name eks-pod-identity-agent --version 1.2.0
# 확인
eksctl get addon --cluster $CLUSTER_NAME
kubectl -n kube-system get daemonset eks-pod-identity-agent
kubectl -n kube-system get pods -l app.kubernetes.io/name=eks-pod-identity-agent
kubectl get ds -n kube-system eks-pod-identity-agent -o yaml | kubectl neat | yh
...
containers:
- args:
- --port
- "80"
- --cluster-name
- myeks
- --probe-port
- "2703"
command:
- /go-runner
- /eks-pod-identity-agent
- server
....
ports:
- containerPort: 80
name: proxy
protocol: TCP
- containerPort: 2703
name: probes-port
protocol: TCP
...
securityContext:
capabilities:
add:
- CAP_NET_BIND_SERVICE
...
hostNetwork: true
...
# 네트워크 정보 확인
## EKS Pod Identity Agent uses the hostNetwork of the node and it uses port 80 and port 2703 on a link-local address on the node.
## This address is 169.254.170.23 for IPv4 and [fd00:ec2::23] for IPv6 clusters.
for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ss -tnlp | grep eks-pod-identit; echo "-----";done
for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ip -c route; done
for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ip -c -br -4 addr; done
for node in $N1 $N2 $N3; do ssh ec2-user@$node sudo ip -c addr; done
policty에 Pod Identity에 관한 새로운 권한이 추가되었습니다.
Pod Identity 실습
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
# 확인
kubectl get sa
eksctl get podidentityassociation --cluster $CLUSTER_NAME
ASSOCIATION ARN NAMESPACE SERVICE ACCOUNT NAME IAM ROLE ARN
arn:aws:eks:ap-northeast-2:911283464785:podidentityassociation/myeks/a-blaanudo8dc1dbddw default s3-sa arn:aws:iam::911283464785:role/s3-eks-pod-identity-role
aws eks list-pod-identity-associations --cluster-name $CLUSTER_NAME | jq
{
"associations": [
{
"clusterName": "myeks",
"namespace": "default",
"serviceAccount": "s3-sa",
"associationArn": "arn:aws:eks:ap-northeast-2:911283464785:podidentityassociation/myeks/a-pm07a3bg79bqa3p24",
"associationId": "a-pm07a3bg79bqa3p24"
}
]
}
# ABAC 지원을 위해 sts:Tagsession 추가
aws iam get-role --query 'Role.AssumeRolePolicyDocument' --role-name s3-eks-pod-identity-role | jq .
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}
EKS 액세스 항목에서도 조회 및 생성이 가능합니다.
신규 파드 생성 및 동작 테스트
aws 명령어가 잘 동작하는 것을 확인할 수 있습니다.
# 서비스어카운트, 파드 생성
kubectl create sa s3-sa
cat <<EOF | kubectl apply -f -
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
EOF
#
kubectl get pod eks-pod-identity -o yaml | kubectl neat| yh
kubectl exec -it eks-pod-identity -- aws sts get-caller-identity --query Arn
kubectl exec -it eks-pod-identity -- aws s3 ls
kubectl exec -it eks-pod-identity -- env | grep AWS
WS_CONTAINER_CREDENTIALS_FULL_URI=http://169.254.170.23/v1/credentials
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE=/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
AWS_STS_REGIONAL_ENDPOINTS=regional
AWS_DEFAULT_REGION=ap-northeast-2
AWS_REGION=ap-northeast-2
# 토큰 정보 확인
kubectl exec -it eks-pod-identity -- ls /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/
kubectl exec -it eks-pod-identity -- cat /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
'AWS > EKS' 카테고리의 다른 글
EKS IaC(Terraform) - AEWS 8주차 (1) | 2024.04.28 |
---|---|
EKS CI/CD - AEWS 7주차 (1) | 2024.04.20 |
EKS 오토스케일링(Autoscaling) - AEWS 5주차 (0) | 2024.04.07 |
EKS 옵저버빌리티(Obsivability) - AEWS 4주차 (0) | 2024.03.31 |
EKS 노드그룹(Nodegroup) - AEWS 3주차 2 (0) | 2024.03.23 |