K8s NetworkPolicy 네트워크 격리

K8s NetworkPolicy란?

Kubernetes NetworkPolicy는 Pod 간 네트워크 트래픽을 제어하는 방화벽 규칙입니다. 기본적으로 K8s 클러스터 내 모든 Pod는 서로 통신할 수 있습니다. NetworkPolicy를 적용하면 허용된 트래픽만 통과시키는 화이트리스트 방식으로 보안을 강화합니다.

마이크로서비스 환경에서 서비스 간 불필요한 통신을 차단하면 공격 표면을 줄이고, 장애 전파를 방지할 수 있습니다.

NetworkPolicy 기본 구조

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-server-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server        # 이 정책이 적용될 Pod
  policyTypes:
    - Ingress                 # 들어오는 트래픽 제어
    - Egress                  # 나가는 트래픽 제어
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend   # frontend Pod에서만 접근 허용
      ports:
        - protocol: TCP
          port: 3000
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: postgres   # postgres Pod로만 나갈 수 있음
      ports:
        - protocol: TCP
          port: 5432

podSelector로 정책 대상을 지정하고, ingress/egress로 허용할 트래픽을 정의합니다. 정책이 하나라도 적용되면 해당 방향의 모든 트래픽이 기본 차단되고, 규칙에 명시된 것만 허용됩니다.

Default Deny: 기본 차단 정책

보안의 시작점은 모든 트래픽을 기본 차단하는 것입니다:

# 네임스페이스 전체 Ingress 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}             # 빈 셀렉터 = 모든 Pod
  policyTypes:
    - Ingress                 # Ingress 규칙 없음 = 모두 차단

---
# 네임스페이스 전체 Egress 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress

---
# DNS는 허용 (Egress 차단 시 필수!)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to: []
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

중요: Egress를 차단하면 DNS 조회도 불가능해집니다. DNS(포트 53)는 반드시 별도로 허용해야 서비스 디스커버리가 동작합니다.

네임스페이스 기반 격리

멀티 팀 환경에서 네임스페이스 간 통신을 제어합니다:

# monitoring 네임스페이스에서만 메트릭 수집 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-monitoring
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              purpose: monitoring    # 네임스페이스 레이블
          podSelector:
            matchLabels:
              app: prometheus        # AND 조건
      ports:
        - port: 9090
          protocol: TCP

---
# 같은 네임스페이스 내부 통신만 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-same-namespace
  namespace: production
spec:
  podSelector: {}
  ingress:
    - from:
        - podSelector: {}           # 같은 NS의 모든 Pod

namespaceSelectorpodSelector같은 from 항목에 넣으면 AND 조건, 별도 항목으로 나누면 OR 조건입니다:

# AND: monitoring NS의 prometheus Pod만
- from:
    - namespaceSelector:
        matchLabels: { purpose: monitoring }
      podSelector:
        matchLabels: { app: prometheus }

# OR: monitoring NS의 모든 Pod 또는 아무 NS의 prometheus Pod
- from:
    - namespaceSelector:
        matchLabels: { purpose: monitoring }
    - podSelector:
        matchLabels: { app: prometheus }

CIDR 기반: 외부 트래픽 제어

# 외부 API만 호출 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-external-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: payment-service
  policyTypes:
    - Egress
  egress:
    # 결제 API (특정 IP 대역)
    - to:
        - ipBlock:
            cidr: 203.0.113.0/24
      ports:
        - port: 443
          protocol: TCP
    # 클러스터 내부 DB
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - port: 5432
    # DNS
    - ports:
        - port: 53
          protocol: UDP

ipBlock으로 외부 IP 대역을 지정합니다. except로 특정 IP를 제외할 수도 있습니다:

ipBlock:
  cidr: 10.0.0.0/8
  except:
    - 10.0.1.0/24       # 이 대역은 제외

실전 아키텍처 예시

3-티어 아키텍처에서의 NetworkPolicy 설계입니다:

티어 Ingress 허용 Egress 허용
Frontend Ingress Controller만 API Server + DNS
API Server Frontend만 DB + Redis + 외부 API + DNS
Database API Server만 DNS만
# Database: API Server에서만 접근 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: postgres
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api-server
      ports:
        - port: 5432
  egress:
    - ports:
        - port: 53
          protocol: UDP

이 설계는 K8s Ingress NGINX 심화에서 다룬 Ingress Controller와 연동됩니다. Ingress → Frontend → API → DB 순으로 트래픽이 흐르고, 각 단계에서 NetworkPolicy로 허용된 경로만 열어둡니다.

CNI 플러그인 요구사항

NetworkPolicy는 CNI 플러그인이 지원해야 동작합니다:

CNI NetworkPolicy 비고
Calico ✅ 완전 지원 가장 많이 사용, GlobalNetworkPolicy 추가 지원
Cilium ✅ 완전 지원 eBPF 기반, L7 정책 지원
Weave Net ✅ 지원 Ingress만
Flannel ❌ 미지원 Calico와 함께 사용 필요

주의: NetworkPolicy를 생성해도 CNI가 지원하지 않으면 아무 효과 없이 무시됩니다. 에러도 발생하지 않으므로 반드시 CNI를 확인하세요.

디버깅과 테스트

# 적용된 NetworkPolicy 확인
kubectl get networkpolicy -n production
kubectl describe networkpolicy api-server-policy -n production

# 연결 테스트: 임시 Pod에서 curl
kubectl run test-pod --rm -it --image=curlimages/curl 
  -n production -- curl -v http://api-server:3000/health

# 다른 네임스페이스에서 테스트 (차단 확인)
kubectl run test-pod --rm -it --image=curlimages/curl 
  -n staging -- curl -v --connect-timeout 3 
  http://api-server.production:3000/health

# Calico: 정책 평가 시뮬레이션
calicoctl get networkpolicy -n production -o yaml

# Pod에 적용된 정책 확인
kubectl get pods -n production -l app=api-server 
  -o jsonpath='{.items[*].metadata.labels}'

K8s Istio Service Mesh를 사용 중이라면, Istio의 AuthorizationPolicy와 NetworkPolicy를 함께 사용해 L4(NetworkPolicy) + L7(Istio) 이중 보안을 구현할 수 있습니다.

마무리

K8s NetworkPolicy는 클러스터 내부 네트워크 보안의 기본입니다. Default Deny로 시작해 필요한 트래픽만 명시적으로 허용하고, 네임스페이스 격리로 팀 간 경계를 설정하며, CIDR 기반으로 외부 통신을 제어합니다. AND/OR 조건의 차이를 정확히 이해하고, DNS 허용을 빠뜨리지 않는 것이 실전에서의 핵심입니다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux