/////

Kubernetes NetworkPolicy Failure Modes: Default-Deny Semantics, DNS Egress, Selector Union, and CNI Enforcement Gaps

Kubernetes NetworkPolicy의 대표적인 실패 모드는 “정책을 만들었는데도 막히지 않음”과 “default-deny를 넣었더니 정상 트래픽까지 끊김”으로 나뉜다. 핵심 원인은 네 가지다. 1. NetworkPolicy는 CNI 플러그인이 지원·집행하지 않으면 API 객체만 존재하고 실제 차단은 일어나지 않는다. 2. default-deny는 방향별 ingress/egress isolation semantics를 정확히 이해해야 한다. 3. 여러 정

/////

Summary#

Kubernetes NetworkPolicy의 대표적인 실패 모드는 “정책을 만들었는데도 막히지 않음”과 “default-deny를 넣었더니 정상 트래픽까지 끊김”으로 나뉜다. 핵심 원인은 네 가지다.

  1. NetworkPolicy는 CNI 플러그인이 지원·집행하지 않으면 API 객체만 존재하고 실제 차단은 일어나지 않는다.
  2. default-deny는 방향별 ingress/egress isolation semantics를 정확히 이해해야 한다.
  3. 여러 정책과 여러 selector 항목은 대부분 “허용 규칙의 합집합”으로 동작하므로, 하나의 허용 정책이 있으면 다른 정책의 deny 의도를 우회할 수 있다.
  4. egress default-deny를 적용하면 DNS, 특히 CoreDNS/kube-dns로 향하는 UDP/TCP 53 트래픽을 별도로 허용하지 않아 서비스 디스커버리가 실패한다.

운영적으로는 “네트워크 정책을 작성했다”보다 “해당 CNI가 해당 방향의 정책을 집행하는지”, “default-deny 이후 DNS와 필수 control/data-plane egress가 살아 있는지”, “selector가 의도한 namespace/pod 집합을 정확히 가리키는지”를 검증하는 것이 중요하다.

Key Points#

  • NetworkPolicy는 기본적으로 allow 모델에서 시작한다.
  • 아무 NetworkPolicy도 Pod를 선택하지 않으면 해당 Pod의 ingress/egress는 기본적으로 허용된다.
  • 특정 방향의 정책이 Pod를 선택하면 그 방향에 대해 isolated 상태가 되고, 명시적으로 허용된 트래픽만 통과한다.
  • 즉 “정책을 하나 만들었다”는 사실만으로 전체 namespace가 deny 되는 것은 아니다. podSelector: {}policyTypes를 사용해 모든 Pod와 특정 방향을 명확히 선택해야 한다.

  • default-deny는 ingress와 egress를 분리해서 생각해야 한다.

  • ingress default-deny 예시는 보통 podSelector: {}, policyTypes: ["Ingress"]에 ingress rule을 비워 둔다.
  • egress default-deny는 policyTypes: ["Egress"]와 빈 egress rule이 필요하다.
  • policyTypes를 생략하면 Kubernetes가 ingress/egress 필드 존재 여부에 따라 추론하므로, 의도와 다르게 한 방향만 적용될 수 있다.
  • 운영에서는 ingress default-deny와 egress default-deny를 별도 정책으로 두면 리뷰와 롤백이 쉽다.

  • egress default-deny는 DNS를 자주 끊는다.

  • Pod가 my-service.my-namespace.svc.cluster.local 같은 이름을 해석하려면 일반적으로 CoreDNS/kube-dns에 UDP 53, 경우에 따라 TCP 53으로 접근해야 한다.
  • egress default-deny 후 DNS 허용 규칙을 빼먹으면 애플리케이션 로그에는 “connection refused”보다 NXDOMAIN, timeout, Temporary failure in name resolution, client-side service discovery 실패처럼 보일 수 있다.
  • 권장 패턴은 kube-system namespace의 DNS Pod를 namespaceSelector + podSelector로 선택하고 UDP/TCP 53을 허용하는 것이다.
  • 단, CoreDNS 라벨은 클러스터 배포 방식에 따라 k8s-app: kube-dns 등으로 다를 수 있으므로 실제 라벨을 확인해야 한다.

  • selector semantics에서 가장 흔한 오해는 AND와 OR의 위치다.

  • 하나의 from 또는 to 항목 안에 namespaceSelectorpodSelector를 함께 쓰면 보통 “해당 namespace 안의 해당 pod”를 뜻하는 교집합이다.
  • 반대로 from/to 목록에 항목을 여러 개 쓰면 각 항목은 OR, 즉 합집합이다.
  • 여러 NetworkPolicy가 같은 Pod를 선택하면 허용되는 트래픽은 각 정책이 허용한 트래픽의 합집합이다.
  • 따라서 “정책 A에서 막았다”는 표현은 부정확하다. Kubernetes NetworkPolicy는 일반적으로 명시적 deny 규칙이 아니라 allow-list의 합으로 동작한다.

  • 예시: 같은 뜻처럼 보이지만 다른 selector 구조

  • 같은 항목 안에 두 selector를 넣으면 특정 namespace의 특정 pod만 허용한다.
  • 항목을 분리하면 특정 namespace의 모든 pod 또는 모든 namespace의 특정 pod를 허용하는 식으로 범위가 넓어진다.
  • YAML 들여쓰기와 리스트 위치가 실제 보안 경계를 바꿀 수 있으므로 리뷰 시 반드시 selector tree를 펼쳐서 읽어야 한다.

  • CNI enforcement gap이 있으면 정책은 무력하다.

  • Kubernetes API 서버는 NetworkPolicy 객체를 저장하지만, 실제 패킷 필터링은 CNI/network plugin이 수행한다.
  • NetworkPolicy를 지원하지 않는 CNI를 쓰거나, egress 정책을 제대로 지원하지 않는 구성이라면 default-deny를 적용해도 트래픽이 계속 흐를 수 있다.
  • 클러스터 생성 도구나 managed Kubernetes 환경에서 기본 CNI가 무엇인지, NetworkPolicy를 지원하는지, ingress/egress 양방향을 지원하는지 확인해야 한다.
  • 문제 진단 시 kubectl get networkpolicy만으로는 충분하지 않다. 실제 Pod 간 통신 테스트와 DNS lookup 테스트가 필요하다.

  • ipBlock은 만능이 아니다.

  • ipBlock은 외부 CIDR 허용에 유용하지만, Pod IP는 ephemeral이고 Service ClusterIP/DNAT 처리 순서는 CNI 구현에 따라 체감 동작이 달라질 수 있다.
  • 클러스터 내부 서비스 접근을 ipBlock으로 허용하려는 설계는 이식성과 가독성이 떨어질 수 있다.
  • 내부 Pod 집합은 가능하면 namespaceSelectorpodSelector로 표현하는 편이 안전하다.

  • 검증 체크리스트

  • 사용 중인 CNI가 NetworkPolicy를 지원하는가?
  • ingress와 egress 중 어느 방향을 default-deny 했는가?
  • default-deny 정책의 podSelector가 의도한 Pod를 실제로 선택하는가?
  • DNS egress UDP/TCP 53이 허용되어 있는가?
  • namespaceSelectorpodSelector가 같은 list item 안에 있는가, 별도 item으로 분리되어 있는가?
  • 같은 Pod를 선택하는 다른 NetworkPolicy가 더 넓은 허용을 추가하고 있지 않은가?
  • 테스트 Pod에서 nslookup, curl, nc 등으로 실제 허용/차단 결과를 확인했는가?

Cautions#

  • 이 초안은 공개 Kubernetes 문서와 주요 CNI 문서에 기반한 일반 아키텍처 메모다. 특정 클러스터의 실제 동작은 Kubernetes 버전, CNI 플러그인, managed Kubernetes 설정, kube-proxy/eBPF datapath 구성에 따라 달라질 수 있다.
  • 이 실행 환경에는 별도의 WebSearch/WebFetch 도구가 제공되지 않아 실시간 공개 웹 검색 및 fetch 검증은 수행하지 못했다. 아래 Sources는 공개적으로 접근 가능한 신뢰도 높은 문서 URL을 기준으로 선정했다.
  • CoreDNS/kube-dns 라벨은 배포판마다 다를 수 있다. 예시 정책을 그대로 적용하기 전에 kubectl -n kube-system get pods --show-labels로 실제 라벨을 확인해야 한다.
  • NetworkPolicy는 L7 정책이 아니다. HTTP path, method, JWT claim 같은 애플리케이션 계층 조건은 기본 Kubernetes NetworkPolicy만으로 표현할 수 없다.
  • NetworkPolicy는 일반적으로 allow-list 모델이다. “deny 정책을 추가한다”는 식의 표현은 오해를 만들 수 있다. 실제로는 어떤 Pod가 isolated 되었고 어떤 트래픽이 허용 목록에 들어갔는지를 봐야 한다.
  • hostNetwork Pod, node-local traffic, NAT 전후 IP 기준, Service VIP 처리, DNS cache, eBPF datapath 등은 CNI별 차이가 있을 수 있다. 보안 경계로 사용할 경우 반드시 대상 CNI의 공식 문서와 실제 패킷 테스트로 확인해야 한다.

Sources#

  • https://kubernetes.io/docs/concepts/services-networking/network-policies/
  • https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy/
  • https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
  • https://docs.tigera.io/calico/latest/network-policy/get-started/kubernetes-default-deny
  • https://docs.cilium.io/en/stable/security/policy/kubernetes/
  • https://docs.cilium.io/en/stable/network/kubernetes/policy/

Sagwan Revalidation 2026-07-22T01:24:14Z#

  • verdict: ok
  • note: Kubernetes NetworkPolicy 핵심 의미와 운영 주의점은 현재도 유효함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1