K8s NetworkPolicy 네트워크 격리

K8s NetworkPolicy란?

Kubernetes NetworkPolicy는 Pod 간 네트워크 트래픽을 제어하는 방화벽 규칙입니다. 기본적으로 K8s 클러스터 내 모든 Pod는 서로 자유롭게 통신할 수 있지만, 프로덕션 환경에서는 최소 권한 원칙(Least Privilege)에 따라 필요한 트래픽만 허용해야 합니다. 이 글에서는 NetworkPolicy의 핵심 개념부터 Ingress/Egress 규칙 설계, Namespace 격리, 실무 패턴까지 심화 내용을 다룹니다.

기본 구조: podSelector와 policyTypes

NetworkPolicy는 podSelector로 대상 Pod를 지정하고, ingress/egress 규칙으로 허용할 트래픽을 정의합니다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-server-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: nginx-ingress
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - protocol: TCP
          port: 5432

이 정책은 app: api-server Pod에 대해 nginx-ingress에서 오는 8080 포트 트래픽만 허용하고, postgres로의 5432 포트 트래픽만 허용합니다. 명시하지 않은 모든 트래픽은 차단됩니다.

Default Deny: 제로 트러스트 시작점

실무에서는 먼저 모든 트래픽을 차단하고, 필요한 것만 열어가는 Default Deny 패턴을 적용합니다.

# 모든 Ingress 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}          # 네임스페이스 내 모든 Pod
  policyTypes:
    - Ingress              # Ingress만 차단, Egress는 허용

---
# 모든 Egress도 차단 (완전 격리)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

주의: Egress를 전부 차단하면 DNS 조회도 막힙니다. 반드시 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

Namespace 격리: 멀티테넌트 환경

마이크로서비스 환경에서 Namespace 간 트래픽을 제어하는 것은 핵심 보안 요구사항입니다. namespaceSelector를 활용합니다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-monitoring
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
    - Ingress
  ingress:
    # 같은 네임스페이스의 특정 Pod
    - from:
        - podSelector:
            matchLabels:
              app: frontend
    # 다른 네임스페이스(monitoring)에서 접근 허용
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
          podSelector:
            matchLabels:
              app: prometheus
      ports:
        - protocol: TCP
          port: 9090

중요: namespaceSelectorpodSelector를 같은 from 항목에 넣으면 AND 조건(해당 네임스페이스의 해당 Pod), 별도 항목으로 분리하면 OR 조건입니다. 이 차이를 혼동하면 의도치 않은 트래픽이 열릴 수 있습니다.

ipBlock: 외부 네트워크 제어

클러스터 외부 IP 대역을 제어할 때는 ipBlock을 사용합니다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: external-api-egress
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: payment-service
  policyTypes:
    - Egress
  egress:
    # PG사 API 서버 대역만 허용
    - to:
        - ipBlock:
            cidr: 203.0.113.0/24
      ports:
        - protocol: TCP
          port: 443
    # 내부 DB 접근
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - protocol: TCP
          port: 5432
    # DNS 허용
    - to: []
      ports:
        - protocol: UDP
          port: 53

ipBlock에서 except를 사용하면 특정 IP를 제외할 수 있습니다. 이는 K8s RBAC 권한 관리와 함께 클러스터 보안의 양대 축을 구성합니다.

실무 패턴: 3-Tier 아키텍처 네트워크 정책

Frontend → Backend → Database 구조에서의 완전한 NetworkPolicy 세트입니다.

# 1. Frontend: Ingress Controller에서만 접근 가능
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: frontend-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      tier: frontend
  policyTypes: [Ingress, Egress]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - {protocol: TCP, port: 3000}
  egress:
    - to:
        - podSelector: {matchLabels: {tier: backend}}
      ports:
        - {protocol: TCP, port: 8080}
    - ports: [{protocol: UDP, port: 53}]

---
# 2. Backend: Frontend에서만 Ingress, DB로만 Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      tier: backend
  policyTypes: [Ingress, Egress]
  ingress:
    - from:
        - podSelector: {matchLabels: {tier: frontend}}
      ports:
        - {protocol: TCP, port: 8080}
  egress:
    - to:
        - podSelector: {matchLabels: {tier: database}}
      ports:
        - {protocol: TCP, port: 5432}
    - to:
        - podSelector: {matchLabels: {tier: cache}}
      ports:
        - {protocol: TCP, port: 6379}
    - ports: [{protocol: UDP, port: 53}]

---
# 3. Database: Backend에서만 접근 가능, Egress 없음
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      tier: database
  policyTypes: [Ingress, Egress]
  ingress:
    - from:
        - podSelector: {matchLabels: {tier: backend}}
      ports:
        - {protocol: TCP, port: 5432}
  egress: []  # 외부 통신 완전 차단

CNI별 지원 범위

NetworkPolicy는 CNI 플러그인이 구현합니다. CNI에 따라 지원 범위가 다릅니다:

CNI Ingress Egress FQDN 기반 L7 정책
Calico ✅ (GlobalNetworkPolicy)
Cilium ✅ (CiliumNetworkPolicy) ✅ (eBPF)
Weave Net
Flannel

Flannel은 NetworkPolicy를 지원하지 않습니다. NetworkPolicy를 사용하려면 Calico나 Cilium으로 전환해야 합니다.

디버깅과 검증

NetworkPolicy 적용 후 의도대로 동작하는지 검증하는 방법입니다:

# 정책 목록 확인
kubectl get networkpolicy -n production

# 특정 정책 상세 보기
kubectl describe networkpolicy api-server-policy -n production

# 연결 테스트 (임시 Pod에서)
kubectl run test-pod --rm -it --image=busybox -n production -- sh
# Pod 내부에서:
wget -qO- --timeout=3 http://api-server:8080/health
nc -zv postgres 5432

# Calico 사용 시: calicoctl로 실제 적용된 규칙 확인
calicoctl get networkpolicy -n production -o yaml

# Cilium 사용 시: 정책 적용 상태 확인
cilium policy get -n production
kubectl exec -n kube-system cilium-xxx -- cilium monitor --type policy-verdict

Cilium CiliumNetworkPolicy: L7 정책

표준 NetworkPolicy는 L3/L4(IP, Port)만 제어합니다. Cilium의 확장 CRD를 사용하면 HTTP 경로, 메서드 단위로 제어할 수 있습니다:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-l7-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: GET
                path: "/api/v1/products.*"
              - method: POST
                path: "/api/v1/orders"

L7 정책은 K8s Gateway API 라우팅과 조합하면 트래픽 제어와 보안을 동시에 구현할 수 있습니다.

운영 베스트 프랙티스

  • 라벨 표준화: app, tier, env 라벨을 일관되게 사용해야 정책 관리가 수월합니다
  • 점진적 적용: 먼저 --dry-run으로 검증 후, staging에서 테스트하고 production에 적용
  • DNS Egress 필수: Default Deny Egress 적용 시 UDP/TCP 53 포트를 반드시 열어야 합니다
  • 모니터링 네임스페이스 예외: Prometheus, Grafana 등 모니터링 도구의 접근을 별도로 허용
  • GitOps 관리: NetworkPolicy도 코드로 관리하고 PR 리뷰를 거쳐야 실수를 방지합니다

마무리

NetworkPolicy는 쿠버네티스 클러스터의 네트워크 레벨 보안을 구현하는 핵심 도구입니다. Default Deny로 시작해 필요한 트래픽만 명시적으로 허용하는 화이트리스트 방식이 가장 안전합니다. CNI 선택(Calico/Cilium)에 따라 L7 정책과 FQDN 기반 제어도 가능하므로, 프로젝트 요구사항에 맞는 CNI를 선택하고 체계적으로 정책을 관리하세요.

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