Kubernetes NetworkPolicy 완벽 가이드: Zero Trust 구현을 위한 트래픽 제어 실전
쿠버네티스(Kubernetes)는 컨테이너 오케스트레이션의 혁신을 가져왔지만, 그 편리함 이면에는 '네트워크 보안'이라는 거대한 숙제가 도사리고 있습니다. 기본적으로 쿠버네티스 클러스터는 파드 간의 통신을 가능하게 하지만, 이는 '신뢰'를 전제로 합니다. 만약 공격자가 클러스터 내부의 한 파드에 침투했다면, 이 파드는 마치 내부망의 자유로운 공간처럼 다른 서비스에 접근할 수 있는 위험에 노출될 수 있습니다.
이러한 내부 위협을 방어하고, 모든 통신을 '검증된 신뢰' 하에 두는 것이 바로 **제로 트러스트 아키텍처(Zero Trust Architecture, ZTA)**의 핵심 원칙입니다. 그리고 이 원칙을 쿠버네티스 레벨에서 구현하는 가장 강력하고 네이티브한 방법이 바로 NetworkPolicy입니다.
본 가이드는 단순한 개념 설명에 그치지 않고, 실제 운영 환경에서 요구되는 최소 권한 원칙(Principle of Least Privilege)에 입각하여 트래픽을 정밀하게 제어하는 방법을 YAML 코드와 함께 깊이 있게 다룹니다.
쿠버네티스 네트워킹의 한계와 NetworkPolicy의 필요성
기존의 쿠버네티스 네트워킹은 주로 L3/L4 레벨의 연결성을 보장하는 데 초점을 맞춥니다. Service나 Ingress는 '어떤 포트로 접근할 수 있는지'를 정의하지만, '누가, 어떤 조건으로 접근할 수 있는지'에 대한 세밀한 제어는 부족했습니다.
💡 비유로 이해하기: 쿠버네티스 클러스터를 대규모 오피스 빌딩이라고 가정해 봅시다.
- 기본 네트워킹: 건물 전체에 전기와 인터넷이 연결되어 있다는 것과 같습니다. (연결성은 보장됨)
- NetworkPolicy: 각 사무실(파드)마다 출입 카드(Selector)가 필요하고, 특정 시간대(Port/Protocol)에만 특정 층(Namespace)으로 접근할 수 있도록 출입 통제 시스템(ACL)을 설치하는 것과 같습니다.
NetworkPolicy는 이 출입 통제 시스템을 구현하여, '명시적으로 허용된 트래픽'만 통과시키고 나머지는 모두 차단하는 역할을 수행합니다.
NetworkPolicy의 기본 원리: Default Deny와 선택자(Selector)
NetworkPolicy를 이해하는 데 있어 가장 중요한 개념은 Default Deny(기본 거부) 원칙입니다.
NetworkPolicy를 한 번이라도 특정 네임스페이스나 파드 그룹에 적용하면, 해당 범위 내의 모든 트래픽은 기본적으로 차단됩니다. 즉, 정책을 적용하기 전에는 '모두 허용' 상태였다면, 정책 적용 후에는 '아무것도 허용되지 않음' 상태로 돌아갑니다.
이 기본 거부 상태에서 우리가 필요한 통신 경로만 **명시적으로 허용(Allow)**하는 방식으로 보안을 구축해야 합니다.
핵심 구성 요소: Selector와 Pod/Namespace 지정
NetworkPolicy는 podSelector와 namespaceSelector를 통해 정책의 대상을 매우 정교하게 지정합니다.
podSelector: 정책을 적용할 파드 그룹을 지정합니다. (예:app: backend인 모든 파드)namespaceSelector: 정책이 적용될 네임스페이스 그룹을 지정합니다.
이 두 가지를 조합하여 "특정 레이블을 가진 네임스페이스 내의, 특정 레이블을 가진 파드들"에게만 정책을 적용할 수 있습니다.
🛡️ Ingress 제어: 외부/다른 파드로부터의 접근 통제 (Inbound)
Ingress 제어는 외부(또는 클러스터 내부의 다른 서비스)에서 우리 파드로 들어오는 트래픽을 통제하는 것입니다. 이는 마치 회사 건물로 들어오는 방문객을 관리하는 것과 같습니다.
[YAML 예시 1: 특정 네임스페이스의 특정 파드만 접근 허용]
다음 정책은 backend 레이블을 가진 파드에 대해, 오직 frontend 레이블을 가진 파드에서 오는 HTTP(80 포트) 트래픽만 허용하고 나머지는 모두 차단합니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: default
spec:
podSelector:
matchLabels:
app: backend # 이 정책이 적용될 대상 파드
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend # 오직 이 레이블을 가진 파드에서만 허용
ports:
- protocol: TCP
port: 80📤 Egress 제어: 파드가 외부로 나가는 트래픽 통제 (Outbound)
Egress 제어는 파드가 외부 인터넷이나 클러스터 내의 다른 서비스로 데이터를 전송하는 것을 통제합니다. 이는 내부 직원이 외부 거래처와 접촉할 때, 반드시 필요한 사람에게만 연락하도록 제한하는 것과 같습니다.
[YAML 예시 2: 특정 외부 IP 대역으로의 아웃바운드 통신만 허용]
이 정책은 api-worker 파드가 오직 특정 데이터베이스 서버의 IP 대역(10.0.1.0/24)으로만 아웃바운드 통신을 하도록 제한합니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-egress-to-db
namespace: default
spec:
podSelector:
matchLabels:
app: api-worker # 이 정책이 적용될 대상 파드
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.1.0/24 # 허용할 외부 IP 대역
ports:
- protocol: TCP
port: 5432 # PostgreSQL 포트🧩 실전 시나리오: 복합 정책과 트러블슈팅
실제 운영 환경에서는 여러 정책이 겹치거나, 특정 포트/프로토콜만 허용해야 하는 복합적인 경우가 많습니다.
[YAML 예시 3: 복합 허용 정책 (HTTP와 내부 메트릭 수집만 허용)]
이 정책은 파드에 대한 접근을 허용하되, 포트 80(HTTP)과 9100(Prometheus 메트릭) 두 가지 트래픽만 허용하는 예시입니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-http-and-metrics
namespace: monitoring
spec:
podSelector:
matchLabels:
app: web-service
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0 # 모든 외부 트래픽 허용 (예시)
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 9090 # 다른 포트도 추가 가능💡 디버깅 팁: 정책 적용 순서와 충돌
네트워크 정책은 가장 구체적인 규칙이 가장 높은 우선순위를 가집니다. 만약 특정 포트를 열고 싶지 않은데, 다른 정책에서 0.0.0.0/0 (모든 IP)를 허용하고 있다면, 그 광범위한 규칙이 우선하여 의도치 않은 포트가 열릴 수 있습니다. 항상 필요한 최소한의 포트와 소스 IP만 지정하는 것이 보안상 가장 안전합니다.
요약:
- Ingress (Inbound): 외부에서 컨테이너로 들어오는 트래픽 제어 (가장 중요).
- Egress (Outbound): 컨테이너가 외부로 나가는 트래픽 제어 (보안 강화 시 필수).
- 원칙: 필요한 것만 허용하고, 나머지는 기본적으로 차단하는 'Default Deny' 원칙을 지키세요.
이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.
댓글
불러오는 중...