(3/5) 마이크로세그멘테이션 실전 — 트래픽 흐름 위에 default-deny 그리기
- 금융권 망분리, 내부망과 외부망 — 왜 갈랐고 지금은 왜 푸는가
- 제로트러스트 설계 — 망분리 다음의 금융보안을 구성요소로 뜯어보기
- 마이크로세그멘테이션 실전 — 트래픽 흐름 위에 default-deny 그리기 ← 지금 글
- 가시성·정책 자동화 — 흐름을 관찰하고 정책을 코드로 굴리기
- 멀티 클라우드로 넓히기 — 클라우드가 여럿일 때 깨지는 것들
Summary
지난 글에서 제로트러스트의 “침해를 가정한다” 는 원칙을 봤어요. 한 곳이 뚫려도 옆으로 못 번지게 하려면, 내부를 잘게 쪼개야 합니다. 그게 마이크로세그멘테이션이에요.
말은 멋진데 막상 “그래서 어떻게 그리느냐”가 막막하죠. 이번 글은 개념보다 실전입니다. 흐름을 그리는 것부터 시작해서, 3-tier 앱에 쿠버네티스 NetworkPolicy 로 default-deny 를 깔고, 허용 경로만 열고, 실제로 횡적 이동이 막히는지 테스트하는 데까지 손을 움직여볼게요.
💡 이 글에서 다루는 것
- 마이크로세그멘테이션이 막는 것 — 횡적 이동(lateral movement)
- 가장 먼저 할 일 — 트래픽 흐름 매핑
- 구현 방식 4가지 비교 (네트워크·호스트·하이퍼바이저·컨테이너)
- 핵심 원칙 — default-deny + allowlist
- 실전: 3-tier 앱에
NetworkPolicy적용 + 연결 테스트- 관찰(monitor)에서 시행(enforce)으로 가는 점진적 전환과 함정
1. 무엇을 막는가 — 횡적 이동
공격은 대개 한 번에 코어를 뚫지 않아요. 약한 곳(예: 웹 서버) 하나를 잡은 뒤, 거기서 옆으로 기어가 결국 DB·코어에 닿습니다. 이걸 횡적 이동(lateral movement) 이라고 해요.
평평한 내부망에선 이게 너무 쉬워요. 일단 안에 들어오면 옆 서버로 가는 길이 다 열려 있으니까요. 마이크로세그멘테이션은 그 길을 끊습니다.
🚨 망분리가 내부망–외부망 두 칸으로 나눴다면, 마이크로세그멘테이션은 내부를 수십·수백 칸으로 나눠요. 한 칸이 뚫려도 폭발 반경(blast radius)이 그 칸에 갇히는 거죠.
핵심은 “web 이 api 한테 말 거는 건 정상, web 이 db 한테 직접 말 거는 건 비정상” 처럼 정상 흐름만 남기고 나머지를 다 막는 거예요.
2. 가장 먼저 — 흐름을 그린다
실전에서 제일 흔한 실패가 도구부터 사는 것이에요. 무엇이 무엇과 통신하는지 모르는 채로 정책을 조이면, 멀쩡한 업무가 끊깁니다.
그래서 1번은 항상 트래픽 흐름 매핑이에요. 지킬 대상(보호면, protect surface)을 좁게 정하고, 거기로 드나드는 흐름을 그립니다.
[인터넷] ──▶ web (:443)
│ :8080
▼
api ──:5432──▶ db
│
└──:53──▶ DNS (kube-system)
이 그림이 곧 허용 목록(allowlist) 의 초안이에요. 여기 없는 흐름은 전부 막을 대상이 됩니다.
✅ 흐름 매핑은 보통 관찰 도구(흐름 로그·플로우 가시화)로 며칠~몇 주 트래픽을 모아서 그려요. 머릿속 다이어그램만 믿으면 꼭 빠지는 흐름(배치 잡, 모니터링 에이전트, DNS)이 있어서 정책 적용하자마자 장애가 납니다.
3. 어떻게 구현하나 — 4가지 방식
세그먼트를 실제로 강제하는 지점은 여러 곳일 수 있어요. 환경에 맞게 고릅니다.
| 방식 | 적용 지점 | 특징 · 한계 |
|---|---|---|
| 네트워크 기반 | 스위치·방화벽 VLAN · ACL |
익숙하지만 굵게 나뉨 (사실상 매크로 분할) |
| 호스트 기반 | OS 방화벽 / 에이전트 iptables · nftables |
워크로드 단위로 세밀 에이전트 관리 부담 |
| 하이퍼바이저 기반 | 가상화 오버레이 (예: NSX) |
VM 환경에 밀착 가상화 종속 |
| 컨테이너 기반 | CNI NetworkPolicy서비스 메시 |
파드·서비스 단위 클러스터 한정 |
네트워크 기반은 익숙하지만 VLAN 단위라 굵게 나뉘어요. 진짜 “마이크로”하게 가려면 호스트 기반(워크로드마다 OS 방화벽으로 규칙)이나, 컨테이너 환경이면 NetworkPolicy 가 현실적인 답이에요. 아래 실전 예제는 쿠버네티스를 기준으로 갑니다.
4. 핵심 원칙 — default-deny + allowlist
마이크로세그멘테이션 정책은 단 한 줄로 요약돼요.
💡 기본은 전부 막고(default-deny), 정상 흐름만 하나씩 연다(allowlist).
블랙리스트(나쁜 것만 막기)가 아니라 화이트리스트예요. “막을 것”을 나열하는 건 끝이 없지만, “허용할 것”은 2번에서 그린 흐름 그래프가 곧 전부니까요.
쿠버네티스 NetworkPolicy 의 동작 규칙도 이 원칙에 딱 맞아요.
- 파드는 기본적으로 비격리 — 아무 정책도 없으면 인·아웃 다 열림.
- 어떤 정책이 파드를 선택하는 순간 그 파드는 격리 — 명시적으로 허용한 흐름만 통과.
- 연결이 성립하려면 양쪽이 다 허용해야 함 — 보내는 쪽 egress + 받는 쪽 ingress 둘 다.
5. 실전 — 3-tier 앱에 NetworkPolicy 적용
core-banking 네임스페이스에 tier: web / api / db 로 라벨링된 파드가 있다고 할게요. 2번에서 그린 흐름(web→api:8080, api→db:5432, 모두 DNS 필요)을 그대로 정책으로 내립니다.
5-1. 전부 막기 (default-deny)
먼저 네임스페이스 전체를 기본 차단으로 깔아요. podSelector: {} 가 모든 파드를 선택합니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: core-banking
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
이 순간 네임스페이스 안 모든 통신이 끊겨요. DNS 까지 막히는 게 함정인데, 다음에서 바로 엽니다.
5-2. DNS 먼저 열기 (안 그러면 전부 타임아웃)
앱은 api, db 같은 이름을 IP 로 바꾸려고 DNS 를 씁니다. 이게 막히면 “연결이 안 된다”가 아니라 타임아웃으로 나타나서 디버깅이 괴로워요. 그래서 제일 먼저 엽니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: core-banking
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
5-3. 허용 경로 하나 열기 (web → api:8080)
연결은 양쪽이 다 허용해야 하니, api 의 ingress(web 으로부터 받기)와 web 의 egress(api 로 보내기)를 같이 깝니다.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-from-web
namespace: core-banking
spec:
podSelector:
matchLabels:
tier: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: web
ports:
- protocol: TCP
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-allow-to-api
namespace: core-banking
spec:
podSelector:
matchLabels:
tier: web
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
tier: api
ports:
- protocol: TCP
port: 8080
api → db:5432 도 라벨과 포트만 바꿔 똑같은 짝(ingress/egress)으로 한 번 더 깔면 돼요. 흐름 그래프의 화살표 하나 = 정책 한 짝.
5-4. 적용하고 테스트
적용은 평범하게.
kubectl apply -f core-banking-netpol.yaml
networkpolicy.networking.k8s.io/default-deny-all created
networkpolicy.networking.k8s.io/allow-dns-egress created
networkpolicy.networking.k8s.io/api-allow-from-web created
networkpolicy.networking.k8s.io/web-allow-to-api created
이제 허용한 경로(web → api:8080)는 통해야 해요.
kubectl exec -n core-banking deploy/web -- \
curl -s -o /dev/null -w "%{http_code}\n" http://api:8080/health
200
그리고 허용 안 한 경로(web → db:5432, 횡적 이동 시도)는 막혀야 합니다.
kubectl exec -n core-banking deploy/web -- \
nc -zv -w 3 db 5432
nc: connect to db port 5432 (tcp) timed out: Operation now in progress
command terminated with exit code 1
web 이 db 에 직접 손을 뻗는 길이 끊긴 거예요. 설령 web 파드가 털려도, 공격자는 거기서 db 로 바로 못 건너갑니다. 이게 마이크로세그멘테이션이 눈에 보이는 순간이에요.
6. 관찰에서 시행으로 — 점진적 전환
5번을 보고 “운영에 이걸 한 번에 적용한다고?” 싶을 거예요. 맞아요, 빅뱅 적용은 거의 사고로 이어집니다. 그래서 단계로 갑니다.
- 관찰(monitor) 모드 — 정책을 만들되 차단하지 않고 “이 정책이면 무엇이 끊겼을지” 만 로그로 남김. (Calico 의 staged policy, 서비스 메시의 permissive 모드 등 벤더 기능 활용)
- 로그 검토 — 끊길 예정인 흐름 중 정상 업무가 있으면 allowlist 에 추가. 빠졌던 배치·모니터링 흐름이 여기서 잡혀요.
- 시행(enforce) — 더 이상 정상 흐름이 안 걸리면 실제 차단으로 전환.
- 확대 — 가장 위험한 자산(코어 DB) 한 세그먼트부터 시작해 옆으로 넓힘.
🚨 빠지기 쉬운 함정 몇 개:
- DNS·헬스체크·모니터링 흐름 누락 → 적용 직후 광범위 타임아웃. (5-2 가 그래서 1순위)
- 레거시·배치 잡 → 평소엔 안 보이다 월말에만 도는 흐름이 끊김. 관찰 기간을 한 주기 이상.
- 정책 폭증 → 화살표가 수백 개면 사람이 못 관리. 라벨 설계와 자동화가 받쳐줘야 함.
마치며
마이크로세그멘테이션은 거창한 게 아니라 “흐름을 그리고 → 기본은 막고 → 정상만 연다” 의 반복이에요. 망분리가 비웠던 자리를 제로트러스트로 채운다면, 그 제로트러스트의 “침해 가정” 원칙을 네트워크에서 실제로 구현하는 게 바로 이 작업이고요.
세 편에 걸쳐 끊기(망분리) → 검증하기(제로트러스트) → 쪼개기(마이크로세그멘테이션) 로 내려왔어요. 큰 그림에서 시작해 NetworkPolicy YAML 한 짝까지 손에 잡혔다면 이 시리즈의 목적은 다 한 셈입니다.
일단 오늘은 여기까지…..
다음 글에서는 이 세그먼트들을 어떻게 한눈에 관찰하고(가시성) 정책을 자동화하는지 정리해볼게요.
← 이전 글: (2/5) 제로트러스트 설계 — 망분리 다음의 금융보안을 구성요소로 뜯어보기 | 다음 글 →: (4/5) 가시성·정책 자동화 — 흐름을 관찰하고 정책을 코드로 굴리기
참고: Kubernetes Network Policies (공식 문서) · Microsegmentation (Wikipedia)