Kubernetes의 기본 네트워크는 흔히 Flat Network라고 불립니다.
모든 Pod가 서로 자유롭게 통신할 수 있기 때문입니다.
이는 초기 개발 단계에서는 편리하지만, 보안 관점에서는 다음과 같은 문제가 있습니다.
Network Segmentation 패턴은 이러한 문제를 해결하기 위해
Pod 간 통신 경계를 명시적으로 정의하고,
필요한 통신만 허용하는 방식으로 보안 범위를 최소화합니다.
과거에는 방화벽이나 네트워크 장비 설정을 통해
운영자가 애플리케이션 간 통신을 직접 제어했습니다.
하지만 마이크로서비스 환경에서는 서비스 수와 의존성이 빠르게 증가하면서
이 방식은 확장성과 민첩성 측면에서 한계에 부딪히게 됩니다.
Kubernetes는 이 문제를 Shift-Left 보안 모델로 접근합니다.
이는 특히 멀티테넌시 환경에서
각 팀이 자신의 네트워크 경계를 스스로 책임지게 만드는 중요한 전환점입니다.
Kubernetes에서 네트워크 세그멘테이션은 두 계층에서 구현됩니다.
| 계층 | 기술 | 역할 |
|---|---|---|
| L3/L4 | NetworkPolicy | IP/Port 기반 통신 제어 |
| L7 | Service Mesh AuthorizationPolicy | HTTP 메서드/경로/ID 기반 제어 |
두 계층은 경쟁 관계가 아니라 상호 보완 관계입니다.
NetworkPolicy는 흔히 오해되지만 차단 규칙(Deny Rule)이 아닙니다.
이 특성 때문에 정책이 늘어날수록
정책 간 상호작용과 영향도 분석이 중요해집니다.
아래 표는 NetworkPolicy에서 가장 혼동하기 쉬운 부분 중 하나입니다.
| podSelector | namespaceSelector | 허용 범위 |
|---|---|---|
{} | {} | 모든 네임스페이스의 모든 Pod |
{} | {...} | 특정 네임스페이스의 모든 Pod |
{...} | {} | 모든 네임스페이스의 특정 Pod |
{...} | {...} | 특정 네임스페이스의 특정 Pod |
--- | {} | 정책 네임스페이스 내 모든 Pod |
{} | --- | 정책 네임스페이스 내 모든 Pod |
--는 필드 자체가 생략(Unset) 된 경우를 의미하며,{}(Empty, 전체 선택)와는 동작 의미가 다를 수 있습니다.policyTypes 기본값: [Ingress][Ingress, Egress]policyTypes: [Egress]를 명시해야 합니다.이를 놓치면 의도하지 않게 ingress까지 차단되는 문제가 발생할 수 있습니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: sample
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-backend-to-db
namespace: sample
spec:
podSelector:
matchLabels:
role: database
ingress:
- from:
- podSelector:
matchLabels:
role: backend

- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
외부 인터넷 접근을 차단하고 클러스터 내부 통신만 허용하는 패턴입니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-internal-egress
namespace: sample
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector: {}
namespaceSelector: {}는 모든 네임스페이스를 의미하므로,
클러스터 내부의 모든 Pod와 통신은 허용하되 외부로의 트래픽은 차단됩니다.
특정 IP 대역을 제외하고 외부 통신을 허용하는 패턴입니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-specific-external
namespace: sample
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 192.168.0.0/16
이 정책은 모든 외부 IP(0.0.0.0/0)로의 통신을 허용하되,
내부 사설 네트워크 대역(10.0.0.0/8, 192.168.0.0/16)은 제외합니다.
실무에서는 외부 API 호출이 필요한 Pod에 선택적으로 적용합니다.
labels:
role-database-client: "true"
라벨은 곧 접근 권한이므로,
RBAC, Admission Controller, GitOps 파이프라인을 통해
라벨 변경을 통제하는 것이 중요합니다.
NetworkPolicy는 IP/Port 수준(L3/L4)의 제어만 가능합니다.
Service Mesh는 각 Pod에 사이드카 프록시를 주입하여
모든 L7 트래픽(HTTP 요청)을 프록시가 가로채 검사합니다.
이 덕분에 메서드, 경로, 요청 주체(mTLS ID) 단위의 제어가 가능합니다.
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: prometheus-scrape
namespace: istio-system
spec:
selector:
matchLabels:
has-metrics: "true"
action: ALLOW
rules:
- from:
- source:
namespaces: ["prometheus"]
to:
- operation:
methods: ["GET"]
paths: ["/metrics/*"]
이 예제는 NetworkPolicy로는 구현할 수 없는
L7 수준의 세밀한 제어를 보여줍니다.
hostNetwork: true Pod는 NetworkPolicy를 우회할 수 있습니다.app.kubernetes.io/name, app.kubernetes.io/component 등 표준 라벨 활용)앞서 살펴본 NetworkPolicy의 기본 개념과 패턴은 여전히 유효합니다.
그러나 v1.35 환경에서는 운영 현실이 달라졌습니다.
Kubernetes v1.35 환경에서는 NetworkPolicy의 동작 품질과 운영 경험이
iptables 기반 CNI와 eBPF 기반 CNI 사이에서 크게 달라집니다.
| 비교 항목 | iptables 기반 | eBPF 기반 (Cilium 등) |
|---|---|---|
| 성능 | 규칙 수 증가 시 선형 저하 | 해시맵 기반, 규칙 수와 무관 |
| 관찰 가능성 | 별도 도구 필요 | Hubble 내장 (실시간 흐름 시각화) |
| L7 정책 | 불가능 | CiliumNetworkPolicy로 지원 |
| 디버깅 | iptables 규칙 직접 분석 | Flow Log 기반 직관적 추적 |
핵심 메시지: NetworkPolicy는 명세(Spec)이고, CNI는 현실(Implementation)입니다.
따라서 NetworkPolicy를 이해할 때 CNI 구현을 함께 고려해야 합니다.
iptables 기반 설명만으로는 "왜 정책이 느린지", "왜 디버깅이 어려운지"를 설명할 수 없습니다.
운영 중인 서비스에 Default Deny를 바로 적용하면 100% 장애가 발생합니다.
이것은 과장이 아니라 운영 현실입니다.
1. Flow Logging으로 현재 트래픽 분석
└─ "실제로 누가 누구와 통신하는가?"
2. Allow-list 초안 작성
└─ 분석된 흐름을 기반으로 정책 설계
3. Audit 모드로 검증
└─ 차단 없이 "이 정책이면 뭐가 막혔을지" 로그 확인
4. 실제 차단(Deny) 적용
└─ Audit에서 문제 없음을 확인한 후에만 적용
"기존에 잘 되던 것"이 Default Deny 이후 갑자기 깨지는 원인은
대부분 문서화되지 않은 인프라 통신입니다.
Kubernetes v1.25에서 PodSecurityPolicy(PSP)가 제거되고,
Pod Security Admission(PSA)이 표준이 되었습니다.
그러나 PSA와 NetworkPolicy는 서로 다른 보안 영역을 담당합니다.
| 보안 계층 | 담당 기술 | 통제 대상 |
|---|---|---|
| 실행 권한 | PSA | "무엇을 실행할 수 있는가" (privileged, hostPath 등) |
| 통신 권한 | NetworkPolicy | "어디로 통신할 수 있는가" (Pod 간 네트워크) |
PSA는 통신을 통제하지 않습니다.
PSA로 restricted 프로파일을 적용해도,
해당 Pod가 클러스터 내 모든 서비스와 자유롭게 통신할 수 있다면
침해 시 Lateral Movement는 여전히 가능합니다.
따라서 NetworkPolicy는 "선택 사항"이 아니라
PSA 이후 보안 공백을 메우는 필수 계층입니다.
DNS만 열어두면 될 것 같지만, 실제 운영에서는 부족합니다.
| 대상 | 용도 | 허용 필요 |
|---|---|---|
| CoreDNS | 이름 해석 | UDP/TCP 53 |
| API Server | 컨트롤러, 오퍼레이터 통신 | HTTPS 443 |
| Prometheus | 메트릭 수집 (Pull 방식) | TCP 9090 |
| 클라우드 메타데이터 | IRSA, Instance Identity | 169.254.169.254 |
| 외부 레지스트리 | 이미지 Pull (init 시) | HTTPS 443 |
주의: 위 항목은 대표적인 예시이며, 클러스터 구성에 따라 다릅니다.
반드시 Flow Logging으로 실제 트래픽을 확인한 후 정책을 설계하세요.
실무에서 자주 발생하는 장애 시나리오:
현재 상태: Kubernetes v1.29+, Alpha 단계
AdminNetworkPolicy(ANP)는 클러스터 관리자가 설정하는 전역 정책입니다.
| 구분 | NetworkPolicy | AdminNetworkPolicy |
|---|---|---|
| 범위 | 네임스페이스 단위 | 클러스터 전역 |
| 설정 주체 | 개발자/팀 | 클러스터 관리자 |
| 우선순위 | 낮음 | 높음 (ANP가 먼저 평가) |
멀티테넌시 환경에서 개발팀이 실수로 보안 구멍을 만들 수 있습니다.
ANP는 "개발자 정책이 보안 가이드라인을 넘지 못하게" 강제합니다.
예시:
deny-egress-to-metadata 설정주의: Alpha 기능이므로 프로덕션 적용은 권장하지 않습니다.
다만 Kubernetes가 "거버넌스와 정책 계층화" 방향으로 진화하고 있음을 보여주는 기능입니다.
Kubernetes v1.35에서 Gateway API가 GA(정식 출시)되면서
네트워크 보안의 역할 분리가 더 명확해졌습니다.
| 영역 | 담당 기술 | 트래픽 방향 |
|---|---|---|
| North-South | Gateway API, Ingress | 클러스터 외부 ↔ 내부 |
| East-West | NetworkPolicy | 클러스터 내부 Pod 간 |

최근 트렌드: Gateway API + NetworkPolicy 조합으로
Service Mesh 없이도 L7 보안을 구현하는 패턴이 등장하고 있습니다.
HTTPRoute로 외부 진입점 L7 라우팅정책을 설계하려면 현재 트래픽을 먼저 파악해야 합니다.
| 도구 | 용도 | 사용 시점 |
|---|---|---|
| Cilium Hubble | 실시간 트래픽 흐름 시각화 | 정책 설계 전 트래픽 분석 |
| Network Policy Viewer | 정책 간 관계 시각화 | 복잡한 정책 구조 파악 |
| kube-iptables-tailer | Drop된 패킷 원인 추적 | 정책 적용 후 디버깅 |
| Cilium Network Policy Editor | 시각적 정책 작성 | 정책 초안 설계 |
권장 워크플로우: