Gasida-서종호님과 악분일상-최성욱님 Jerry-이정훈님의 노고에 다시한번 🙇 감사드립니다.
6주차의 주제는 EKS Security 입니다.
K8s에서의 권한에 대한 인증/인가를 적용하는 방법을 확인하고, EKS에서 이를 구현하는 과정에서 어떤 방식으로 차이점이 발생하는지 확인하였습니다.
보안 관련 내용은 여전히 어렵지만, IRSA는 이전 시간인 [eks]Nodeless Concept에서 시스템과 관련된 모든것을 Fargate로 만들어보는 과정에서, 미리 스터디할 수 있었습니다.
6주차는 구경열님, 유형욱님의 경험 및 기술 공유를 발표하셨습니다.
구경열님은 HPC를 on-premise 환경에서 구축하면서 겪은 경험 내용을 상세하게 전달해 주셨습니다. 그 과정에서 힘들었던점, 왜 클라우드 전환이 필요한지에 대해서도 말씀해주셨습니다.
유형욱님은 hashicorp Vault 내용을 주제로 경험을 공유해주셨습니다. Vault에 대한 대략적인 내용에 대한 전달, 핸즈온 처럼 직접 시연을 하면서 Vault를 사용해 나가는 방법을 공유하며 해당 서비스에 대해서 더욱 잘 알게된 시간이었습니다.
두분께 다시한번 감사드립니다.
쿠버네티스 권한의 인증/인가에 대해 이해합니다.
EKS에서의 권한의 인증/인가에 대해 이해합니다.
EC2 Instance Profile이 아닌, 파드에 역할을 배치하여 사용하는 IRSA를 사용합니다.
이번주차에서는 oneclick5.yaml 을 통해 배포하였습니다.
K8S(API 접근) 인증/인가 소개
X.509 발음
을 어떻게 하시나요? - 링크인증(Authentication)
인가(Authorization)
인가 방식 : RBAC(Role, RoleBinding), ABAC, Webhook, Node Authorization
RBAC : 역할 기반의 권한 관리, 사용자와 역할을 별개로 선언 후 두가지를 조합(binding)해서 사용자에게 권한을 부여하여 kubectl or API로 관리 가능
.kube/config
파일 내용
clusters
: kubectl 이 사용할 쿠버네티스 API 서버의 접속 정보 목록. 원격의 쿠버네티스 API 서버의 주소를 추가해 사용 가능합니다.users
: 쿠버네티스의 API 서버에 접속하기 위한 사용자 인증 정보 목록. (서비스 어카운트의 토큰, 혹은 인증서의 데이터 등)contexts
: cluster 항목과 users 항목에 정의된 값을 조합해 최종적으로 사용할 쿠버네티스 클러스터의 정보(컨텍스트)를 설정.
- 예를 들어 clusters 항목에 클러스터 A,B 가 정의돼 있고, users 항목에 사용자 a,b 가 정의돼 있다면 cluster A + user a 를 조합해, 'cluster A 에 user a 로 인증해 쿠버네티스를 사용한다' 라는 새로운 컨텍스트를 정의할 수 있습니다.
kubectl
을 사용하려면 여러 개의 컨텍스트 중 하나를 선택.
실습 환경
- 쿠버네티스에 사용자를 위한 서비스 어카운트(Service Account, SA)를 생성 : dev-k8s, infra-k8s
- 사용자는 각기 다른 권한(Role, 인가)을 가짐 : dev-k8s(dev-team 네임스페이스 내 모든 동작) , infra-k8s(dev-team 네임스페이스 내 모든 동작)
- 각각 별도의 kubectl 파드를 생성하고, 해당 파드에 SA 를 지정하여 권한에 대한 테스트를 진행
- 네임스페이스와 서비스 어카운트 생성 후 확인
- 파드 기동 시 서비스 어카운트 한 개가 할당되며, 서비스 어카운트 기반 인증/인가를 함, 미지정 시 기본 서비스 어카운트가 할당
- 서비스 어카운트에 자동 생성된 시크릿에 저장된 토큰으로 쿠버네티스 API에 대한 인증 정보로 사용 할 수 있다 ← 1.23 이전 버전의 경우에만 해당
# 네임스페이스(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
서비스 어카운트를 지정하여 파드 생성 후 권한 테스트
# 각각 네임스피이스에 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.24.10 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.24.10 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 # 각각 파드로 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 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
- 각각 네임스페이스에 롤(Role)를 생성 후 서비스 어카운트 바인딩
- 롤(Role) : apiGroups 와 resources 로 지정된 리소스에 대해 verbs 권한을 인가
- 실행 가능한 조작(verbs) : *(모두 처리), create(생성), delete(삭제), get(조회), list(목록조회), patch(일부업데이트), update(업데이트), watch(변경감시)
# 각각 네임스페이스내의 모든 권한에 대한 롤 생성 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
서비스 어카운트를 지정하여 생성한 파드에서 다시 권한 테스트
# 각각 파드로 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 k1 run nginx --image nginx:1.20-alpine k1 get pods k1 delete pods nginx k1 get pods -n kube-system k1 get nodes k2 get pods k2 run nginx --image nginx:1.20-alpine k2 get pods k2 delete pods nginx k2 get pods -n kube-system k2 get nodes # (옵션) kubectl auth can-i 로 kubectl 실행 사용자가 특정 권한을 가졌는지 확인 k1 auth can-i get pods yes
사용자/애플리케이션 → k8s 사용 시 ⇒ 인증
은 AWS IAM, 인가
는 K8S RBAC
출처 - https://kimalarm.tistory.com/65
RBAC 관련 krew 플러그인
# 설치 kubectl krew install access-matrix rbac-tool rbac-view rolesum # Show an RBAC access matrix for server resources kubectl access-matrix # Review access to cluster-scoped resources kubectl access-matrix --namespace default # Review access to namespaced resources in 'default' # RBAC Lookup by subject (user/group/serviceaccount) name kubectl rbac-tool lookup kubectl rbac-tool lookup system:masters SUBJECT | SUBJECT TYPE | SCOPE | NAMESPACE | ROLE +----------------+--------------+-------------+-----------+---------------+ system:masters | Group | ClusterRole | | cluster-admin kubectl rbac-tool lookup system:nodes # eks:node-bootstrapper kubectl rbac-tool lookup system:bootstrappers # eks:node-bootstrapper kubectl describe ClusterRole eks:node-bootstrapper # RBAC List Policy Rules For subject (user/group/serviceaccount) name kubectl rbac-tool policy-rules kubectl rbac-tool policy-rules -e '^system:.*' # Generate ClusterRole with all available permissions from the target cluster kubectl rbac-tool show # Shows the subject for the current context with which one authenticates with the cluster kubectl rbac-tool whoami {Username: "kubernetes-admin", UID: "aws-iam-authenticator:911283.....:AIDA5ILF2FJ......", Groups: ["system:masters", "system:authenticated"], Extra: {accessKeyId: ["AKIA5ILF2FJ....."], arn: ["arn:aws:iam::911283....:user/admin"], canonicalArn: ["arn:aws:iam::911283....:user/admin"], principalId: ["AIDA5ILF2FJ....."], sessionName: [""]}} # Summarize RBAC roles for subjects : ServiceAccount(default), User, Group kubectl rolesum -h kubectl rolesum aws-node -n kube-system kubectl rolesum -k User system:kube-proxy kubectl rolesum -k Group system:masters # [터미널1] A tool to visualize your RBAC permissions kubectl rbac-view INFO[0000] Getting K8s client INFO[0000] serving RBAC View and http://localhost:8800 ## 이후 해당 작업용PC 공인 IP:8800 웹 접속 echo -e "RBAC View Web http://$(curl -s ipinfo.io/ip):8800"
RBAC 관련 내용을 한눈에 확인할 수 있습니다.
출처 - aews 스터디 박준희님
EKS의 인증/인가 흐름도의 핵심은 인증은 AWS IAM, 인가는 K8S RBAC에서 처리한다 입니다.
출처 - https://docs.aws.amazon.com/eks/latest/userguide/cluster-auth.html
- kubectl 명령 → aws eks get-token → EKS Service endpoint(STS)에 토큰 요청 ⇒ 응답값 디코드(Pre-Signed URL 이며 GetCallerIdentity..) - 링크
STS Security Token Service : AWS 리소스에 대한 액세스를 제어할 수 있는 임시 보안 자격 증명(STS)을 생성하여 신뢰받는 사용자에게 제공할 수 있음
https://ap-northeast-2.console.aws.amazon.com/cloudtrail/home?region=ap-northeast-2#/events?EventSource=sts.amazonaws.comAWS CLI 버전 1.16.156 이상에서는 별도 aws-iam-authenticator 설치 없이 aws eks get-token으로 사용 가능 - Docs
# sts caller id의 ARN 확인 aws sts get-caller-identity --query Arn "arn:aws:iam::<자신의 Account ID>:user/admin" # 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 # Get a token for authentication with an Amazon EKS cluster. # This can be used as an alternative to the aws-iam-authenticator. aws eks get-token help # 임시 보안 자격 증명(토큰)을 요청 : expirationTimestamp 시간경과 시 토큰 재발급됨 aws eks get-token --cluster-name $CLUSTER_NAME | jq aws eks get-token --cluster-name $CLUSTER_NAME | jq -r '.status.token'
2. kubectl의 Client-Go 라이브러리는 Pre-Signed URL을 Bearer Token으로 EKS API Cluster Endpoint로 요청을 보냅니다.
3. EKS API는 Token Review 를 Webhook token authenticator에 요청 ⇒ (STS GetCallerIdentity 호출) AWS IAM 해당 호출 인증 완료 후 User/Role에 대한 ARN 반환
# tokenreviews api 리소스 확인 kubectl api-resources | grep authentication tokenreviews authentication.k8s.io/v1 false TokenReview # 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 인가를 처리합니다.
- 해당 IAM User/Role 확인이 되면 k8s aws-auth configmap에서 mapping 정보를 확인하게 됩니다.
- aws-auth 컨피그맵에 'IAM 사용자, 역할 arm, K8S 오브젝트' 로 권한 확인 후 k8s 인가 허가가 되면 최종적으로 동작 실행을 합니다.
- 참고로 EKS를 생성한 IAM principal은 aws-auth 와 상관없이 kubernetes-admin Username으로 system:masters 그룹에 권한을 가짐 - 링크
# Webhook api 리소스 확인 kubectl api-resources | grep Webhook mutatingwebhookconfigurations admissionregistration.k8s.io/v1 false MutatingWebhookConfiguration validatingwebhookconfigurations admissionregistration.k8s.io/v1 false ValidatingWebhookConfiguration # validatingwebhookconfigurations 리소스 확인 kubectl get validatingwebhookconfigurations NAME WEBHOOKS AGE eks-aws-auth-configmap-validation-webhook 1 50m vpc-resource-validating-webhook 2 50m aws-load-balancer-webhook 3 8m27s kubectl get validatingwebhookconfigurations eks-aws-auth-configmap-validation-webhook -o yaml | kubectl neat | yh # aws-auth 컨피그맵 확인 kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh apiVersion: v1 kind: ConfigMap metadata: name: aws-auth namespace: kube-system data: mapRoles: | - groups: - system:bootstrappers - system:nodes rolearn: arn:aws:iam::91128.....:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-1OS1WSTV0YB9X username: system:node:{{EC2PrivateDNSName}} #---<아래 생략(추정), ARN은 EKS를 설치한 IAM User , 여기 있었을경우 만약 실수로 삭제 시 복구가 가능했을까?--- mapUsers: | - groups: - system:masters userarn: arn:aws:iam::111122223333:user/admin username: kubernetes-admin # EKS 설치한 IAM User 정보 >> system:authenticated는 어떤 방식으로 추가가 되었는지 궁금??? kubectl rbac-tool whoami {Username: "kubernetes-admin", UID: "aws-iam-authenticator:9112834...:AIDA5ILF2FJIR2.....", Groups: ["system:masters", "system:authenticated"], ... # 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 Name: cluster-admin Labels: kubernetes.io/bootstrapping=rbac-defaults Annotations: rbac.authorization.kubernetes.io/autoupdate: true Role: Kind: ClusterRole Name: cluster-admin Subjects: Kind Name Namespace ---- ---- --------- Group system:masters # cluster-admin 의 PolicyRule 확인 : 모든 리소스 사용 가능! kubectl describe clusterrole cluster-admin Name: cluster-admin Labels: kubernetes.io/bootstrapping=rbac-defaults Annotations: rbac.authorization.kubernetes.io/autoupdate: true PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- *.* [] [] [*] [*] [] [*] # 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
데브옵스 신입 사원을 위한 myeks-bastion-2에 설정 해보기
01. [myeks-bastion] testuser 사용자 생성
# testuser 사용자 생성 aws iam create-user --user-name testuser # 사용자에게 프로그래밍 방식 액세스 권한 부여 aws iam create-access-key --user-name testuser { "AccessKey": { "UserName": "testuser", "AccessKeyId": "AKIA5ILF2##", "Status": "Active", "SecretAccessKey": "TxhhwsU8##", "CreateDate": "2023-05-23T07:40:09+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::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
02. [myeks-bastion-2] testuser 자격증명 설정 및 확인
# get-caller-identity 확인 >> 왜 안될까요? aws sts get-caller-identity --query Arn # 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::911283464785:user/testuser" # kubectl 시도 >> testuser도 AdministratorAccess 권한을 가지고 있는데, 실패 이유는? kubectl get node -v6 ls ~/.kube
03. [myeks-bastion] testuser에 system:masters 그룹 부여로 EKS 관리자 수준 권한 설정
# 방안1 : eksctl 사용 >> iamidentitymapping 실행 시 aws-auth 컨피그맵 작성해줌 # 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
04. [myeks-bastion-2] 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 # rbac-tool 후 확인 >> 기존 계정과 비교해보자 >> system:authenticated 는 system:masters 설정 시 따라오는 것 같은데, 추가 동작 원리는 모르겠네요??? kubectl krew install rbac-tool && kubectl rbac-tool whoami {Username: "testuser", UID: "aws-iam-authenticator:911283464785:AIDA5ILF2FJIV65KG6RBM", Groups: ["system:masters", "system:authenticated"], Extra: {accessKeyId: ["AKIA5ILF2FJIZJUZSG4D"], arn: ["arn:aws:iam::911283464785:user/testuser"], canonicalArn: ["arn:aws:iam::911283464785:user/testuser"], ...
05. [myeks-bastion] testuser 의 Group 변경(system:masters → system:authenticated)으로 RBAC 동작 확인
# 방안2 : 아래 edit로 mapUsers 내용 직접 수정 system:authenticated kubectl edit cm -n kube-system aws-auth ... # 확인 eksctl get iamidentitymapping --cluster $CLUSTER_NAME
06. [myeks-bastion-2] testuser kubectl 사용 확인
# 시도 kubectl get node -v6 kubectl api-resources -v5
07. [myeks-bastion]에서 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
08. [myeks-bastion-2] testuser kubectl 사용 확인
# 시도 kubectl get node -v6 kubectl api-resources -v5
EC2 Instance Profile : 사용하기 편하지만, 최소 권한 부여 원칙에 위배하며 보안상 권고하지 않음 - https://malwareanalysis.tistory.com/579 → IRSA 권장
# 설정 예시 1 : eksctl 사용 시
eksctl create cluster --name $CLUSTER_NAME ... --external-dns-access --full-ecr-access --asg-access
# 설정 예시 2 : eksctl로 yaml 파일로 노드 생성 시
cat myeks.yaml | yh
...
managedNodeGroups:
- amiFamily: AmazonLinux2
iam:
withAddonPolicies:
albIngress: false
appMesh: false
appMeshPreview: false
autoScaler: true
awsLoadBalancerController: false
certManager: true
cloudWatch: true
ebs: false
efs: false
externalDNS: true
fsx: false
imageBuilder: true
xRay: false
...
# 설정 예시 3 : 테라폼
...
Service Account Token Volume Projection
: '서비스 계정 토큰'의 시크릿 기반 볼륨 대신 'projected volume' 사용Service Account Token Volume Projection
기능을 사용하면 이러한 부족한 점들을 해결할 수 있습니다.Bound Service Account Token Volume 바인딩된 서비스 어카운트 토큰 볼륨
Kubernetes v1.22 [stable]
프로젝티드 볼륨은 세 가지로 구성된다.
kube-apiserver
로부터 TokenRequest API를 통해 얻은 서비스어카운트토큰(ServiceAccountToken)
. 서비스어카운트토큰은 기본적으로 1시간 뒤에, 또는 파드가 삭제될 때 만료된다. 서비스어카운트토큰은 파드에 연결되며 kube-apiserver를 위해 존재한다.컨피그맵(ConfigMap)
.DownwardA
[projected](https://kubernetes.io/docs/concepts/storage/volumes/#projected)
Volume to mount several existing volume sources into the same directory. Currently, secret
, configMap
, downwardAPI
, and serviceAccountToken
volumes can be projected.serviceAccountToken
is not a volume type.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 속성 사용해보기
# 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
k8s api 접근 단계
AuthN → AuthZ → Admisstion Control 권한이 있는 사용자에 한해서 관리자(Admin)가 특정 행동을 제한(validate) 혹은 변경(mutate) - 링크 Slack
AuthN & AuthZ - MutatingWebhook - Object schema validation - ValidatingWebhook → etcd
출처 - https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/
Admission Control도 Webhook으로 사용자에게 API가 열려있고, 사용자는 자신만의 Admission Controller를 구현할 수 있으며,
이를 Dynamic Admission Controller라고 부르고, 크게 MutatingWebhook 과 ValidatingWebhook 로 나뉩니다.
MutatingWebhook
은 사용자가 요청한 request에 대해서 관리자가 임의로 값을 변경하는 작업입니다.
ValidatingWebhook
은 사용자가 요청한 request에 대해서 관리자기 허용을 막는 작업입니다.
JWT
: Bearer type - JWT(JSON Web Token) X.509 Certificate의 lightweight JSON 버전
.
으로 합쳐지게 됩니다.OIDC
: 사용자를 인증해 사용자에게 액세스 권한을 부여할 수 있게 해주는 프로토콜 ⇒ [커피고래]님 블로그 OpenID Connect - 링크
iss
: 토큰 발행자sub
: 사용자를 구분하기 위한 유니크한 구분자email
: 사용자의 이메일iat
: 토큰이 발행되는 시간을 Unix time으로 표기한 것exp
: 토큰이 만료되는 시간을 Unix time으로 표기한 것aud
: ID Token이 어떤 Client를 위해 발급된 것인지.The IAM service uses these public keys to validate the token. The workflow is as follows - JWT(JSON Web Token), JWKS(JSON Web Key Set)
출처 - https://aws.amazon.com/ko/blogs/containers/diving-into-iam-roles-for-service-accounts/
출처 - https://tech.devsisters.com/posts/pod-iam-role/
# 파드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 get pod
kubectl describe pod
>
# 로그 확인
kubectl logs eks-iam-test1
>
# 파드1 삭제
kubectl delete pod eks-iam-test1
Topic - Kubernetes Service Accounts
# 파드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
# 확인
kubectl get pod
kubectl describe pod
# aws 서비스 사용 시도
kubectl exec -it eks-iam-test2 -- aws s3 ls
# 서비스 어카운트 토큰 확인
SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $SA_TOKEN
# jwt 혹은 아래 JWT 웹 사이트 이용
jwt decode $SA_TOKEN --json --iso8601
...
#헤더
{
"alg": "RS256",
"kid": "1a8fcaee12b3a8f191327b5e9b997487ae93baab"
}
# 페이로드 : OAuth2에서 쓰이는 aud, exp 속성 확인! > projectedServiceAccountToken 기능으로 토큰에 audience,exp 항목을 덧붙힘
## iss 속성 : EKS OpenID Connect Provider(EKS IdP) 주소 > 이 EKS IdP를 통해 쿠버네티스가 발급한 토큰이 유요한지 검증
{
"aud": [
"https://kubernetes.default.svc" # 해당 주소는 k8s api의 ClusterIP 서비스 주소 도메인명, kubectl get svc kubernetes
],
"exp": 1716619848,
"iat": 1685083848,
"iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/F6A7523462E8E6CDADEE5D41DF2E71F6",
"kubernetes.io": {
"namespace": "default",
"pod": {
"name": "eks-iam-test2",
"uid": "10dcccc8-a16c-4fc7-9663-13c9448e107a"
},
"serviceaccount": {
"name": "default",
"uid": "acb6c60d-0c5f-4583-b83b-1b629b0bdd87"
},
"warnafter": 1685087455
},
"nbf": 1685083848,
"sub": "system:serviceaccount:default:default"
}
# 파드2 삭제
kubectl delete pod eks-iam-test2
Topics - amazon-eks-pod-identity-webhook
eksctl create iamserviceaccount
command creates:# 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>
# 파드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에 없던 내용이 추가됨!!!!!
# Pod Identity Webhook은 mutating webhook을 통해 아래 Env 내용과 1개의 볼륨을 추가함
kubectl get pod eks-iam-test3
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 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
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
STS Assumerole 결과
버킷명 가져오기 실행 결과
IRSA를 가장 취약하게 사용하는 방법 : 정보 탈취 시 키/토큰 발급 약용 가능 - 링크
# AWS_WEB_IDENTITY_TOKEN_FILE 토큰 값 변수 지정 IAM_TOKEN=$(kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token) echo $IAM_TOKEN # ROLE ARN 확인 후 변수 직접 지정 eksctl get iamserviceaccount --cluster $CLUSTER_NAME ROLE_ARN=<각자 자신의 ROLE ARN> ROLE_ARN=arn:aws:iam::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1W8J3Q0GAMA6U # assume-role-with-web-identity STS 임시자격증명 발급 요청 aws sts assume-role-with-web-identity --role-arn $ROLE_ARN --role-session-name mykey --web-identity-token $IAM_TOKEN | jq { "Credentials": { "AccessKeyId": "ASIA5ILF2FJIZLOCB36X", "SecretAccessKey": "IvuD2BEt/TtScyv6uq3U5mF3RStuxya5gHydlz2Z", "SessionToken": "IQoJb3JpZ2luX2VjELH//////////wEaDmFwLW5vcnRoZWFzdC0yIkYwRAIgFsxs4rNyxuWTgqIuQWONuU8lkb+S1E9rvY4YLMtAR0ACIAfRrLYpisS1Ql+2agL0meQ+iy08bLv992tTCr0vZkqVKvkECOr//////////wEQARoMOTExMjgzNDY0Nzg1IgwmJn8sjNaBM0F+L/gqzQSnX6M6BlgzqiX3Sob0R8QZo0TEumVqCsLopwdHBzIZL6VU3kFaeqIpUh9uuZ+JaR7MlFKS7FYhIq7r+fMh5f8toWojtyKwLjT9eN2yi5A5ZfWahln1MIu9fv/dASR4USMxLtbMHOGpx3BE/pCHhV+u85z/LoHSlVNaF5IrQiCXbo3f9DrJ0kHQZuQY3N0pFDlGzXuv5hCedGlJQU2IzUcmW5kHQ/jNyIf+xEO2nTSksna5iE3r9TNnO6b6v8gZc5zDUs3fGfJfP4QwKjRXOUDMnydJ9LzME+mYoYHObdeCqncGuGwJ3GIXx9qw9ZABXuAlvATuLROaYkeLsXuote1UOqPILxvETvWo1VHA2f0hYL9ZFDSF3j6yGU+GEHbFGoMRVaVFqdbpF1bMEbC9FlmR5AWbAkkYZs8kfXRcObfpZxLB4vQBXeqj9OU/yDPvvNA+CgOoA5HFI0SjeFQHOVB7S5KVm6CAOKtoMIzTeKnKpKmN07dqJzvGrpwtNMh/GhCouunvbgNG7jF/TM3jgAniDxoD9IzCQMICNgxdioOFnB6Oe1AzMkKui53MP8Af/lcDiUKTIUNrKxdm0719kuXqR88coihzrLGkdA7Eb5Gg/gCnk+SzPu7Wu5xZaYXBe6Xqh0/c1dKsN1YQLOANo5aF3B2RCGJFDwr78rOUvNWxXs84us/Uz5k6LIGZseVzZcGh5U9ztJqhzoKFvnphbtU8b1Ctg/pTrF8EnjLGR0s4QggdrsW1b7vznisMwFrHh0F+FhSy7ldvfeXmwpQgMNP666MGOpsBmNam9fm/qK6EjmllDDvf6mR9l99Vop++V2vf1GoM4ru8/TfgP25+B1N9gEbnRuhMTxQrN6VGcyaNNlKBkwxtAs+aikBcvjk3cm0jPZmiQntTkNtBw92NAJwbRmhSIQynznxN7I1FnFukP06J9V9MiuhsJGXpGXi0kWOOQnqb9u2YraRLKJdTqVfv5dGt7aM67PeJrr/0v2YKU0M=", "Expiration": "2023-06-03T09:44:03+00:00" }, "SubjectFromWebIdentityToken": "system:serviceaccount:default:my-sa", "AssumedRoleUser": { "AssumedRoleId": "AROA5ILF2FJI7UWTLJWKW:mykey", "Arn": "arn:aws:sts::911283464785:assumed-role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1W8J3Q0GAMA6U/mykey" }, "Provider": "arn:aws:iam::911283464785:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/8883A42CB049E2FA9B642086E7021450", "Audience": "sts.amazonaws.com" }
실습 내용과 같이 Token을 탈취 당하게 되면, STS 임시자격증명 발급을 요청하여 발급받을 수 있습니다.