네트워크 정책 (Network Policies)
지원 버전: Kubernetes 1.31, 1.32, 1.33 마지막 업데이트: 2026년 7월 3일
Kubernetes 네트워크 정책은 Pod 간 트래픽을 제어하는 방화벽 규칙입니다. 이 문서에서는 기본 NetworkPolicy부터 Cilium과 Calico의 확장 기능까지 상세히 다룹니다.
목차
- 네트워크 정책 개요
- Kubernetes NetworkPolicy 스펙
- 기본 거부 정책
- 정책 순서 및 평가
- Cilium 네트워크 정책 확장
- Calico 네트워크 정책 확장
- 설계 패턴
- 네트워크 정책 테스트
- EKS 고려사항
- 시각화 도구
네트워크 정책 개요
네트워크 정책이란?
네트워크 정책은 Kubernetes에서 Pod 수준의 방화벽 역할을 합니다. 기본적으로 Kubernetes Pod는 모든 다른 Pod와 자유롭게 통신할 수 있지만, 네트워크 정책을 통해 이 트래픽을 제한할 수 있습니다.
┌─────────────────────────────────────────────────────────────────────────┐
│ 네트워크 정책 없음 (기본 상태) │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Pod A │◀──────▶│ Pod B │◀──────▶│ Pod C │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ ▲ ▲ ▲ │
│ │ │ │ │
│ └──────────────────┴──────────────────┘ │
│ 모든 Pod 간 자유로운 통신 가능 │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ 네트워크 정책 적용 후 │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Pod A │───────▶│ Pod B │ │ Pod C │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ ▲ │
│ │ 허용 │
│ ┌────┴────┐ │
│ │ 정책에 │ │
│ │ 의해 │ │
│ │ 제어됨 │ │
│ └─────────┘ │
└─────────────────────────────────────────────────────────────────────────┘네트워크 정책의 특징
| 특성 | 설명 |
|---|---|
| 네임스페이스 범위 | NetworkPolicy는 네임스페이스 내 리소스에 적용 |
| 추가적(Additive) | 여러 정책이 있으면 모든 정책의 합집합이 적용 |
| 선택적 적용 | podSelector로 대상 Pod 지정 |
| 방향별 제어 | Ingress(수신)와 Egress(송신) 별도 제어 |
| CNI 의존 | CNI 플러그인이 NetworkPolicy를 지원해야 함 |
CNI별 NetworkPolicy 지원
| CNI | 기본 NetworkPolicy | 확장 기능 | L7 정책 |
|---|---|---|---|
| Cilium | ✓ | CiliumNetworkPolicy, CiliumClusterwideNetworkPolicy | ✓ |
| Calico | ✓ | GlobalNetworkPolicy, NetworkSet | ✓ (Enterprise) |
| Weave Net | ✓ | 제한적 | ✗ |
| Flannel | ✗ | ✗ | ✗ |
| Amazon VPC CNI | ✗ (별도 설치 필요) | Security Groups for Pods | ✗ |
Kubernetes NetworkPolicy 스펙
기본 구조
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: example-policy
namespace: default
spec:
# 정책이 적용될 Pod 선택
podSelector:
matchLabels:
app: web
# 정책 유형 (생략 시 policyTypes 자동 추론)
policyTypes:
- Ingress
- Egress
# 인그레스 규칙 (수신 트래픽)
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
- namespaceSelector:
matchLabels:
project: myproject
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
# 이그레스 규칙 (송신 트래픽)
egress:
- to:
- podSelector:
matchLabels:
role: database
ports:
- protocol: TCP
port: 5432podSelector
정책이 적용될 Pod를 선택합니다.
# 특정 레이블을 가진 Pod에 적용
spec:
podSelector:
matchLabels:
app: api
version: v1
# 모든 Pod에 적용 (빈 셀렉터)
spec:
podSelector: {}
# matchExpressions 사용
spec:
podSelector:
matchExpressions:
- key: app
operator: In
values:
- api
- web
- key: environment
operator: NotIn
values:
- developmentnamespaceSelector
다른 네임스페이스의 Pod를 선택합니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-monitoring
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
# monitoring 네임스페이스의 모든 Pod 허용
- namespaceSelector:
matchLabels:
name: monitoring
# production 네임스페이스의 특정 Pod 허용
- namespaceSelector:
matchLabels:
name: production
podSelector:
matchLabels:
role: frontend주의: namespaceSelector와 podSelector를 함께 사용할 때 AND vs OR 구분:
# OR 조건 (두 개의 별도 규칙)
ingress:
- from:
- namespaceSelector: # 규칙 1
matchLabels:
name: team-a
- podSelector: # 규칙 2
matchLabels:
role: frontend
# AND 조건 (하나의 규칙)
ingress:
- from:
- namespaceSelector: # 두 조건 모두 충족해야 함
matchLabels:
name: team-a
podSelector:
matchLabels:
role: frontendipBlock
특정 IP 범위를 허용하거나 차단합니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-traffic
namespace: default
spec:
podSelector:
matchLabels:
app: public-api
policyTypes:
- Ingress
- Egress
ingress:
- from:
# 외부 로드밸런서 IP 범위 허용
- ipBlock:
cidr: 10.0.0.0/8
# 특정 외부 IP 허용
- ipBlock:
cidr: 203.0.113.0/24
egress:
- to:
# 외부 API 서버 접근 허용
- 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: 443ports
허용할 포트와 프로토콜을 지정합니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: port-specific-policy
namespace: default
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- ports:
# 특정 포트
- protocol: TCP
port: 80
- protocol: TCP
port: 443
# 포트 범위 (Kubernetes 1.25+)
- protocol: TCP
port: 8000
endPort: 8080
# Named 포트
- protocol: TCP
port: http기본 거부 정책
Ingress 기본 거부
모든 인바운드 트래픽을 차단하는 기본 정책:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # 모든 Pod에 적용
policyTypes:
- Ingress
# ingress 규칙이 없으면 모든 인바운드 트래픽 차단Egress 기본 거부
모든 아웃바운드 트래픽을 차단하는 기본 정책:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
# egress 규칙이 없으면 모든 아웃바운드 트래픽 차단전체 거부 (Ingress + Egress)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- EgressDNS 허용과 함께 기본 거부
Egress를 차단할 때 DNS 조회를 허용하는 일반적인 패턴:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress-allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
# kube-dns/CoreDNS 접근 허용
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53Zero Trust 아키텍처 기본 정책
---
# 1. 모든 트래픽 기본 거부
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: zero-trust-default
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# 2. DNS만 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
---
# 3. 필요한 통신만 명시적으로 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080정책 순서 및 평가
정책 평가 규칙
NetworkPolicy는 다음 규칙에 따라 평가됩니다:
┌─────────────────────────────────────────────────────────────────┐
│ NetworkPolicy 평가 흐름 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. Pod에 적용되는 정책이 있는가? │
│ │ │
│ ├─ 없음 → 모든 트래픽 허용 (기본 동작) │
│ │ │
│ └─ 있음 → 정책 평가 시작 │
│ │ │
│ ▼ │
│ 2. 해당 방향(Ingress/Egress)의 정책이 있는가? │
│ │ │
│ ├─ 없음 → 해당 방향 트래픽 허용 │
│ │ │
│ └─ 있음 → 규칙 매칭 시작 │
│ │ │
│ ▼ │
│ 3. 트래픽이 하나 이상의 규칙과 매칭되는가? │
│ │ │
│ ├─ 매칭됨 → 트래픽 허용 │
│ │ │
│ └─ 매칭 안됨 → 트래픽 차단 │
│ │
└─────────────────────────────────────────────────────────────────┘여러 정책의 조합
여러 NetworkPolicy가 동일한 Pod에 적용될 때, 모든 정책의 규칙이 합쳐집니다 (Union):
---
# 정책 1: frontend에서 오는 트래픽 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
---
# 정책 2: monitoring에서 오는 트래픽 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 8080
- protocol: TCP
port: 9090결과: app: api Pod는 frontend Pod의 8080 접근과 monitoring 네임스페이스의 8080, 9090 접근 모두 허용됩니다.
정책 평가 순서
NetworkPolicy에는 우선순위 개념이 없습니다. 모든 정책은 동등하게 처리됩니다:
┌─────────────────────────────────────────────────────────────────┐
│ │
│ Policy A Policy B Policy C │
│ (allow X) (allow Y) (allow Z) │
│ │ │ │ │
│ └───────────┼───────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ 합집합 │ │
│ │ (X OR Y OR Z) │ │
│ └───────────────┘ │
│ │ │
│ ▼ │
│ 최종 허용 트래픽: │
│ X, Y, Z 모두 허용 │
│ │
└─────────────────────────────────────────────────────────────────┘Cilium 네트워크 정책 확장
CiliumNetworkPolicy
Cilium은 기본 NetworkPolicy를 확장하여 더 강력한 기능을 제공합니다.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: cilium-l7-policy
namespace: production
spec:
# 엔드포인트 선택
endpointSelector:
matchLabels:
app: api
# L3/L4 규칙 (기본 NetworkPolicy와 유사)
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
# L7 규칙 (Cilium 확장)
rules:
http:
- method: GET
path: "/api/v1/.*"
- method: POST
path: "/api/v1/users"
headers:
- 'Content-Type: application/json'L7 HTTP 정책
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: http-api-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: api-server
ingress:
- fromEndpoints:
- matchLabels:
app: web-frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
# GET 요청만 허용
- method: GET
path: "/api/v1/products"
# 특정 경로 패턴 허용
- method: GET
path: "/api/v1/products/[0-9]+"
# POST는 특정 헤더가 있을 때만 허용
- method: POST
path: "/api/v1/orders"
headers:
- "X-API-Key: .*"
- "Content-Type: application/json"
# PUT/DELETE는 admin만 허용
- method: "PUT|DELETE"
path: "/api/v1/.*"
headers:
- "X-User-Role: admin"L7 Kafka 정책
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: kafka-policy
namespace: data
spec:
endpointSelector:
matchLabels:
app: kafka
ingress:
- fromEndpoints:
- matchLabels:
app: producer
toPorts:
- ports:
- port: "9092"
protocol: TCP
rules:
kafka:
# 특정 토픽에만 produce 허용
- role: produce
topic: "orders"
- role: produce
topic: "events"
- fromEndpoints:
- matchLabels:
app: consumer
toPorts:
- ports:
- port: "9092"
protocol: TCP
rules:
kafka:
# 특정 토픽에서만 consume 허용
- role: consume
topic: "orders"
clientID: "order-processor-.*"L7 DNS 정책
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: dns-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: web
egress:
# DNS 조회 허용
- toEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
rules:
dns:
# 특정 도메인만 조회 허용
- matchPattern: "*.amazonaws.com"
- matchPattern: "api.example.com"
- matchName: "database.production.svc.cluster.local"
# 조회한 도메인으로의 연결 허용
- toFQDNs:
- matchPattern: "*.amazonaws.com"
- matchName: "api.example.com"
toPorts:
- ports:
- port: "443"
protocol: TCPCiliumClusterwideNetworkPolicy
클러스터 전체에 적용되는 정책:
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: cluster-default-deny
spec:
# 모든 엔드포인트에 적용
endpointSelector: {}
ingress:
- fromEntities:
- cluster # 클러스터 내부 트래픽만 허용
egress:
- toEntities:
- cluster
- world # 외부 트래픽 허용 (필요시)
- toEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: kube-system
toPorts:
- ports:
- port: "53"
protocol: UDPCilium 엔티티 기반 정책
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: entity-based-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: web
ingress:
- fromEntities:
- world # 클러스터 외부
- cluster # 클러스터 내부
egress:
- toEntities:
- world # 인터넷
toPorts:
- ports:
- port: "443"
protocol: TCP
- toEntities:
- host # 노드 자체
toPorts:
- ports:
- port: "10250" # kubelet
protocol: TCP
- toEntities:
- kube-apiserver # API 서버Calico 네트워크 정책 확장
Calico NetworkPolicy
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: calico-policy
namespace: production
spec:
# 정책 순서 (낮을수록 먼저 평가)
order: 100
selector: app == 'api'
types:
- Ingress
- Egress
ingress:
- action: Allow
protocol: TCP
source:
selector: app == 'frontend'
destination:
ports:
- 8080
egress:
- action: Allow
protocol: TCP
destination:
selector: app == 'database'
ports:
- 5432GlobalNetworkPolicy
클러스터 전체에 적용되는 Calico 정책:
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: default-deny-all
spec:
# 네임스페이스가 아닌 전체 셀렉터
selector: all()
order: 1000 # 낮은 우선순위 (다른 정책이 먼저 평가됨)
types:
- Ingress
- Egress
# 규칙 없음 = 모든 트래픽 차단
---
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: allow-dns
spec:
selector: all()
order: 100
types:
- Egress
egress:
- action: Allow
protocol: UDP
destination:
selector: k8s-app == 'kube-dns'
namespaceSelector: projectcalico.org/name == 'kube-system'
ports:
- 53
- action: Allow
protocol: TCP
destination:
selector: k8s-app == 'kube-dns'
namespaceSelector: projectcalico.org/name == 'kube-system'
ports:
- 53NetworkSet
재사용 가능한 IP 집합 정의:
apiVersion: projectcalico.org/v3
kind: NetworkSet
metadata:
name: external-apis
namespace: production
spec:
nets:
- 203.0.113.0/24 # 외부 API 서버
- 198.51.100.10/32 # 특정 서비스
---
apiVersion: projectcalico.org/v3
kind: GlobalNetworkSet
metadata:
name: blocked-ips
spec:
nets:
- 192.0.2.0/24 # 차단할 IP 대역
- 10.0.0.5/32 # 특정 차단 IPNetworkSet 사용:
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: allow-external-apis
namespace: production
spec:
selector: app == 'web'
types:
- Egress
egress:
- action: Allow
destination:
selector: projectcalico.org/name == 'external-apis'
namespaceSelector: projectcalico.org/name == 'production'Tier 기반 정책
Calico Enterprise에서 지원하는 계층형 정책:
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
name: security
spec:
order: 100
---
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
name: platform
spec:
order: 200
---
apiVersion: projectcalico.org/v3
kind: Tier
metadata:
name: application
spec:
order: 300
---
# Security Tier 정책 (가장 먼저 평가)
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: security.block-known-threats
spec:
tier: security
order: 100
selector: all()
types:
- Ingress
ingress:
- action: Deny
source:
selector: global(name == 'blocked-ips')
---
# Platform Tier 정책
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
name: platform.allow-dns
spec:
tier: platform
order: 100
selector: all()
types:
- Egress
egress:
- action: Allow
protocol: UDP
destination:
ports:
- 53설계 패턴
마이크로세그멘테이션
각 서비스를 개별적으로 격리:
---
# 1. 네임스페이스 기본 거부
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# 2. DNS 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
---
# 3. Frontend -> API
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-to-api
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
---
# 4. API -> Database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-to-database
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
---
# 5. API 외부 접근 (필요시)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-external-access
namespace: production
spec:
podSelector:
matchLabels:
app: api
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네임스페이스 격리
---
# 1. 네임스페이스 레이블 설정
apiVersion: v1
kind: Namespace
metadata:
name: team-a
labels:
team: team-a
environment: production
---
# 2. 같은 팀 내부만 통신 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-team
namespace: team-a
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
team: team-a
---
# 3. 다른 팀의 특정 서비스 접근 허용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-shared-services
namespace: team-a
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
shared-services: "true"
podSelector:
matchLabels:
exposed: "true"데이터베이스 보호
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-protection
namespace: database
spec:
podSelector:
matchLabels:
app: postgresql
policyTypes:
- Ingress
- Egress
ingress:
# 애플리케이션에서만 접근 허용
- from:
- namespaceSelector:
matchLabels:
environment: production
podSelector:
matchLabels:
database-access: "true"
ports:
- protocol: TCP
port: 5432
# 모니터링 접근 허용
- from:
- namespaceSelector:
matchLabels:
name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9187 # postgres_exporter
egress:
# 복제용 다른 DB 인스턴스 접근
- to:
- podSelector:
matchLabels:
app: postgresql
ports:
- protocol: TCP
port: 5432
# DNS
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 533-Tier 아키텍처 정책
---
# Web Tier
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-tier-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: web
policyTypes:
- Ingress
- Egress
ingress:
# 외부(Ingress Controller)에서 접근 허용
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 80
egress:
# App Tier로만 통신
- to:
- podSelector:
matchLabels:
tier: app
ports:
- protocol: TCP
port: 8080
# DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
---
# App Tier
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: app-tier-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: app
policyTypes:
- Ingress
- Egress
ingress:
# Web Tier에서만 접근 허용
- from:
- podSelector:
matchLabels:
tier: web
ports:
- protocol: TCP
port: 8080
egress:
# Data Tier로만 통신
- to:
- podSelector:
matchLabels:
tier: data
ports:
- protocol: TCP
port: 5432
- protocol: TCP
port: 6379
# DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
---
# Data Tier
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: data-tier-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: data
policyTypes:
- Ingress
- Egress
ingress:
# App Tier에서만 접근 허용
- from:
- podSelector:
matchLabels:
tier: app
ports:
- protocol: TCP
port: 5432
- protocol: TCP
port: 6379
egress:
# 같은 tier 내 복제 통신
- to:
- podSelector:
matchLabels:
tier: data네트워크 정책 테스트
netshoot을 사용한 테스트
# netshoot Pod 배포
kubectl run netshoot --image=nicolaka/netshoot -it --rm -- /bin/bash
# 연결 테스트
curl -v http://api-service:8080/health
nc -zv database-service 5432
nslookup api-service.production.svc.cluster.local
# TCP 연결 테스트
curl --connect-timeout 5 http://target-service:8080kubectl exec을 사용한 테스트
# Pod에서 다른 서비스로 연결 테스트
kubectl exec -it frontend-pod -- curl -v http://api-service:8080
# DNS 확인
kubectl exec -it frontend-pod -- nslookup api-service
# 포트 스캔
kubectl exec -it frontend-pod -- nc -zv api-service 8080Cilium Connectivity Test
# Cilium 연결성 테스트 실행
cilium connectivity test
# 특정 테스트만 실행
cilium connectivity test --test pod-to-pod
cilium connectivity test --test pod-to-service
# 정책 테스트
cilium connectivity test --test to-entities-world
cilium connectivity test --test to-cidr-external자동화된 테스트 스크립트
#!/bin/bash
# network-policy-test.sh
echo "=== Network Policy Test Suite ==="
# 테스트용 Pod 생성
kubectl run test-pod --image=nicolaka/netshoot --restart=Never --labels="app=test" -- sleep 3600
# Pod가 준비될 때까지 대기
kubectl wait --for=condition=Ready pod/test-pod --timeout=60s
# 테스트 케이스 실행
run_test() {
local name=$1
local command=$2
local expected=$3
echo -n "Testing: $name... "
result=$(kubectl exec test-pod -- timeout 5 sh -c "$command" 2>&1)
if [[ "$expected" == "success" && $? -eq 0 ]]; then
echo "PASS"
elif [[ "$expected" == "fail" && $? -ne 0 ]]; then
echo "PASS (correctly blocked)"
else
echo "FAIL"
echo " Result: $result"
fi
}
# 테스트 케이스
run_test "DNS resolution" "nslookup kubernetes.default" "success"
run_test "API server access" "curl -k https://kubernetes.default/healthz" "success"
run_test "External access blocked" "curl -s --connect-timeout 3 http://example.com" "fail"
run_test "Database access" "nc -zv database-service 5432" "success"
# 정리
kubectl delete pod test-pod --force --grace-period=0EKS 고려사항
Amazon VPC CNI와 NetworkPolicy
Amazon VPC CNI는 기본적으로 NetworkPolicy를 지원하지 않습니다. NetworkPolicy를 사용하려면 추가 구성이 필요합니다:
# 옵션 1: VPC CNI의 NetworkPolicy 지원 활성화 (v1.14.0+)
kubectl set env daemonset aws-node -n kube-system ENABLE_NETWORK_POLICY=true
# VPC CNI 버전 확인
kubectl describe daemonset aws-node -n kube-system | grep Image
# 옵션 2: Calico 정책 엔진 추가 설치
kubectl apply -f https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/master/config/master/calico-operator.yaml
kubectl apply -f https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/master/config/master/calico-crs.yamlEKS Enhanced Network Security Policies (2025년 12월)
발표일: 2025년 12월 15일 · 출처
EKS는 기존 네임스페이스 범위의 NetworkPolicy에 더해 두 가지 기능을 추가했습니다.
- ClusterNetworkPolicy: 네임스페이스별로 정책을 개별 관리하는 대신, 클러스터 전체에 일관된 네트워크 정책을 중앙에서 한 번에 적용할 수 있는 신규 리소스입니다.
- DNS(FQDN) 기반 Egress 제어: 목적지 IP 대신 도메인 이름을 기준으로 egress 트래픽을 허용/차단합니다. SaaS API나 외부 엔드포인트처럼 IP가 자주 바뀌는 대상을 IP 기반
ipBlock보다 안정적으로 제어할 수 있습니다.
요구사항:
- Kubernetes 1.29+ 신규 클러스터에서 사용 가능
ClusterNetworkPolicy는 VPC CNI v1.21.0+에서 모든 launch mode(IP/Prefix 등)를 지원- DNS 기반 정책은 EKS Auto Mode로 생성된 EC2 노드에서만 지원
- 추가 비용 없음
# ClusterNetworkPolicy 예시: 클러스터 전체 기본 거부 + DNS 허용
apiVersion: policy.networking.k8s.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
name: cluster-default-deny
spec:
priority: 100
subject:
namespaces: {}
egress:
- name: allow-dns
action: Allow
to:
- namespaces:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53# DNS(FQDN) 기반 Egress 정책 예시: 특정 SaaS 도메인만 허용
apiVersion: policy.networking.k8s.io/v1alpha1
kind: ClusterNetworkPolicy
metadata:
name: allow-saas-fqdn-egress
spec:
priority: 200
subject:
namespaces:
matchLabels:
team: payments
egress:
- name: allow-external-api
action: Allow
to:
- fqdns:
- "api.stripe.com"
- "*.datadoghq.com"
ports:
- protocol: TCP
port: 443Security Groups for Pods
EKS에서 Pod에 직접 Security Group을 적용할 수 있습니다:
# SecurityGroupPolicy 정의
apiVersion: vpcresources.k8s.aws/v1beta1
kind: SecurityGroupPolicy
metadata:
name: database-sg-policy
namespace: production
spec:
podSelector:
matchLabels:
app: database
securityGroups:
groupIds:
- sg-0123456789abcdef0 # 데이터베이스용 Security Group
---
# Pod가 자동으로 Security Group 적용
apiVersion: v1
kind: Pod
metadata:
name: database-pod
namespace: production
labels:
app: database
spec:
containers:
- name: postgres
image: postgres:15Security Group 설정 예시:
# Terraform으로 Security Group 정의
resource "aws_security_group" "database_pods" {
name_prefix = "database-pods-"
vpc_id = module.vpc.vpc_id
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.app_pods.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}VPC 레벨 제어와 NetworkPolicy 조합
# NetworkPolicy (Pod 레벨)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-policy
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
database-access: "true"
ports:
- protocol: TCP
port: 5432# Security Group (VPC 레벨)
# 추가적인 네트워크 격리 제공
resource "aws_security_group" "database_pods" {
# ... (위 예시 참조)
}
# NACL (서브넷 레벨)
# 서브넷 간 트래픽 제어
resource "aws_network_acl_rule" "database_subnet" {
network_acl_id = aws_network_acl.database.id
rule_number = 100
egress = false
protocol = "tcp"
rule_action = "allow"
cidr_block = "10.0.0.0/16"
from_port = 5432
to_port = 5432
}EKS에서 Cilium 사용
# VPC CNI 제거 (선택적)
kubectl delete daemonset aws-node -n kube-system
# Cilium 설치
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium --version 1.15.0 \
--namespace kube-system \
--set eni.enabled=true \
--set ipam.mode=eni \
--set egressMasqueradeInterfaces=eth0 \
--set routingMode=native시각화 도구
Cilium Network Policy Editor
Cilium은 웹 기반 정책 편집기를 제공합니다:
# Cilium Hubble UI 활성화
cilium hubble enable --ui
# Hubble UI 접근
cilium hubble ui
# 또는 포트 포워딩
kubectl port-forward -n kube-system svc/hubble-ui 12000:80Cilium Policy Verdict 확인
# 실시간 정책 결정 확인
hubble observe --verdict DROPPED
hubble observe --verdict FORWARDED
# 특정 Pod의 트래픽 확인
hubble observe --pod production/api-server
# JSON 형식으로 출력
hubble observe --output json | jq '.flow.verdict'Calico Enterprise UI
Calico Enterprise는 정책 시각화 UI를 제공합니다:
# Calico Enterprise 대시보드 접근
kubectl port-forward -n calico-system svc/cnx-manager 9443:443Network Policy 시각화 도구
# kubectl-np-viewer 설치
kubectl krew install np-viewer
# 정책 시각화
kubectl np-viewer -n production
# 특정 Pod의 정책 확인
kubectl np-viewer -n production --pod api-serverKube-hunter를 사용한 보안 테스트
# 클러스터 보안 스캔
kubectl run kube-hunter --image=aquasec/kube-hunter --restart=Never -- --pod
# 결과 확인
kubectl logs kube-hunter모범 사례
1. 기본 거부 정책 적용
# 모든 프로덕션 네임스페이스에 적용
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress2. 최소 권한 원칙
필요한 통신만 명시적으로 허용:
# 명시적이고 구체적인 규칙
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-minimal-access
spec:
podSelector:
matchLabels:
app: api
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
protocol: TCP3. 정책 문서화
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-ingress
namespace: production
annotations:
description: "Allow traffic from frontend to API on port 8080"
owner: "platform-team"
last-reviewed: "2026-02-21"
spec:
# ...4. 정기적인 정책 감사
#!/bin/bash
# audit-network-policies.sh
echo "=== Network Policy Audit ==="
# 정책이 없는 네임스페이스 찾기
for ns in $(kubectl get namespaces -o jsonpath='{.items[*].metadata.name}'); do
policies=$(kubectl get networkpolicies -n "$ns" --no-headers 2>/dev/null | wc -l)
if [[ $policies -eq 0 ]]; then
echo "WARNING: No NetworkPolicy in namespace: $ns"
fi
done
# 기본 거부 정책 확인
for ns in $(kubectl get namespaces -o jsonpath='{.items[*].metadata.name}'); do
deny_policy=$(kubectl get networkpolicies -n "$ns" -o json | jq -r '.items[] | select(.spec.podSelector == {} and .spec.ingress == null) | .metadata.name')
if [[ -z "$deny_policy" ]]; then
echo "INFO: No default-deny policy in namespace: $ns"
fi
done요약
Kubernetes 네트워크 정책은 클러스터 내 Pod 통신을 제어하는 핵심 보안 메커니즘입니다:
- 기본 NetworkPolicy: 네임스페이스 범위, podSelector/namespaceSelector/ipBlock 지원
- Cilium 확장: L7 정책, DNS FQDN 기반 정책, 클러스터 와이드 정책
- Calico 확장: GlobalNetworkPolicy, NetworkSet, Tier 기반 정책
- EKS 고려사항: VPC CNI NetworkPolicy 활성화, Security Groups for Pods, ClusterNetworkPolicy 및 DNS(FQDN) 기반 Egress 제어
권장 사항
- 모든 프로덕션 네임스페이스에 기본 거부 정책 적용
- 최소 권한 원칙에 따라 필요한 트래픽만 허용
- 정기적인 정책 감사 및 테스트
- L7 정책이 필요한 경우 Cilium 사용 고려