기본 원리 세 가지
- 정책이 없으면 전부 허용.
- 어떤 파드가 Ingress 정책에 선택되면, 그 파드로 들어오는 트래픽은 어느 정책이든 허용한 것만 들어온다(Egress 도 같음).
- 정책은 허용만 한다. 여러 정책은 합집합이다. “거부 규칙”은 없다.
NetworkPolicy 를 실제로 강제하는 것은 CNI 입니다. Calico·Cilium 은 지원하지만 Flannel 은 지원하지 않아서, 정책을 만들어도 아무 효과가 없습니다.
구조
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-allow-api
namespace: secure
spec:
podSelector: # ① 누구를 보호하나(이 네임스페이스의 app=db 파드)
matchLabels:
app: db
policyTypes: # ② 어느 방향을 통제하나
- Ingress
ingress: # ③ 무엇을 허용하나
- from:
- podSelector:
matchLabels:
app: api # 같은 네임스페이스의 app=api 파드에서
ports:
- protocol: TCP
port: 6379 # 6379 로만
from / to 에 쓸 수 있는 것
| 항목 | 뜻 |
|---|---|
podSelector | 정책과 같은 네임스페이스의 그 레이블 파드 |
namespaceSelector | 그 레이블을 가진 네임스페이스의 모든 파드 |
namespaceSelector + podSelector (같은 항목) | 그 네임스페이스들의 그 파드(AND) |
ipBlock | IP 대역(cidr, except) — 보통 클러스터 밖 |
★ AND 와 OR — 가장 많이 틀리는 곳
# (가) AND: team=frontend 네임스페이스 안의 app=ui 파드만
ingress:
- from:
- namespaceSelector:
matchLabels: {team: frontend}
podSelector: # ← 앞에 '-' 없음 = 같은 항목
matchLabels: {app: ui}
# (나) OR: team=frontend 네임스페이스의 모든 파드 + 같은 네임스페이스의 app=ui 파드
ingress:
- from:
- namespaceSelector:
matchLabels: {team: frontend}
- podSelector: # ← '-' 있음 = 다른 항목
matchLabels: {app: ui}
- 하나 차이로 뜻이 완전히 달라집니다.
네임스페이스는 이름이 아니라 레이블로 고릅니다. 모든 네임스페이스에는 자동으로 kubernetes.io/metadata.name=<이름> 레이블이 붙어 있어서, 이름으로 고르고 싶으면 이것을 씁니다.
kubectl get ns --show-labels
kubectl label ns web-ui team=frontend
자주 쓰는 틀
# 네임스페이스 전체 ingress 차단
spec:
podSelector: {}
policyTypes: [Ingress]
# 네임스페이스 전체 ingress·egress 차단
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
# DNS 만 나가게 (egress 차단과 함께 쓴다)
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# 같은 네임스페이스 안끼리는 모두 허용
spec:
podSelector: {}
ingress:
- from:
- podSelector: {}
egress 를 막으면 DNS 도 막힙니다. IP 로는 되는데 이름으로 안 되면 53번 egress 를 열었는지 확인하세요.
확인하기
kubectl get netpol -n secure
kubectl describe netpol db-allow-api -n secure
# 허용돼야 하는 쪽: 레이블을 흉내 낸 임시 파드로
kubectl run t --rm -it --image=busybox:1.37 --restart=Never -n secure --labels=app=api -- nc -zv -w2 <db파드IP> 6379
# 막혀야 하는 쪽
kubectl run t --rm -it --image=busybox:1.37 --restart=Never -n secure --labels=app=other -- nc -zv -w2 <db파드IP> 6379
막힌 연결은 거부(refused)가 아니라 시간 초과(timed out) 로 끝납니다. 이 차이로 “정책이 막았다”와 “포트에 아무도 없다”를 구분합니다.