infra
Platform

모듈 맵

[Kubernetes] NetworkPolicy를 활용한 특정 파드 간 통신 차단 및 제한

0 / 29 완료

펼치기
0 / 29 완료0%

쿠버네티스 & GitOps · 17 / 29

[Kubernetes] NetworkPolicy를 활용한 특정 파드 간 통신 차단 및 제한

NetworkPolicy로 파드 간 트래픽을 명시적으로 허용/차단하여 클러스터 내 네트워크 보안 경계를 구축합니다

🚨INCIDENT ALERT
HIGH

결제 API는 주문 서비스에서만 접근해야 하는데 같은 Namespace의 임시 파드에서도 접속이 됩니다. 클러스터 내부 네트워크를 모두 신뢰하면 침해 사고가 한 서비스에서 다른 서비스로 쉽게 번집니다. NetworkPolicy는 Pod 간 통신을 필요한 경로로만 제한하는 방화벽 역할을 합니다.

NetworkPolicy — 파드 간 통신 화이트리스트

보안 감사 결과가 나왔습니다. "클러스터 내 모든 파드가 서로 통신 가능한 상태입니다. 결제 서비스가 사용자 서비스 DB에 직접 접근할 수 있고, 개발 네임스페이스에서 프로덕션 DB로 쿼리를 날릴 수 있습니다." 이건 단순한 설정 실수가 아니라 아키텍처 문제입니다. 기본적으로 K8s 클러스터 안의 파드들은 네임스페이스나 서비스 경계와 관계없이 모두 서로 통신할 수 있습니다. 이 열린 상태에서 한 서비스가 침해되면 공격자는 내부 네트워크에서 횡이동(lateral movement)하며 모든 서비스에 접근할 수 있습니다. NetworkPolicy는 이 문제를 해결합니다. 어떤 파드가 어떤 파드와 통신할 수 있는지 명시적으로 선언하는 화이트리스트 방식으로, 허용되지 않은 모든 트래픽은 자동으로 차단됩니다.

이번 챕터에서 배울 것
  • 1NetworkPolicy 없는 클러스터의 기본 통신 구조를 설명할 수 있다
  • 2Ingress/Egress 규칙과 podSelector, namespaceSelector를 작성할 수 있다
  • 3화이트리스트 기반 정책 설계 원칙을 적용할 수 있다
  • 4네임스페이스 격리와 서비스 간 통신 허용 패턴을 구성할 수 있다
  • 5기본 거부(Deny All) 정책을 구성할 수 있다
  • 6NetworkPolicy 디버깅 도구로 트러블슈팅할 수 있다
실습 환경 준비
CNI 플러그인 확인 (NetworkPolicy 지원 필요)
kubectl get pods -n kube-system | grep -E 'calico|cilium|weave'
실습 네임스페이스 생성
kubectl create namespace netpol-demo
네임스페이스에 레이블 추가 (namespaceSelector용)
kubectl label namespace netpol-demo env=demo
현재 NetworkPolicy 확인
kubectl get networkpolicy -A

NetworkPolicy 규칙 구조 이해

NetworkPolicy는 podSelector로 대상 파드를 고르고, ingress(들어오는 트래픽)와 egress(나가는 트래픽) 규칙을 정의합니다.

YAML
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: 규칙-이름
  namespace: 적용-네임스페이스
spec:
  podSelector:           # 이 정책이 적용될 파드
    matchLabels:
      app: 대상파드
  policyTypes:           # Ingress, Egress 또는 둘 다
    - Ingress
    - Egress
  ingress:               # 허용할 인바운드 트래픽
    - from:
        - podSelector:   # 이 파드에서 오는 트래픽
            matchLabels:
              app: 허용파드
      ports:
        - protocol: TCP
          port: 8080
  egress:                # 허용할 아웃바운드 트래픽
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: 허용네임스페이스
      ports:
        - protocol: TCP
          port: 5432

패킷 하나가 허용·차단되기까지 — NetworkPolicy 평가 순서

💡개념

파드 간 트래픽이 허용/차단되는 법 — NetworkPolicy 평가

curl http://payment:8080 한 줄이 성공하거나 타임아웃이 납니다. 그 차이는 목적지 파드에 닿으려는 패킷이 일련의 판정을 통과했는지에서 갈립니다. 핵심 규칙은 단 하나 — 어떤 NetworkPolicy도 그 파드를 선택하지 않으면 전부 허용(all-allow), 하지만 정책이 하나라도 그 파드를 podSelector로 선택하는 순간 그 방향(ingress/egress)은 "명시 허용된 것만 통과"로 뒤집힙니다(기본 격리). 이 전환점을 알면 "정책을 만들었는데 왜 안 막히지" 또는 "왜 갑자기 다 막혔지"를 단계로 짚을 수 있습니다.

TEXT
[출발 파드]  order → payment:8080 로 패킷 전송
   │
   ① CNI가 정책을 강제하는가?         (Calico·Cilium 등 정책 지원 CNI인지 — Flannel이면 판정 자체가 없음)
   │    → 미지원이면 정책과 무관하게 그냥 통과
   │
   ② 목적지 파드를 선택하는 Ingress 정책이 있는가?
   │    → 하나도 없음 → 그 파드는 격리 안 됨 → 인바운드 전부 허용 (all-allow)
   │    → 하나라도 있음 → 그 파드는 격리됨 → 아래 규칙에 맞는 것만 허용
   │
   ③ 그 파드를 선택하는 정책들의 ingress.from 규칙을 모두 합침(OR)
   │    (여러 NetworkPolicy는 합집합 — 어느 하나라도 허용하면 통과)
   │
   ④ 출발지가 from에 매칭되는가?       (podSelector·namespaceSelector·ipBlock + ports 대조)
   │    → 매칭 → 허용,  비매칭 → 차단(패킷 드롭)
   │
   ⑤ (출발 파드 쪽) egress 정책도 대칭으로 판정
   │    → order를 선택하는 egress 정책이 있으면 payment:8080·DNS(53)가 to에 있어야 나감
   ▼
[도착/차단]  허용이면 응답, 차단이면 응답이 없어 curl은 timeout

각 단계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:

단계하는 일여기서 막히면
① CNI 강제각 노드의 CNI 에이전트(Calico felix·Cilium 등)가 정책을 iptables·eBPF로 번역해 커널에서 차단CNI가 NetworkPolicy 미지원(Flannel) → 정책을 만들어도 아무것도 안 막힘(전부 통과)
② 선택 여부목적지 파드를 podSelector로 잡는 정책이 하나라도 있으면 그 방향이 기본 격리로 전환아무 정책도 그 파드를 안 잡음 → 여전히 all-allow(막으려 했는데 안 막히는 대표 원인)
③ 정책 합집합그 파드를 선택하는 모든 정책의 ingress·egress 규칙을 OR로 합침넓은 허용 정책이 하나 섞여 있으면 좁은 정책을 깔아도 그 경로가 열려 있음
④ from/to 매칭출발지를 podSelector·namespaceSelector·ipBlockports로 대조라벨 불일치·포트 누락 → 매칭 실패 → 드롭. namespaceSelector용 라벨이 없으면 크로스 네임스페이스가 안 열림
⑤ egress + DNS출발 파드에 egress 정책이 걸렸으면 목적지뿐 아니라 DNS(UDP/TCP 53)to에 있어야 이름 해석이 됨DNS 미허용 → 서비스 이름 조회 실패(i/o timeout)로 마치 목적지가 죽은 것처럼 보임

핵심은 세 가지입니다. 첫째, NetworkPolicy는 화이트리스트이자 합집합(OR) 입니다 — 여러 정책 중 하나라도 허용하면 통과하므로, 좁게 막으려면 넓은 허용 정책이 섞이지 않았는지 봐야 합니다. 둘째, 격리는 방향별(ingress/egress 따로) 로 켜집니다 — ingress 정책만 걸면 나가는 트래픽은 여전히 all-allow입니다. 셋째, 실제 강제는 CNI가 하므로 정책이 있어도 CNI가 미지원이면 무효입니다.

진단은 판정 순서를 거꾸로 밟는 것입니다. kubectl exec <pod> -- curl http://<svc>:<port> --max-time 5에서 timed out이면 NetworkPolicy 차단(패킷 드롭)이고 Connection refused면 정책이 아니라 목적지 서비스 문제입니다. 막혔다면 ① kubectl get pods -n kube-system | grep -E 'calico|cilium'로 CNI부터 확인하고, ② kubectl get networkpolicy -n <ns>로 목적지 파드를 잡는 정책이 있는지, ③ kubectl describe networkpolicy <name>from·to·ports가 출발지와 맞는지, ④ egress를 걸었다면 DNS(53) 규칙이 있는지 순서로 좁힙니다.

💡개념

기본 거부 정책 (Deny All)

NetworkPolicy를 개별 서비스에 하나씩 추가하다 보면 설정이 없는 서비스가 모든 파드에서 접근 가능한 상태로 방치됩니다. 새 서비스가 배포될 때마다 보안팀이 일일이 허용 목록을 관리해야 해서 누락이 생깁니다. 화이트리스트 방식으로 전환하려면 먼저 네임스페이스 전체 트래픽을 기본 차단해야 합니다. "기본 거부 후 필요한 것만 허용"이 실무에서 NetworkPolicy를 적용하는 표준 순서입니다. 이 CB에서는 네임스페이스 전체에 Deny All 정책을 적용하는 YAML 패턴과 적용 후 확인 방법을 다룹니다.

NetworkPolicy 기본 거부와 Ingress/Egress — 기본적으로 파드 간 통신은 전부 허용되지만, 빈 셀렉터의 deny-all 정책을 적용하면 해당 네임스페이스의 모든 인입(Ingress)·송출(Egress)을 차단. 이후 필요한 통신만 화이트리스트로 허용 규칙을 추가하는 방식이라, 새 서비스가 와도 명시 허용 전엔 격리됨확대

YAML
# deny-all-ingress.yaml — 모든 인바운드 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
  namespace: production
spec:
  podSelector: {}        # 네임스페이스의 모든 파드에 적용
  policyTypes:
    - Ingress            # from을 지정하지 않으면 모두 차단
---
# deny-all-egress.yaml — 모든 아웃바운드 차단
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress             # to를 지정하지 않으면 모두 차단
Kubernetes
kubectl apply -f deny-all-ingress.yaml -f deny-all-egress.yaml

# 통신 차단 확인
kubectl run test-pod --image=curlimages/curl -n production -- sleep 3600
kubectl exec -it test-pod -n production -- curl http://payment-service:8080 --max-time 5
# curl: (28) Connection timed out after 5001 milliseconds
💡개념

서비스 간 통신 허용 패턴

Deny All 정책을 적용하고 나면 기존에 잘 동작하던 서비스들이 타임아웃이 납니다. 결제 서비스가 주문 서비스의 호출을 받지 못하고, 모니터링 에이전트가 메트릭을 수집하지 못합니다. 어떤 서비스가 어떤 서비스와 통신해야 하는지 명확하게 정의하지 않으면 복구 과정에서 너무 넓은 허용 규칙을 열어버리는 실수가 생깁니다. deny-all을 설정한 뒤 필요한 통신만 명시적으로 허용하는 것이 올바른 접근입니다. 이 CB에서는 결제 서비스 예시로 인바운드와 아웃바운드 허용 규칙을 조합하는 NetworkPolicy 패턴을 다룹니다. 아래는 결제 서비스가 주문 서비스의 요청만 받고, PostgreSQL에만 쿼리할 수 있는 예시입니다.

YAML
# payment-netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-service-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: payment          # payment 파드에 적용
  policyTypes:
    - Ingress
    - Egress
  ingress:
    # 주문 서비스에서 오는 트래픽만 허용
    - from:
        - podSelector:
            matchLabels:
              app: order-service
      ports:
        - protocol: TCP
          port: 8080
    # 모니터링 에이전트도 허용 (메트릭 수집)
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
          podSelector:
            matchLabels:
              app: prometheus
      ports:
        - protocol: TCP
          port: 9090
  egress:
    # PostgreSQL DB에만 쿼리 허용
    - to:
        - podSelector:
            matchLabels:
              app: payment-db
      ports:
        - protocol: TCP
          port: 5432
    # DNS 조회는 항상 허용 (없으면 서비스 이름 조회 불가)
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
Kubernetes
kubectl apply -f payment-netpol.yaml

# 허용된 통신 확인 (order-service 파드에서)
kubectl exec -it order-pod -n production -- curl http://payment-service:8080/health
# {"status": "ok"}

# 차단된 통신 확인 (catalog-service 파드에서 — 정책에 없음)
kubectl exec -it catalog-pod -n production -- curl http://payment-service:8080 --max-time 5
# curl: (28) Connection timed out after 5001 milliseconds
💡개념

네임스페이스 격리 — dev와 prod 분리

개발자가 개발 환경에서 DB 연결 테스트를 하다가 운영 DB 주소를 잘못 입력했고, 운영 Namespace의 PostgreSQL에 쿼리가 직접 날아갔습니다. Namespace를 나눠도 기본적으로 파드 간 네트워크 통신은 열려 있어 실수가 대형 사고로 이어질 수 있습니다. 네임스페이스 단위 격리 정책을 적용하면 개발 Namespace에서 운영 Namespace로의 트래픽을 차단하고, 외부 진입은 Ingress Controller 경로만 허용할 수 있습니다. 이 CB에서는 production Namespace를 내부 파드와 Ingress Controller에서만 접근 가능하도록 격리하는 NetworkPolicy 설정을 다룹니다.

YAML
# prod-namespace-isolation.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: prod-namespace-isolation
  namespace: production
spec:
  podSelector: {}    # production 전체 파드에 적용
  policyTypes:
    - Ingress
  ingress:
    # production 네임스페이스 내부 파드 간 통신만 허용
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: production
    # 인그레스 컨트롤러에서 오는 외부 트래픽 허용
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
          podSelector:
            matchLabels:
              app.kubernetes.io/name: ingress-nginx
Kubernetes
# 네임스페이스 레이블 확인
kubectl get namespace --show-labels

# 개발 파드에서 프로덕션 서비스 접근 시도 (차단됨)
kubectl exec -it debug-pod -n development -- \
  curl http://payment-service.production.svc.cluster.local:8080 --max-time 5
# curl: (28) Connection timed out

실습: 3계층 아키텍처 트래픽 제어

프론트엔드 → API → DB 구조에서 각 계층 간 통신만 허용하는 정책을 만듭니다.

3계층 아키텍처의 NetworkPolicy 트래픽 제어 — frontend·api·db 파드를 tier 라벨로 구분하고, default-deny로 네임스페이스 전체를 막은 뒤 frontend→api(:8080)와 api→db(:5432)만 podSelector 화이트리스트로 허용한다. frontend가 db에 직접 연결하는 경로는 정책에 없어 차단되며, 모든 파드의 egress :53(UDP)은 DNS 이름 해석을 위해 열어둔다. 라벨 기반이라 파드가 재생성돼 IP가 바뀌어도 정책이 유효하다확대

Kubernetes
# 실습 환경 구성
kubectl create namespace three-tier

# 계층별 파드 배포
kubectl run frontend --image=nginx --labels="tier=frontend,app=web" -n three-tier
kubectl run api --image=nginx --labels="tier=api,app=backend" -n three-tier
kubectl run db --image=postgres --labels="tier=db,app=database" \
  --env="POSTGRES_PASSWORD=test" -n three-tier

kubectl expose pod api --port=8080 --target-port=80 -n three-tier
kubectl expose pod db --port=5432 --target-port=5432 -n three-tier
YAML
# three-tier-netpol.yaml
# 1. 기본 거부
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: three-tier
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]
---
# 2. API가 프론트엔드의 요청만 수신
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow-frontend
  namespace: three-tier
spec:
  podSelector:
    matchLabels:
      tier: api
  policyTypes: [Ingress, Egress]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              tier: frontend
      ports:
        - port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              tier: db
      ports:
        - port: 5432
    - to: [{}]
      ports:
        - port: 53
          protocol: UDP
---
# 3. DB가 API의 연결만 수신
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-allow-api-only
  namespace: three-tier
spec:
  podSelector:
    matchLabels:
      tier: db
  policyTypes: [Ingress]
  ingress:
    - from:
        - podSelector:
            matchLabels:
              tier: api
      ports:
        - port: 5432
Kubernetes
kubectl apply -f three-tier-netpol.yaml

# 허용: 프론트엔드 → API
kubectl exec -it frontend -n three-tier -- curl http://api:8080 --max-time 5
# 성공

# 차단: 프론트엔드 → DB (직접 접근 불가)
kubectl exec -it frontend -n three-tier -- \
  nc -zv db 5432 --wait 5 2>&1
# nc: connect to db port 5432 (tcp) timed out: Operation in progress

NetworkPolicy를 적용한 직후부터 파드가 서비스 이름으로 다른 서비스에 연결하지 못합니다. IP로는 접근이 되지만 서비스 이름(hostname)으로는 타임아웃이 발생합니다.

Kubernetes
# 증상
kubectl logs payment-pod -n production
# Error: dial tcp: lookup order-service on 10.96.0.10:53: i/o timeout
# ← DNS 조회 실패! IP가 아닌 호스트명 조회 실패

# 진단: DNS 서버로의 Egress 규칙 확인
kubectl get networkpolicy payment-service-policy -n production -o yaml | grep -A 20 egress

# Egress에 DNS 허용 규칙이 있는지 확인
# (없으면 kube-dns로의 UDP 53 포트가 차단됨)
로컬 터미널
# 원인: Egress NetworkPolicy를 설정했지만 DNS 트래픽(UDP 53)을 허용하지 않음
# kube-dns는 kube-system 네임스페이스의 10.96.0.10에서 동작

# 해결: DNS 허용 규칙 추가
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: production
spec:
  podSelector: {}    # 모든 파드에 적용
  policyTypes:
    - Egress
  egress:
    # kube-dns 허용 (CoreDNS가 있는 kube-system 네임스페이스)
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
EOF

# 적용 후 DNS 조회 확인
kubectl exec -it payment-pod -n production -- \
  nslookup order-service.production.svc.cluster.local
# Server:   10.96.0.10
# Name:  order-service.production.svc.cluster.local
# Address: 10.100.42.15
Kubernetes
# 추가 디버깅 도구
# Cilium 클러스터에서 정책 시각화
kubectl exec -it -n kube-system cilium-xxx -- \
  cilium policy get

# NetworkPolicy 적용 현황 확인 (Calico)
kubectl get networkpolicies -A

# 특정 파드의 실효 정책 확인
kubectl describe pod payment-pod -n production | grep -A 5 "Labels"
# 레이블을 기반으로 어떤 NetworkPolicy가 적용되는지 수동으로 매칭

핵심 교훈: Egress NetworkPolicy를 적용할 때는 DNS(UDP/TCP 53)를 반드시 허용해야 합니다. kube-dns가 차단되면 서비스 이름 조회가 실패하여 마치 서비스가 죽은 것처럼 보입니다. 또한 CNI가 NetworkPolicy를 지원하지 않는 경우 정책이 생성되어도 아무 효과가 없으므로, Calico/Cilium/Weave 중 하나를 사용 중인지 먼저 확인하세요.

심화 — 정책이 실제로 강제되는 곳과 selector 논리

💡개념

심화: CNI 데이터패스와 selector 논리 — AND vs OR가 경계를 뒤집는다

NetworkPolicy를 정확히 썼는데도 트래픽이 예상과 다르게 흐른다면, 이 오브젝트가 어디서 어떻게 강제되는지와 selector가 어떻게 해석되는지를 알아야 합니다. YAML 한 칸의 들여쓰기가 보안 경계를 통째로 뒤집을 수 있습니다.

  • 강제하는 주체는 CNI다: NetworkPolicy는 선언일 뿐이고, 실제 차단은 각 노드의 CNI 에이전트(Calico의 felix, Cilium 에이전트 등)가 정책을 iptables 규칙이나 eBPF 프로그램으로 번역해 커널 데이터패스에서 수행합니다. 그래서 CNI가 NetworkPolicy를 지원하지 않으면 정책이 있어도 무효이고, 새 정책이 모든 노드에 전파되기까지 짧은 지연도 존재합니다.
  • 정책은 stateful이다: 판정은 커널 conntrack(연결 추적) 위에서 이뤄져, 연결을 시작하는 방향만 허용하면 그 응답 패킷은 자동으로 통과합니다. Ingress를 열었다고 응답용 Egress를 대칭으로 또 열 필요가 없습니다(파드가 먼저 바깥으로 연결을 여는 흐름은 그 방향의 Egress가 별도로 필요).
  • from/to의 AND vs OR가 함정이다: 하나의 규칙 항목(- 하나) 안에서 namespaceSelector와 podSelector를 함께 두면 그 네임스페이스에 있으면서 그 라벨을 가진 파드만 허용(AND)합니다. 반대로 둘을 각각 별도 - 항목으로 나누면 그 네임스페이스의 모든 파드 또는 어느 네임스페이스든 그 라벨을 가진 파드로 범위가 넓어집니다(OR). 들여쓰기 한 칸 차이가 특정 파드만을 네임스페이스 전체로 바꿉니다.

정책이 의도대로 좁게 걸렸는지 보려면 -o yaml로 from/to 배열의 항목 수(- 개수)를 세어, 두 셀렉터가 한 항목에 묶였는지 갈라졌는지부터 확인해야 합니다.

상황: monitoring 네임스페이스의 prometheus 파드만 payment 서비스의 9090 포트에 접근하도록 허용하려 했습니다. 그런데 보안 감사에서 monitoring 네임스페이스의 아무 파드나, 심지어 다른 네임스페이스라도 app=prometheus 라벨만 달면 payment:9090에 붙는다고 나왔습니다.

원인: ingress의 from에서 namespaceSelector와 podSelector를 각각 별도 리스트 항목(-)으로 썼습니다. NetworkPolicy는 이를 OR로 해석해 monitoring 네임스페이스의 모든 파드 또는 app=prometheus 라벨을 가진 모든 파드(네임스페이스 무관)를 전부 허용했습니다. 의도한 것은 둘을 동시에 만족하는(AND) 파드였습니다.

진단: 정책 YAML의 from 배열 구조를 확인합니다.

Kubernetes
kubectl get networkpolicy payment-service-policy -n production -o yaml
YAML
# 잘못된 형태 — 별도 항목이라 OR로 해석됨
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: monitoring
      - podSelector:
          matchLabels:
            app: prometheus

from 아래 -가 두 개면 두 조건이 OR입니다.

해결: 두 셀렉터를 하나의 항목 안에 합쳐 AND로 만듭니다.

YAML
# 올바른 형태 — 한 항목 안이라 AND로 해석됨
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            kubernetes.io/metadata.name: monitoring
        podSelector:
          matchLabels:
            app: prometheus

podSelector의 -를 빼서 namespaceSelector와 같은 항목(같은 들여쓰기)에 두면, monitoring 네임스페이스에 있으면서 app=prometheus인 파드만 허용됩니다. 적용 후 다른 네임스페이스의 prometheus나 monitoring의 다른 파드로 접근이 차단되는지 다시 확인합니다.

💼
실무 맥락
현업 패턴

시나리오: PCI DSS 컴플라이언스를 위한 결제 서비스 네트워크 격리

보안팀으로부터 "결제 서비스는 PCI DSS 규정상 다른 서비스와 네트워크 격리가 필요합니다"라는 요건을 받았습니다. 결제 서비스가 받는 요청은 API 게이트웨이에서만 와야 하고, 결제 서비스가 연결하는 것은 자체 DB와 외부 PG사 API뿐이어야 합니다.

Kubernetes
# payment 네임스페이스 생성 및 레이블 부여
kubectl create namespace payment-zone
kubectl label namespace payment-zone compliance=pci-dss

# 결제 서비스 격리 정책 배포
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: payment-strict-isolation
  namespace: payment-zone
spec:
  podSelector:
    matchLabels:
      app: payment-service
  policyTypes: [Ingress, Egress]
  ingress:
    # API 게이트웨이에서만 수신
    - from:
        - namespaceSelector:
            matchLabels:
              app: api-gateway
          podSelector:
            matchLabels:
              app: api-gateway
      ports:
        - port: 8080
  egress:
    # 자체 DB
    - to:
        - podSelector:
            matchLabels:
              app: payment-db
      ports:
        - port: 5432
    # 외부 PG사 API (IP 기반 egress)
    - to:
        - ipBlock:
            cidr: 203.0.113.0/24    # PG사 IP 대역
      ports:
        - port: 443
    # DNS
    - to: [{}]
      ports:
        - port: 53
          protocol: UDP
EOF

# 정책 적용 검증
kubectl describe networkpolicy payment-strict-isolation -n payment-zone

실무 포인트: NetworkPolicy는 컴플라이언스 감사에서 "네트워크 세그멘테이션 증적"으로 활용됩니다. 정책 YAML 파일을 Git에 관리하고, CI/CD에서 변경 시 보안팀 승인을 요구하는 프로세스를 만들면 "언제 누가 결제 서비스 통신 규칙을 바꿨는지" 추적할 수 있습니다.

핵심 요약

개념명령/설정실무 사용 빈도
NetworkPolicy 조회kubectl get networkpolicy -n <ns>디버깅 시
Deny All 적용podSelector: {} + policyTypes: [Ingress] (from 없음)네임스페이스 격리 시
파드 간 허용ingress.from.podSelector서비스 연결 허용
네임스페이스 간 허용ingress.from.namespaceSelector크로스 네임스페이스 통신
외부 IP 허용egress.to.ipBlock.cidr외부 API 연결
DNS 허용 (필수!)egress: port 53 UDP/TCPEgress 정책 시 항상
CNI 확인kubectl get pods -n kube-system정책 미작동 시
실습 단계
1

실습 네임스페이스 및 파드 생성

kubectl create namespace netpol-demo kubectl label namespace netpol-demo env=demo kubectl run frontend --image=nginx --labels='app=frontend' -n netpol-demo kubectl run backend --image=nginx --labels='app=backend' -n netpol-demo kubectl get pods -n netpol-demo

예상 출력

NAME       READY   STATUS    RESTARTS   AGE
backend    1/1     Running   0          10s
frontend   1/1     Running   0          10s
2

기본 거부(Deny All) 정책 적용 및 차단 확인

kubectl apply -f - <<EOF apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all namespace: netpol-demo spec: podSelector: {} policyTypes: [Ingress] EOF kubectl get networkpolicy -n netpol-demo

예상 출력

NAME       POD-SELECTOR   AGE
deny-all   <none>         5s
3

frontend → backend 통신 허용 정책 적용

kubectl apply -f - <<EOF apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend-to-backend namespace: netpol-demo spec: podSelector: matchLabels: app: backend policyTypes: [Ingress] ingress: - from: - podSelector: matchLabels: app: frontend ports: - port: 80 EOF

예상 출력

networkpolicy.networking.k8s.io/allow-frontend-to-backend created
4

정책 적용 결과 확인

kubectl get networkpolicy -n netpol-demo kubectl describe networkpolicy allow-frontend-to-backend -n netpol-demo | grep -A5 'Allowing ingress'

예상 출력

NAME                         POD-SELECTOR   AGE
allow-frontend-to-backend    app=backend    10s
deny-all                     <none>         30s
🔍실행 후 확인할 것
  • kubectl get networkpolicy -n <namespace> 에서 생성한 정책이 목록에 보여야 적용된 것 — 없으면 kubectl apply 가 성공했는지, 올바른 네임스페이스를 지정했는지 확인
  • kubectl exec -it <pod> -- curl http://<service>:<port> --max-time 5 에서 "Connection timed out" 이 나오면 NetworkPolicy가 차단 중인 것. "Connection refused" 는 서비스 자체 문제 (차단이 아님)
  • DNS 허용 확인: Egress 정책 적용 후 kubectl exec -it <pod> -- nslookup <service> 가 실패하면 UDP 53 포트 Egress 규칙 누락 — "i/o timeout" 에러가 핵심 단서
  • CNI 지원 여부: kubectl get pods -n kube-system | grep -E "calico|cilium|weave" 에서 아무것도 안 나오고 Flannel만 있으면 NetworkPolicy가 생성되어도 실제 트래픽 제어 없음
  • 정책 적용 범위 확인: kubectl describe networkpolicy <name> -n <ns> 의 PodSelector에 의도한 레이블이 맞는지 확인. 빈 셀렉터({})이면 네임스페이스 전체 파드에 적용됨
  • 조합 해석 — Deny All 적용 후 curl 타임아웃이고 CNI가 Calico/Cilium이면 → 정책이 정상 동작 중인 것. 이후 필요한 서비스별 허용 규칙 추가 진행

명령어·단축키 빠른 참조

이 모듈에서 다룬 NetworkPolicy 조회·구조 진단·연결 테스트 명령을 실전 옵션과 함께 모았습니다. curl 타임아웃은 정책 차단, refused는 서비스 자체 문제로 구분합니다.

명령어/단축키용도자주 쓰는 예
kubectl get netpol적용된 정책 목록 확인kubectl get networkpolicy -A(전 네임스페이스)
kubectl describe netpolPodSelector·허용 규칙 확인kubectl describe networkpolicy payment-service-policy -n production
kubectl get netpol -o yamlfrom/to 배열 구조(-개수=AND/OR) 진단kubectl get networkpolicy <name> -o yaml(from 밑 - 수 세기)
kubectl get pods -n kube-system | grepNetworkPolicy 지원 CNI 확인kubectl get pods -n kube-system | grep -E 'calico|cilium|weave'
kubectl exec -- curl --max-time허용/차단 연결 테스트kubectl exec -it <pod> -- curl http://payment:8080 --max-time 5(timeout=차단)
kubectl exec -- nc -zv포트 단위 연결 테스트kubectl exec -it frontend -- nc -zv db 5432 --wait 5
kubectl exec -- nslookupEgress 정책 후 DNS(53) 허용 확인kubectl exec -it <pod> -- nslookup order-service.production
kubectl label namespacenamespaceSelector용 레이블 부여kubectl label namespace netpol-demo env=demo
kubectl get namespace --show-labelsnamespaceSelector 매칭 레이블 확인kubectl get namespace --show-labels
kubectl describe pod | grep Labels파드 레이블로 적용 정책 수동 매칭kubectl describe pod payment-pod -n production | grep -A5 Labels

관련 모듈로 더 깊이:

다음 모듈 istio-service-mesh에서는 NetworkPolicy보다 더 정교한 L7 트래픽 제어를 다룹니다. Envoy 사이드카를 통한 mTLS 자동화, VirtualService로 카나리 가중치 분배, AuthorizationPolicy로 서비스 간 L7 접근 제어를 구성합니다.

지식 확인

퀴즈 — 8문제

Q1

NetworkPolicy를 적용했지만 파드 간 통신이 여전히 가능한 이유로 가장 가능성이 높은 것은?

Q2

podSelector: {} (빈 셀렉터)가 의미하는 것은?

Q3

Ingress NetworkPolicy에서 from을 지정하지 않고 policyTypes만 ['Ingress']로 설정하면?

Q4

네임스페이스 간 통신을 허용하기 위해 사용하는 NetworkPolicy 셀렉터는?

Q5

한 파드에 어떤 NetworkPolicy도 적용되지 않은 상태의 기본 통신 동작은?

Q6

제로 트러스트로 한 네임스페이스의 모든 파드 인바운드를 기본 차단한 뒤 필요한 것만 여는 패턴의 시작점은?

Q7

[심화] NetworkPolicy의 ingress.from 배열에서 namespaceSelector와 podSelector를 배치하는 방식에 따라 의미가 달라진다. 맞는 설명은?

Q8

[심화] monitoring 네임스페이스의 prometheus 파드에만 열어주려던 ingress가, 실제로는 monitoring의 아무 파드나 그리고 어느 네임스페이스든 app=prometheus 라벨을 가진 파드까지 허용하고 있었다. YAML에서 가장 먼저 확인할 것은?

0 / 8 답변

🧪 실습으로 확인하기

K8s 기초 — Pod/Deployment/Service 생성

초급

kubectl로 nginx Pod를 생성하고 Deployment와 Service를 차례로 만들어 클러스터 외부에서 접근 가능한 상태까지 구성한다. K8s 3대 리소스의 역할과 관계를 직접 손으로 익힌다.

55📋 5단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요