교육, 커뮤니티 후기/AEWS 스터디

AEWS 스터디 6주차 - EKS Security

Somaz 2023. 6. 3. 20:59
728x90
반응형

출처 : 스터디원 이현수님

 

Overview

CloudNet@ AEWS 스터디 6주차는 EKS Security 이다.

 


 

0. 실습 환경 배포

 


 

Amazon EKS (myeks) 윈클릭 배포 

(bastion ec2 2대, eksctl 0.143.0 - EKS 1.27 support, IMDSv2 by default - 링크) & 기본 설정

# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/eks-oneclick5.yaml

# CloudFormation 스택 배포
예시) aws cloudformation deploy --template-file eks-oneclick5.yaml --stack-name myeks --parameter-overrides KeyName=somaz-key SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32  MyIamUserAccessKeyID=AKIA5... MyIamUserSecretAccessKey='CVNa2...' ClusterBaseName=myeks --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text
3.35.18.59

# 작업용 EC2 SSH 접속
ssh -i ~/.ssh/somaz-key.pem ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)

스택 확인

 

기본 설정

# default 네임스페이스 적용
kubectl ns default

# (옵션) context 이름 변경
NICK=<각자 자신의 닉네임>
NICK=somaz
kubectl ctx
somaz@myeks.ap-northeast-2.eksctl.io

kubectl config rename-context admin@myeks.ap-northeast-2.eksctl.io $NICK


# ExternalDNS
MyDomain=<자신의 도메인>
echo "export MyDomain=<자신의 도메인>" >> /etc/profile
MyDomain=somaz.link
echo "export MyDomain=somaz.link" >> /etc/profile
MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)
echo $MyDomain, $MyDnzHostedZoneId
somaz.link, /hostedzone/Z03204211VEUZG9O0RLE5

curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml
MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -

# AWS LB Controller
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=$CLUSTER_NAME \
  --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller

# 노드 IP 확인 및 PrivateIP 변수 지정
N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})
N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2b -o jsonpath={.items[0].status.addresses[0].address})
N3=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})
echo "export N1=$N1" >> /etc/profile
echo "export N2=$N2" >> /etc/profile
echo "export N3=$N3" >> /etc/profile
echo $N1, $N2, $N3
192.168.1.143, 192.168.2.151, 192.168.3.8

# 노드 보안그룹 ID 확인
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values='*ng1*' --query "SecurityGroups[*].[GroupId]" --output text)
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.0/24

# 워커 노드 SSH 접속
for node in $N1 $N2 $N3; do ssh ec2-user@$node hostname; done

 

프로메테우스 & 그라파나(admin / prom-operator) 설치

  • 대시보드 추천 15757 17900 15172
# 사용 리전의 인증서 ARN 확인
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo $CERT_ARN

# repo 추가
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# 파라미터 파일 생성
cat <<EOT > monitor-values.yaml
prometheus:
  prometheusSpec:
    podMonitorSelectorNilUsesHelmValues: false
    serviceMonitorSelectorNilUsesHelmValues: false
    retention: 5d
    retentionSize: "10GiB"

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - prometheus.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'

grafana:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - grafana.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'

defaultRules:
  create: false
kubeControllerManager:
  enabled: false
kubeEtcd:
  enabled: false
kubeScheduler:
  enabled: false
alertmanager:
  enabled: false
EOT

# 배포
kubectl create ns monitoring
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 45.27.2 \
--set prometheus.prometheusSpec.scrapeInterval='15s' --set prometheus.prometheusSpec.evaluationInterval='15s' \
-f monitor-values.yaml --namespace monitoring

# Metrics-server 배포
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

 


 

1. K8S 인증/인가

2023.05.10 - [Container Orchestration/Kubernetes] - Kuberntes Service Account란?

 

Kuberntes Service Account란?

Overview 오늘은 Kubernetes Service Account에 대해서 공부해보려고 한다. 먼저 간단하게 User Account와 Service Account에 대해 설명해보자면, 사용자 어카운트는 사람을 위한 것이다. 서비스 어카운트는 파드

somaz.tistory.com

 


 

K8S(API 접근) 인증/인가 소개

  • 서비스 어카운트(Service Account)
  • API 서버 사용 : kubectl(config, 다수 클러스터 관리 가능), 서비스 어카운트, https(x.509 Client Certs) ⇒ X.509 발음을 어떻게 하시나요? - 링크
  • API 서버 접근 과정 : 인증 → 인가 → Admission Control(API 요청 검증, 필요 시 변형 - 예. ResourceQuota, LimitRange) - 참고

출처 : 김태민님 기술 블로그

인증(Authentication)

  • X.509 Client Certs : kubeconfig 에 CA crt(발급 기관 인증서) , Client crt(클라이언트 인증서) , Client key(클라이언트 개인키) 를 통해 인증
  • kubectl : 여러 클러스터(kubeconfig)를 관리 가능 - contexts 에 클러스터와 유저 및 인증서/ 참고
  • Service Account : 기본 서비스 어카운트(default) - 시크릿(CA crt 와 token)

출처 : 김태민님 기술 블로그

 

인가(Authorization)

  • 인가 방식 : RBAC(Role, RoleBinding), ABAC, Webhook, Node Authorization
  • RBAC : 역할 기반의 권한 관리, 사용자와 역할을 별개로 선언 후 두가지를 조합(binding)해서 사용자에게 권한을 부여하여 kubectl or API로 관리 가능
    • Namespace/Cluster - Role/ClusterRole, RoleBinding/ClusterRoleBinding, Service Account
    • Role(롤) - (RoleBinding 롤 바인딩) - Service Account(서비스 어카운트) : 롤 바인딩은 롤과 서비스 어카운트를 연결
    • Role(네임스페이스내 자원의 권한) vs ClusterRole(클러스터 수준의 자원의 권한)

출처 : 김태민님 기술 블로그

.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 을 사용하려면 여러 개의 컨텍스트 중 하나를 선택
cat .kube/config
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeE1Ea3dNVEl5TkRjMU1sb1hEVE14TURnek1ESXlORGMxTWxvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTG1qCml1cW11UUxWTXN6UE83VUpxTkdCNHdXQ3RnYTl1cFcwYUVNVmUrZm41YXZZMWxUWUZqZjBCb1VlQXhOWmc5YXoKRU1FZVJMWCt1ZzhqTDNETjhCTzEwdUEwSzF6b3ZpQVVtbDlCU2dNWU9FOHpUMFJsV2tvcnBtVDNGai9td1lJagpEemRxYld6MlpuQ1FoQ3dvYURzdlpoUVNMRTh6dnFwU0F5c0hNSUdzV3J0anI4aC9QaW52dnF5bUo0UlFhWlY3CnNuZ0lzMDBqakdGbFowcUVueWZMSGtBeHpjSktVUnJHamFsZm1RdmZ3WkZ2Z0pjam5rSG9jb3g0T0JKUEh0N2EKdFE1OEpBTTF3cng0b3pFSjh1MExsa21LOWYwWGVzQmRGeUhFamZ1elhTYml0Q09sbTR1Q1o3UkVRVmRjZWk1SAo3Tjg1M1RjbWRIck9tRkQwZVpVQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZLRVYvZFNBUkJteVhyLytxUkVnb1h5QUg3UTZNQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFDQ0M4cDRQRmdoVVFDbW5weWk1SDAxYVRNYXp0Si9pdkw0amxiMWJNdXc3ZjJNZmM0UQpDRGw2UWVNd2FpYk9raHNrVGhMTEtRckQwQ0xqWXNCSy9iNVhQSTNtMmoxS0cvc1ExREFPL0hNdmt6RmkzUDdrCmJHOUErdWk1YXJPREs5eWJFQ2NtUG5adnVmWkFSY3d3dkp1ZGRMUy9QZERkOW9ZVGgzV3FQMjloVk9tZnZUS3kKNFhzeVg0cHk5dzVTNkYxaGVpUE9odnprMWRzNWFZZENBR1E5R0ZRb3BIQSs1Wm9YOWJjazFuN0FiMDVua0UrUQprMTVnc1VhQWFEMGVGUlRHY0tRTzM5dW1ZdkxhVnUrL20xcDFFRWU0YWdLdktvUGZlZ1VJTFQ0dGtLdjFwcWYvCmhIZldDUFo3Vy9ldmRZODI5WmtudE1HWHZ5QXZaWHFUZE1KZwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
    server: https://192.168.100.10:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    namespace: default
    user: kubernetes-admin
  name: admin@k8s
current-context: admin@k8s
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJUzFnbmhwU0N5Q2d3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TVRBNU1ERXlNalEzTlRKYUZ3MHlNakE1TURFeU1qUTNOVFZhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQW52eXoxc1R1SXRpKzE3WmQKVVRXTFVxMUxIL2VJN01lMkI0K2ZNZlhKSStlM2xCVnp5RXpIV0ZOR1phM2JYbkYvS0VJaDJRcmpOcXh0bGswSgpIOW83dUtVZmRyVjhNL3IzZmxidUN1VG9lZnN3UFROQmJhbGladzVPRXl0VWV6V3ZxK3VUZzFmeExZVUl6Zk4xCldxMzhiU2pjYlhQa3Q3UWJZVThqUEpMMmlKalBlbVFRN1FnTW9pUmlsNXM2TzRCZnNYbzNCbDNrdUY0VDlCK1MKVzE2VmpQTnRMQ0pxQW1ENEt1ZWdBcWl3RHdDNFVScjhNbDhJaHJmL2FzT2JTZnVqTG5HL1Npd2V6dnJ4bHJnUgo0QVBlNjFSOU1RZFFjaldsT1Z2TXQrSXhlSnlrbWdmeHJsNFJmbytFOWVNK0VTNzFHaVhnQmtycFp0NGxQWURsClllSVZQd0lEQVFBQm8xWXdWREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JTaEZmM1VnRVFac2w2Ly9xa1JJS0Y4Z0IrMApPakFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBa0ZqdDJPNW5ZQUkxRHRrZnh6R1RPbFdGT1F3b3FKelBHQXJSCmRoTnFXL3JjUlhyYkgzZ3FHaXF4cmQ2anczblJiYThCRWxOazE0YUtYWGVYRnU0U0YyYTJCY3RzKzhkNE9VSkwKeU1pUVBpN0g2Q3RrQ0o2QzRCZDU4Vk5XaVM0YVg4b0ExQWloZWp0cURRc2U2MCtna2JoSlJwdnM0WGRVUkNTdgpFL3NqZWgvc1JIVjBJYWNrNzlTVEduSUdlVVUrbUxwVlF1bHZkd1lkVDhXK08zMkpRbFk1Z3pTZllFMkI2YjB4Ci9TK1dORU9QTzhhaTlmQkQ5cWJ1dWdRd2wzSkNYT005amZLV1gzOTBZZzhYcWhndEhuR0JDdlcwbjQxY0ZLUDgKQVFFdXRnbDNhQ0ZibWZFZ2Z3cWlUVFc3R3EzSklZSTZrZ3EwNGxUbVdKa1gvQnZmaXc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
    client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBbnZ5ejFzVHVJdGkrMTdaZFVUV0xVcTFMSC9lSTdNZTJCNCtmTWZYSkkrZTNsQlZ6CnlFekhXRk5HWmEzYlhuRi9LRUloMlFyak5xeHRsazBKSDlvN3VLVWZkclY4TS9yM2ZsYnVDdVRvZWZzd1BUTkIKYmFsaVp3NU9FeXRVZXpXdnErdVRnMWZ4TFlVSXpmTjFXcTM4YlNqY2JYUGt0N1FiWVU4alBKTDJpSmpQZW1RUQo3UWdNb2lSaWw1czZPNEJmc1hvM0JsM2t1RjRUOUIrU1cxNlZqUE50TENKcUFtRDRLdWVnQXFpd0R3QzRVUnI4Ck1sOElocmYvYXNPYlNmdWpMbkcvU2l3ZXp2cnhscmdSNEFQZTYxUjlNUWRRY2pXbE9Wdk10K0l4ZUp5a21nZngKcmw0UmZvK0U5ZU0rRVM3MUdpWGdCa3JwWnQ0bFBZRGxZZUlWUHdJREFRQUJBb0lCQUQzOHFPR0R4cFV2akxqdQpFVlFvWERuUDl3cHZxS01vK24vWUwybDdPd0VVeHk2bGJvOFo0RjgvbUtMc05pdU1kTmR0Y1dUK0tiaVhZZUxJCkJsYTA3N1ArTFZaTFRERzRGK2JhWGRWQmlxS0VuVG8vVWJNLzUyM20xZW9EYXR6ZkFhODJHajJMZkMwVFFXdUwKRUtaYVQ2RC8zWEdQVGcyUjIxc0ZUK2UrSlFEOGRnc25oNE9vVlQrTkRacC9kU0JHYXZNQTFZUmo0bFhwY1U5RAo5bW15ckxRZFlRcE56K1U4cGZKdHhIcXlGSWhOakZmK0JkNHdRdEhrN3NOODE4Um9JalZHV3RYeGVhZXFOMXVtCnFlWEhFNHVDRG5tYS9qTElLLzBRaWlMZTZ1WGVTMk1udG1UUjJ1d0paOWh5V3NsYnlTb2oyQmNONVBaaHpGK3kKMUtyZEFZRUNnWUVBenNEeUFtZ1dUUXI5M083ZnlSR1U5azBad01LRFVSK25Lb0xQcUNhSmxQeE4xaG1zTkJmWApKWURsZ3cwVTk5R1lmRGJZUTdjS3BaRE8xWHZpWTI4K1UxY21nM2xVMVFVOTdFR0N3ejVxMnNjUFY0SDBhZmxnCmNUQko5dGo1ZTkzVS9sVDFpd0M1eEFONlpjektTbzhYSytNQ29nUkEyeEFZZjFJZnJTZmhoVzBDZ1lFQXhOc2kKQ2oxS29FQzV0TjlEaW41eFQzMUVBTjlwVmtONkZlcy9nZC9JSFREWXJLSytaMnNpVVNhR1NyaHYwZkc1ZGVwagpIMjdEeVF6cW1aUUlpaE44cFB5TzRSOXMya21la3RISUZqMjRnSUpQZDNzS3BaS1QwQjJmZUErTXVCOFlsclRGCk0ycTJ2V1JHeHFmMERMZmpWNm5JVkZkQ1hJWFZLMjlRcWprdkZkc0NnWUFmUGRxVDhJU0dLY1lJajNQelh4dkMKU0E0L0tXVk1hZHNKdW5DRWVTWkxCQUVDL0NnZ1N3WHduZFNRZy9hS0ovckJza3ZsbDVBZFNvOW1oT3pGbDdhMApRelFIbzlya3dZRUU1VFZNS1c5ZUZieEV2ZGRmK0JYUnBMbFllcHJnVTdudW9Jbmw4anNmMm1LeFpVdWdEcFV5CnhYL05XWlV2UlBSZXNOc21nQ004MVFLQmdRQ0xSOFFJM0o3TlRaNVhNOVJVeSt1ZDR6SlhMN3NXMXIwdGZ2bTcKQ1R0TU5BQkovUWVjb25kd1ZVS1U0WFAwWmdQalF3Z0krRlM4RGxCNmd2dWJ2ZmZsdisvVHBtbGM5Tk9tYTVrVwo2MnA4T2piQmdhUGh6QmliR2lwM1J3RTRVSUFVT1NpQm5aSlg0L2dUbkVlWExCQkZPUkpOWWtQSXRNUkRiQW4xCnRtbnpHd0tCZ0J3NHhLanNEUUozcCtxWW50cTdtVzhLS2hPWTFMRWczOVJ4Snd1aEord0VSZUh5TGhIcEU5SFkKUndxbUVCYjdvY2dDcmV6bWR5WndUSXZkMGEzaStBbWpucTd1QU1DUFpNUjU0a2FkNUpmZmVib0FzbXcwSW5aeApvVGltQXNya3BmRlVxZzZsSVBIMEtuUEVTVWQxQlJLS2I5dTUzTWpwZEZiVkhWZVZhVEtlCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

 


 

실습 환경

출처 : CloudNet@ 스터디

 

  • 쿠버네티스에 사용자를 위한 서비스 어카운트(Service Account, SA)를 생성 : dev-k8s, infra-k8s
  • 사용자는 각기 다른 권한(Role, 인가)을 가짐 : dev-k8s(dev-team 네임스페이스 내 모든 동작) , infra-k8s(dev-team 네임스페이스 내 모든 동작)
  • 각각 별도의 kubectl 파드를 생성하고, 해당 파드에 SA 를 지정하여 권한에 대한 테스트를 진행

 


 

네임스페이스와 서비스 어카운트 생성 후 확인

  • 파드 기동 시 서비스 어카운트 한 개가 할당되며, 서비스 어카운트 기반 인증/인가를 함, 미지정 시 기본 서비스 어카운트가 할당
  • 서비스 어카운트에 자동 생성된 시크릿에 저장된 토큰으로 쿠버네티스 API에 대한 인증 정보로 사용 할 수 있다. ← 1.23 이전 버전의 경우에만 해당

설치되는 EKS 버전은 1.24 이다.

따라서, SA를 생성해도 Secret이 자동으로 생성되지 않는다.

 

 

# 네임스페이스(Namespace, NS) 생성 및 확인
kubectl create namespace dev-team
kubectl create ns infra-team

# 네임스페이스 확인
kubectl get ns
NAME              STATUS   AGE
default           Active   14m
dev-team          Active   15s
infra-team        Active   14s
kube-node-lease   Active   14m
kube-public       Active   14m
kube-system       Active   14m

# 네임스페이스에 각각 서비스 어카운트 생성 : serviceaccounts 약자(=sa)
k version --short | grep Server
Server Version: v1.24.13-eks-0a21954

# 쿠버네티스 버전이 1.24 이기 때문에 아래와 같이 serviceaccount와 secret RBAC를 생성해준다.
# kubectl create sa dev-k8s -n dev-team (x)
# kubectl create sa infra-k8s -n infra-team (x)

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: dev-k8s
  namespace: dev-team
---
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
  name: dev-k8s
  namespace: dev-team
  annotations:
    kubernetes.io/service-account.name: "dev-k8s"
EOF

k apply -f dev-team-serviceaccount.yaml -n dev-team
serviceaccount/dev-k8s unchanged
secret/dev-k8s unchanged

# 서비스 어카운트 정보 확인
kubectl get sa -n dev-team
NAME      SECRETS   AGE
default   0         34m
dev-k8s   0         4m17s

kubectl get secret -n dev-team
NAME      TYPE                                  DATA   AGE
dev-k8s   kubernetes.io/service-account-token   3      4m22s

kubectl get sa dev-k8s -n dev-team -o yaml | yh
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"dev-k8s","namespace":"dev-team"}}
  creationTimestamp: "2023-05-29T14:58:24Z"
  name: dev-k8s
  namespace: dev-team
  resourceVersion: "10585"
  uid: dae5f46b-e130-4759-87a4-e9a1fd5bc774

 

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: infra-k8s
  namespace: infra-team
---
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
  name: infra-k8s
  namespace: infra-team
  annotations:
    kubernetes.io/service-account.name: "infra-k8s"
EOF
  
k apply -f infra-team-serviceaccount.yaml -n infra-team
serviceaccount/infra-k8s created
secret/infra-k8s created  

kubectl get sa -n infra-team
NAME        SECRETS   AGE
default     0         42s
infra-k8s   0         16s

k get secret -n infra-team
NAME        TYPE                                  DATA   AGE
infra-k8s   kubernetes.io/service-account-token   3      7s

kubectl get sa infra-k8s -n infra-team -o yaml | yh
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"infra-k8s","namespace":"infra-team"}}
  creationTimestamp: "2023-05-29T15:08:06Z"
  name: infra-k8s
  namespace: infra-team
  resourceVersion: "13024"
  uid: 743b8888-5808-4c86-81e9-749202fa1f4a

 

(심화 참고) dev-k8s 서비스어카운트의 토큰 정보 확인

출처 : https://coffeewhale.com/kubernetes/authentication/http-auth/2020/05/03/auth02/

# dev-k8s 서비스어카운트의 토큰 정보 확인?? 안된다.
DevTokenName=$(kubectl get sa dev-k8s -n dev-team -o jsonpath="{.secrets[0].name}")
DevToken=$(kubectl get secret -n dev-team $DevTokenName -o jsonpath="{.data.token}" | base64 -d)
echo $DevToken

# 직접 sa 이름을 적어준다.
kubectl get secret -n dev-team dev-k8s -o jsonpath="{.data.token}" | base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg2dFRWNVNaeFlGcDJlR0tuRDJDWWpWaHJxbXF4clBnNnIzbjlmMktvTjgifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZXYtdGVhbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZXYtazhzIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRldi1rOHMiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJkYWU1ZjQ2Yi1lMTMwLTQ3NTktODdhNC1lOWExZmQ1YmM3NzQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGV2LXRlYW06ZGV2LWs4cyJ9.mHYqgVHJ96aeI4YwG8Jr3MZy5694TpgPb8srtvaVc32MhmUst9J4MY_Ynq_G-Nw79SA6kJz_9-A77GYUVOhLrBfseee33nDSdBRuG_-40_kD1RW_E7Ysn7353YUQr__1ipILW6pOIHlTEDPJL27f6CQ9uMKnzEvKM2nLMXboQVp2V3PWL_CO-N-1oBMrKLYFp6yMSTWc9ZCZrksTntaFG-qNWwPmWvb0gDqPPkgEmhxT4PKUycwaRx0h7EOQIheoD5XXAtTEsad7iKya06rh5AogG5Ci_bTC87Tdds4MV-kRNhPQ-0-GstIk6uApToEAdh5E8GjVrnBkkDAK4wTikQ

 


 

서비스 어카운트를 지정하여 파드 생성 후 권한 테스트

# 각각 네임스피이스에 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

k get po -n dev-team
NAME          READY   STATUS    RESTARTS   AGE
dev-kubectl   1/1     Running   0          5s

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

k get po -n infra-team
NAME            READY   STATUS    RESTARTS   AGE
infra-kubectl   1/1     Running   0          7s

# 확인
kubectl get pod -A
kubectl get pod -A
NAMESPACE     NAME                                            READY   STATUS    RESTARTS   AGE
dev-team      dev-kubectl                                     1/1     Running   0          6m32s
infra-team    infra-kubectl                                   1/1     Running   0          17s
kube-system   aws-load-balancer-controller-5f99d5f58f-4jzmz   1/1     Running   0          42m
kube-system   aws-load-balancer-controller-5f99d5f58f-wt9hn   1/1     Running   0          42m
kube-system   aws-node-9ljgx                                  1/1     Running   0          46m
...

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
ca.crt  namespace  token

kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6...

kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/namespace
dev-team

kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/ca.crt
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
...
ee4Feyg0iB2jMq1pWPiUzFbhnOdBB/GkMB5bK/mg8bt9WKxAw9U/CF3NB5D7pShF
NJU=
-----END CERTIFICATE-----

# 각각 파드로 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 와 동일한 실행 명령이다!
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"

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 와 동일한 실행 명령이다!
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:infra-team:infra-k8s" cannot list resource "pods" in API group "" in the namespace "infra-team"

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)를 생성 후 서비스 어카운트 바인딩

아래에는 간단하게 admin 역할을 생성 후 바인딩 한다.

  • 롤(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
NAME            CREATED AT
role-dev-team   2023-05-29T15:25:03Z

kubectl get roles -n infra-team
NAME              CREATED AT
role-infra-team   2023-05-29T15:25:09Z

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
NAME             ROLE                 AGE
roleB-dev-team   Role/role-dev-team   13s

kubectl get rolebindings -n infra-team
NAME               ROLE                   AGE
roleB-infra-team   Role/role-infra-team   3m26s

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
NAME            READY   STATUS    RESTARTS   AGE
infra-kubectl   1/1     Running   0          31s

k2 run nginx --image nginx:1.20-alpine
pod/nginx created

k2 get pods
NAME            READY   STATUS    RESTARTS   AGE
infra-kubectl   1/1     Running   0          52s
nginx           1/1     Running   0          16s

k2 delete pods nginx
pod "nginx" deleted

# 당연히 안된다! 이유는 다들 아실거라고 생각이 된다~ 모르면 구글링!
k2 get pods -n kube-system
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:infra-team:infra-k8s" cannot list resource "pods" in API group "" in the namespace "kube-system"

k2 get nodes

# (옵션) kubectl auth can-i 로 kubectl 실행 사용자가 특정 권한을 가졌는지 확인
k1 auth can-i get pods
yes

 

리소스 삭제 

kubectl delete ns dev-team infra-team
namespace "dev-team" deleted
namespace "infra-team" deleted

 


 

2. EKS 인증/인가

사용자/애플리케이션 → k8s 사용 시 ⇒ 인증은 AWS IAM, 인가는 K8S RBAC 이다.

2023.03.30 - [AWS] - AWS IAM이란?

 

AWS IAM이란?

Overview AWS IAM에 대해서 공부해보려고 한다. AWS IAM이란? AWS Identity and Access Management(IAM)는 사용자의 AWS 리소스에 대한 액세스를 안전하게 제어하는 ​​데 도움이 되는 서비스이다. IAM을 사용하면 A

somaz.tistory.com

2023.04.19 - [Container Orchestration/Kubernetes] - Kubernetes API Server, Group / RBAC란?

 

Kubernetes API Server, Group / RBAC란?

Overview 오늘은 Kubernets API Server, Group 그리고 RBAC 에 대해서 공부해보려고 한다. Kubernetes에 개념과 간단한 실습을 하고싶다면 아래의 링크를 참고하길 바란다. 2022.03.22 - [Container Orchestration/Kubernetes]

somaz.tistory.com

 


 

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:.*'

kubectl rbac-tool show

# 
kubectl rbac-tool whoami
{Username: "kubernetes-admin",
 UID:      "aws-iam-authenticator:6118xxxxxxxx:AIDAY45ESHEKOUPOLBOMM",
 Groups:   ["system:masters",
            "system:authenticated"],
 Extra:    {accessKeyId:  ["AKIAY45ESHEKEHEB7SXG"],
            arn:          ["arn:aws:iam::6118xxxxxxxx:user/somaz"],
            canonicalArn: ["arn:aws:iam::6118xxxxxxxx:user/somaz"],
            principalId:  ["AIDAYxxxxxxxxxxxxxxxxxx"],
            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 View Web http://3.xx.xx.xx:8800

접속완료!

 


 

인증/인가 완벽 분석 해보기

핵심 : 인증은 AWS IAM, 인가는 K8S RBAC에서 처리

출처 : https://docs.aws.amazon.com/eks/latest/userguide/cluster-auth.html

 

출처 : https://awskoreamarketingasset.s3.amazonaws.com/2022 Summit/pdf/T10S1_EKS 환경을 더 효율적으로 더 안전하게.pdf

 

1.  kubectl 명령 → aws eks get-token → EKS Service endpoint(STS)에 토큰 요청한다.

  • ​STS Security Token Service : AWS 리소스에 대한 액세스를 제어할 수 있는 임시 보안 자격 증명(STS)을 생성하여 신뢰받는 사용자에게 제공할 수 있다.
  • AWS 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"
"arn:aws:iam::6118xxxxxxxx:user/somaz"

# 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
{
  "kind": "ExecCredential",
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "spec": {},
  "status": {
    "expirationTimestamp": "2023-05-29T15:52:20Z",
    "token": "k8s-aws-v1.aHR0cHMxxxx...."
  }
}

aws eks get-token --cluster-name $CLUSTER_NAME | jq -r '.status.token'
k8s-aws-v1.aHR0cHMxxx....

 

CloudTrail 이벤트기록에서 확인 가능하다.

 

2. kubectl의 Client-Go 라이브러리는 Pre-Signed URL을 Bearer Token으로 EKS API Cluster Endpoint로 요청을 보낸다.

  • ​토큰을 jwt 사이트에 복붙으로 디코드 정보 확인(HS384 → HS256) PAYLOAD 정보 확인 : 일반적인 AWS API 호출과 유사하다.
  • JWT(JSON Web Token) : 당사자 간에 정보를 JSON 객체로 안전하게 전송하기 위한 간결하고 독립적인 방법을 정의하는 표준(RFC 7519)이다. 

출처 : https://youtu.be/zIZ6_tYujts?t=231 'AWS 보안 웨비나'

 

PAYLOAD의 값을 URL Decode Online 에서 DECODE로 확인

https://sts.ap-northeast-2.amazonaws.com/?

Action=GetCallerIdentity&

Version=2011-06-15&

X-Amz-Algorithm=AWS4-HMAC-SHA256&

X-Amz-Credential=AKIA5ILF.../20230525/ap-northeast-2/sts/aws4_request&

X-Amz-Date=20230525T120720Z&

X-Amz-Expires=60&

X-Amz-SignedHeaders=host;x-k8s-aws-id&

X-Amz-Signature=6e09b846da702767f38c78831986cb558.....

JWT Token 사이트에 발급받은 STS token 정보 입력 후 PAYLOAD 값 복사
URL DECODE PAYLOAD 값 복사 후 DECODE

 

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.

 

4. 쿠버네티스 RBAC 인가를 처리한다. 가장 중요한 정보는 aws-auth configmap이다.

  • 해당 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 컨피그맵 확인(해당 컨피그맵을 활용해서, 다른 account의 계정도 접근가능하게 설정할 수 있다.)
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::61184.....:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-I6F14U2QK492
      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:61184...: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에 설정해보기

 

1. testuser 사용자 생성

# testuser 사용자 생성
aws iam create-user --user-name testuser
{
    "User": {
        "Path": "/",
        "UserName": "testuser",
        "UserId": "AIDAYxxxxx",
        "Arn": "arn:aws:iam::6118xxxxxxxx:user/testuser",
        "CreateDate": "2023-05-29T15:59:15+00:00"
    }
}

# 사용자에게 프로그래밍 방식 액세스 권한 부여
aws iam create-access-key --user-name testuser
{
    "AccessKey": {
        "UserName": "testuser",
        "AccessKeyId": "AIDAYxxx",
        "Status": "Active",
        "SecretAccessKey": "b1J8Bh7Y##",
        "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::6118xxxxxxxx:user/somaz"

# 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
----------------------------------------------------------------------
|                          DescribeInstances                         |
+---------------------+-----------------+-----------------+----------+
|    InstanceName     |  PrivateIPAdd   |   PublicIPAdd   | Status   |
+---------------------+-----------------+-----------------+----------+
|  myeks-ng1-Node     |  192.168.3.8    |  3.34.183.xxx   |  running |
|  myeks-ng1-Node     |  192.168.2.151  |  3.34.30.xxx    |  running |
|  myeks-ng1-Node     |  192.168.1.143  |  3.36.72.xx     |  running |
|  myeks-bastion-EC2-2|  192.168.1.200  |  43.201.31.168  |  running |
|  myeks-bastion-EC2  |  192.168.1.100  |  3.35.18.xxx    |  running |
+---------------------+-----------------+-----------------+----------+

 

2. testuser 자격증명 설정 및 확인

2번째 bastion 서버로 접속!

# get-caller-identity 확인 >> 왜 안될까요?(aws configure) 때문
aws sts get-caller-identity --query Arn
Unable to locate credentials. You can configure credentials by running "aws configure".

# 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::6118xxxxxxxx:user/somaz"

# kubectl 시도 >> testuser도 AdministratorAccess 권한을 가지고 있는데, 실패 이유는? .kube/config 파일이 없기 때문!
kubectl get node -v6
I0530 01:04:19.950008   32707 round_trippers.go:553] GET http://localhost:8080/api?timeout=32s  in 0 milliseconds
E0530 01:04:19.950108   32707 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused

ls ~/.kube
ls: cannot access /root/.kube: No such file or directory

 

 

3. testuser에 system:masters 그룹 부여로 EKS 관리자 수준 권한 설정

# 방안1 : eksctl 사용 >> iamidentitymapping 실행 시 aws-auth 컨피그맵 작성해줌(첫번째 bastion서버로 와서 실행)
# 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

# 확인
kubectl get cm -n kube-system aws-auth -o yaml | kubectl neat | yh
apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::6118xxxxxxxx:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-I6F14U2QK492
      username: system:node:{{EC2PrivateDNSName}}
  mapUsers: |
    - groups:
      - system:masters
      userarn: arn:aws:iam::6118xxxxxxxx:user/testuser
      username: testuser
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
  
kubectl get node -v6
I0530 01:07:23.027601   16013 loader.go:374] Config loaded from file:  /root/.kube/config
I0530 01:07:24.003117   16013 round_trippers.go:553] GET https://BFCC1FC7944D4ADF6C383773FB300C86.yl4.ap-northeast-2.eks.amazonaws.com/api/v1/nodes?limit=500 200 OK in 966 milliseconds
NAME                                               STATUS   ROLES    AGE    VERSION
ip-192-168-1-143.ap-northeast-2.compute.internal   Ready    <none>   103m   v1.24.13-eks-0a21954
ip-192-168-2-151.ap-northeast-2.compute.internal   Ready    <none>   103m   v1.24.13-eks-0a21954
ip-192-168-3-8.ap-northeast-2.compute.internal     Ready    <none>   103m   v1.24.13-eks-0a21954


# 방안2 : 아래 edit로 mapUsers 내용 직접 추가!
kubectl edit cm -n kube-system aws-auth
---
apiVersion: v1
data: 
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::911283464785:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-LHQ7DWHQQRZJ
      username: system:node:{{EC2PrivateDNSName}}
  mapUsers: |
    - groups:
      - system:masters
      userarn: arn:aws:iam::911283464785:user/testuser
      username: testuser
...

# 확인 : 기존에 있는 role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-YYYYY 는 어떤 역할/동작을 하는 걸까요?
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
ARN                                                                                     USERNAME                       GROUPS                                   ACCOUNT
arn:aws:iam::6118xxxxxxxx:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-I6F14U2QK492 system:node:{{EC2PrivateDNSName}}       system:bootstrappers,system:nodes
arn:aws:iam::6118xxxxxxxx:user/testuser                                                 testuser                       system:masters

 

4. testuser kubeconfig 생성 및 kubectl 사용 확인

# testuser kubeconfig 생성 >> aws eks update-kubeconfig 실행이 가능한 이유는?, 3번 설정 후 약간의 적용 시간 필요
aws eks update-kubeconfig --name $CLUSTER_NAME --user-alias testuser
Added new context testuser to /root/.kube/config
(testuser:N/A) [root@myeks-bastion-2 ~]#

# 첫번째 bastic ec2의 config와 비교해보자
cat ~/.kube/config | yh

# kubectl 사용 확인
kubectl ns default
kubectl get node -v6
NAME                                               STATUS   ROLES    AGE    VERSION
ip-192-168-1-143.ap-northeast-2.compute.internal   Ready    <none>   105m   v1.24.13-eks-0a21954
ip-192-168-2-151.ap-northeast-2.compute.internal   Ready    <none>   105m   v1.24.13-eks-0a21954
ip-192-168-3-8.ap-northeast-2.compute.internal     Ready    <none>   105m   v1.24.13-eks-0a21954

# 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::6118xxxxxxxx:user/testuser"],
            canonicalArn: ["arn:aws:iam::6118xxxxxxxx:user/testuser"],
...

 

5. 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

 

6. testuser kubectl 사용 확인

# 시도
kubectl get node -v6
kubectl api-resources -v5

 

7. testuser IAM 맵핑 삭제

# testuser IAM 맵핑 삭제
eksctl delete iamidentitymapping --cluster $CLUSTER_NAME --arn  arn:aws:iam::$ACCOUNT_ID:user/testuser
2023-05-30 01:10:39 [ℹ]  removing identity "arn:aws:iam::611841095956:user/testuser" from auth ConfigMap (username = "testuser", groups = ["system:masters"])

# Get IAM identity mapping(s)
eksctl get iamidentitymapping --cluster $CLUSTER_NAME
ARN                                                                                     USERNAME                       GROUPS                                   ACCOUNT
arn:aws:iam::6118xxxxxxxx:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-I6F14U2QK492 system:node:{{EC2PrivateDNSName}}       system:bootstrappers,system:nodes

kubectl get cm -n kube-system aws-auth -o yaml | yh
apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::6118xxxxxxxx:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-I6F14U2QK492
      username: system:node:{{EC2PrivateDNSName}}
  mapUsers: |
    []
kind: ConfigMap
metadata:
  creationTimestamp: "2023-05-29T14:23:12Z"
  name: aws-auth
  namespace: kube-system
  resourceVersion: "28794"
  uid: 6c71a563-7354-4cc4-88dc-8a6919121dd2

 

8. testuser kubectl 사용 확인

# 시도
kubectl get node -v6
I0530 01:15:24.813685    1274 round_trippers.go:553] GET http://localhost:8080/api?timeout=32s  in 0 milliseconds
E0530 01:15:24.814005    1274 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused

kubectl api-resources -v5

 

9. config 샘플 - 링크

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::111122223333:role/my-role
      username: system:node:{{EC2PrivateDNSName}}
    - groups:
      - eks-console-dashboard-full-access-group
      rolearn: arn:aws:iam::111122223333:role/my-console-viewer-role
      username: my-console-viewer-role
  mapUsers: |
    - groups:
      - system:masters
      userarn: arn:aws:iam::111122223333:user/admin
      username: admin
    - groups:
      - eks-console-dashboard-restricted-access-group      
      userarn: arn:aws:iam::444455556666:user/my-user
      username: my-user

 


 

3. IRSA

간단하게 IRSA에 대해서 정리해 보았다.

2023.05.28 - [AWS] - AWS IRSA(IAM Roles for Service Accounts)란?

 

AWS IRSA(IAM Roles for Service Accounts)란?

Overview 오늘은 IRSA(IAM Roles for Service Accounts)에 대해서 공부해보려고 한다. 클러스터 내부에 있는 Pod가 다른 리소스들에 대한 권한을 얻는 것은 K8S의 자체 기능이며, 관련된 리소스로는 Role, RoleBindi

somaz.tistory.com

EC2 Instance Profile : 사용하기 편하지만, 최소 권한 부여 원칙에 위배하며 보안상 권고하지 않음 - 링크

→ 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 : 테라폼
...

 

동작은 아래와 같다.

k8s파드 → AWS 서비스 사용 시 ⇒ AWS STS/IAM ↔ IAM OIDC Identity Provider(EKS IdP) 인증/인가

Service Account Token Volume Projection이란?

서비스 계정 토큰의 시크릿 기반 볼륨 대신 projected volume 사용한다.

토큰을 사용하는 대상(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

 

Bound Service Account Token Volume 바인딩된 서비스 어카운트 토큰 볼륨

서비스 어카운트 어드미션 컨트롤러는 토큰 컨트롤러에서 생성한 만료되지 않은 서비스 계정 토큰에 시크릿 기반 볼륨 대신 다음과 같은 프로젝티드 볼륨을 추가한다.

- 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

 


 

IRSA 소개

출처 : https://awskoreamarketingasset.s3.amazonaws.com/2022 Summit/pdf/T10S1_EKS 환경을 더 효율적으로 더 안전하게.pdf

AWS SDK는 AWS_ROLE_ARNAWS_WEB_IDENTITY_TOKEN_FILE 이름의 환경변수를 읽어들여 Web Identity 토큰으로 AssumeRoleWithWebIdentify를 호출함으로써 Assume Role을 시도하여 임시 자격 증명을 획득하고, 특정 IAM Role 역할을 사용할 수 있게 된다.

이때 Assume Role 동작을 위한 인증은 AWS가 아닌 외부 Web IdP(EKS IdP)에 위임하여 처리한다.

출처: https://tech.devsisters.com/posts/pod-iam-role/

 


 

실습1

ServiceAccountToken의 자동 발급 기능 OFF인 상태이다. 따라서 Pod의 S3 접근할 수 있는 권한이 없다.

# 파드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

# 확인(SA Token 볼륨이 없다?)
kubectl get pod
NAME            READY   STATUS   RESTARTS   AGE
eks-iam-test1   0/1     Error    0          15s

kubectl describe pod
Name:             eks-iam-test1
Namespace:        default
Priority:         0
Service Account:  default
Node:             ip-192-168-2-223.ap-northeast-2.compute.internal/192.168.2.223
Start Time:       Sat, 03 Jun 2023 18:56:42 +0900
Labels:           <none>
Annotations:      kubernetes.io/psp: eks.privileged
Status:           Failed
IP:               192.168.2.81
IPs:
  IP:  192.168.2.81
Containers:
  my-aws-cli:
    Container ID:  containerd://426d21da280e9709f6204ab41db79c6b802e572d43a4018aba629e5e705dc9e6
    Image:         amazon/aws-cli:latest
    Image ID:      docker.io/amazon/aws-cli@sha256:221ff6588005667221ff7e5e3b6cde2e1d2dd04fbe124652292e5fac652a8234
...
Volumes: <none>

# 로그 확인
kubectl logs eks-iam-test1
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

# 파드1 삭제
kubectl delete pod eks-iam-test1
  • CloudTrail 이벤트 ListBuckets 확인 → 기록 표시까지 약간의 시간 필요
{
...
  "userIdentity": {
    "type": "AssumedRole",
    "principalId": "xxxx",
    "arn": "arn:aws:sts::111122223333:assumed-role/eksctl-eks-oidc-demo-nodegroup-ng-NodeInstanceRole-xxxx/xxxx",
    "accountId": "111122223333",
    "accessKeyId": "AKIAIOSFODNN7EXAMPLE",
    "sessionContext": {
      "sessionIssuer": {
        "type": "Role",
        "principalId": "xxxx",
        "arn": "arn:aws:iam::xxxx:role/eksctl-eks-oidc-demo-nodegroup-ng-NodeInstanceRole-xxxx",
        "accountId": "111122223333",
        "userName": "eksctl-eks-oidc-demo-nodegroup-ng-NodeInstanceRole-xxxx"
      },
      "webIdFederationData": {},
      "attributes": {
        "creationDate": "2021-12-04T14:54:49Z",
        "mfaAuthenticated": "false"
      },
      "ec2RoleDelivery": "2.0"
    }
  },
  "eventTime": "2021-12-04T15:09:20Z",
  "eventSource": "s3.amazonaws.com",
  "eventName": "ListBuckets",
  "awsRegion": "us-east-2",
  "sourceIPAddress": "192.0.2.1",
  "userAgent": "[aws-cli/2.4.5 Python/3.8.8 Linux/5.4.156-83.273.amzn2.x86_64 docker/x86_64.amzn.2 prompt/off command/s3.ls]",
  "errorCode": "AccessDenied",
  "errorMessage": "Access Denied",
  "requestParameters": {
    "Host": "s3.us-east-2.amazonaws.com"
  },
...
}

위와 같이 CloudTrail - 이벤트 기록 - ListBuckets에서 조회해준다.

 


 

실습2 - Kubernetes Service Accounts : https://jwt.io/

이번에는 ServiceAccountToken의 자동 발급 기능 OFF 하지 않았다.

Service Account를 생성하게 되면 Kubernetes Secret에서 JWT token을 자동으로 생성한다.

# 파드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

# 확인( 이번엔 SA volume이 잘 생성되었다. )
kubectl get pod
NAME            READY   STATUS    RESTARTS   AGE
eks-iam-test2   1/1     Running   0          4s

kubectl describe pod
Name:             eks-iam-test2
Namespace:        default
Priority:         0
Service Account:  default
Node:             ip-192-168-2-223.ap-northeast-2.compute.internal/192.168.2.223
Start Time:       Sat, 03 Jun 2023 18:59:28 +0900
Labels:           <none>
Annotations:      kubernetes.io/psp: eks.privileged
Status:           Running
IP:               192.168.2.81
...
Volumes:
  kube-api-access-4rz29:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607

# aws 서비스 사용 시도(그래도 아직도 조회 되지 않는다.)
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

# 서비스 어카운트 토큰 확인
SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
echo $SA_TOKEN
eyJhbGciOiJSUzI1NiIsImtpZCI6IjNiYT...

# jwt 혹은 아래 JWT 웹 사이트 이용
jwt decode $SA_TOKEN --json --iso8601
...

https://jwt.io/ 해당 사이트로 가서 SA Token을 붙여넣는다.

 

#헤더
{
  "alg": "RS256",
  "kid": "3ba384644812773d17b7b6e0514eafb99bf31037"
}

# 페이로드 : OAuth2에서 쓰이는 aud, exp 속성 확인! > projectedServiceAccountToken 기능으로 토큰에 audience,exp 항목을 덧붙힘
## iss 속성 : EKS OpenID Connect Provider(EKS IdP) 주소 > 이 EKS IdP를 통해 쿠버네티스가 발급한 토큰이 유요한지 검증
{
  "aud": [
    "https://kubernetes.default.svc"
  ],
  "exp": 1717322859,
  "iat": 1685786859,
  "iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/7ED7FBC0122AD2374C52640AD4B0F4E4",
  "kubernetes.io": {
    "namespace": "default",
    "pod": {
      "name": "eks-iam-test2",
      "uid": "2cc0c42a-af3c-4d1d-bcc4-f0a676940d93"
    },
    "serviceaccount": {
      "name": "default",
      "uid": "881c3d7f-1518-41ff-8a4d-83f3840404ae"
    },
    "warnafter": 1685790466
  },
  "nbf": 1685786859,
  "sub": "system:serviceaccount:default:default"
}

# 파드2 삭제
kubectl delete pod eks-iam-test2

 


 

실습3

  • The eksctl create iamserviceaccount command creates
    1. A Kubernetes Service Account
    2. An IAM role with the specified IAM policy
    3. A trust policy on that IAM role

aws-eks-pod-identity-webhook을 사용해 IRSA를 사용할 수 있게 조치해본다.

# Create an iamserviceaccount - AWS IAM role bound to a Kubernetes service account
# my-sa라는 Serviceaccount에 S3를 읽을 수 있는 권한을 부여하고 새로 생성한다.
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 확인!
CloudFormation이 자동으로 IAM Role을 만들고 S3ReadOnlyAccess 권한을 부여한다.
그리고 신롸 관계 셋팅을 해준다. Federated에는 EKS OIDC 공급자 주소를 부여해준다.
이렇게 말이다!

 

# 확인 >> 웹 관리 콘솔에서 CloudFormation Stack >> IAM Role 확인
# aws-load-balancer-controller IRSA는 어떤 동작을 수행할 것 인지 생각해보자!
eksctl get iamserviceaccount --cluster $CLUSTER_NAME
NAMESPACE       NAME                            ROLE ARN
default         my-sa                           arn:aws:iam::6118xxxxxxxx:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1SO30XF33627L
kube-system     aws-load-balancer-controller    arn:aws:iam::6118xxxxxxxx:role/eksctl-myeks-addon-iamserviceaccount-kube-sy-Role1-DKYNY4P03SZF

# Inspecting the newly created Kubernetes Service Account, we can see the role we want it to assume in our pod.
kubectl get sa
NAME      SECRETS   AGE
default   0         48m
my-sa     0         94s

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::611841095956:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1SO30XF33627L
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
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: pod-identity-webhook
webhooks:
- admissionReviewVersions:
  - v1beta1
  clientConfig:
    caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJek1EWXdNekE1TXpNek1Wb1hEVE16TU
...

# 파드 생성 yaml에 없던 내용이 추가됨!!!!!
# Pod Identity Webhook은 mutating webhook을 통해 아래 Env 내용과 1개의 볼륨을 추가함
kubectl get pod eks-iam-test3
NAME            READY   STATUS    RESTARTS   AGE
eks-iam-test3   1/1     Running   0          48s

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::6118xxxxxxxx:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1SO30XF33627L
      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-fkrsc (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::6118xxxxxx:assumed-role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1SO30XF33627L/botocore-session-1685788242"

# 되는 것고 안되는 것은 왜그런가?
# 그 이유는 SA가 S3ReadOnlyAccess 권한만 가지고 있기 때문이다.

kubectl exec -it eks-iam-test3 -- aws s3 ls
2023-01-08 11:53:24 somaz-k8s-s3

kubectl exec -it eks-iam-test3 -- aws ec2 describe-instances --region ap-northeast-2
An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.

kubectl exec -it eks-iam-test3 -- aws ec2 describe-vpcs --region ap-northeast-2
An error occurred (UnauthorizedOperation) when calling the DescribeVpcs operation: You are not authorized to perform this operation.

 

# 파드에 볼륨 마운트 2개 확인
kubectl get pod eks-iam-test3 -o json | jq -r '.spec.containers | .[].volumeMounts'
[
  {
    "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
    "name": "kube-api-access-fkrsc",
    "readOnly": true
  },
  {
    "mountPath": "/var/run/secrets/eks.amazonaws.com/serviceaccount",
    "name": "aws-iam-token",
    "readOnly": true
  }
]

# aws-iam-token 볼륨 정보 확인 : JWT 토큰이 담겨져있고, exp, aud 속성이 추가되어 있음
kubectl get pod eks-iam-test3 -o json | jq -r '.spec.volumes[] | select(.name=="aws-iam-token")'
{
  "name": "aws-iam-token",
  "projected": {
    "defaultMode": 420,
    "sources": [
      {
        "serviceAccountToken": {
          "audience": "sts.amazonaws.com",
          "expirationSeconds": 86400,
          "path": "token"
        }
      }
    ]
  }
}

# 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
NAME                            WEBHOOKS   AGE
pod-identity-webhook            1          147m
vpc-resource-mutating-webhook   1          147m

# pod-identity-webhook 확인
kubectl describe MutatingWebhookConfiguration pod-identity-webhook 
kubectl get MutatingWebhookConfiguration pod-identity-webhook -o yaml | yh

 

동일하게 Web Token 파일도 https://jwt.io/ 로 가서 $IAM_TOKEN 넣고 확인하면 된다!

# 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
eyJhbGciOiJSUzI1NiIsIm....

# JWT 웹 확인 
{
  "aud": [
    "sts.amazonaws.com"
  ],
  "exp": 1685175662,
  "iat": 1685089262,
  "iss": "https://oidc.eks.ap-northeast-2.amazonaws.com/id/F6A7523462E8E6CDADEE5D41DF2E71F6",
  "kubernetes.io": {
    "namespace": "default",
    "pod": {
      "name": "eks-iam-test3",
      "uid": "73f66936-4d66-477a-b32b-853f7a1c22d9"
    },
    "serviceaccount": {
      "name": "my-sa",
      "uid": "3b31aa85-2718-45ed-8c1c-75ed012c1a68"
    }
  },
  "nbf": 1685089262,
  "sub": "system:serviceaccount:default:my-sa"
}

# env 변수 확인
kubectl get pod eks-iam-test3 -o json | jq -r '.spec.containers | .[].env'
[
  {
    "name": "AWS_STS_REGIONAL_ENDPOINTS",
    "value": "regional"
  },
  {
    "name": "AWS_DEFAULT_REGION",
    "value": "ap-northeast-2"
  },
  {
    "name": "AWS_REGION",
    "value": "ap-northeast-2"
  },
  {
    "name": "AWS_ROLE_ARN",
    "value": "arn:aws:iam::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1MJUYW59O6QGH"
  },
  {
    "name": "AWS_WEB_IDENTITY_TOKEN_FILE",
    "value": "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
  }
]

 

# Let’s take a look at this endpoint. We can use the aws eks describe-cluster command to get the OIDC Provider URL.
IDP=$(aws eks describe-cluster --name myeks --query cluster.identity.oidc.issuer --output text)

# Reach the Discovery Endpoint
curl -s $IDP/.well-known/openid-configuration | jq -r '.'

# In the above output, you can see the jwks (JSON Web Key set) field, which contains the set of keys containing the public keys used to verify JWT (JSON Web Token). 
# Refer to the documentation to get details about the JWKS properties.
curl -s $IDP/keys | jq -r '.'

#실습 완료 후 파드 삭제
kubectl delete pod eks-iam-test3

 


 

4. OWASP Kubernetes Top Ten

출처 : https://awskoreamarketingasset.s3.amazonaws.com/2022 Summit/pdf/T10S1_EKS 환경을 더 효율적으로 더 안전하게.pdf

 


 

EKS pod가 IMDS API를 악용하는 시나리오

< 기초 지식 >

WAF란 Web Application Firewall의 약자로 웹 애플리케이션 보안에 특화된 전용 방화벽이다.

SQL Injection 공격, Cross-Site Scripting(XSS) 공격, Cross-Site Request Forgery(CSRF) 공격 등과 같이 웹 서비스 취약점에 대한 공격을 탐지하고 차단하는 기능을 수행한다.


Kubelet 미흡한 인증/인가 설정 시 위험 + kubeletct 툴

  • 링크 Youtube
  • 출처 : (최성욱님) 악성코드분석님 

 

myeks-bastion

# 노드의 kubelet API 인증과 인가 관련 정보 확인
ssh ec2-user@$N1 cat /etc/kubernetes/kubelet/kubelet-config.json | jq
{
  "kind": "KubeletConfiguration",
  "apiVersion": "kubelet.config.k8s.io/v1beta1",
  "address": "0.0.0.0",
  "authentication": {
    "anonymous": {
      "enabled": false
    },
    "webhook": {
      "cacheTTL": "2m0s",
      "enabled": true
    },
    "x509": {
      "clientCAFile": "/etc/kubernetes/pki/ca.crt"
    }
...
ssh ec2-user@$N1 cat /var/lib/kubelet/kubeconfig | yh
apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority: /etc/kubernetes/pki/ca.crt
    server: https://7ED7FBC0122AD2374C52640AD4B0F4E4.yl4.ap-northeast-2.eks.amazonaws.com
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubelet
  name: kubelet
current-context: kubelet
users:
- name: kubelet
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      command: /usr/bin/aws-iam-authenticator
      args:
        - "token"
        - "-i"
        - "myeks"
        - --region
        - "ap-northeast-2"

# 노드의 kubelet 사용 포트 확인 
ssh ec2-user@$N1 sudo ss -tnlp | grep kubelet
LISTEN 0      4096       127.0.0.1:10248      0.0.0.0:*    users:(("kubelet",pid=2940,fd=20))
LISTEN 0      4096               *:10250            *:*    users:(("kubelet",pid=2940,fd=21))

# 데모를 위해 awscli 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: myawscli
spec:
  #serviceAccountName: my-sa
  containers:
    - name: my-aws-cli
      image: amazon/aws-cli:latest
      command: ['sleep', '36000']
  restartPolicy: Never
EOF

# 파드 사용
kubectl exec -it myawscli -- aws sts get-caller-identity --query Arn
"arn:aws:sts::6118xxxxxxxx:assumed-role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-CX0XBAW3ADLD/i-02fa5431cf9632ac2"

# default SA이기 때문에 s3 조회는 안된다.
kubectl exec -it myawscli -- aws s3 ls

# ec2 instance profile 덕분에 해당 명령어는 조회가 된다.
kubectl exec -it myawscli -- aws ec2 describe-instances --region ap-northeast-2 --output table --no-cli-pager
------------------------------------------------------------------------------------------------------------------------------------------------
|                                                               DescribeInstances                                                              |
+----------------------------------------------------------------------------------------------------------------------------------------------+
||                                                                Reservations                                                                ||
|+----------------------------------------------------------+---------------------------------------------------------------------------------+|
||  OwnerId                                                 |  611841095956                                                                   ||
||  RequesterId                                             |  154997438628                                                                   ||
||  ReservationId                                           |  r-0e31f2df842254967                                                            ||
|+----------------------------------------------------------+---------------------------------------------------------------------------------+|
....
kubectl exec -it myawscli -- aws ec2 describe-vpcs --region ap-northeast-2 --output table --no-cli-pager

 

myeks-bastion2 kubeletct 설치 및 사용

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
----------------------------------------------------------------------
|                          DescribeInstances                         |
+---------------------+-----------------+-----------------+----------+
|    InstanceName     |  PrivateIPAdd   |   PublicIPAdd   | Status   |
+---------------------+-----------------+-----------------+----------+
|  myeks-ng1-Node     |  192.168.3.103  |  3.34.197.127   |  running |
|  myeks-ng1-Node     |  192.168.2.223  |  3.35.15.52     |  running |
|  myeks-bastion-EC2  |  192.168.1.100  |  3.36.92.226    |  running |
|  myeks-ng1-Node     |  192.168.1.42   |  52.79.53.67    |  running |
|  myeks-bastion-EC2-2|  192.168.1.200  |  13.125.106.96  |  running |
+---------------------+-----------------+-----------------+----------+

ssh -i ~/.ssh/somaz-key.pem ec2-user@13.125.106.96
The authenticity of host '13.125.106.96 (13.125.106.96)' can't be established.
ED25519 key fingerprint is SHA256:JiHkqWa07U39UTx4DhM4aeF88Ln+nLR7vWcXWGEpmlY.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '13.125.106.96' (ED25519) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
6 package(s) needed for security, out of 7 available
Run "sudo yum update" to apply all updates.
[root@myeks-bastion-2 ~]#

# 기존 kubeconfig 삭제
rm -rf ~/.kube

k get nodes
E0603 20:39:59.764641     316 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
E0603 20:39:59.764855     316 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
E0603 20:39:59.766235     316 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
E0603 20:39:59.767570     316 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
E0603 20:39:59.768829     316 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
The connection to the server localhost:8080 was refused - did you specify the right host or port?

# 다운로드
curl -LO https://github.com/cyberark/kubeletctl/releases/download/v1.9/kubeletctl_linux_amd64 && chmod a+x ./kubeletctl_linux_amd64 && mv ./kubeletctl_linux_amd64 /usr/local/bin/kubeletctl
kubeletctl version

 _           _           _                         _
| |         | |         | |         _          _  | |
| |  _ _   _| |__  _____| | _____ _| |_ ____ _| |_| |
| |_/ ) | | |  _ \| ___ | || ___ (_   _) ___|_   _) |
|  _ (| |_| | |_) ) ____| || ____| | |( (___  | |_| |
|_| \_)____/|____/|_____)\_)_____)  \__)____)  \__)\_)

Author: Eviatar Gerzi
Version: 1.9

kubeletctl help

# 노드1 IP 변수 지정
N1=<각자 자신의 노드1의 PrivateIP>
[root@myeks-bastion-2 ~]# N1=192.168.1.42

# 노드1 IP로 Scan
[root@myeks-bastion-2 ~]# kubeletctl scan --cidr $N1/32

# 노드1에 kubelet API 호출 시도
[root@myeks-bastion-2 ~]# curl -k https://$N1:10250/pods; echo
Unauthorized

 

myeks-bastion → 노드1 접속 : kubelet-config.json 수정

(somaz@myeks:N/A) [root@myeks-bastion ~]# N1=192.168.1.42

# 노드1 접속
ssh ec2-user@$N1
-----------------------------
# 미흡한 인증/인가 설정으로 변경
vi /etc/kubernetes/kubelet/kubelet-config.json
...
"authentication": {
    "anonymous": {
      "enabled": true
...
  },
  "authorization": {
    "mode": "AlwaysAllow",
...

# kubelet restart
systemctl restart kubelet
systemctl status kubelet
● kubelet.service - Kubernetes Kubelet
   Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: disabled)
  Drop-In: /etc/systemd/system/kubelet.service.d
           └─10-kubelet-args.conf, 30-kubelet-extra-args.conf
   Active: active (running) since Sat 2023-06-03 11:52:02 UTC; 4s ago
-----------------------------

 

myeks-bastion-2 kubeletctl 사용

# 파드 목록 확인
curl -s -k https://$N1:10250/pods | jq
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {},
  "items": [
    {
...

# kubelet-config.json 설정 내용 확인
curl -k https://$N1:10250/configz | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2905  100  2905    0     0   410k      0 --:--:-- --:--:-- --:--:--  472k
{
  "kubeletconfig": {
    "enableServer": true,
    "syncFrequency": "1m0s",
...

# kubeletct 사용
# Return kubelet's configuration
kubeletctl -s $N1 configz | jq

# Get list of pods on the node
kubeletctl -s $N1 pods
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                        Pods from Kubelet                                       │
├───┬───────────────────────────────────────────────┬─────────────┬──────────────────────────────┤
│   │ POD                                           │ NAMESPACE   │ CONTAINERS                   │
├───┼───────────────────────────────────────────────┼─────────────┼──────────────────────────────┤
│ 1 │ kube-proxy-4zwbp                              │ kube-system │ kube-proxy                   │
│   │                                               │             │                              │
├───┼───────────────────────────────────────────────┼─────────────┼──────────────────────────────┤
│ 2 │ coredns-6777fcd775-kkgsl                      │ kube-system │ coredns                      │
│   │                                               │             │                              │
├───┼───────────────────────────────────────────────┼─────────────┼──────────────────────────────┤
│ 3 │ ebs-csi-node-w9n7w                            │ kube-system │ ebs-plugin                   │
│   │                                               │             │ node-driver-registrar        │
│   │                                               │             │ liveness-probe               │
│   │                                               │             │                              │
├───┼───────────────────────────────────────────────┼─────────────┼──────────────────────────────┤
│ 4 │ aws-load-balancer-controller-5f99d5f58f-qjc9r │ kube-system │ aws-load-balancer-controller │
│   │                                               │             │                              │
├───┼───────────────────────────────────────────────┼─────────────┼──────────────────────────────┤
│ 5 │ myawscli                                      │ default     │ my-aws-cli                   │
│   │                                               │             │                              │
├───┼───────────────────────────────────────────────┼─────────────┼──────────────────────────────┤
│ 6 │ aws-node-cvg25                                │ kube-system │ aws-node                     │
│   │                                               │             │                              │
└───┴───────────────────────────────────────────────┴─────────────┴──────────────────────────────┘

# Scans for nodes with opened kubelet API > Scans for for all the tokens in a given Node
kubeletctl -s $N1 scan token
1. Pod: myawscli
   Namespace: default
   Container: my-aws-cli
   Url: https://192.168.1.42:10250/run/default/myawscli/my-aws-cli
   Output:
eyJhb...

# kubelet API로 명령 실행 : <네임스페이스> / <파드명> / <컨테이너명>
curl -k https://$N1:10250/run/default/myawscli/my-aws-cli -d "cmd=aws --version"
aws-cli/2.11.25 Python/3.11.3 Linux/5.10.179-166.674.amzn2.x86_64 docker/x86_64.amzn.2 prompt/off

# Scans for nodes with opened kubelet API > remote code execution on their containers
kubeletctl -s $N1 scan rce
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                           Node with pods vulnerable to RCE                                          │
├───┬──────────────┬───────────────────────────────────────────────┬─────────────┬──────────────────────────────┬─────┤
│   │ NODE IP      │ PODS                                          │ NAMESPACE   │ CONTAINERS                   │ RCE │
├───┼──────────────┼───────────────────────────────────────────────┼─────────────┼──────────────────────────────┼─────┤
│   │              │                                               │             │                              │ RUN │
├───┼──────────────┼───────────────────────────────────────────────┼─────────────┼──────────────────────────────┼─────┤
│ 1 │ 192.168.1.42 │ aws-node-cvg25                                │ kube-system │ aws-node                     │ -   │
├───┼──────────────┼───────────────────────────────────────────────┼─────────────┼──────────────────────────────┼─────┤
│ 2 │              │ kube-proxy-4zwbp                              │ kube-system │ kube-proxy                   │ -   │
├───┼──────────────┼───────────────────────────────────────────────┼─────────────┼──────────────────────────────┼─────┤
....
│ 8 │              │ myawscli                                      │ default     │ my-aws-cli                   │ +   │
└───┴──────────────┴───────────────────────────────────────────────┴─────────────┴──────────────────────────────┴─────┘

# Run commands inside a container
kubeletctl -s $N1 exec "/bin/bash" -n default -p myawscli -c my-aws-cli
--------------------------------
export
aws --version
aws-cli/2.11.25 Python/3.11.3 Linux/5.10.179-166.674.amzn2.x86_64 docker/x86_64.amzn.2 prompt/off

aws ec2 describe-vpcs --region ap-northeast-2 --output table --no-cli-pager
----------------------------------------------------------------------------------------------------------------------------------------------
|                                                                DescribeVpcs                                                                |
+--------------------------------------------------------------------------------------------------------------------------------------------+
||                                                                   Vpcs                                                                   ||
|+---------------------------------------------------------+--------------------------------------------------------------------------------+|
||  CidrBlock                                              |  172.31.0.0/16                                                                 ||
||  DhcpOptionsId                                          |  dopt-0be4e016089ff564e                                                        ||
||  InstanceTenancy                                        |  default                                                                       ||
||  IsDefault                                              |  True                                                                          ||
||  OwnerId                                                |  611841095956                                                                  ||
||  State                                                  |  available                                                                     ||
||  VpcId                                                  |  vpc-0be33812ca98ab8fa                                                         ||
|+---------------------------------------------------------+--------------------------------------------------------------------------------+|
...
exit
--------------------------------

# Return resource usage metrics (such as container CPU, memory usage, etc.)
kubeletctl -s $N1 metrics

 


6. Securing Secrets

2022.10.11 - [Networking, Security, Protocols] - Vault란?

 

Vault란?

Overview 오늘은 Hashicorp Vault에 대해서 공부해보려고 한다. Vault란? HashiCorp Vault는 ID 기반 비밀 및 암호화 관리 시스템이다. Secret은 API 암호화 키, 암호 및 인증서와 같이 액세스를 엄격하게 제어하

somaz.tistory.com

 


 

7. 파드/컨테이너 보안 컨텍스트

보안 컨텍스트 내용과 실습 참고 책 ⇒ 쿠버네티스 완벽 가이드 👍🏻

 


 

컨테이너 보안 컨텍스트 SecurityContext

  • 링크 ← 파드가 아님 주의한다.

각 컨테이너에 대한 보안 설정 → 침해사고 발생 시 침해사고를 당한 권한을 최대한 축소하여 그 사고에 대한 확대를 방치한다.

출처 : CloudNet@ 스터디

 


 

컨테이너 보안 컨텍스트 확인 : kube-system 파드 내 컨테이너 대상

kubectl get pod -n kube-system -o jsonpath={.items[*].spec.containers[*].securityContext} | jq
{
  "allowPrivilegeEscalation": false,
  "readOnlyRootFilesystem": true,
  "runAsNonRoot": true
}
...

 

readOnlyRootFilesystem : root 파일 시스템을 읽기 전용으로 사용

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: rootfile-readonly
spec:
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      readOnlyRootFilesystem: true
  terminationGracePeriodSeconds: 0
EOF

# 파일 생성 시도
kubectl exec -it rootfile-readonly -- touch /tmp/text.txt
touch: /tmp/text.txt: Read-only file system
command terminated with exit code 1

# 기존 파일 수정 시도 : 아래 /etc/hosts파일 말고 다른 파일로 예제 만들어 두자
## 기본적으로  mount 옵션이 ro 이긴 한데. 특정 파일이나 폴더가 rw로 mount가 되어서 그곳에서는 파일 생성, 삭제등이 가능하네요.
## 특히 /etc/hosts 파일은 HostAliases로 항목 추가가 가능한데, 해당 파링은 kubelet에 의해 관리되고, 파드 생성/재시작 중 덮었여질 수 있다.
## /dev 라던가 /sys/fs/cgroup 폴더 안에서도 가능하네요.
## /etc/hostname 같은 경우는 호스트와 별도의 파일이지만 mount가 / (ro)에 속하게 되어 제한이 걸리네요.
kubectl exec -it rootfile-readonly -- cat /etc/hosts
kubectl exec -it rootfile-readonly -- sh -c "echo write > /etc/hosts"
kubectl exec -it rootfile-readonly -- cat /etc/hosts

# 특정 파티션, 파일의 ro/rw 확인
kubectl exec -it rootfile-readonly -- mount | grep hosts
/dev/root on /etc/hosts type ext4 (rw,relatime,discard)

kubectl exec -it rootfile-readonly -- mount | grep ro
overlay on / type overlay (ro,relatime~~~~~~~~~~

## /proc, /dev, /sys/fs/cgroup, /etc/hosts, /proc/kcore, /proc/keys, /proc/timer_list
kubectl exec -it rootfile-readonly -- mount | grep rw
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755,inode64)
...

# 파드 상세 정보 확인
kubectl get pod rootfile-readonly -o jsonpath={.spec.containers[0].securityContext} | jq
{
  "readOnlyRootFilesystem": true
}

 

Linux Capabilities : Give a process some privileges, but not all the privileges of the root user - 링크

Linux Capabilities : 슈퍼 유저의 힘을 작은 조각으로 나눔, Capabilities are a per-thread attribute - 링크 man-pages

# Linux Capabilities 확인 : 현재 38개
capsh --print
...
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read

# proc 에서 확인 : bit 별 Capabilities - 링크
cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm:	0000003fffffffff
CapEff:	0000003fffffffff

# 노드 Linux Capabilities 확인 : 아래 굵은색은 파드 기본 Linux Capabilities(14개)
ssh ec2-user@$N1 sudo yum -y install libcap-ng-utils
ssh ec2-user@$N1 sudo capsh --print
cap_chown                   파일이나 디렉토리의 소유자를 변경할 수 있는 권한
cap_dac_override            파일이나 디렉토리의 접근 권한을 무시하고 파일이나 디렉토리에 대한 접근을 수행할 수 있는 권한 (DAC의 약자는 Discretionary access control이다)
cap_dac_read_search         파일이나 디렉토리를 읽거나 검색할 수 있는 권한
cap_fowner                  파일이나 디렉토리의 소유자를 변경할 수 있는 권한
cap_fsetid                  일이나 디렉토리의 Set-User-ID (SUID) 또는 Set-Group-ID (SGID) 비트를 설정할 수 있는 권한
cap_kill                    다른 프로세스를 종료할 수 있는 권한
cap_setgid                  프로세스가 그룹 ID를 변경할 수 있는 권한
cap_setuid                  프로세스가 사용자 ID를 변경할 수 있는 권한
cap_setpcap                 프로세스가 자신의 프로세스 권한을 변경할 수 있는 권한
cap_linux_immutable         파일의 immutability(불변성) 속성을 변경할 수 있는 권한을 제공
cap_net_bind_service        프로그램이 특정 포트에 바인딩(bind)하여 소켓을 개방할 수 있는 권한
cap_net_broadcast           프로세스가 네트워크 브로드캐스트 메시지를 보낼 수 있는 권한
cap_net_admin               네트워크 인터페이스나 소켓 설정을 변경할 수 있는 권한
cap_net_raw                 네트워크 패킷을 송수신하거나 조작할 수 있는 권한
cap_ipc_lock                메모리 영역을 잠금(lock)하고 언락(unlock)할 수 있는 권한
cap_ipc_owner               IPC 리소스(Inter-Process Communication Resources)를 소유하고, 권한을 변경할 수 있는 권한
cap_sys_module              커널 모듈을 로드하거나 언로드할 수 있는 권한
cap_sys_rawio               입출력(I/O) 포트와 같은 하드웨어 리소스를 직접 접근할 수 있는 권한
cap_sys_chroot              프로세스가 chroot() 시스템 콜을 호출하여 프로세스의 루트 디렉토리를 변경할 수 있는 권한
cap_sys_ptrace              다른 프로세스를 추적(trace)하거나 디버깅할 수 있는 권한
cap_sys_pacct               프로세스 회계(process accounting)를 위한 파일에 접근할 수 있는 권한
cap_sys_admin               시스템 관리자 권한을 제공하는 권한
cap_sys_boot                시스템 부팅과 관련된 작업을 수행할 수 있는 권한
cap_sys_nice                프로세스의 우선순위를 변경할 수 있는 권한
cap_sys_resource            자원 제한(resource limit)과 관련된 작업을 수행할 수 있는 권한
cap_sys_time                시스템 시간을 변경하거나, 시간 관련 시스템 콜을 사용할 수 있는 권한
cap_sys_tty_config          터미널 설정을 변경할 수 있는 권한
cap_mknod                   mknod() 시스템 콜을 사용하여 파일 시스템에 특수 파일을 생성할 수 있는 권한
cap_lease                   파일의 잠금과 관련된 작업을 수행할 수 있는 권한
cap_audit_write             시스템 감사(audit) 로그에 대한 쓰기 권한
cap_audit_control           시스템 감사(audit) 설정과 관련된 작업을 수행할 수 있는 권한
cap_setfcap                 파일 시스템 캡러빌리티(file system capability)을 설정할 수 있는 권한
cap_mac_override            SELinux 또는 AppArmor과 같은 MAC(Mandatory Access Control) 시스템을 우회하고 자신의 프로세스가 접근 가능한 파일, 디바이스, 네트워크 등을 제한 없이 접근할 수 있는 권한
cap_mac_admin               SELinux 또는 AppArmor과 같은 MAC(Mandatory Access Control) 시스템을 관리하고 수정할 수 있는 권한
cap_syslog                  시스템 로그를 읽거나, 쓸 수 있는 권한
cap_wake_alarm              시스템의 RTC(Real-Time Clock)를 사용하여 장치를 깨우거나 슬립 모드를 해제할 수 있는 권한
cap_block_suspend           시스템의 전원 관리 기능 중 하나인 Suspend(절전 모드)를 방지하는 권한
cap_audit_read              시스템 감사(audit) 로그를 읽을 수 있는 권한

 

파드의 Linux Capabilities 기본 확인

# 샘플 파드 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: sample-capabilities
spec:
  containers:
  - name: nginx-container
    image: masayaaoyama/nginx:capsh
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 파드의 Linux Capabilities 기본 확인
kubectl exec -it sample-capabilities -- capsh --print | grep Current
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+ep
cap_chown,
cap_dac_override,
cap_fowner,
cap_fsetid,
cap_kill,
cap_setgid,
cap_setuid,
cap_setpcap,
cap_net_bind_service,
cap_net_raw,
cap_sys_chroot,
cap_mknod,
cap_audit_write,
cap_setfcap+ep

# proc 에서 확인 : bit 별 Capabilities - 링크
kubectl exec -it sample-capabilities -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm:	00000000a80425fb
CapEff:	00000000a80425fb

# 파드에서 시간 변경 시도
kubectl exec -it sample-capabilities -- date
Sat Apr  1 02:43:58 UTC 2023

# 파드에서 시간 변경 시도
kubectl exec -it sample-capabilities -- date -s "12:00:00"
kubectl exec -it sample-capabilities -- date

---
# 파드가 배포된 워커노드 확인
NAME                            NOMINATED NODE
sample-capabilities2            i-038c6921803a6372b

# 파드가 배포된 워커노드 IP 확인 
kubectl get node -o wide | grep i-038c6921803a6372b
i-038c6921803a6372b   172.30.42.82    3.36.92.81

# 노드1로 접근
ssh ec2-user@$N1

# 노드1에 systemd-timesyncd 종료
root@i-038c6921803a6372b:~# systemctl stop systemd-timesyncd

# 파드에서 시간 변경 확인 >> 파드 변경 확인 후 노드의 date와 비교해보자
kubectl exec -it sample-capabilities2 -- date -s "12:00:00"
kubectl exec -it sample-capabilities2 -- date

 

파드에 Linux Capabilities 부여 및 삭제

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: sample-capabilities2
spec:
  containers:
  - name: nginx-container
    image: masayaaoyama/nginx:capsh
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]
        drop: ["AUDIT_WRITE"]
  terminationGracePeriodSeconds: 0
EOF

# 파드의 Linux Capabilities 기본 확인
kubectl exec -it sample-capabilities2 -- capsh --print | grep Current
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_admin,cap_net_raw,cap_sys_chroot,cap_sys_time,cap_mknod,cap_setfcap+ep
cap_chown,
cap_dac_override,
cap_fowner,
cap_fsetid,
cap_kill,
cap_setgid,
cap_setuid,
cap_setpcap,
cap_net_bind_service,
cap_net_admin, # 추가
cap_net_raw,
cap_sys_chroot,
cap_sys_time, # 추가
cap_mknod,
cap_setfcap+ep
# 제거됨 cap_audit_write

# 파드 상세 정보 확인
kubectl get pod sample-capabilities2 -o jsonpath={.spec.containers[0].securityContext} | jq
{
  "capabilities": {
    "add": [
      "NET_ADMIN",
      "SYS_TIME"
    ],
    "drop": [
      "AUDIT_WRITE"
    ]
  }
}

# proc 에서 확인 : bit 별 Capabilities - 링크
kubectl exec -it sample-capabilities2 -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm:	000000008a0435fb
CapEff:	000000008a0435fb

# 파드에서 시간 변경 시도 : 시간 동기화하는 다른 우선순위가 있는것 같습니다! 혹신 아시는 분들은 댓글 부탁드립니다!
## 확인해 보니 시간이 변경되었지만 해당 파드가 동작되는 노드의 시간을 sync 하고 있습니다. 
## 노드에 접근해서 systemd-timesyncd 종료하면은 노드의 바뀐 시간까지는 따라가는 것을 확인했습니다. 
## systemctl stop systemd-timesyncd 그리고 파드에서 시간 변경시 해당 노드의 시간도 바뀌는 것을 확인 했습니다.
kubectl exec -it sample-capabilities2 -- date
kubectl exec -it sample-capabilities2 -- date -s "12:00:00"
kubectl exec -it sample-capabilities2 -- date

 

특수 권한 컨테이너 생성 : 호스트와 동등한 권한 부여됨

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: sample-capabilities3
spec:
  containers:
  - name: nginx-container
    image: masayaaoyama/nginx:capsh
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      privileged: true
  terminationGracePeriodSeconds: 0
EOF

# 파드의 Linux Capabilities 기본 확인
kubectl exec -it sample-capabilities3 -- capsh --print | grep Current

# 파드 상세 정보 확인
kubectl get pod sample-capabilities3 -o jsonpath={.spec.containers[0].securityContext} | jq
{
  "privileged": true
}

# proc 에서 확인 : bit 별 Capabilities - 링크
kubectl exec -it sample-capabilities3 -- cat /proc/1/status | egrep 'CapPrm|CapEff'
CapPrm:	000001ffffffffff
CapEff:	000001ffffffffff

# 다음 실습을 위해서 파드 삭제
kubectl delete pod --all

 


파드 보안 컨텍스트

  • 파드 레벨에서 보안 컨텍스트를 적용 : 파드에 포함된 모든 컨테이너가 영향을 받음
  • 파드와 컨테이너 정책 중복 시, 컨테이너 정책이 우선 적용됨

출처 : CloudNet@ 스터디

 

컨테이너 보안 컨텍스트 확인 : kube-system 파드 내 컨테이너 대상

kubectl get pod -n kube-system -o jsonpath={.items[*].spec.securityContext} | jq
...

 

실행 사용자 변경

runuser 파드는 실행 사용자를 nobody(UID:65534) 사용자로 실행, 실행권한에 서브그룹 1001/1002 추가한다.

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: rundefault
spec:
  containers:
  - name: centos
    image: centos:7
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      readOnlyRootFilesystem: true
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: runuser
spec:
  securityContext:
    runAsUser: 65534
    runAsGroup: 65534
    supplementalGroups:
    - 1001
    - 1002
  containers:
  - name: centos
    image: centos:7
    command: ["tail"]
    args: ["-f", "/dev/null"]
    securityContext:
      readOnlyRootFilesystem: true
  terminationGracePeriodSeconds: 0
EOF

#
kubectl get pod rundefault -o jsonpath={.spec.securityContext} | jq
kubectl get pod runuser -o jsonpath={.spec.securityContext} | jq

# 실행 사용자 정보 확인
kubectl exec -it rundefault -- id
uid=0(root) gid=0(root) groups=0(root)

kubectl exec -it runuser    -- id
uid=65534 gid=65534 groups=65534,1001,1002

# 프로세스 정보 확인
kubectl exec -it rundefault -- ps -axo uid,user,gid,group,pid,comm
  UID USER       GID GROUP        PID COMMAND
    0 root         0 root           1 tail
    0 root         0 root          13 ps

kubectl exec -it runuser    -- ps -axo uid,user,gid,group,pid,comm
  UID USER       GID GROUP        PID COMMAND
65534 65534    65534 65534          1 tail
65534 65534    65534 65534         19 ps

 

root 사용자로 실행 제한

사용자를 변경하지 않고 단순히 root 사용자로 실행을 거부하도록 설정 시 동작은 어떻게 될까?

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: nonroot
spec:
  securityContext:
    runAsNonRoot: true
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 이벤트 확인
kubectl events --for pod/nonroot

 

파일 시스템 그룹 지정 

  • 일반적으로 마운트한 볼륨의 소유자와 그룹은 root:root로 되어 있다. 실행 사용자를 변경한 경우에는 마운트한 볼륨에 권한이 없는 경우가 있다
  • 따라서 마운트하는 볼륨의 그룹을 변경할 수 있도록 되어 있다 (setdig도 설정된다) - 예) emptyDir 혹은 PV 등 volumeMounts
#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: fsgoup1
spec:
  volumes:
  - name: vol1
    emptyDir: {}
  containers:
  - name: centos
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: vol1
      mountPath: /data/demo
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: fsgoup2
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  - name: vol2
    emptyDir: {}
  containers:
  - name: centos
    image: centos:7
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: vol2
      mountPath: /data/demo
  terminationGracePeriodSeconds: 0
EOF

#
kubectl get pod fsgoup1 -o jsonpath={.spec.securityContext} | jq
kubectl get pod fsgoup2 -o jsonpath={.spec.securityContext} | jq

# 실행 사용자 정보 확인
kubectl exec -it fsgoup1 -- id
uid=0(root) gid=0(root) groups=0(root)

kubectl exec -it fsgoup2 -- id
uid=1000 gid=3000 groups=3000,2000

# 프로세스 정보 확인
kubectl exec -it fsgoup1 -- ps -axo uid,user,gid,group,pid,comm
kubectl exec -it fsgoup2 -- ps -axo uid,user,gid,group,pid,comm

# 디렉터리 정보 확인 : fsgoup2파드의 마운트 볼륨 그룹의 GID가 2000 (fsGroup: 2000)
kubectl exec -it fsgoup1 -- ls -l /data
drwxrwxrwx 2 root root 4096 Apr  1 05:15 demo

kubectl exec -it fsgoup2 -- ls -l /data
drwxrwsrwx 2 root 2000 4096 Apr  1 05:15 demo

# fsgoup2파드에서 파일 생성 및 확인
kubectl exec -it fsgoup2 -- sh -c "echo write > /data/demo/sample.txt"
kubectl exec -it fsgoup2 -- cat /data/demo/sample.txt
kubectl exec -it fsgoup2 -- ls -l /data/demo/sample.txt
-rw-r--r-- 1 1000 2000 6 Apr  1 05:20 /data/demo/sample.txt

# fsgoup2파드에서 다른 디렉토리에 파일 생성 시도 >> 안되는 이유가 멀까요?
kubectl exec -it fsgoup2 -- sh -c "echo write > /data/sample.txt"
sh: /data/sample.txt: Permission denied
command terminated with exit code 1

 

sysctl을 사용한 커널 파라미터 설정

커널 파라미터 변경 적용을 위해서는 컨테이너에서도 설정 필요, 파드 수준 적용으로 컨테이너 간에 공유된다.

  • 링크
  • 커널 파라미터는 안전한 것(safe)과 안전하지 않은 것(unsafe)으로 분류된다.
    • 안전한 것 safe : 호스트의 커널과 적절하게 분리되어 있으며 다른 파드에 영향이 없는 것, 파드가 예상치 못한 리소스를 소비하지 않는 것
      • kernel.shm_rmid_forced
      • net.ipv4.ip_local_port_range
      • net.ipv4.tcp_syncookies
      • net.ipv4.ping_group_range (since Kubernetes 1.18),
      • net.ipv4.ip_unprivileged_port_start (since Kubernetes 1.22).
    • 안전하지 않은 것 unsafe : 사실상 대부분의 커널 파라미터 ⇒ 적용을 위해서는 kubelet 설정 필요
# Unsafe sysctls are enabled on a node-by-node basis with a flag of the kubelet
kubelet --allowed-unsafe-sysctls 'kernel.msg*,net.core.somaxconn' ...

 

unsafe 파라미터를 변경 시도한다.

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: unsafe
spec:
  securityContext:
    sysctls:
    - name: net.core.somaxconn
      value: "12345"
  containers:
    - name: centos-container
      image: centos:7
      command: ["/bin/sleep", "3600"]
  terminationGracePeriodSeconds: 0
EOF

# 
kubectl events --for pod/unsafe
LAST SEEN   TYPE      REASON            OBJECT       MESSAGE
4s          Normal    Scheduled         Pod/unsafe   Successfully assigned default/unsafe to i-01af337c3d1004e24

 

safe 파라미터 수정 

sysctl2 파드가 배포된 노드의 net.ipv4.ip_local_port_range 값과 다를 경우에는 어떻게 동작할까요?

#
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: sysctl1
spec:
  containers:
    - name: centos-container
      image: centos:7
      command: ["/bin/sleep", "3600"]
  terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
  name: sysctl2
spec:
  securityContext:
    sysctls:
    - name: net.ipv4.ip_local_port_range
      value: "1025 61000"
  containers:
    - name: centos-container
      image: centos:7
      command: ["/bin/sleep", "3600"]
  terminationGracePeriodSeconds: 0
EOF

#
kubectl get pod sysctl1 -o jsonpath={.spec.securityContext} | jq
kubectl get pod sysctl2 -o jsonpath={.spec.securityContext} | jq

# 
kubectl exec -it sysctl1 -- sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 60999

kubectl exec -it sysctl2 -- sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 1025	61000

 

initContainer 와 privileged 를 활용하여 unsafe 커널 파라미터를 강제로 변경한다.

#
curl -s -O https://raw.githubusercontent.com/MasayaAoyama/kubernetes-perfect-guide/ko/2nd-edition/samples/chapter13/sample-sysctl-initcontainer.yaml
cat sample-sysctl-initcontainer.yaml| yh
kubectl apply -f sample-sysctl-initcontainer.yaml

#
kubectl describe pod sample-sysctl-initcontainer
kubectl get pod sample-sysctl-initcontainer -o json | jq

# 확인
kubectl exec -it sample-sysctl-initcontainer -c tools-container -- sysctl net.core.somaxconn
net.core.somaxconn = 12345

# 다음 실습을 위해서 파드 삭제 
kubectl delete pod --all

 


 

(실습 완료 후) 자원 삭제

 

1. testuser IAM User는 AWS 웹 관리콘솔에서 삭제

 

2. DVWA Ingress 삭제

kubectl delete ingress ingress-dvwa

 

3. Helm Chart 삭제

helm uninstall -n monitoring kube-prometheus-stack

 

4. EKS 클러스터 삭제

eksctl delete cluster --name $CLUSTER_NAME && aws cloudformation delete-stack --stack-name $CLUSTER_NAME

 


Reference

  1. [커피고래님] - 인증 시리즈 X.509 HTTP인증 OpenID Connect Webhook Proxy인증 , Admisstion Control
  2. AWS Cross-Accounts IRSA 적용기 - 링크
  3. [learnk8s] User and workload identities in Kubernetes - 링크
    • Limiting access to Kubernetes resources with RBAC - 링크
    • Implementing a custom Kubernetes authentication method - 링크
    • Authentication between microservices using Kubernetes identities - 링크
  4. [Youtube] 쿠버네티스 해킹과 방어 (데모 포함)
    • The Hacker's Guide to Kubernetes - Patrycja Wegrzynowicz, Form3 - 링크
    • Hacking & Defending Kubernetes Clusters - 링크
  5. [Youtube] Kubecon 2023 Europe
    • Keycloak: The Open-Source IAM for Modern Application - 링크
    • Kyverno Introduction and Deep Dive - 링크
    • Open Policy Agent. (OPA) Intro & Deep Dive - 링크

 

 

728x90
반응형