1. 쿠버네티스의 보안
- 인증/인가
- kube/config
- 쿠버네티스의 API 접근 제어
2. 실습환경
3. EKS IRSA & Pod Identity
4. OWASP Kubernetes Top Ten
쿠버네티스(Kubernetes)에서 사용하는 암호화 방식
| 암호화 유형 | 대상 및 목적 | 사용 기술 및 알고리즘 | 특징 및 설명 |
| Encryption at Rest (저장 시 암호화) |
- etcd에 저장되는 데이터 보호 - Secret, ConfigMap, PersistentVolumeClaim 등 민감 정보 |
- AES-GCM - AES-CBC - Secretbox |
- 데이터를 etcd에 저장할 때 암호화하여 디스크 상의 데이터를 보호 - API서버 설정(EncryptionConfiguration)으로 적용 |
| Encryption in Transit (데이터 전송 암호화) |
- 클러스터 내 컴포넌트 간 통신 보호 - 마스터 <-> 워커노드 간 통신 보호 - 사용자와 API 서버 간 통신 보호 |
- TLS 1.2 이상 (TLS 1.3 권장) - X.509 인증서 기반 |
- API 서버는 기본적으로 HTTPS 통신 - 클러스터 내 모든 컴포넌트 간 통신을 TLS로 암호화하여 중간자 공격 방지 |
| Secret 관리 및 보호 | - 쿠버네티스의 Secret 오브젝트를 보호하기 위한 추가 보안 조치 | - 외부 보안 솔루션(HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager 등) - CSI 드라이버(Secrets Store CSI Driver) |
- 기본 Secret은 단순 Base64 인코딩으로 안전하지 않음 - 외부 Secret 관리 솔루션을 통해 강력한 암호화와 접근 제어 가능 |
| Service Mesh 암호화 (Mutual TLS, mTLS) |
- Pod 간 통신 보호 - 클러스터 내 서비스 간 요청과 응답 데이터 암호화 - Pod 간 통신을 추가적으로 보호 |
- Istio, Linkerd, Consul Connect 등 서비스 메쉬 기술 - Mutual TLS (mTLS) - X.509 인증서 기반 |
- 클러스터 내부 네트워크 트래픽까지 보호 가능 - 서비스 간 상호 인증을 통한 인증된 통신 및 데이터 무결성 확보 |
| 컨테이너 이미지 암호화 | - 컨테이너 레지스트리에 저장된 컨테이너 이미지 보호 - 악의적 접근 및 변조 방지 |
- OCI(컨테이너 이미지) 서명 및 검증 기술 - Notary, Cosign 등 이미지 서명 도구 활용 - 레지스트리 데이터 암호화(storage encryption) |
- 컨테이너 이미지 무결성 보장 - 신뢰 가능한 이미지만 클러스터에 배포 |
| 노드 및 호스트 디스크 암호화 | - 쿠버네티스 노드의 운영체제와 호스트 디스크 데이터 보호 - 전체 노드 레벨의 데이터 보호 |
- OS 레벨 디스크 암호화 (예: LUKS, AWS EBS 암호화 등) | - 노드 또는 VM 디스크가 탈취되더라도 데이터 보호 가능 - OS/클라우드 공급자 레벨에서 지원됨 |
| 쿠버네티스 인증서 관리 | - 클러스터 내부 인증서 및 API 서버 인증서의 안전한 관리 및 주기적 갱신 | - cert-manager, kubeadm 등 인증서 관리 도구 - 자동 갱신(Automated rotation) 사용 |
- 인증서 유효기간 만료로 인한 보안 사고 예방 - 인증서 관리를 자동화하여 실수 방지 |
| 사용자 인증 및 접근제어(RBAC) | - 인증된 사용자와 서비스 어카운트만 API 접근 허용 - 권한 있는 리소스 접근만 허용 |
- RBAC (역할 기반 접근제어) - OIDC, LDAP, OAuth2 기반 인증 |
- API 접근권한 최소화 - 무단 접근 및 정보 유출 방지 |
EKS 인증/인가
| 인증 | IAM 기반 인증 | IAM 사용자/역할로 클러스터 접근 | eksctl, aws CLI |
| 인증 | IRSA | Kubernetes의 Service Account에 IAM 역할 매핑 | Pod AWS API 사용 |
| 인증 | OIDC 연동 | 외부 IdP와 연동하여 인증 수행 | Okta, Azure AD 연동 |
| 인가 | RBAC | 리소스 접근 권한을 세부적으로 설정 | Role, ClusterRole |
| 인가 | IAM과 RBAC 연동 | IAM 인증과 RBAC 정책을 함께 사용하여 인가 | aws-auth ConfigMap |
인증

인가

쿠버네티스의 API 접근 제어
| 인증 (Authentication) |
API 요청자의 신원을 확인합니다. | - 클라이언트 인증서 - 베어러 토큰 - HTTP 기본 인증 - 익명 요청 |
kubeconfig의 클라이언트 인증서, ServiceAccount 토큰 |
| 인가 (Authorization) |
인증된 사용자가 수행할 수 있는 작업의 권한을 확인합니다. | - RBAC (Role-based Access Control) - ABAC (Attribute-based Access Control) - Node 인증 - Webhook 인증 |
Role, RoleBinding 설정 |
| 어드미션 제어 (Admission Control) |
클러스터 정책에 따라 요청을 승인, 수정, 거부합니다. | - 리소스 쿼터(ResourceQuota) - PodSecurityAdmission - LimitRanger - ValidatingAdmissionWebhook - MutatingAdmissionWebhook |
파드 생성 시 CPU, Memory 제한, 보안 정책 적용 |
.kube/config는 일반적으로 다음과 같은 항목을 가진다.
- clusters: 접근할 Kubernetes 클러스터 정보를 정의
- users: 사용자 인증 정보를 정의
- contexts: 클러스터, 사용자, 네임스페이스를 묶어 컨텍스트를 정의
- current-context: 현재 사용 중인 컨텍스트 지정
| 섹션 | 하위필드 | 설명 | 비고 |
| clusters | name | 클러스터의 고유 이름 | kubectl config에서 사용 |
| server | Kubernetes API 서버 URL | API 서버의 HTTPS 주소 | |
| certificate-authority certificate-authority-data |
클러스터 인증에 사용되는 CA 인증서 | base64 인코딩된 데이터 또는 파일경로 지정 | |
| users | name | 사용자 식별 이름 | |
| user.exec | 인증 명령어 설정(예: AWS IAM 인증) | IAM Authenticator 등 | |
| user.client-certificate user.client-key |
클라이언트 TLS 인증서 및 키 | 클라이언트 인증서 방식 | |
| user.token | 직접 베어러 토큰을 지정할 경우 사용 | 토큰 인증 방식 | |
| contexts | name | 컨텍스트의 고유 이름 | 사용자가 지정한 컨텍스트 이름 |
| context.cluster | 연결할 클러스터 지정 | clusters의 이름 | |
| user | 사용할 사용자 정보 | users에서 정의된 이름 | |
| namespace | 기본 네임스페이스 | 생략 가능 | |
| current-context | - | 현재 사용할 컨텍스트 이름 | kubectl 명령어가 기본으로 사용 |
.kube/config 예시
| apiVersion: v1 kind: Config preferences: {} clusters: - cluster: certificate-authority-data: <base64 encoded CA certificate> server: https://123456789ABCDEF.gr7.ap-northeast-2.eks.amazonaws.com name: my-cluster users: - name: my-user user: exec: apiVersion: client.authentication.k8s.io/v1beta1 command: aws args: - eks - get-token - --cluster-name - my-cluster - --region - ap-northeast-2 contexts: - context: cluster: my-cluster user: my-user namespace: default name: my-context current-context: my-context |
실습환경
| # YAML 파일 다운로드 curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/myeks-6week.yaml # 변수 지정 CLUSTER_NAME=myeks SSHKEYNAME=<SSH 키 페이 이름> MYACCESSKEY=<IAM Uesr 액세스 키> MYSECRETKEY=<IAM Uesr 시크릿 키> # CloudFormation 스택 배포 aws cloudformation deploy --template-file myeks-6week.yaml --stack-name $CLUSTER_NAME --parameter-overrides KeyName=$SSHKEYNAME SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 MyIamUserAccessKeyID=$MYACCESSKEY MyIamUserSecretAccessKey=$MYSECRETKEY ClusterBaseName=$CLUSTER_NAME --region ap-northeast-2 # CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력 aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text |
| #eksctl get cluster # kubeconfig 생성 aws eks update-kubeconfig --name myeks --user-alias $(aws sts get-caller-identity --query Arn --output text) aws eks update-kubeconfig --name myeks --user-alias admin # kubectl ns default kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone kubectl get pod -A kubectl get pdb -n kube-system |
| # EC2 공인 IP 변수 지정 # *remoteAccess* 포함된 보안그룹 ID aws ec2 describe-security-groups --filters "Name=group-name,Values=*remoteAccess*" | jq export MNSGID=$(aws ec2 describe-security-groups --filters "Name=group-name,Values=*remoteAccess*" --query 'SecurityGroups[*].GroupId' --output text) # 해당 보안그룹 inbound 에 자신의 집 공인 IP 룰 추가 aws ec2 authorize-security-group-ingress --group-id $MNSGID --protocol '-1' --cidr $(curl -s ipinfo.io/ip)/32 # 해당 보안그룹 inbound 에 운영서버 내부 IP 룰 추가 aws ec2 authorize-security-group-ingress --group-id $MNSGID --protocol '-1' --cidr 172.20.1.100/32 aws ec2 authorize-security-group-ingress --group-id $MNSGID --protocol '-1' --cidr 172.20.1.200/32 # 워커 노드 SSH 접속 for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -o StrictHostKeyChecking=no ec2-user@$i hostname; echo; done |
| # default 네임스페이스 적용 kubectl ns default # 환경변수 정보 확인 export | egrep 'ACCOUNT|AWS_|CLUSTER|KUBERNETES|VPC|Subnet' export | egrep 'ACCOUNT|AWS_|CLUSTER|KUBERNETES|VPC|Subnet' | egrep -v 'KEY' # krew 플러그인 확인 : 보안 관련 플러그인 다수 설치 kubectl krew list ![]() # 인스턴스 정보 확인 aws ec2 describe-instances --query "Reservations[*].Instances[*].{InstanceID:InstanceId, PublicIPAdd:PublicIpAddress, PrivateIPAdd:PrivateIpAddress, InstanceName:Tags[?Key=='Name']|[0].Value, Status:State.Name}" --filters Name=instance-state-name,Values=running --output table # 노드 IP 확인 및 PrivateIP 변수 지정 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 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 # 노드 IP 로 ping 테스트 for i in $N1 $N2 $N3; do echo ">> node $i <<"; ping -c 1 $i ; echo; done # 변수 지정 export CLUSTER_NAME=myeks export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" --query 'Vpcs[*].VpcId' --output text) export PubSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text) export PubSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text) export PubSubnet3=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet3" --query "Subnets[0].[SubnetId]" --output text) export N1=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2a" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text) export N2=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2b" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text) export N3=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node" "Name=availability-zone,Values=ap-northeast-2c" --query 'Reservations[*].Instances[*].PublicIpAddress' --output text) export CERT_ARN=$(aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text) #사용 리전의 인증서 ARN 확인 MyDomain=hey-aws.click MyDnzHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "$MyDomain." --query "HostedZones[0].Id" --output text) echo $CLUSTER_NAME $VPCID $PubSubnet1 $PubSubnet2 $PubSubnet3 echo $N1 $N2 $N3 $MyDomain $MyDnzHostedZoneId tail -n 15 ~/.bashrc |
| # AWS LoadBalancerController helm repo add eks https://aws.github.io/eks-charts 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 # ExternalDNS echo $MyDomain curl -s https://raw.githubusercontent.com/gasida/PKOS/main/aews/externaldns.yaml | MyDomain=$MyDomain MyDnzHostedZoneId=$MyDnzHostedZoneId envsubst | kubectl apply -f - # gp3 스토리지 클래스 생성 cat <<EOF | kubectl apply -f - kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: gp3 annotations: storageclass.kubernetes.io/is-default-class: "true" allowVolumeExpansion: true provisioner: ebs.csi.aws.com volumeBindingMode: WaitForFirstConsumer parameters: type: gp3 allowAutoIOPSPerGBIncrease: 'true' encrypted: 'true' fsType: xfs # 기본값이 ext4 EOF kubectl get sc # kube-ops-view helm repo add geek-cookbook https://geek-cookbook.github.io/charts/ https://geek-cookbook.github.io/charts helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=ClusterIP --set env.TZ="Asia/Seoul" --namespace kube-system # kubeopsview 용 Ingress 설정 : group 설정으로 1대의 ALB를 여러개의 ingress 에서 공용 사용 echo $CERT_ARN cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN alb.ingress.kubernetes.io/group.name: study alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]' alb.ingress.kubernetes.io/load-balancer-name: $CLUSTER_NAME-ingress-alb alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/ssl-redirect: "443" alb.ingress.kubernetes.io/success-codes: 200-399 alb.ingress.kubernetes.io/target-type: ip labels: app.kubernetes.io/name: kubeopsview name: kubeopsview namespace: kube-system spec: ingressClassName: alb rules: - host: kubeopsview.$MyDomain http: paths: - backend: service: name: kube-ops-view port: number: 8080 # name: http path: / pathType: Prefix EOF # 설치된 파드 정보 확인 kubectl get pods -n kube-system # service, ep, ingress 확인 kubectl get ingress,svc,ep -n kube-system # Kube Ops View 접속 정보 확인 : 조금 오래 기다리면 접속됨... echo -e "Kube Ops View URL = https://kubeopsview.$MyDomain/#scale=1.5" open "https://kubeopsview.$MyDomain/#scale=1.5" # macOS |

| # 네임스페이스(Namespace, NS) 생성 및 확인 kubectl create namespace dev-team kubectl create ns infra-team # 네임스페이스 확인 kubectl get ns # 네임스페이스에 각각 서비스 어카운트 생성 : serviceaccounts 약자(=sa) kubectl create sa dev-k8s -n dev-team kubectl create sa infra-k8s -n infra-team # 서비스 어카운트 정보 확인 kubectl get sa -n dev-team kubectl get sa dev-k8s -n dev-team -o yaml kubectl get sa -n infra-team kubectl get sa infra-k8s -n infra-team -o yaml kubectl create token dev-k8s -n dev-team ![]() |
dev-k8s 서비스어카운트의 토큰 정보 확인
- https://jwt.io/ → Bearer type - JWT(JSON Web Token) 확인

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

| # 각각 네임스피이스에 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.31.4 command: ["tail"] args: ["-f", "/dev/null"] terminationGracePeriodSeconds: 0 EOF cat <<EOF | kubectl create -f - apiVersion: v1 kind: Pod metadata: name: infra-kubectl namespace: infra-team spec: serviceAccountName: infra-k8s containers: - name: kubectl-pod image: bitnami/kubectl:1.31.4 command: ["tail"] args: ["-f", "/dev/null"] terminationGracePeriodSeconds: 0 EOF # 확인 kubectl get pod -A kubectl get pod -o dev-kubectl -n dev-team -o yaml serviceAccount: dev-k8s ... kubectl get pod -o infra-kubectl -n infra-team -o yaml serviceAccount: infra-k8s ... # 파드에 기본 적용되는 서비스 어카운트(토큰) 정보 확인 kubectl exec -it dev-kubectl -n dev-team -- ls /run/secrets/kubernetes.io/serviceaccount kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/token kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/namespace kubectl exec -it dev-kubectl -n dev-team -- cat /run/secrets/kubernetes.io/serviceaccount/ca.crt ![]() # 각각 파드로 Shell 접속하여 정보 확인 : 단축 명령어(alias) 사용 alias k1='kubectl exec -it dev-kubectl -n dev-team -- kubectl' alias k2='kubectl exec -it infra-kubectl -n infra-team -- kubectl' # 권한 테스트 - 권한없음 k1 get pods # kubectl exec -it dev-kubectl -n dev-team -- kubectl get pods 와 동일한 실행 명령이다! k1 run nginx --image nginx:1.20-alpine k1 get pods -n kube-system k2 get pods # kubectl exec -it infra-kubectl -n infra-team -- kubectl get pods 와 동일한 실행 명령이다! k2 run nginx --image nginx:1.20-alpine k2 get pods -n kube-system # kubectl auth can-i 로 kubectl 실행 사용자가 특정 권한을 가졌는지 확인 k1 auth can-i get pods no |
- 각각 네임스페이스에 롤(Role)를 생성 후 서비스 어카운트 바인딩
- 롤(Role): apiGroups 와 resources 로 지정된 리소스에 대해 verbs 권한을 인가
- 실행 가능한 조작(verbs) : *(모두 처리), create(생성), delete(삭제), get(조회), list(목록조회), patch(일부업데이트), update(업데이트), watch(변경감시)
| # Print the supported API resources on the server. kubectl api-resources # Print the supported API resources with more information kubectl api-resources -o wide # Print the supported API resources with a specific APIGroup kubectl api-resources --api-group="" kubectl api-resources --api-group="apps" kubectl api-resources --api-group=metrics.k8s.io kubectl api-resources --api-group=admissionregistration.k8s.io kubectl api-resources --api-group=rbac.authorization.k8s.io kubectl api-resources --api-group=apiextensions.k8s.io ![]() # 각각 네임스페이스내의 모든 권한에 대한 롤 생성 cat <<EOF | kubectl create -f - apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: role-dev-team namespace: dev-team rules: - apiGroups: ["*"] resources: ["*"] verbs: ["*"] EOF cat <<EOF | kubectl create -f - apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: role-infra-team namespace: infra-team rules: - apiGroups: ["*"] resources: ["*"] verbs: ["*"] EOF # 롤 확인 kubectl get roles -n dev-team kubectl get roles -n infra-team kubectl get roles -n dev-team -o yaml kubectl describe roles role-dev-team -n dev-team Name: role-dev-team Labels: <none> Annotations: <none> PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- *.* [] [] [*] # 롤바인딩 생성 : '서비스어카운트 <-> 롤' 간 서로 연동 cat <<EOF | kubectl create -f - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: roleB-dev-team namespace: dev-team roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: role-dev-team subjects: - kind: ServiceAccount name: dev-k8s namespace: dev-team EOF cat <<EOF | kubectl create -f - apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: roleB-infra-team namespace: infra-team roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: role-infra-team subjects: - kind: ServiceAccount name: infra-k8s namespace: infra-team EOF # 롤바인딩 확인 kubectl get rolebindings -n dev-team kubectl get rolebindings -n infra-team kubectl get rolebindings -n dev-team -o yaml kubectl describe rolebindings roleB-dev-team -n dev-team Name: roleB-dev-team Labels: <none> Annotations: <none> 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 pods -n kube-system -v=6 k1 get nodes k1 get nodes -v=6 k2 get pods k2 run nginx --image nginx:1.20-alpine k2 get pods k2 delete pods nginx k2 get pods -n kube-system k2 get nodes # (옵션) kubectl auth can-i 로 kubectl 실행 사용자가 특정 권한을 가졌는지 확인 k1 auth can-i get pods yes ![]() |
2. EKS 인증/인가
동작 : 사용자/애플리케이션 → k8s 사용 시 ⇒ 인증은 AWS IAM, 인가는 K8S RBAC

- 롤(Role) : apiGroups 와 resources 로 지정된 리소스에 대해 verbs 권한을 인가
- 실행 가능한 조작(verbs) : *(모두 처리), create(생성), delete(삭제), get(조회), list(목록조회), patch(일부업데이트), update(업데이트), watch(변경감시)
| # 설치 kubectl krew install access-matrix rbac-tool rolesum whoami # k8s 인증된 주체 확인 kubectl whoami arn:aws:iam::9112...:user/admin # Show an RBAC access matrix for server resources kubectl access-matrix -h kubectl access-matrix # Review access to cluster-scoped resources LIST CREATE UPDATE DELETE apiservices.apiregistration.k8s.io ✔ ✔ ✔ ✔ kubectl access-matrix --namespace default # Review access to namespaced resources in 'default' configmaps ✔ ✔ ✔ ✔ # RBAC Lookup by subject (user/group/serviceaccount) name 특정 ServiceAccount나 유저가 어떤 권한을 가지고 있는지 확인. 특정 리소스에 접근 가능한 계정 또는 서비스어카운트를 확인할 수 있음. 어떤 role이나 clusterrole이 어떤 리소스에 어떤 액션을 허용하는지 보기 쉽게 보여줌. kubectl rbac-tool -h 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 policy-rules -e '^system:authenticated' # Generate ClusterRole with all available permissions from the target cluster kubectl rbac-tool show ![]() # Shows the subject for the current context with which one authenticates with the cluster kubectl rbac-tool whoami ![]() # Summarize RBAC roles for subjects : ServiceAccount(default), User, Group kubectl rolesum -h 주체별 역할 요약: 특정 서비스 어카운트, 사용자, 또는 그룹에 할당된 역할과 권한을 요약하여 표시 kubectl rolesum aws-node -n kube-system # sa kubectl rolesum -k User system:kube-proxy # user kubectl rolesum -k Group system:masters # group kubectl rolesum -k Group system:nodes kubectl rolesum -k Group system:authenticated
![]() # [운영서버1 EC2 : 터미널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 ## 이후 해당 운영서버1 EC2 공인 IP:8800 웹 접속 : 최초 접속 후 정보 가져오는데 다시 시간 걸림 (2~3분 정도 후 화면 출력됨) echo -e "RBAC View Web http://$(curl -s ipinfo.io/ip):8800" ![]() |
kubectl rbac-tool visualize

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

1. kubectl 명령 → aws eks get-token → STS에 토큰 요청 ⇒ 응답값 디코드(Pre-Signed URL 이며 GetCallerIdentity..)
| # sts caller id의 ARN 확인 aws sts get-caller-identity --query Arn "arn:aws:iam::<자신의 Account ID>:user/admin" # kubeconfig 정보 확인 cat ~/.kube/config ... - name: admin@myeks.ap-northeast-2.eksctl.io user: exec: apiVersion: client.authentication.k8s.io/v1beta1 args: - eks - get-token - --output - json - --cluster-name - myeks - --region - ap-northeast-2 command: aws env: - name: AWS_STS_REGIONAL_ENDPOINTS value: regional interactiveMode: IfAvailable provideClusterInfo: false # Get a token for authentication with an Amazon EKS cluster. # This can be used as an alternative to the aws-iam-authenticator. aws eks get-token help # 임시 보안 자격 증명(토큰)을 요청 : expirationTimestamp 시간경과 시 토큰 재발급됨 aws eks get-token --cluster-name $CLUSTER_NAME | jq aws eks get-token --cluster-name $CLUSTER_NAME | jq -r '.status.token' aws eks get-token --cluster-name $CLUSTER_NAME --debug | jq "expirationTimestamp": "2025-03-11T12:56:14Z", |
[ConfigMap 방식] 이제 쿠버네티스 RBAC 인가를 처리
| kubectl get cm -n kube-system aws-auth -o yaml apiVersion: v1 data: mapRoles: | - groups: - system:bootstrappers - system:nodes rolearn: arn:aws:iam::866816795:role/eksctl-myeks-nodegroup-ng1-NodeInstanceRole-yiaf3M53O username: system:node:{{EC2PrivateDNSName}} kind: ConfigMap metadata: creationTimestamp: "2025-03-11T11:38:02Z" name: aws-auth namespace: kube-system resourceVersion: "1975" uid: c7b7c38f-2e24-4b32-894b-9e89d3bda67b (chunki:default) [root@operator-host ~]# kubectl rbac-tool lookup system:authenticated SUBJECT | SUBJECT TYPE | SCOPE | NAMESPACE | ROLE | BINDING -----------------------+--------------+-------------+-----------+---------------------------+---------------------------- system:authenticated | Group | ClusterRole | | system:basic-user | system:basic-user system:authenticated | Group | ClusterRole | | system:discovery | system:discovery system:authenticated | Group | ClusterRole | | system:public-info-viewer | system:public-info-viewer (chunki:default) [root@operator-host ~]# kubectl rolesum -k Group system:masters Group: system:masters Policies: • [CRB] */cluster-admin ⟶ [CR] */cluster-admin Resource Name Exclude Verbs G L W C U P D DC *.* [*] [-] [-] ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ (chunki:default) [root@operator-host ~]# kubectl rolesum -k Group system:authenticated Group: system:authenticated Policies: • [CRB] */system:basic-user ⟶ [CR] */system:basic-user Resource Name Exclude Verbs G L W C U P D DC selfsubjectaccessreviews.authorization.k8s.io [*] [-] [-] ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖ selfsubjectreviews.authentication.k8s.io [*] [-] [-] ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖ selfsubjectrulesreviews.authorization.k8s.io [*] [-] [-] ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖ • [CRB] */system:discovery ⟶ [CR] */system:discovery • [CRB] */system:public-info-viewer ⟶ [CR] */system:public-info-viewer 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 |
| # testuser 사용자 생성 aws iam create-user --user-name testuser # 사용자에게 프로그래밍 방식 액세스 권한 부여 aws iam create-access-key --user-name testuser { "AccessKey": { "UserName": "testuser", "AccessKeyId": "AKIA5ILF2##", "Status": "Active", "SecretAccessKey": "TxhhwsU8##", "CreateDate": "2023-05-23T07:40:09+00:00" } } # testuser 사용자에 정책을 추가 aws iam attach-user-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --user-name testuser |
2번서버에서 testuser로 접근
| # get-caller-identity 확인 >> 왜 안될까요? aws sts get-caller-identity --query Arn # testuser 자격증명 설정 aws configure AWS Access Key ID [None]: AKIA5ILF2F... AWS Secret Access Key [None]: ePpXdhA3cP.... Default region name [None]: ap-northeast-2 # get-caller-identity 확인 aws sts get-caller-identity --query Arn "arn:aws:iam::911283464785:user/testuser" kubectl get node -v6 ![]() 권한 복사(기존계정에서) 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 mapUsers: | - groups: - system:masters userarn: arn:aws:iam::826816795:user/testuser username: testuser kubectl ns default ![]() |
[myeks-bastion] testuser 의 Group 변경(system:masters → system:authenticated)으로 RBAC 동작 확인
| # 방안2 : 아래 edit로 mapUsers 내용 직접 수정 system:masters -> system:authenticated을 복원한다 kubectl edit cm -n kube-system aws-auth ![]() [myeks-bastion-2] testuser kubectl 사용 확인 권한이 없어진것을 확인할수있다. ![]() |
3. EKS IRSA & Pod Identity
- Service Account Token Volume Projection, Admission Control, JWT(JSON Web Token), OIDC
- 서비스 계정 토큰을 이용해서 서비스와 서비스, 즉 파드(pod)와 파드(pod)의 호출에서 자격 증명으로 사용가능?
- 불행히도 기본 서비스 계정 토큰으로는 사용하기에 부족함이 있습니다. 토큰을 사용하는 대상(audience), 유효 기간(expiration) 등 토큰의 속성을 지정할 필요가 있기 때문입니다.
- `Service Account Token Volume Projection` 기능을 사용하면 이러한 부족한 점들을 해결할 수 있습니다.
| 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 바인딩된 서비스 어카운트 토큰 볼륨
| apiVersion: v1 kind: Pod metadata: name: test-projected-volume spec: containers: - name: test-projected-volume image: busybox:1.28 args: - sleep - "86400" volumeMounts: - name: all-in-one mountPath: "/projected-volume" readOnly: true volumes: - name: all-in-one projected: sources: - secret: name: user - secret: name: pass # Create the Secrets: ## Create files containing the username and password: echo -n "admin" > ./username.txt echo -n "1f2d1e2abcdf" > ./password.txt ## Package these files into secrets: kubectl create secret generic user --from-file=./username.txt kubectl create secret generic pass --from-file=./password.txt # 파드 생성 kubectl apply -f https://k8s.io/examples/pods/storage/projected.yaml # 파드 확인 kubectl get pod test-projected-volume -o yaml | kubectl neat ... preemptionPolicy: PreemptLowerPriority priority: 0 serviceAccountName: default tolerations: - effect: NoExecute key: node.kubernetes.io/not-ready operator: Exists tolerationSeconds: 300 - effect: NoExecute key: node.kubernetes.io/unreachable operator: Exists tolerationSeconds: 300 volumes: - name: all-in-one projected: sources: - secret: name: user - secret: name: pass - name: kube-api-access-d6blw projected: sources: - serviceAccountToken: expirationSeconds: 3607 path: token - configMap: items: - key: ca.crt path: ca.crt name: kube-root-ca.crt - downwardAPI: items: - fieldRef: fieldPath: metadata.namespace path: namespace # 시크릿 확인 kubectl exec -it test-projected-volume -- ls /projected-volume/ password.txt username.txt ![]() # 삭제 kubectl delete pod test-projected-volume && kubectl delete secret user pass |
k8s api 접근 단계

IRSA 소개 : 파드가 특정 IAM 역할로 Assume 할때 토큰을 AWS에 전송하고, AWS는 토큰과 EKS IdP를 통해 해당 IAM 역할을 사용할 수 있는지 검증


| # 파드1 생성 cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: eks-iam-test1 spec: containers: - name: my-aws-cli image: amazon/aws-cli:latest args: ['s3', 'ls'] restartPolicy: Never automountServiceAccountToken: false terminationGracePeriodSeconds: 0 EOF # 확인 kubectl get pod kubectl describe pod # 로그 확인 kubectl logs eks-iam-test1 .... because no identity-based policy allows the s3:ListAllMyBuckets action # 파드1 삭제 kubectl delete pod eks-iam-test1 # 파드2 생성 cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: eks-iam-test2 spec: containers: - name: my-aws-cli image: amazon/aws-cli:latest command: ['sleep', '36000'] restartPolicy: Never terminationGracePeriodSeconds: 0 EOF # 확인 kubectl get pod kubectl describe pod kubectl get pod eks-iam-test2 -o yaml kubectl exec -it eks-iam-test2 -- ls /var/run/secrets/kubernetes.io/serviceaccount kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token ;echo # aws 서비스 사용 시도 kubectl exec -it eks-iam-test2 -- aws s3 ls - no identity-based policy allows the s3:ListAllMyBuckets action # 서비스 어카운트 토큰 확인 SA_TOKEN=$(kubectl exec -it eks-iam-test2 -- cat /var/run/secrets/kubernetes.io/serviceaccount/token) echo $SA_TOKEN # jwt 혹은 아래 JWT 웹 사이트 이용 https://jwt.io/ jwt decode $SA_TOKEN --json --iso8601 ... ![]() #헤더 { "alg": "RS256", "kid": "1a8fcaee12b3a8f191327b5e9b997487ae93baab" } # 페이로드 : OAuth2에서 쓰이는 aud, exp 속성 확인! > projectedServiceAccountToken 기능으로 토큰에 audience,exp 항목을 덧붙힘 ## iss 속성 : EKS OpenID Connect Provider(EKS IdP) 주소 > 이 EKS IdP를 통해 쿠버네티스가 발급한 토큰이 유요한지 검증 { "aud": [ "https://kubernetes.default.svc" # 해당 주소는 k8s api의 ClusterIP 서비스 주소 도메인명, kubectl get svc kubernetes ], # 파드2 삭제 kubectl delete pod eks-iam-test2 |
| # Create an iamserviceaccount - AWS IAM role bound to a Kubernetes service account eksctl create iamserviceaccount \ --name my-sa \ --namespace default \ --cluster $CLUSTER_NAME \ --approve \ --attach-policy-arn $(aws iam list-policies --query 'Policies[?PolicyName==`AmazonS3ReadOnlyAccess`].Arn' --output text) # 확인 >> 웹 관리 콘솔에서 CloudFormation Stack >> IAM Role 확인 # aws-load-balancer-controller IRSA는 어떤 동작을 수행할 것 인지 생각해보자! eksctl get iamserviceaccount --cluster $CLUSTER_NAME # Inspecting the newly created Kubernetes Service Account, we can see the role we want it to assume in our pod. kubectl get sa kubectl describe sa my-sa Name: my-sa Namespace: default Labels: app.kubernetes.io/managed-by=eksctl Annotations: eks.amazonaws.com/role-arn: arn:aws:iam::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-1MJUYW59O6QGH Image pull secrets: <none> Mountable secrets: <none> Tokens: <none> Events: <none> # 파드3번 생성 cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: eks-iam-test3 spec: serviceAccountName: my-sa containers: - name: my-aws-cli image: amazon/aws-cli:latest command: ['sleep', '36000'] restartPolicy: Never terminationGracePeriodSeconds: 0 EOF # 해당 SA를 파드가 사용 시 mutatingwebhook으로 Env,Volume 추가함 : AWS IAM 역할을 Pod에 자동으로 주입 kubectl get mutatingwebhookconfigurations pod-identity-webhook -o yaml # Pod Identity Webhook은 mutating webhook을 통해 아래 Env 내용과 1개의 볼륨을 추가함 kubectl get pod eks-iam-test3 kubectl get pod eks-iam-test3 -o yaml ... volumeMounts: - mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount name: aws-iam-token readOnly: true ... volumes: - name: aws-iam-token projected: sources: - serviceAccountToken: audience: sts.amazonaws.com expirationSeconds: 86400 path: token ... kubectl exec -it eks-iam-test3 -- ls /var/run/secrets/eks.amazonaws.com/serviceaccount token kubectl exec -it eks-iam-test3 -- cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token ; echo ... ![]() kubectl describe pod eks-iam-test3 ... Environment: AWS_STS_REGIONAL_ENDPOINTS: regional AWS_DEFAULT_REGION: ap-northeast-2 AWS_REGION: ap-northeast-2 AWS_ROLE_ARN: arn:aws:iam::911283464785:role/eksctl-myeks-addon-iamserviceaccount-default-Role1-GE2DZKJYWCEN AWS_WEB_IDENTITY_TOKEN_FILE: /var/run/secrets/eks.amazonaws.com/serviceaccount/token Mounts: /var/run/secrets/eks.amazonaws.com/serviceaccount from aws-iam-token (ro) /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-69rh8 (ro) ... Volumes: aws-iam-token: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 86400 kube-api-access-sn467: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt ConfigMapOptional: <nil> DownwardAPI: true ... # 파드에서 aws cli 사용 확인 eksctl get iamserviceaccount --cluster $CLUSTER_NAME kubectl exec -it eks-iam-test3 -- aws sts get-caller-identity --query Arn "arn:aws:sts::911283464785:assumed-role/eksctl-myeks-addon-iamserviceaccount-default-Role1-GE2DZKJYWCEN/botocore-session-1685179271" # 되는 것고 안되는 것은 왜그런가? 권한차이 kubectl exec -it eks-iam-test3 -- aws s3 ls AmazonS3ReadOnlyAccess ![]() kubectl exec -it eks-iam-test3 -- aws ec2 describe-instances --region ap-northeast-2 - policy allows the ec2:DescribeInstances action kubectl exec -it eks-iam-test3 -- aws ec2 describe-vpcs --region ap-northeast-2 - because no identity-based policy allows the ec2:DescribeVpcs action |
4. OWASP Kubernetes Top Ten
OWASP Kubernetes Top Ten 항목
| 순위 | 항목 | 내용 |
| K01 | Insecure Workload Configurations (불안전한 워크로드 구성) |
워크로드가 적절히 보안 설정되지 않아 발생하는 위험 |
| K02 | Supply Chain Vulnerabilities (공급망 취약점) |
컨테이너 이미지 및 소프트웨어 공급망의 취약점 |
| K03 | Overly Permissive RBAC Configurations (과도하게 허용적인 RBAC 설정) |
권한 관리가 부적절하여 공격자가 시스템 권한을 얻기 쉬운 설정 |
| K04 | Lack of Centralized Policy Enforcement (중앙화된 정책 집행의 부재) |
일관된 보안 정책이 없어 위험 요소 관리가 어렵거나 미흡한 경우 |
| K05 | Inadequate Logging and Monitoring (불충분한 로깅 및 모니터링) |
위협이나 사고를 탐지할 수 없을 만큼 부족한 로깅/모니터링 |
| K06 | Broken Authentication Mechanisms (취약한 인증 메커니즘) |
인증 체계가 약하거나 잘못 구현되어 접근 통제가 무력화될 수 있는 경우 |
| K07 | Missing Network Segmentation Controls (부족한 네트워크 분리 및 통제) |
워크로드 간 네트워크 분리가 제대로 되지 않아 위협이 확산될 수 있는 경우 |
| K08 | Secrets Management Failures (시크릿 관리 실패) |
인증 정보(Secrets)가 안전하게 관리되지 않아 정보가 유출될 위험 |
| K09 | Misconfigured Cluster Components (잘못 구성된 클러스터 구성 요소) |
Kubernetes 자체 구성 요소(API Server, etcd, kubelet 등)가 안전하게 구성되지 않은 경우 |
| K10 | Outdated and Vulnerable Kubernetes Components (오래되고 취약한 Kubernetes 구성요소) |
Kubernetes 자체 버전이 오래되었거나 업데이트되지 않아 존재하는 취약점 |
파드 권한과 호스트 네임스페이스 공유로 호스트 탈취
| NODE_NAME=myk8s-control-plane kubectl create -n kube-system -f - <<EOF apiVersion: v1 kind: Pod metadata: name: root-shell-$NODE_NAME namespace: kube-system spec: nodeName: $NODE_NAME containers: - command: - /bin/cat image: alpine:3 name: root-shell securityContext: privileged: true tty: true stdin: true volumeMounts: - mountPath: /host name: hostroot hostNetwork: true hostPID: true hostIPC: true tolerations: - effect: NoSchedule operator: Exists - effect: NoExecute operator: Exists volumes: - hostPath: path: / name: hostroot EOF kubectl -n kube-system exec -it root-shell-$NODE_NAME -- chroot /host /bin/bash root@myk8s-control-plane:/# id uid=0(root) gid=0(root) groups=0(root),1(daemon),2(bin),3(sys),4(adm),6(disk),10(uucp),11,20(dialout),26(tape),27(sudo) |
Kyverno
다음은 Kyverno의 주요 기능과 예시를 보기 쉽게 정리한 표입니다.
| 기능 | 설명 | 예시 |
| ✅ Validation (정책 기반 검증) |
Kubernetes 리소스가 정책에 정의된 규칙을 준수하는지 확인하고, 위반된 리소스를 거부하거나 경고합니다. | - Privileged 모드 컨테이너 배포 차단 - 허용되지 않은 이미지 배포 방지 |
| 🔄 Mutation (자동 변경 및 기본값 주입) |
리소스가 배포될 때 특정 속성(예: label, annotation)을 자동으로 추가하거나 설정을 변경합니다. | - 모든 리소스에 보안 관련 Annotation 자동 추가 - 컨테이너 리소스 제한 기본값 자동 설정 |
| 🚧 Generate (리소스 자동 생성) |
특정 이벤트 발생 시 자동으로 추가 리소스를 생성합니다. | - Namespace 생성 시 NetworkPolicy 자동 생성 - 네임스페이스 리소스 쿼터 자동 설정 |
| 🔐 Software Supply Chain (소프트웨어 공급망 보호) |
이미지 서명 및 SBOM 등을 이용해 배포되는 컨테이너 이미지의 무결성과 보안을 검증합니다. | - 미서명 이미지 또는 취약 이미지 배포 차단 - 이미지 서명 검증 정책 적용 |
| # 설치 # EKS 설치 시 참고 https://kyverno.io/docs/installation/platform-notes/#notes-for-eks-users # 모니터링 참고 https://kyverno.io/docs/monitoring/ cat << EOF > kyverno-value.yaml config: resourceFiltersExcludeNamespaces: [ kube-system ] admissionController: serviceMonitor: enabled: true backgroundController: serviceMonitor: enabled: true cleanupController: serviceMonitor: enabled: true reportsController: serviceMonitor: enabled: true EOF kubectl create ns kyverno helm repo add kyverno https://kyverno.github.io/kyverno/ helm install kyverno kyverno/kyverno --version 3.3.7 -f kyverno-value.yaml -n kyverno # 확인 kubectl get all -n kyverno kubectl get crd | grep kyverno kubectl get pod,svc -n kyverno # (참고) 기본 인증서 확인 https://kyverno.io/docs/installation/customization/#default-certificates # step-cli 설치 https://smallstep.com/docs/step-cli/installation/ wget https://dl.smallstep.com/cli/docs-cli-install/latest/step-cli_amd64.rpm sudo rpm -i step-cli_amd64.rpm # kubectl -n kyverno get secret kubectl -n kyverno get secret kyverno-svc.kyverno.svc.kyverno-tls-ca -o jsonpath='{.data.tls\.crt}' | base64 -d kubectl -n kyverno get secret kyverno-svc.kyverno.svc.kyverno-tls-ca -o jsonpath='{.data.tls\.crt}' | base64 -d | step certificate inspect --short X.509v3 Root CA Certificate (RSA 2048) [Serial: 0] Subject: *.kyverno.svc Issuer: *.kyverno.svc Valid from: 2024-04-07T06:05:52Z to: 2025-04-07T07:05:52Z # kubectl get validatingwebhookconfiguration kyverno-policy-validating-webhook-cfg -o jsonpath='{.webhooks[0].clientConfig.caBundle}' | base64 -d | step certificate inspect --short X.509v3 Root CA Certificate (RSA 2048) [Serial: 0] Subject: *.kyverno.svc Issuer: *.kyverno.svc Valid from: 2024-04-07T06:05:52Z to: 2025-04-07T07:05:52Z ![]() |
프로메테우스에서 검색했을 시
- count(count(kyverno_policy_rule_info_total{policy_type="cluster"}==1) by (policy_name)) 조회불가.

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 사용자로 실행을 거부하도록 설정 시 동작
| # 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 ![]() |
후기
쿠버네티스에 접근방법을 다양하게 제어할 수 있는 방법을 테스트할 수 있는 기회였으며
argoCD에서 viewer 계정을 생성하여 namespace 별 접근/제어이 가능 할 수 있도록 응용해보았다.



















