infra
Platform

모듈 맵

[Kubernetes] NetworkPolicy를 활용한 특정 파드 간 통신 차단 및 제한

0 / 29 완료

펼치기
0 / 29 완료0%

Kubernetes · 17 / 29

[Kubernetes] NetworkPolicy를 활용한 특정 파드 간 통신 차단 및 제한

NetworkPolicy로 파드 간 트래픽을 명시적으로 허용/차단하여 클러스터 내 네트워크 보안 경계를 구축합니다

🚨INCIDENT ALERT
HIGH

결제 API는 주문 서비스에서만 접근해야 하는데 같은 Namespace의 임시 파드에서도 접속이 됩니다. 클러스터 내부 네트워크를 모두 신뢰하면 침해 사고가 한 서비스에서 다른 서비스로 쉽게 번집니다. NetworkPolicy는 Pod 간 통신을 필요한 경로로만 제한하는 방화벽 역할을 합니다.

NetworkPolicy — 파드 간 통신 화이트리스트

보안 감사 결과가 나왔습니다. "클러스터 내 모든 파드가 서로 통신 가능한 상태입니다. 결제 서비스가 사용자 서비스 DB에 직접 접근할 수 있고, 개발 네임스페이스에서 프로덕션 DB로 쿼리를 날릴 수 있습니다." 이건 단순한 설정 실수가 아니라 아키텍처 문제입니다. 기본적으로 K8s 클러스터 안의 파드들은 네임스페이스나 서비스 경계와 관계없이 모두 서로 통신할 수 있습니다. 이 열린 상태에서 한 서비스가 침해되면 공격자는 내부 네트워크에서 횡이동(lateral movement)하며 모든 서비스에 접근할 수 있습니다. NetworkPolicy는 이 문제를 해결합니다. 어떤 파드가 어떤 파드와 통신할 수 있는지 명시적으로 선언하는 화이트리스트 방식으로, 허용되지 않은 모든 트래픽은 자동으로 차단됩니다.

이번 챕터에서 배울 것
  • 1NetworkPolicy 없는 클러스터의 기본 통신 구조
  • 2Ingress/Egress 규칙과 podSelector, namespaceSelector
  • 3화이트리스트 기반 정책 설계 원칙
  • 4네임스페이스 격리와 서비스 간 통신 허용 패턴
  • 5기본 거부(Deny All) 정책 구성
  • 6NetworkPolicy 디버깅 도구와 트러블슈팅
실습 환경 준비
CNI 플러그인 확인 (NetworkPolicy 지원 필요)
kubectl get pods -n kube-system | grep -E 'calico|cilium|weave'
실습 네임스페이스 생성
kubectl create namespace netpol-demo
네임스페이스에 레이블 추가 (namespaceSelector용)
kubectl label namespace netpol-demo env=demo
현재 NetworkPolicy 확인
kubectl get networkpolicy -A

NetworkPolicy 규칙 구조 이해

NetworkPolicy는 podSelector로 대상 파드를 고르고, ingress(들어오는 트래픽)와 egress(나가는 트래픽) 규칙을 정의합니다.

YAML
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: 규칙-이름
  namespace: 적용-네임스페이스
spec:
  podSelector:           # 이 정책이 적용될 파드
    matchLabels:
      app: 대상파드
  policyTypes:           # Ingress, Egress 또는 둘 다
    - Ingress
    - Egress
  ingress:               # 허용할 인바운드 트래픽
    - from:
        - podSelector:   # 이 파드에서 오는 트래픽
            matchLabels:
              app: 허용파드
      ports:
        - protocol: TCP
          port: 8080
  egress:                # 허용할 아웃바운드 트래픽
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: 허용네임스페이스
      ports:
        - protocol: TCP
          port: 5432
💡개념

기본 거부 정책 (Deny All)

NetworkPolicy를 개별 서비스에 하나씩 추가하다 보면 설정이 없는 서비스가 모든 파드에서 접근 가능한 상태로 방치됩니다. 새 서비스가 배포될 때마다 보안팀이 일일이 허용 목록을 관리해야 해서 누락이 생깁니다. 화이트리스트 방식으로 전환하려면 먼저 네임스페이스 전체 트래픽을 기본 차단해야 합니다. "기본 거부 후 필요한 것만 허용"이 실무에서 NetworkPolicy를 적용하는 표준 순서입니다. 이 CB에서는 네임스페이스 전체에 Deny All 정책을 적용하는 YAML 패턴과 적용 후 확인 방법을 다룹니다.

기본 거부 정책 (Deny All)

YAML
# deny-all-ingress.yaml — 모든 인바운드 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
  namespace: production
spec:
  podSelector: {}        # 네임스페이스의 모든 파드에 적용
  policyTypes:
    - Ingress            # from을 지정하지 않으면 모두 차단
---
# deny-all-egress.yaml — 모든 아웃바운드 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress             # to를 지정하지 않으면 모두 차단
Kubernetes
kubectl apply -f deny-all-ingress.yaml -f deny-all-egress.yaml

# 통신 차단 확인
kubectl run test-pod --image=curlimages/curl -n production -- sleep 3600
kubectl exec -it test-pod -n production -- curl http://payment-service:8080 --max-time 5
# curl: (28) Connection timed out after 5001 milliseconds
💡개념

서비스 간 통신 허용 패턴

Deny All 정책을 적용하고 나면 기존에 잘 동작하던 서비스들이 타임아웃이 납니다. 결제 서비스가 주문 서비스의 호출을 받지 못하고, 모니터링 에이전트가 메트릭을 수집하지 못합니다. 어떤 서비스가 어떤 서비스와 통신해야 하는지 명확하게 정의하지 않으면 복구 과정에서 너무 넓은 허용 규칙을 열어버리는 실수가 생깁니다. deny-all을 설정한 뒤 필요한 통신만 명시적으로 허용하는 것이 올바른 접근입니다. 이 CB에서는 결제 서비스 예시로 인바운드와 아웃바운드 허용 규칙을 조합하는 NetworkPolicy 패턴을 다룹니다. 아래는 결제 서비스가 주문 서비스의 요청만 받고, PostgreSQL에만 쿼리할 수 있는 예시입니다.

YAML
# payment-netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-service-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: payment          # payment 파드에 적용
  policyTypes:
    - Ingress
    - Egress
  ingress:
    # 주문 서비스에서 오는 트래픽만 허용
    - from:
        - podSelector:
            matchLabels:
              app: order-service
      ports:
        - protocol: TCP
          port: 8080
    # 모니터링 에이전트도 허용 (메트릭 수집)
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
          podSelector:
            matchLabels:
              app: prometheus
      ports:
        - protocol: TCP
          port: 9090
  egress:
    # PostgreSQL DB에만 쿼리 허용
    - to:
        - podSelector:
            matchLabels:
              app: payment-db
      ports:
        - protocol: TCP
          port: 5432
    # DNS 조회는 항상 허용 (없으면 서비스 이름 조회 불가)
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
Kubernetes
kubectl apply -f payment-netpol.yaml

# 허용된 통신 확인 (order-service 파드에서)
kubectl exec -it order-pod -n production -- curl http://payment-service:8080/health
# {"status": "ok"}

# 차단된 통신 확인 (catalog-service 파드에서 — 정책에 없음)
kubectl exec -it catalog-pod -n production -- curl http://payment-service:8080 --max-time 5
# curl: (28) Connection timed out after 5001 milliseconds
💡개념

네임스페이스 격리 — dev와 prod 분리

개발자가 개발 환경에서 DB 연결 테스트를 하다가 운영 DB 주소를 잘못 입력했고, 운영 Namespace의 PostgreSQL에 쿼리가 직접 날아갔습니다. Namespace를 나눠도 기본적으로 파드 간 네트워크 통신은 열려 있어 실수가 대형 사고로 이어질 수 있습니다. 네임스페이스 단위 격리 정책을 적용하면 개발 Namespace에서 운영 Namespace로의 트래픽을 차단하고, 외부 진입은 Ingress Controller 경로만 허용할 수 있습니다. 이 CB에서는 production Namespace를 내부 파드와 Ingress Controller에서만 접근 가능하도록 격리하는 NetworkPolicy 설정을 다룹니다.

YAML
# prod-namespace-isolation.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: prod-namespace-isolation
  namespace: production
spec:
  podSelector: {}    # production 전체 파드에 적용
  policyTypes:
    - Ingress
  ingress:
    # production 네임스페이스 내부 파드 간 통신만 허용
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: production
    # 인그레스 컨트롤러에서 오는 외부 트래픽 허용
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
          podSelector:
            matchLabels:
              app.kubernetes.io/name: ingress-nginx
Kubernetes
# 네임스페이스 레이블 확인
kubectl get namespace --show-labels

# 개발 파드에서 프로덕션 서비스 접근 시도 (차단됨)
kubectl exec -it debug-pod -n development -- \
  curl http://payment-service.production.svc.cluster.local:8080 --max-time 5
# curl: (28) Connection timed out

실습: 3계층 아키텍처 트래픽 제어

프론트엔드 → API → DB 구조에서 각 계층 간 통신만 허용하는 정책을 만듭니다.

Kubernetes
# 실습 환경 구성
kubectl create namespace three-tier

# 계층별 파드 배포
kubectl run frontend --image=nginx --labels="tier=frontend,app=web" -n three-tier
kubectl run api --image=nginx --labels="tier=api,app=backend" -n three-tier
kubectl run db --image=postgres --labels="tier=db,app=database" \
  --env="POSTGRES_PASSWORD=test" -n three-tier

kubectl expose pod api --port=8080 --target-port=80 -n three-tier
kubectl expose pod db --port=5432 --target-port=5432 -n three-tier
YAML
# three-tier-netpol.yaml
# 1. 기본 거부
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: three-tier
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]
---
# 2. API가 프론트엔드의 요청만 수신
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow-frontend
  namespace: three-tier
spec:
  podSelector:
    matchLabels:
      tier: api
  policyTypes: [Ingress, Egress]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              tier: frontend
      ports:
        - port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              tier: db
      ports:
        - port: 5432
    - to: [{}]
      ports:
        - port: 53
          protocol: UDP
---
# 3. DB가 API의 연결만 수신
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-allow-api-only
  namespace: three-tier
spec:
  podSelector:
    matchLabels:
      tier: db
  policyTypes: [Ingress]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              tier: api
      ports:
        - port: 5432
Kubernetes
kubectl apply -f three-tier-netpol.yaml

# 허용: 프론트엔드 → API
kubectl exec -it frontend -n three-tier -- curl http://api:8080 --max-time 5
# 성공

# 차단: 프론트엔드 → DB (직접 접근 불가)
kubectl exec -it frontend -n three-tier -- \
  nc -zv db 5432 --wait 5 2>&1
# nc: connect to db port 5432 (tcp) timed out: Operation in progress

NetworkPolicy를 적용한 직후부터 파드가 서비스 이름으로 다른 서비스에 연결하지 못합니다. IP로는 접근이 되지만 서비스 이름(hostname)으로는 타임아웃이 발생합니다.

Kubernetes
# 증상
kubectl logs payment-pod -n production
# Error: dial tcp: lookup order-service on 10.96.0.10:53: i/o timeout
# ← DNS 조회 실패! IP가 아닌 호스트명 조회 실패

# 진단: DNS 서버로의 Egress 규칙 확인
kubectl get networkpolicy payment-service-policy -n production -o yaml | grep -A 20 egress

# Egress에 DNS 허용 규칙이 있는지 확인
# (없으면 kube-dns로의 UDP 53 포트가 차단됨)
로컬 터미널
# 원인: Egress NetworkPolicy를 설정했지만 DNS 트래픽(UDP 53)을 허용하지 않음
# kube-dns는 kube-system 네임스페이스의 10.96.0.10에서 동작

# 해결: DNS 허용 규칙 추가
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: production
spec:
  podSelector: {}    # 모든 파드에 적용
  policyTypes:
    - Egress
  egress:
    # kube-dns 허용 (CoreDNS가 있는 kube-system 네임스페이스)
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
EOF

# 적용 후 DNS 조회 확인
kubectl exec -it payment-pod -n production -- \
  nslookup order-service.production.svc.cluster.local
# Server:   10.96.0.10
# Name:  order-service.production.svc.cluster.local
# Address: 10.100.42.15
Kubernetes
# 추가 디버깅 도구
# Cilium 클러스터에서 정책 시각화
kubectl exec -it -n kube-system cilium-xxx -- \
  cilium policy get

# NetworkPolicy 적용 현황 확인 (Calico)
kubectl get networkpolicies -A

# 특정 파드의 실효 정책 확인
kubectl describe pod payment-pod -n production | grep -A 5 "Labels"
# 레이블을 기반으로 어떤 NetworkPolicy가 적용되는지 수동으로 매칭

핵심 교훈: Egress NetworkPolicy를 적용할 때는 DNS(UDP/TCP 53)를 반드시 허용해야 합니다. kube-dns가 차단되면 서비스 이름 조회가 실패하여 마치 서비스가 죽은 것처럼 보입니다. 또한 CNI가 NetworkPolicy를 지원하지 않는 경우 정책이 생성되어도 아무 효과가 없으므로, Calico/Cilium/Weave 중 하나를 사용 중인지 먼저 확인하세요.

💼
실무 맥락
현업 패턴

시나리오: PCI DSS 컴플라이언스를 위한 결제 서비스 네트워크 격리

보안팀으로부터 "결제 서비스는 PCI DSS 규정상 다른 서비스와 네트워크 격리가 필요합니다"라는 요건을 받았습니다. 결제 서비스가 받는 요청은 API 게이트웨이에서만 와야 하고, 결제 서비스가 연결하는 것은 자체 DB와 외부 PG사 API뿐이어야 합니다.

Kubernetes
# payment 네임스페이스 생성 및 레이블 부여
kubectl create namespace payment-zone
kubectl label namespace payment-zone compliance=pci-dss

# 결제 서비스 격리 정책 배포
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-strict-isolation
  namespace: payment-zone
spec:
  podSelector:
    matchLabels:
      app: payment-service
  policyTypes: [Ingress, Egress]
  ingress:
    # API 게이트웨이에서만 수신
    - from:
        - namespaceSelector:
            matchLabels:
              app: api-gateway
          podSelector:
            matchLabels:
              app: api-gateway
      ports:
        - port: 8080
  egress:
    # 자체 DB
    - to:
        - podSelector:
            matchLabels:
              app: payment-db
      ports:
        - port: 5432
    # 외부 PG사 API (IP 기반 egress)
    - to:
        - ipBlock:
            cidr: 203.0.113.0/24    # PG사 IP 대역
      ports:
        - port: 443
    # DNS
    - to: [{}]
      ports:
        - port: 53
          protocol: UDP
EOF

# 정책 적용 검증
kubectl describe networkpolicy payment-strict-isolation -n payment-zone

실무 포인트: NetworkPolicy는 컴플라이언스 감사에서 "네트워크 세그멘테이션 증적"으로 활용됩니다. 정책 YAML 파일을 Git에 관리하고, CI/CD에서 변경 시 보안팀 승인을 요구하는 프로세스를 만들면 "언제 누가 결제 서비스 통신 규칙을 바꿨는지" 추적할 수 있습니다.

핵심 요약

개념명령/설정실무 사용 빈도
NetworkPolicy 조회kubectl get networkpolicy -n <ns>디버깅 시
Deny All 적용podSelector: {} + policyTypes: [Ingress] (from 없음)네임스페이스 격리 시
파드 간 허용ingress.from.podSelector서비스 연결 허용
네임스페이스 간 허용ingress.from.namespaceSelector크로스 네임스페이스 통신
외부 IP 허용egress.to.ipBlock.cidr외부 API 연결
DNS 허용 (필수!)egress: port 53 UDP/TCPEgress 정책 시 항상
CNI 확인kubectl get pods -n kube-system정책 미작동 시

지식 확인

퀴즈 — 4문제

Q1

NetworkPolicy를 적용했지만 파드 간 통신이 여전히 가능한 이유로 가장 가능성이 높은 것은?

Q2

podSelector: {} (빈 셀렉터)가 의미하는 것은?

Q3

Ingress NetworkPolicy에서 from을 지정하지 않고 policyTypes만 ['Ingress']로 설정하면?

Q4

네임스페이스 간 통신을 허용하기 위해 사용하는 NetworkPolicy 셀렉터는?

0 / 4 답변

🧪 실습으로 확인하기

K8s 기초 — Pod/Deployment/Service 생성

초급

kubectl로 nginx Pod를 생성하고 Deployment와 Service를 차례로 만들어 클러스터 외부에서 접근 가능한 상태까지 구성한다. K8s 3대 리소스의 역할과 관계를 직접 손으로 익힌다.

40📋 5단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

kubernetes고급 · 60
[Kubernetes] Node Affinity와 Taint/Toleration 기반 스케줄링 제어
Kubernetes 트랙 계속
docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점