Part 12/26: NetworkPolicy
기본적으로 Kubernetes 클러스터 내의 모든 Pod는 서로 자유롭게 통신할 수 있다. 이는 마이크로서비스 간 통신에 편리하지만, 보안 관점에서는 위험할 수 있다. NetworkPolicy는 Pod 수준에서 네트워크 트래픽을 제어하는 방화벽 역할을 한다.
NetworkPolicy의 필요성
기본 네트워크 동작
Kubernetes의 기본 네트워크 모델:
- 모든 Pod는 NAT 없이 다른 모든 Pod와 통신 가능
- 모든 Node는 NAT 없이 모든 Pod와 통신 가능
- Pod가 자신의 IP로 보는 것과 다른 Pod가 보는 것이 동일
이는 완전히 개방된 네트워크를 의미하며, 다음과 같은 위험이 있다:
- 프론트엔드 Pod가 데이터베이스에 직접 접근 가능
- 침해된 Pod가 클러스터 전체로 확산 가능
- 규정 준수(Compliance) 요구사항 위반
NetworkPolicy의 역할
1
2
3
4
5
6
7
8
9
10
11
12
13
NetworkPolicy 없이:
┌─────────────────────────────────────────┐
│ Frontend ←→ Backend ←→ Database │
│ ↕ ↕ ↕ │
│ (모든 Pod가 모든 Pod와 통신 가능) │
└─────────────────────────────────────────┘
NetworkPolicy 적용 후:
┌─────────────────────────────────────────┐
│ Frontend → Backend → Database │
│ (단방향, 필요한 통신만) │
│ Frontend ✗→ Database (직접 접근 차단) │
└─────────────────────────────────────────┘
NetworkPolicy 지원
CNI 플러그인 요구사항
NetworkPolicy는 CNI 플러그인이 지원해야 동작한다.
| CNI 플러그인 | NetworkPolicy 지원 |
|---|---|
| Calico | 완전 지원 |
| Cilium | 완전 지원 |
| Weave Net | 지원 |
| Antrea | 지원 |
| Flannel | 미지원 |
| kubenet | 미지원 |
주의: Flannel만 사용하는 클러스터에서는 NetworkPolicy 리소스를 생성해도 실제로 적용되지 않는다.
1
2
# CNI 확인
kubectl get pods -n kube-system | grep -E 'calico|cilium|weave'
NetworkPolicy 기본 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: example-policy
namespace: default
spec:
podSelector: # 정책이 적용될 Pod 선택
matchLabels:
app: backend
policyTypes: # 정책 유형 (Ingress, Egress, 또는 둘 다)
- Ingress
- Egress
ingress: # 들어오는 트래픽 규칙
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress: # 나가는 트래픽 규칙
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
주요 개념
podSelector: 정책이 적용될 Pod 선택
- 빈 selector(
{})는 namespace의 모든 Pod 선택 - 특정 label을 가진 Pod만 선택 가능
policyTypes: 정책 유형 지정
Ingress: 들어오는 트래픽 제어Egress: 나가는 트래픽 제어- 둘 다 지정 가능
중요 동작 원리:
- NetworkPolicy가 하나라도 적용된 Pod는 기본 거부(default deny) 상태가 됨
- 정책에서 명시적으로 허용한 트래픽만 통과
- 여러 정책이 적용되면 OR 연산 (하나라도 허용하면 통과)
Ingress 규칙
Pod 기반 허용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector: # 같은 namespace의 Pod
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
Namespace 기반 허용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-namespace
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector: # 특정 namespace에서 오는 트래픽
matchLabels:
env: staging
ports:
- protocol: TCP
port: 8080
IP 블록 기반 허용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external
namespace: default
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 10.0.0.0/8 # 허용할 IP 범위
except:
- 10.0.1.0/24 # 제외할 IP 범위
ports:
- protocol: TCP
port: 80
복합 조건 (AND vs OR)
OR 조건 (배열 요소가 다름):
1
2
3
4
5
6
7
8
ingress:
- from:
- podSelector: # 조건 1: frontend Pod
matchLabels:
app: frontend
- namespaceSelector: # OR 조건 2: monitoring namespace
matchLabels:
name: monitoring
AND 조건 (같은 요소 내):
1
2
3
4
5
6
7
8
ingress:
- from:
- podSelector: # AND 조건: staging namespace의
matchLabels: # frontend Pod만
app: frontend
namespaceSelector:
matchLabels:
env: staging
주의: 이 차이점은 매우 중요하다. 들여쓰기와 하이픈 위치에 따라 의미가 완전히 달라진다.
Egress 규칙
특정 서비스로만 허용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-egress
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
DNS 허용 (필수)
Egress를 제한하면 DNS 쿼리도 차단된다. 대부분의 경우 DNS는 허용해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
# DNS 허용
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# 또는 kube-system namespace로
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
외부 서비스 접근 허용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-api
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8 # 클러스터 내부 제외
- 172.16.0.0/12
- 192.168.0.0/16
ports:
- protocol: TCP
port: 443
Default 정책
Default Deny All Ingress
1
2
3
4
5
6
7
8
9
10
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: default
spec:
podSelector: {} # 모든 Pod에 적용
policyTypes:
- Ingress
# ingress 규칙 없음 = 모든 ingress 차단
Default Deny All Egress
1
2
3
4
5
6
7
8
9
10
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
# egress 규칙 없음 = 모든 egress 차단
Default Deny All (Ingress + Egress)
1
2
3
4
5
6
7
8
9
10
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Default Allow All Ingress
1
2
3
4
5
6
7
8
9
10
11
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- {} # 모든 ingress 허용
Default Allow All Egress
1
2
3
4
5
6
7
8
9
10
11
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- {} # 모든 egress 허용
실전 시나리오
3-Tier 애플리케이션 보안
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# 1. 기본 거부 정책
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# 2. Frontend: 외부에서 접근 허용, Backend로만 통신
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: frontend
policyTypes:
- Ingress
- Egress
ingress:
- from: [] # 모든 곳에서 ingress 허용 (Ingress Controller 포함)
ports:
- protocol: TCP
port: 80
egress:
- to:
- podSelector:
matchLabels:
tier: backend
ports:
- protocol: TCP
port: 8080
- to: # DNS 허용
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
---
# 3. Backend: Frontend에서만 접근, Database로만 통신
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
tier: database
ports:
- protocol: TCP
port: 5432
- to: # DNS 허용
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
---
# 4. Database: Backend에서만 접근
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: database
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
tier: backend
ports:
- protocol: TCP
port: 5432
egress:
- to: # DNS만 허용
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
Namespace 격리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 특정 namespace 간 통신만 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: team-a
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {} # 같은 namespace 내에서만
---
# 다른 namespace 접근 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring
namespace: team-a
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 9090 # metrics 포트
트러블슈팅
연결 문제 진단
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. NetworkPolicy 목록 확인
kubectl get networkpolicy -A
# 2. 특정 NetworkPolicy 상세 확인
kubectl describe networkpolicy <policy-name>
# 3. Pod에 적용된 정책 확인
kubectl get networkpolicy -o yaml | grep -A 20 "podSelector"
# 4. 연결 테스트
kubectl run test --rm -it --image=busybox --restart=Never -- \
wget -qO- --timeout=2 http://<target-service>
# 5. DNS 테스트
kubectl run test --rm -it --image=busybox --restart=Never -- \
nslookup kubernetes.default
흔한 실수
1. policyTypes 누락
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 잘못된 예 - policyTypes 없이 egress만 정의
spec:
podSelector: {}
egress:
- to:
- podSelector:
matchLabels:
app: db
# policyTypes가 없으면 egress 규칙이 무시될 수 있음
# 올바른 예
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: db
2. DNS 차단
1
2
3
4
5
6
7
8
9
10
# Egress를 제한하면 DNS도 차단됨
# 반드시 DNS 허용 규칙 추가 필요
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
3. Namespace selector 오류
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 잘못된 예 - 다른 namespace의 Pod를 선택하려면 namespaceSelector 필요
spec:
podSelector:
matchLabels:
app: frontend
ingress:
- from:
- podSelector: # 이것은 같은 namespace만 선택
matchLabels:
app: api
# 올바른 예
ingress:
- from:
- namespaceSelector: # 다른 namespace 지정
matchLabels:
name: other-ns
podSelector:
matchLabels:
app: api
4. AND vs OR 혼동
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# OR 조건 (두 개의 from 항목)
from:
- podSelector:
matchLabels:
app: a
- podSelector:
matchLabels:
app: b
# AND 조건 (하나의 from 항목에 두 selector)
from:
- podSelector:
matchLabels:
app: a
namespaceSelector:
matchLabels:
env: prod
기술 면접 대비
자주 묻는 질문
Q: NetworkPolicy의 기본 동작은?
A: Kubernetes의 기본 네트워크는 완전히 개방되어 있다. NetworkPolicy가 없으면 모든 Pod가 서로 통신 가능하다. NetworkPolicy가 Pod에 적용되면 해당 Pod는 기본 거부(default deny) 상태가 되고, 정책에서 명시적으로 허용한 트래픽만 통과한다. 여러 정책이 적용되면 OR 연산으로 합쳐진다.
Q: NetworkPolicy가 동작하지 않는 이유는?
A: CNI 플러그인이 NetworkPolicy를 지원해야 한다. Flannel은 NetworkPolicy를 지원하지 않아, Flannel만 사용하면 NetworkPolicy 리소스를 생성해도 실제로 적용되지 않는다. Calico, Cilium, Weave Net 등이 지원한다.
Q: Egress 정책 적용 시 주의사항은?
A: Egress를 제한하면 DNS 쿼리도 차단된다. 대부분의 애플리케이션은 Service 이름으로 통신하므로 DNS 조회가 필요하다. Egress 정책 적용 시 반드시 kube-system의 kube-dns(CoreDNS)로의 UDP 53 포트를 허용해야 한다.
Q: podSelector: {}의 의미는?
A: 빈 selector는 해당 namespace의 모든 Pod를 선택한다. default deny 정책을 만들 때 주로 사용한다. spec.podSelector: {}는 모든 Pod에 정책을 적용하고, ingress/egress 규칙을 비워두면 모든 트래픽을 차단한다.
Q: NetworkPolicy는 어떤 수준에서 동작하는가?
A: L3/L4 수준에서 동작한다. IP 주소, 포트, 프로토콜(TCP/UDP/SCTP)을 기준으로 필터링한다. L7(HTTP 경로, 헤더 등)은 기본 NetworkPolicy로 제어할 수 없다. L7 수준 제어가 필요하면 Istio, Cilium의 확장 기능 등을 사용해야 한다.
CKA 시험 대비 필수 명령어
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# NetworkPolicy 조회
kubectl get networkpolicy -A
kubectl get netpol -A # 축약형
kubectl describe networkpolicy <name>
# NetworkPolicy 생성 (YAML 필수)
kubectl apply -f networkpolicy.yaml
# 빠른 테스트
kubectl run test --rm -it --image=busybox --restart=Never -- \
wget -qO- --timeout=2 http://web-service
# Pod label 확인
kubectl get pods --show-labels
# Namespace label 확인
kubectl get ns --show-labels
# Namespace에 label 추가
kubectl label namespace production env=prod
CKA 빈출 시나리오
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 시나리오 1: Default Deny Ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
# 시나리오 2: 특정 Pod에서만 접근 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
# 시나리오 3: 특정 Namespace에서만 접근 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-monitoring
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- protocol: TCP
port: 9090
# 시나리오 4: Egress 제한 (DNS 허용)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-egress
namespace: production
spec:
podSelector:
matchLabels:
app: secure-app
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53