Network Policy — Pod 간 트래픽을 제어하는 방화벽 규칙
Kubernetes 클러스터에서 기본적으로 모든 Pod이 서로 통신할 수 있다면, 보안상 어떤 문제가 생길 수 있을까요?
기본 상태의 Kubernetes 클러스터에서는 어떤 Pod이든 다른 모든 Pod에 접근할 수 있습니다. 프론트엔드 Pod이 데이터베이스 Pod에 직접 접근하거나, 한 네임스페이스의 Pod이 다른 네임스페이스의 내부 서비스에 접근하는 것이 가능합니다. Network Policy는 이런 무분별한 통신을 레이블 기반으로 제어하는 방화벽 역할을 합니다.
Network Policy 기본 구조
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: production
spec:
podSelector: # 이 정책이 적용되는 Pod
matchLabels:
role: backend
policyTypes:
- Ingress # 들어오는 트래픽 제어
- Egress # 나가는 트래픽 제어
ingress: # 허용할 인바운드 규칙
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 8080
egress: # 허용할 아웃바운드 규칙
- to:
- podSelector:
matchLabels:
role: database
ports:
- protocol: TCP
port: 5432
이 정책은 다음과 같이 동작합니다.
- 대상:
role: backend레이블을 가진 Pod - Ingress:
role: frontendPod에서 오는 8080 포트 트래픽만 허용 - Egress:
role: databasePod의 5432 포트로 나가는 트래픽만 허용 - 그 외 모든 트래픽은 차단됩니다
Default Deny — 제로 트러스트의 시작
보안의 기본 원칙은 "기본 차단, 필요한 것만 허용"입니다.
모든 인바운드 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # 네임스페이스의 모든 Pod
policyTypes:
- Ingress # Ingress 규칙이 비어있으므로 모두 차단
모든 아웃바운드 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress # Egress 규칙이 비어있으므로 모두 차단
모든 방향 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
default-deny를 먼저 적용하고, 필요한 통신만 별도 정책으로 허용하는 것이 권장되는 방식입니다.
셀렉터 조합 규칙
Network Policy의 from/to에서 셀렉터를 조합하는 방식이 중요합니다. 이 부분을 헷갈리기 쉬우니 주의해서 봐야 합니다.
AND 조건 (같은 항목 내)
ingress:
- from:
- namespaceSelector: # 이 네임스페이스에서
matchLabels:
env: production
podSelector: # 이 레이블의 Pod만
matchLabels:
role: frontend
production 네임스페이스의 role: frontend Pod에서 오는 트래픽만 허용합니다.
OR 조건 (별도 항목)
ingress:
- from:
- namespaceSelector: # 이 네임스페이스의 모든 Pod
matchLabels:
env: production
- podSelector: # 또는 같은 네임스페이스의 이 Pod
matchLabels:
role: frontend
배열의 별도 항목이므로 OR 조건입니다. production 네임스페이스의 모든 Pod 또는 같은 네임스페이스의 role: frontend Pod이 허용됩니다.
이 차이는 YAML 들여쓰기 하나로 결정되므로 주의가 필요합니다.
실무 시나리오별 정책 예제
웹 → API → DB 3티어 구조
# DB: API 서버에서만 접근 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-policy
spec:
podSelector:
matchLabels:
tier: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: api
ports:
- protocol: TCP
port: 5432
---
# API: 웹에서만 접근 허용, DB로만 나갈 수 있음
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-policy
spec:
podSelector:
matchLabels:
tier: api
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
tier: web
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
tier: database
ports:
- protocol: TCP
port: 5432
# DNS 허용 (필수!)
- to: []
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
DNS 허용은 필수
Egress를 제한할 때 DNS(UDP/TCP 53) 트래픽을 허용하지 않으면 서비스 이름 해석이 불가능합니다. 거의 항상 DNS 허용 규칙을 추가해야 합니다.
특정 네임스페이스 간 통신 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-monitoring
namespace: production
spec:
podSelector: {} # 모든 Pod
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
purpose: monitoring # monitoring 네임스페이스에서 허용
네임스페이스에 레이블이 있어야 합니다.
kubectl label namespace monitoring purpose=monitoring
IP 블록 기반 규칙
ingress:
- from:
- ipBlock:
cidr: 10.0.0.0/8
except:
- 10.0.1.0/24 # 이 대역은 제외
외부 시스템(사내 네트워크 등)에서의 접근을 제어할 때 사용합니다.
Network Policy 동작 확인
# 정책 목록 확인
kubectl get networkpolicies -n production
# 정책 상세 확인
kubectl describe networkpolicy backend-policy -n production
# 연결 테스트 (허용된 경우)
kubectl exec frontend-pod -- curl -s --max-time 3 http://backend-service:8080
# 200 OK
# 연결 테스트 (차단된 경우)
kubectl exec unauthorized-pod -- curl -s --max-time 3 http://backend-service:8080
# (타임아웃)
CNI 플러그인별 Network Policy 지원
| CNI | 기본 Network Policy | 확장 기능 |
|---|---|---|
| Calico | 지원 | GlobalNetworkPolicy, HostEndpoint |
| Cilium | 지원 | CiliumNetworkPolicy (L7 지원) |
| Flannel | 미지원 | Canal(Flannel+Calico)로 해결 |
| Weave Net | 지원 | 기본 기능만 |
Cilium의 CiliumNetworkPolicy는 HTTP 메서드나 경로 수준의 L7 정책을 지원합니다.
# Cilium L7 정책 예제
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: l7-policy
spec:
endpointSelector:
matchLabels:
app: api
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: "/api/.*"
실무에서 주의할 점
- Network Policy는 네임스페이스 범위입니다: 다른 네임스페이스에는 영향을 주지 않습니다
- 정책은 합집합(OR)으로 동작합니다: 여러 정책이 같은 Pod에 적용되면 허용 규칙이 합쳐집니다
- Egress 제한 시 DNS를 빠뜨리지 마세요: 가장 흔한 실수입니다
- CNI 플러그인을 먼저 확인하세요: Network Policy를 지원하지 않는 CNI에서는 리소스만 생성되고 실제 효과가 없습니다
정리
Network Policy는 Kubernetes에서 Pod 간 트래픽을 레이블 기반으로 제어하는 방화벽입니다. "default-deny 후 필요한 것만 허용" 전략을 기본으로, AND/OR 조건의 셀렉터 조합과 DNS 허용을 주의하면 됩니다. CNI 플러그인이 Network Policy를 지원하는지 반드시 확인하세요.