EKS에서는 클러스터를 생성할 때, 워커 노드에 클러스터 보안 그룹과 동일한 보안 그룹이 자동으로 적용된다. 이 경우, 특정 파드만 통신이 필요하더라도 노드 보안 그룹을 열어야 하므로 모든 파드가 동일한 권한을 가지게 되어 보안적으로 취약해진다.
이를 해결하기 위해서는 목적별로 노드 그룹을 분리할 수 있지만, 관리해야 할 노드 그룹이 늘어나면서 운영 복잡성이 증가한다.
EKS는 이를 개선하기 위해 Pod Security Group(파드 보안 그룹) 기능을 제공한다. 이 기능을 사용하면 파드 단위로 개별 보안 그룹을 적용할 수 있어, **최소 권한 원칙에 따라 필요한 통신만 허용할 수 있다.
EC2 인스턴스에 적용되는 보안 그룹은 인, 아웃바운드 트래픽을 제어하는 가상 방화벽이다. 기본적으로 Amazon VPC CNI는 노드 ENI에 연결된 보안 그룹을 사용하므로, 해당 노드에서 실행되는 모든 파드가 동일한 보안 그룹을 공유한다.
즉, 한 노드에 배포된 파드라면 필요하지 않은 파드까지 RDS 같은 리소스에 접근할 수 있는 구조가 된다. 반면 Pod Security Group을 적용하면 파드 단위로 보안 그룹을 분리할 수 있어, 특정 파드만 외부 리소스에 접근하도록 제한할 수 있다.

위 이미지에서 워커 노드에서 작동하는 모든 애플리케이션 파드는 RDS 데이터베이스 서비스에 액세스할 수 있다.

파드 보안 그룹을 사용하면 다양한 네트워크 보안 요구 사항이 있는 애플리케이션을 실행하여 컴퓨팅 효율성을 개선할 수 있다. 위 이미지는 파드에 적용된 보안 그룹과 이러한 보안 그룹이 애플리케이션 배포 및 노드 아키텍처를 간소화하는 방법을 보여준다.
그림에서는 특정 파드만 Amazon RDS 데이터베이스에 액세스할 수 있다.
그림 출처 : 파드당 보안 그룹
파드의 보안 그룹을 배포하기 전에 다음 제한 사항 및 조건을 고려해야 한다. 디테일한 사항은 개별 파드에 보안 그룹 할당 링크를 참고하면 된다.
개별 파드에 보안 그룹 할당은 특정 타입의 인스턴스에서만 지원한다. m5, c5, r5, m6g, c6g 및 r6g 인스턴스 패밀리가 지원 된다. t 인스턴스 패밀리 유형은 지원되지 않는다.
EC2 노드에서는 파드 보안 그룹은 Amazon VPC CNI 플러그인 버전 1.16.0 이상을 사용해야 한다.
Fargate 노드에서는 Amazon VPC CNI 플러그인 버전 1.7.7 이상을 사용해야 한다.
# DaemonSet의 컨테이너 이미지 항목에서 버전을 확인
kubectl get daemonset aws-node -n kube-system -o yaml | grep image:
f:image: {}
f:image: {}
f:image: {}
image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon-k8s-cni:v1.19.2-eksbuild.1
image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon/aws-network-policy-agent:v1.1.6-eksbuild.1
image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon-k8s-cni-init:v1.19.2-eksbuild.1
kubectl describe daemonset aws-node -n kube-system | grep Image
Image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon-k8s-cni-init:v1.19.2-eksbuild.1
Image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon-k8s-cni:v1.19.2-eksbuild.1
Image: 602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon/aws-network-policy-agent:v1.1.6-eksbuild.1
# AWS VPC CNI에서 Pod마다 ENI 할당 확인
kubectl -n kube-system describe daemonset aws-node | grep ENABLE_POD_ENI
ENABLE_POD_ENI: true
파드에 대한 보안 그룹은 Windows 노드와 함께 사용할 수 없다.

EKS Cluster Role 에서 AmazonEKSVPCResourceControllerPolicy를 추가해야 한다.
VCP CNI인 aws-node daemonset을 수정해야 한다.
kubectl set env daemonset aws-node -n kube-system ENABLE_POD_ENI=true
설정이 되었다면 aws-node pods 가 재기동되고, describe로 확인 시 capacity필드와 allocatable필드에서 vpc.amazonaws.com/pod-eni 가 있는 것을 확인할 수 있다.
kubectl describe no <NODE NAME>
...중략...
Capacity:
cpu: 2
ephemeral-storage: 52416492Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 7824260Ki
pods: 29
vpc.amazonaws.com/pod-eni: 9
Allocatable:
cpu: 1930m
ephemeral-storage: 47233297124
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 7134084Ki
pods: 29
vpc.amazonaws.com/pod-eni: 9
...중략...
EKS에서 VPC CNI 플러그인을 사용하는 경우, 노드에 붙일 수 있는 Pod ENI 수와 허용 가능한 파드의 수를 확인할 수 있다.
아래 sgp.yaml, deploy.yaml파일을 참고하여 리소스를 생성한다.
yaml 관련해서 자세한 사항은 EKS 파드에 대한 보안 그룹 정책 사용링크를 참조하면 좋다.
kubectl apply -f sgp.yaml
kubectl apply -f deploy.yaml
kubectl get pod
NAME READY STATUS RESTARTS AGE
dsshin-pod-scg-7fb85846b8-2nbsn 1/1 Running 0 27s
dsshin-pod-scg-7fb85846b8-9mgqt 1/1 Running 0 27s
dsshin-pod-scg-7fb85846b8-mcmr9 1/1 Running 0 27s
kubectl get sgp
NAME SECURITY-GROUP-IDS
dsshin-sgp ["sg-08da5cfe2b66df925"]
kubectl get pod dsshin-pod-scg-7fb85846b8-2nbsn -o yaml | grep vpc
vpc.amazonaws.com/pod-eni: '[{"eniId":"<ENI ID>","ifAddress":"02:b5:d6:b6:bc:e1","privateIp":"10.229.1.165","ipv6Addr":"","vlanId":2,"subnetCidr":"10.229.0.0/23","subnetV6Cidr":"","associationID":"trunk-assoc-eaf67e8b"}]'
vpc.amazonaws.com/pod-eni: "1"
vpc.amazonaws.com/pod-eni: "1"
key: vpc.amazonaws.com/pod-eni
vpc.amazonaws.com/pod-eni: "1"
vpc.amazonaws.com/pod-eni: "1"
vpc.amazonaws.com/pod-eni: "1"
# sgp.yaml
apiVersion: vpcresources.k8s.aws/v1beta1
kind: SecurityGroupPolicy
metadata:
name: dsshin-sgp
spec:
podSelector:
matchLabels:
app: dsshin-pod-scg
securityGroups:
groupIds:
- sg-08da5cfe2b66df925 # Pod에 연결할 AWS Security Group ID
# deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dsshin-pod-scg
spec:
replicas: 3
selector:
matchLabels:
app: dsshin-pod-scg
template:
metadata:
labels:
app: dsshin-pod-scg
spec:
containers:
- name: netshoot
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
resources:
requests:
cpu: "0.2"
memory: "256Mi"
limits:
cpu: "0.2"
memory: "256Mi"
첫 번째 테스트는 Pod 간 호출하여 각 클라이언트(Pod)에서 Nginx가 설치된 Pod로 호출하는 테스트를 진행하였다.
# 2nbsn pod 에서 nginx 설정
kubectl exec -it dsshin-pod-scg-7fb85846b8-2nbsn /bin/bash
apk update # Alpine Linux 에서 apk를 패키지 관리자로 사용
apk add nginx
nginx -v
# 디렉토리 이동 및 설정
pwd
/etc/nginx/http.d
cat default.conf
# This is a default site configuration which will simply return 404, preventing
# chance access to any other virtualhost.
server {
listen 80 default_server;
listen [::]:80 default_server;
# 웹 루트 디렉토리 지정
root /usr/share/nginx/html; # index.html 파일 위치
index index.html;
# location 블록 수정
location / {
try_files $uri $uri/ =404;
}
}
# 디렉토리 이동 및 설정
pwd
/usr/share/nginx/html
mkdir -p html
cat index.html
hello dsshin-pod
# 기동 및 재기동
nginx ( 기동 )
nginx -s reload ( 재기동 )
# 방화벽을 오픈한 mcmr9 pod 에서 호출
kubectl exec -it dsshin-pod-scg-7fb85846b8-mcmr9 /bin/bash
dsshin-pod-scg-7fb85846b8-mcmr9:~# curl 172.16.16.164:80
hello dsshin-pod
# 방화벽을 오픈하지 않은 9mgqt pod 에서 호출
kubectl exec -it dsshin-pod-scg-7fb85846b8-9mgqt /bin/bash
dsshin-pod-scg-7fb85846b8-9mgqt:~# curl 172.16.16.164:80
curl: (28) Failed to connect to 172.16.16.164 port 80 after 130285 ms: Could not connect to server

파드에서 DNS쿼리 응답 테스트를 진행하였지만, 아래와 같이 DNS 서버쪽에 연결이 되지 않아 timed out 이 발생한 것을 확인할 수 있다.
# DNS 호출 테스트
dsshin-pod-scg-7fb85846b8-9mgqt:~# nslookup google.com
;; communications error to 10.90.0.10#53: timed out
;; communications error to 10.90.0.10#53: timed out
DNS 서버 IP가 10.90.0.10 으로 확인이 되며, 이것은 EKS 클러스터 생성 시 설정된 서비스 대역(10.90.0.0/16)안의 IP가 할당되며 10.90.0.10 으로 확인된다.
kubectl get svc -n kube-system -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
eks-extension-metrics-api ClusterIP 10.90.143.236 <none> 443/TCP 19h <none>
kube-dns ClusterIP 10.90.0.10 <none> 53/UDP,53/TCP,9153/TCP 19h k8s-app=kube-dns
해당 Pod 에서는 nslookup 사용 시 DNS가 coredns를 보고 있고, 이것은 클러스터의 보안 그룹에 파드에 적용한 보안 그룹을 오픈해야 한다.

위 중요 내용에서 "CoreDNS 파드의 보안 그룹에서는 지정하는 보안 그룹의 인바운드 TCP 및 UDP 포트 53 트래픽을 허용해야 합니다." 라는 내용을 확인할 수 있다. 이것은 CoreDNS가 EKS Cluster에서 실행되기에, EKS Cluster 보안 그룹에 방화벽을 오픈해야 한다는 의미이다.
링크 : Amazon EKS 파드에 대한 보안 그룹 정책 사용

위와 같이 TCP, UDP 53 번을 Pod에 적용한 보안 그룹을 규칙을 추가해주자.
EKS Cluster 보안 그룹에 방화벽 규칙을 추가 후에는 정상적인 DNS 질의가 가능했다.
# 규칙 추가 후 DNS 호출 테스트
dsshin-pod-scg-7fb85846b8-9mgqt:~# nslookup google.com
;; Got recursion not available from 10.90.0.10
;; Got recursion not available from 10.90.0.10
;; Got recursion not available from 10.90.0.10
Server: 10.90.0.10
Address: 10.90.0.10#53
Non-authoritative answer:
Name: google.com
Address: 142.250.76.142
Name: google.com
Address: 2404:6800:400a:80e::200e
위에서 파드는 작성자 기준으로 DNS는 10.90.0.10를 보는 것을 확인할 수 있었다. 하지만 AWS VPC 대역은 172.16.0.0/16으로 구성했는데, Route53 Private Hosted 영역에 대하여 쿼리는 어떻게 진행이 되는지 궁금했고, 하나씩 풀어가보자.
# coredns 확인
kubectl -n kube-system get configmap coredns -o yaml
kind: ConfigMap
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
name: coredns
namespace: kube-system
기본적으로 CoreDNS가 VPC DNS(172.16.0.2)를 바라보도록 해야 Private Hosted Zone 레코드를 질의할 수 있다.
CoreDNS ConfigMap을 확인했을 때 따로 DNS IP가 지정되지 않은 것을 볼 수 있는데, 여기서 forward . /etc/resolv.conf 이 부분이 CoreDNS가 외부 도메인 질의를 어떻게 처리할지를 결정하는 핵심이며, 각 노드는 /etc/resolv.conf 파일을 본다는 것을 알 수 있다.
# Node 정보 확인
kubectl get node
NAME STATUS ROLES AGE VERSION
ip-172-16-10-196.ap-northeast-2.compute.internal Ready <none> 20h v1.32.7-eks-3abbec1
ip-172-16-16-160.ap-northeast-2.compute.internal Ready <none> 20h v1.32.7-eks-3abbec1
ip-172-16-20-208.ap-northeast-2.compute.internal Ready <none> 20h v1.32.7-eks-3abbec1

EKS Cluster 의 한 노드에 접속해서 확인한 /etc/resolv.conf 파일을 확인해보자.
여기서는 AWS VPC DNS 서버인 172.16.0.2가 설정이 되어 있기에, 별도의 설정이 없어도 Route53의 Private Hosted Zone에 등록된 DNS 질의가 가능할 것이다.

test.dsshin.com 도메인을 등록 후 파드에서 쿼리를 해보자.

파드에서는 10.90.0.10 을 보고 있지만 coredns 에서 외부 도메인에 대한 처리를 /etc/resolv.conf 파일을 보도록 되어 있기에 Route53의 DNS로 질의가 가능한 것이다.
동작 순서
1. 파드 안에서 nslookup test.dsshin.com 실행 → 요청은 10.90.0.10 (CoreDNS ClusterIP) 으로 요청
2. CoreDNS가 질의 수신 → forward ./etc/resolv.conf 규칙에 따라 모든 질의를 /etc/resolv.conf 안의 nameserver 로 요청
- nameserver 값은 VPC DNS (예: 172.16.0.2)
3. VPC DNS 서버 → Public DNS 또는 Route53 Private Zone으로 질의 처리
- 퍼블릭 도메인(google.com 등)은 퍼블릭 DNS로 전달
- Private Hosted Zone (test.dsshin.com 등)은 VPC 에 연결된 Route53 Private Zone에서 응답
4. CoreDNS → 파드에 최종 응답 반환
그래서 EKS에서 Route53 Private Hosted Zone 을 쓰고 싶을 때도 별도 설정 필요 없이, /etc/resolv.conf 에 VPC DNS(172.16.0.2) 가 들어있기만 하면 바로 동작한다.
EKS에서 Pod Security Group을 활용하면 노드 단위 보안 그룹 공유로 인한 보안 취약점을 해소하고, 필요한 파드만 필요한 리소스에 접근하도록 제어할 수 있다. 이를 통해 보안성을 강화하면서도 노드 그룹 분리에 따른 운영 복잡성을 줄일 수 있다.
Pod Security Group은 EKS 환경에서 최소 권한 보안 원칙을 실현하는 핵심 기능이라고 할 수 있다.