Kubernetes Tools/Cilium

EP09. L7 정책 실습 (Kafka, DNS 등) | HTTP 요청이 아닌 트래픽 제어

ygtoken 2025. 3. 22. 18:58
728x90

이 글에서는 Cilium을 활용하여 HTTP 외의 다른 L7(애플리케이션 계층) 프로토콜인 Kafka와 DNS 트래픽을 제어하는 방법을 실습합니다. 이전 글에서 HTTP 트래픽에 대한 경로, 메서드, 호스트 기반 필터링을 살펴봤다면, 이번에는 메시징 시스템과 도메인 이름 조회 등 다양한 프로토콜에 대한 세밀한 보안 정책을 구현하는 방법을 알아보겠습니다. 특히 toFQDNs, toEndpoints와 같은 Cilium의 고급 기능을 활용하여 실제 마이크로서비스 환경에서 유용하게 적용할 수 있는 실전 예제를 다룹니다.


📌 L7 Kafka 정책

✅ Kafka 프로토콜 개요

Kafka는 대용량 실시간 데이터 스트리밍을 위한 분산 메시징 시스템으로, 마이크로서비스 아키텍처에서 많이 사용됩니다. Kafka의 주요 개념을 간략히 살펴보면:

  • 토픽(Topic): 메시지가 저장되는 카테고리 또는 피드 이름
  • 파티션(Partition): 토픽이 분산 저장되는 단위
  • 프로듀서(Producer): 메시지를 생산하여 토픽에 게시하는 클라이언트
  • 컨슈머(Consumer): 토픽에서 메시지를 구독하는 클라이언트
  • 컨슈머 그룹(Consumer Group): 같은 토픽을 구독하는 컨슈머 집합

✅ Kafka 정책이 필요한 이유

일반적인 L3/L4 정책으로는 Kafka 브로커에 대한 TCP 연결만 제어할 수 있습니다. 그러나 다음과 같은 세밀한 제어가 필요한 경우가 많습니다:

  • 특정 토픽에만 접근 허용
  • 읽기(소비) vs 쓰기(생산) 작업 구분
  • 특정 컨슈머 그룹에 대한 접근 제어
  • 특정 클라이언트만 관리 작업(예: 토픽 생성) 허용

Cilium의 L7 Kafka 정책을 사용하면 이러한 세밀한 접근 제어가 가능해집니다.


📌 Kafka 정책 실습 환경 구성

✅ 테스트용 Kafka 환경 배포

# kafka-test.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: kafka-test  # 테스트를 위한 격리된 네임스페이스 생성
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kafka-broker  # Kafka 브로커 배포
  namespace: kafka-test
spec:
  selector:
    matchLabels:
      app: kafka-broker
  replicas: 1
  template:
    metadata:
      labels:
        app: kafka-broker  # 이 라벨을 가진 Pod가 Kafka 브로커
    spec:
      containers:
      - name: kafka
        image: wurstmeister/kafka:2.13-2.7.0  # Kafka 이미지
        ports:
        - containerPort: 9092  # Kafka 기본 포트
        env:
        - name: KAFKA_ADVERTISED_HOST_NAME
          value: "kafka-broker"  # 서비스 이름으로 광고
        - name: KAFKA_ZOOKEEPER_CONNECT
          value: "zookeeper:2181"  # ZooKeeper 연결 정보
        - name: KAFKA_CREATE_TOPICS
          value: "test:1:1,orders:1:1,payments:1:1,audit:1:1"  # 자동 생성할 토픽들
        - name: KAFKA_AUTO_CREATE_TOPICS_ENABLE
          value: "true"  # 토픽 자동 생성 활성화
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: zookeeper  # Kafka 관리를 위한 ZooKeeper
  namespace: kafka-test
spec:
  selector:
    matchLabels:
      app: zookeeper
  replicas: 1
  template:
    metadata:
      labels:
        app: zookeeper
    spec:
      containers:
      - name: zookeeper
        image: wurstmeister/zookeeper:3.4.6  # ZooKeeper 이미지
        ports:
        - containerPort: 2181  # ZooKeeper 기본 포트
---
apiVersion: v1
kind: Service
metadata:
  name: kafka-broker  # Kafka 브로커 서비스
  namespace: kafka-test
spec:
  selector:
    app: kafka-broker
  ports:
  - port: 9092
    targetPort: 9092
  clusterIP: None  # Headless 서비스 (DNS 이름만 제공)
---
apiVersion: v1
kind: Service
metadata:
  name: zookeeper  # ZooKeeper 서비스
  namespace: kafka-test
spec:
  selector:
    app: zookeeper
  ports:
  - port: 2181
    targetPort: 2181
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kafka-producer  # Kafka 메시지 생산자
  namespace: kafka-test
spec:
  selector:
    matchLabels:
      app: kafka-producer
  replicas: 1
  template:
    metadata:
      labels:
        app: kafka-producer
    spec:
      containers:
      - name: kafka-producer
        image: bitnami/kafka:2.8.0  # Kafka 클라이언트를 포함한 이미지
        command: ["sleep", "infinity"]  # 컨테이너를 계속 실행 상태로 유지
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kafka-consumer  # Kafka 메시지 소비자
  namespace: kafka-test
spec:
  selector:
    matchLabels:
      app: kafka-consumer
  replicas: 1
  template:
    metadata:
      labels:
        app: kafka-consumer
    spec:
      containers:
      - name: kafka-consumer
        image: bitnami/kafka:2.8.0  # Kafka 클라이언트를 포함한 이미지
        command: ["sleep", "infinity"]  # 컨테이너를 계속 실행 상태로 유지
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kafka-admin  # Kafka 관리 작업용 클라이언트
  namespace: kafka-test
spec:
  selector:
    matchLabels:
      app: kafka-admin
  replicas: 1
  template:
    metadata:
      labels:
        app: kafka-admin
    spec:
      containers:
      - name: kafka-admin
        image: bitnami/kafka:2.8.0  # Kafka 클라이언트를 포함한 이미지
        command: ["sleep", "infinity"]  # 컨테이너를 계속 실행 상태로 유지

 

위 매니페스트를 적용합니다:

kubectl apply -f kafka-test.yaml

✅ 정책 적용 전 작동 확인

먼저 모든 Pod가 정상적으로 실행 중인지 확인합니다:

kubectl get pods -n kafka-test

 

이제 Kafka 프로듀서 Pod에 접속하여 메시지를 생성해 보겠습니다:

# Kafka 프로듀서 Pod 이름 가져오기
PRODUCER_POD=$(kubectl get pods -n kafka-test -l app=kafka-producer -o jsonpath='{.items[0].metadata.name}')

# Pod에 접속하여 메시지 생성
kubectl exec -it -n kafka-test $PRODUCER_POD -- bash

# Pod 내부에서 Kafka 프로듀서 실행
kafka-console-producer.sh --broker-list kafka-broker:9092 --topic test
# 메시지 입력 (예: Hello Kafka!)
# Ctrl+D로 종료

 

이제 Kafka 컨슈머 Pod에 접속하여 메시지를 소비해 보겠습니다:

# Kafka 컨슈머 Pod 이름 가져오기
CONSUMER_POD=$(kubectl get pods -n kafka-test -l app=kafka-consumer -o jsonpath='{.items[0].metadata.name}')

# Pod에 접속하여 메시지 소비
kubectl exec -it -n kafka-test $CONSUMER_POD -- bash

# Pod 내부에서 Kafka 컨슈머 실행
kafka-console-consumer.sh --bootstrap-server kafka-broker:9092 --topic test --from-beginning
# 이전에 생산한 메시지가 출력되어야 함
# Ctrl+C로 종료

 

또한 Kafka 관리자 Pod에서 토픽 목록을 확인해 보겠습니다:

# Kafka 관리자 Pod 이름 가져오기
ADMIN_POD=$(kubectl get pods -n kafka-test -l app=kafka-admin -o jsonpath='{.items[0].metadata.name}')

# Pod에 접속하여 토픽 목록 확인
kubectl exec -it -n kafka-test $ADMIN_POD -- bash

# Pod 내부에서 토픽 목록 명령 실행
kafka-topics.sh --list --bootstrap-server kafka-broker:9092
# 생성된 토픽 목록이 표시되어야 함 (test, orders, payments, audit)

 

모든 작업이 정상적으로 수행되면 Cilium의 Kafka 정책을 적용할 준비가 되었습니다.


📌 기본 Kafka L7 정책 적용

Cilium Kafka L7 정책 작동 원리

 

✅ 토픽 기반 접근 제어 정책

다음 정책은 특정 토픽에 대한 접근을 제어합니다:

# kafka-topic-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "kafka-topic-policy"  # 정책 이름
  namespace: kafka-test       # 적용될 네임스페이스
spec:
  endpointSelector:           # 정책이 적용될 대상 Pod
    matchLabels:
      app: kafka-broker       # kafka-broker 라벨을 가진 Pod에 적용
  ingress:                    # 인바운드 트래픽 규칙
  - fromEndpoints:            # 출발지 정의
    - matchLabels:
        app: kafka-producer   # kafka-producer 라벨을 가진 Pod의 접근 허용
    toPorts:                  # 목적지 포트 정의
    - ports:
      - port: "9092"          # Kafka 기본 포트 (9092)
        protocol: TCP         # TCP 프로토콜
      rules:                  # L7 규칙 정의 시작
        kafka:                # Kafka 프로토콜에 대한 규칙
        - apiKey: "produce"   # 메시지 생산(Produce) 작업만 허용
          topic: "test"       # test 토픽에만 접근 허용
        - apiKey: "produce"   # 메시지 생산(Produce) 작업만 허용
          topic: "orders"     # orders 토픽에만 접근 허용
  - fromEndpoints:            # 다른 출발지 정의 (컨슈머)
    - matchLabels:
        app: kafka-consumer   # kafka-consumer 라벨을 가진 Pod의 접근 허용
    toPorts:                  # 목적지 포트 정의
    - ports:
      - port: "9092"          # Kafka 기본 포트 (9092)
        protocol: TCP         # TCP 프로토콜
      rules:                  # L7 규칙 정의 시작
        kafka:                # Kafka 프로토콜에 대한 규칙
        - apiKey: "fetch"     # 메시지 소비(Fetch) 작업만 허용
          topic: "test"       # test 토픽에만 접근 허용
  - fromEndpoints:            # 다른 출발지 정의 (관리자)
    - matchLabels:
        app: kafka-admin      # kafka-admin 라벨을 가진 Pod의 접근 허용
    toPorts:                  # 목적지 포트 정의
    - ports:
      - port: "9092"          # Kafka 기본 포트 (9092)
        protocol: TCP         # TCP 프로토콜
      rules:                  # L7 규칙 정의 시작
        kafka:                # Kafka 프로토콜에 대한 규칙
        - apiKey: "metadata"  # 메타데이터 조회 작업 허용 (토픽 목록 등)
        - apiKey: "apiversions" # 버전 정보 조회 허용
        - apiKey: "findcoordinator" # 코디네이터 정보 조회 허용

 

정책을 적용합니다:

kubectl apply -f kafka-topic-policy.yaml

✅ 정책 적용 결과 테스트

이제 정책이 적용된 후 각 Pod의 접근 권한을 테스트해 봅시다:

 

프로듀서 테스트 (허용된 케이스):

# Kafka 프로듀서 Pod에 접속
kubectl exec -it -n kafka-test $PRODUCER_POD -- bash

# test 토픽에 메시지 생성 (허용됨)
kafka-console-producer.sh --broker-list kafka-broker:9092 --topic test
# 메시지 입력 (예: Allowed message)
# Ctrl+D로 종료

# orders 토픽에 메시지 생성 (허용됨)
kafka-console-producer.sh --broker-list kafka-broker:9092 --topic orders
# 메시지 입력 (예: Order received)
# Ctrl+D로 종료

 

프로듀서 테스트 (차단된 케이스):

# payments 토픽에 메시지 생성 (차단됨)
kafka-console-producer.sh --broker-list kafka-broker:9092 --topic payments
# 연결 오류 또는 타임아웃 발생

 

컨슈머 테스트:

# Kafka 컨슈머 Pod에 접속
kubectl exec -it -n kafka-test $CONSUMER_POD -- bash

# test 토픽의 메시지 소비 (허용됨)
kafka-console-consumer.sh --bootstrap-server kafka-broker:9092 --topic test --from-beginning
# 이전에 생성한 메시지가 표시되어야 함
# Ctrl+C로 종료

# orders 토픽의 메시지 소비 (차단됨 - 정책에서 허용하지 않음)
kafka-console-consumer.sh --bootstrap-server kafka-broker:9092 --topic orders --from-beginning
# 연결 오류 또는 타임아웃 발생

 

관리자 테스트:

# Kafka 관리자 Pod에 접속
kubectl exec -it -n kafka-test $ADMIN_POD -- bash

# 토픽 목록 조회 (허용됨)
kafka-topics.sh --list --bootstrap-server kafka-broker:9092
# 토픽 목록이 표시되어야 함

# 새 토픽 생성 시도 (차단됨 - createtopics API 접근 권한 없음)
kafka-topics.sh --create --bootstrap-server kafka-broker:9092 --topic new-topic --partitions 1 --replication-factor 1
# 연결 오류 또는 타임아웃 발생

📌 고급 Kafka 정책 구성

✅ 정규식 패턴을 이용한 토픽 접근 제어

토픽 이름에 정규식 패턴을 적용하여 여러 토픽을 한번에 관리할 수 있습니다:

# kafka-regex-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "kafka-regex-policy"      # 정책 이름
  namespace: kafka-test           # 적용될 네임스페이스
spec:
  endpointSelector:               # 정책이 적용될 대상 Pod
    matchLabels:
      app: kafka-broker           # kafka-broker 라벨을 가진 Pod에 적용
  ingress:                        # 인바운드 트래픽 규칙
  - fromEndpoints:                # 출발지 정의
    - matchLabels:
        app: kafka-producer       # kafka-producer 라벨을 가진 Pod의 접근 허용
    toPorts:                      # 목적지 포트 정의
    - ports:
      - port: "9092"              # Kafka 기본 포트 (9092)
        protocol: TCP             # TCP 프로토콜
      rules:                      # L7 규칙 정의 시작
        kafka:                    # Kafka 프로토콜에 대한 규칙
        - apiKey: "produce"       # 메시지 생산(Produce) 작업만 허용
          topic: "order-.*"       # 정규식: order- 접두사를 가진 모든 토픽 허용
                                  # 예: order-received, order-processed, order-shipped 등
  - fromEndpoints:                # 다른 출발지 정의 (컨슈머)
    - matchLabels:
        app: kafka-consumer       # kafka-consumer 라벨을 가진 Pod의 접근 허용
    toPorts:                      # 목적지 포트 정의
    - ports:
      - port: "9092"              # Kafka 기본 포트 (9092)
        protocol: TCP             # TCP 프로토콜
      rules:                      # L7 규칙 정의 시작
        kafka:                    # Kafka 프로토콜에 대한 규칙
        - apiKey: "fetch"         # 메시지 소비(Fetch) 작업만 허용
          topic: ".*-events"      # 정규식: -events 접미사를 가진 모든 토픽 허용
                                  # 예: order-events, payment-events, user-events 등

 

기존 정책을 삭제하고 새 정책을 적용합니다:

kubectl delete ciliumnetworkpolicies kafka-topic-policy -n kafka-test
kubectl apply -f kafka-regex-policy.yaml

✅ 컨슈머 그룹 기반 정책

특정 컨슈머 그룹에 대한 접근도 제어할 수 있습니다:

# kafka-group-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "kafka-group-policy"       # 정책 이름
  namespace: kafka-test            # 적용될 네임스페이스
spec:
  endpointSelector:                # 정책이 적용될 대상 Pod
    matchLabels:
      app: kafka-broker            # kafka-broker 라벨을 가진 Pod에 적용
  ingress:                         # 인바운드 트래픽 규칙
  - fromEndpoints:                 # 출발지 정의
    - matchLabels:
        app: kafka-consumer        # kafka-consumer 라벨을 가진 Pod의 접근 허용
    toPorts:                       # 목적지 포트 정의
    - ports:
      - port: "9092"               # Kafka 기본 포트 (9092)
        protocol: TCP              # TCP 프로토콜
      rules:                       # L7 규칙 정의 시작
        kafka:                     # Kafka 프로토콜에 대한 규칙
        - apiKey: "fetch"          # 메시지 소비(Fetch) 작업 허용
          topic: "orders"          # orders 토픽 접근 허용
          apiVersion: "0-2"        # API 버전 0~2 허용
          clientID: "consumer-.*"  # 정규식: consumer- 접두사를 가진
                                   # 모든 클라이언트 ID 허용
        - apiKey: "offsetcommit"   # offset commit 작업 허용
          topic: "orders"          # orders 토픽에 대해
          consumerGroup: "group1"  # group1 컨슈머 그룹만 허용

 

기존 정책을 삭제하고 새 정책을 적용합니다:

kubectl delete ciliumnetworkpolicies kafka-regex-policy -n kafka-test
kubectl apply -f kafka-group-policy.yaml

✅ 관리 작업 권한 부여 정책

토픽 생성, 삭제 등 관리 작업에 대한 권한을 부여하는 정책입니다:

# kafka-admin-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "kafka-admin-policy"      # 정책 이름
  namespace: kafka-test           # 적용될 네임스페이스
spec:
  endpointSelector:               # 정책이 적용될 대상 Pod
    matchLabels:
      app: kafka-broker           # kafka-broker 라벨을 가진 Pod에 적용
  ingress:                        # 인바운드 트래픽 규칙
  - fromEndpoints:                # 출발지 정의
    - matchLabels:
        app: kafka-admin          # kafka-admin 라벨을 가진 Pod의 접근 허용
    toPorts:                      # 목적지 포트 정의
    - ports:
      - port: "9092"              # Kafka 기본 포트 (9092)
        protocol: TCP             # TCP 프로토콜
      rules:                      # L7 규칙 정의 시작
        kafka:                    # Kafka 프로토콜에 대한 규칙
        - apiKey: "metadata"      # 메타데이터 조회 작업 허용
        - apiKey: "apiversions"   # 버전 정보 조회 허용
        - apiKey: "createtopics"  # 토픽 생성 작업 허용
        - apiKey: "deletetopics"  # 토픽 삭제 작업 허용
        - apiKey: "listoffsets"   # offset 정보 조회 허용
        - apiKey: "findcoordinator" # 코디네이터 정보 조회 허용
        - apiKey: "joingroup"     # 그룹 참여 작업 허용
        - apiKey: "leavegroup"    # 그룹 탈퇴 작업 허용
        - apiKey: "syncgroup"     # 그룹 동기화 작업 허용

 

기존 정책을 삭제하고 새 정책을 적용합니다:

kubectl delete ciliumnetworkpolicies kafka-group-policy -n kafka-test
kubectl apply -f kafka-admin-policy.yaml

 

이제 관리자 Pod에서 토픽 생성 및 삭제가 가능해집니다:

# Kafka 관리자 Pod에 접속
kubectl exec -it -n kafka-test $ADMIN_POD -- bash

# 새 토픽 생성 (이제 허용됨)
kafka-topics.sh --create --bootstrap-server kafka-broker:9092 --topic new-test-topic --partitions 1 --replication-factor 1

# 토픽 목록 확인
kafka-topics.sh --list --bootstrap-server kafka-broker:9092

# 토픽 삭제 (이제 허용됨)
kafka-topics.sh --delete --bootstrap-server kafka-broker:9092 --topic new-test-topic

📌 L7 DNS 정책

✅ DNS 요청 제어의 필요성

DNS(Domain Name System)는 도메인 이름을 IP 주소로 변환하는 시스템입니다. 마이크로서비스 환경에서 DNS 제어가 필요한 이유는 다음과 같습니다:

  • 애플리케이션이 접근할 수 있는 외부 도메인 제한
  • 악성 도메인으로의 접근 차단
  • 데이터 유출 방지
  • 규정 준수 요구사항 충족

Cilium은 DNS 기반 필터링을 통해 이러한 보안 요구사항을 충족할 수 있습니다.

✅ toFQDNs 정책 이해하기

Cilium에서는 toFQDNs 셀렉터를 사용하여 특정 도메인 이름에 대한 접근을 제어할 수 있습니다. 이 기능은 다음과 같이 작동합니다:

  1. Pod에서 DNS 쿼리가 발생하면 Cilium은 이를 감시합니다.
  2. DNS 응답을 분석하여 IP 주소를 추출합니다.
  3. 정책에 정의된 도메인 이름과 일치하는 경우, 해당 IP 주소로의 접근을 자동으로 허용합니다.

이 방식은 동적으로 변하는 클라우드 환경에서 특히 유용합니다.

 

Cilium DNS 정책 작동 방식 (toFQDNs)


📌 DNS 정책 실습 환경 구성

✅ 테스트용 클라이언트 Pod 배포

# dns-test.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: dns-test  # 테스트를 위한 격리된 네임스페이스 생성
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dns-client  # DNS 요청을 테스트할 클라이언트
  namespace: dns-test
spec:
  selector:
    matchLabels:
      app: dns-client
  replicas: 1
  template:
    metadata:
      labels:
        app: dns-client  # 이 라벨을 가진 Pod가 DNS 클라이언트
    spec:
      containers:
      - name: dns-client
        image: nicolaka/netshoot  # 다양한 네트워크 도구가 포함된 이미지
        command: ["sleep", "infinity"]  # 컨테이너를 계속 실행 상태로 유지

 

매니페스트를 적용합니다:

kubectl apply -f dns-test.yaml

✅ 정책 적용 전 DNS 조회 테스트

클라이언트 Pod에 접속하여 DNS 조회를 테스트합니다:

# DNS 클라이언트 Pod 이름 가져오기
DNS_CLIENT=$(kubectl get pods -n dns-test -l app=dns-client -o jsonpath='{.items[0].metadata.name}')

# Pod에 접속
kubectl exec -it -n dns-test $DNS_CLIENT -- bash

# 다양한 도메인 조회 테스트
nslookup kubernetes.io
nslookup github.com
nslookup google.com
nslookup example.com

# HTTP 연결 테스트
curl -I https://kubernetes.io
curl -I https://github.com
curl -I https://google.com
curl -I http://example.com

 

모든 DNS 조회와 HTTP 연결이 정상적으로 작동하는 것을 확인할 수 있습니다.


📌 기본 DNS 정책 적용

✅ 특정 도메인만 허용하는 정책

다음 정책은 특정 도메인으로의 접근만 허용합니다:

# dns-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "dns-policy"          # 정책 이름
  namespace: dns-test         # 적용될 네임스페이스
spec:
  endpointSelector:           # 정책이 적용될 대상 Pod
    matchLabels:
      app: dns-client         # dns-client 라벨을 가진 Pod에 적용
  egress:                     # 아웃바운드 트래픽 규칙 (Pod에서 외부로 나가는 트래픽)
  - toEndpoints:              # 내부 DNS 서버(kube-dns/CoreDNS)에 대한 접근 허용
    - matchLabels:
        "k8s:io.kubernetes.pod.namespace": kube-system
        "k8s:k8s-app": kube-dns
    toPorts:                  # 목적지 포트 정의
    - ports:
      - port: "53"            # DNS 포트 (53)
        protocol: UDP         # UDP 프로토콜 (DNS 쿼리에 주로 사용)
  - toFQDNs:                  # 특정 도메인으로의 접근 허용
    - matchName: "kubernetes.io"  # kubernetes.io 도메인 접근 허용
    - matchName: "*.kubernetes.io"  # kubernetes.io의 모든 서브도메인 접근 허용
    - matchName: "github.com"  # github.com 도메인 접근 허용
    toPorts:                  # 목적지 포트 정의
    - ports:                  # HTTPS 포트 허용
      - port: "443"           # HTTPS 포트 (443)
        protocol: TCP         # TCP 프로토콜
      - port: "80"            # HTTP 포트 (80)
        protocol: TCP         # TCP 프로토콜

 

정책을 적용합니다:

kubectl apply -f dns-policy.yaml

✅ 정책 적용 결과 테스트

이제 정책이 적용된 후 DNS 클라이언트 Pod에서 다양한 도메인에 대한 접근을 테스트해 봅시다:

# Pod에 접속
kubectl exec -it -n dns-test $DNS_CLIENT -- bash

# 허용된 도메인 테스트
nslookup kubernetes.io  # 허용됨
nslookup docs.kubernetes.io  # 허용됨 (*.kubernetes.io 패턴과 일치)
nslookup github.com  # 허용됨

# 차단된 도메인 테스트
nslookup google.com  # DNS 조회는 가능하지만 HTTP 연결은 차단됨
nslookup example.com  # DNS 조회는 가능하지만 HTTP 연결은 차단됨

# HTTP/HTTPS 연결 테스트
curl -I https://kubernetes.io  # 허용됨
curl -I https://github.com  # 허용됨
curl -I https://google.com  # 차단됨
curl -I http://example.com  # 차단됨

DNS 조회(nslookup)는 대부분 성공하더라도, 실제 HTTP/HTTPS 연결은 정책에 따라 차단될 수 있습니다. 이는 DNS 조회와 실제 트래픽이 별개의 연결이기 때문입니다.


📌 고급 DNS 정책 구성

✅ 정규식 패턴을 이용한 도메인 매칭

더 유연한 도메인 매칭을 위해 정규식 패턴을 사용할 수 있습니다:

# dns-pattern-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "dns-pattern-policy"      # 정책 이름
  namespace: dns-test             # 적용될 네임스페이스
spec:
  endpointSelector:               # 정책이 적용될 대상 Pod
    matchLabels:
      app: dns-client             # dns-client 라벨을 가진 Pod에 적용
  egress:                         # 아웃바운드 트래픽 규칙
  - toEndpoints:                  # 내부 DNS 서버(kube-dns/CoreDNS)에 대한 접근 허용
    - matchLabels:
        "k8s:io.kubernetes.pod.namespace": kube-system
        "k8s:k8s-app": kube-dns
    toPorts:                      # 목적지 포트 정의
    - ports:
      - port: "53"                # DNS 포트 (53)
        protocol: UDP             # UDP 프로토콜
  - toFQDNs:                      # 특정 도메인으로의 접근 허용
    - matchPattern: "*.io"        # .io로 끝나는 모든 도메인 허용
    - matchPattern: "*.github.*"  # github를 포함하는 모든 도메인 허용
    toPorts:                      # 목적지 포트 정의
    - ports:                      # HTTPS 및 HTTP 포트 허용
      - port: "443"               # HTTPS 포트 (443)
        protocol: TCP             # TCP 프로토콜
      - port: "80"                # HTTP 포트 (80)
        protocol: TCP             # TCP 프로토콜

 

기존 정책을 삭제하고 새 정책을 적용합니다:

kubectl delete ciliumnetworkpolicies dns-policy -n dns-test
kubectl apply -f dns-pattern-policy.yaml

✅ DNS 정책과 L7 정책의 조합

DNS 정책과 L7 정책을 조합하여 더 세밀한 제어가 가능합니다:

# dns-http-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "dns-http-policy"         # 정책 이름
  namespace: dns-test             # 적용될 네임스페이스
spec:
  endpointSelector:               # 정책이 적용될 대상 Pod
    matchLabels:
      app: dns-client             # dns-client 라벨을 가진 Pod에 적용
  egress:                         # 아웃바운드 트래픽 규칙
  - toEndpoints:                  # 내부 DNS 서버(kube-dns/CoreDNS)에 대한 접근 허용
    - matchLabels:
        "k8s:io.kubernetes.pod.namespace": kube-system
        "k8s:k8s-app": kube-dns
    toPorts:                      # 목적지 포트 정의
    - ports:
      - port: "53"                # DNS 포트 (53)
        protocol: UDP             # UDP 프로토콜
  - toFQDNs:                      # 특정 도메인으로의 접근 허용
    - matchName: "api.github.com" # api.github.com 도메인 접근 허용
    toPorts:                      # 목적지 포트 정의
    - ports:                      # HTTPS 포트 허용
      - port: "443"               # HTTPS 포트 (443)
        protocol: TCP             # TCP 프로토콜
      rules:                      # L7 규칙 정의 시작
        http:                     # HTTP 프로토콜에 대한 규칙
        - method: "GET"           # GET 메서드만 허용
          path: "/users/.*"       # /users/로 시작하는 모든 경로 허용
          headers:                # 헤더 기반 필터링
          - 'User-Agent: curl/.*' # curl 클라이언트만 허용
  - toFQDNs:                      # 다른 도메인 설정
    - matchName: "kubernetes.io"  # kubernetes.io 도메인 접근 허용
    toPorts:                      # 목적지 포트 정의
    - ports:                      # HTTPS 포트 허용
      - port: "443"               # HTTPS 포트 (443)
        protocol: TCP             # TCP 프로토콜

 

기존 정책을 삭제하고 새 정책을 적용합니다:

kubectl delete ciliumnetworkpolicies dns-pattern-policy -n dns-test
kubectl apply -f dns-http-policy.yaml

이 정책은 api.github.com에 대해 /users/ 경로에 대한 GET 요청만 허용하고, kubernetes.io에 대해서는 모든 HTTPS 트래픽을 허용합니다.

✅ 조합 정책 테스트

# Pod에 접속
kubectl exec -it -n dns-test $DNS_CLIENT -- bash

# kubernetes.io 테스트 (모든 HTTP 요청 허용)
curl -I https://kubernetes.io  # 허용됨
curl -I https://kubernetes.io/docs/  # 허용됨

# api.github.com 테스트 (특정 경로 및 메서드만 허용)
curl -I https://api.github.com/users/kubernetes  # 허용됨 (GET /users/*)
curl -I https://api.github.com/repos/kubernetes  # 차단됨 (경로가 일치하지 않음)
curl -X POST https://api.github.com/users/test  # 차단됨 (POST 메서드 불허)

# 다른 도메인 테스트
curl -I https://github.com  # 차단됨 (정책에 포함되지 않음)
curl -I https://google.com  # 차단됨 (정책에 포함되지 않음)

📌 toEndpoints를 활용한 내부 서비스 통신 제어

✅ 네임스페이스 간 통신 정책

Cilium의 toEndpoints 셀렉터를 사용하여 서로 다른 네임스페이스의 마이크로서비스 간 통신을 제어할 수 있습니다:

# cross-namespace-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "cross-namespace-policy"    # 정책 이름
  namespace: app-backend            # 정책이 적용될 네임스페이스 (가정)
spec:
  endpointSelector:                 # 정책이 적용될 대상 Pod
    matchLabels:
      app: backend-api              # backend-api 라벨을 가진 Pod에 적용
  ingress:                          # 인바운드 트래픽 규칙
  - fromEndpoints:                  # 출발지 정의
    - matchLabels:                  # 다른 네임스페이스의 Pod 지정
        "k8s:io.kubernetes.pod.namespace": app-frontend  # 네임스페이스 지정
        app: frontend-ui                  # app=frontend-ui 라벨을 가진 Pod만 허용
    toPorts:                        # 목적지 포트 정의
    - ports:
      - port: "8080"                # 8080 포트 트래픽만 허용
        protocol: TCP               # TCP 프로토콜
      rules:                        # L7 규칙 정의 시작
        http:                       # HTTP 프로토콜에 대한 규칙
        - method: "GET"             # GET 메서드만 허용
          path: "/api/v1/public/.*" # /api/v1/public/ 경로만 허용

 

이 정책은 app-frontend 네임스페이스의 frontend-ui 앱에서 app-backend 네임스페이스의 backend-api 앱으로의 HTTP GET 요청만 허용합니다.

✅ 마이크로서비스 간 통신 제어

여러 마이크로서비스가 있는 환경에서 서비스 간 통신을 제어하는 정책 예시:

# microservice-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "payment-service-policy"    # 정책 이름
  namespace: microservices          # 적용될 네임스페이스
spec:
  endpointSelector:                 # 정책이 적용될 대상 Pod
    matchLabels:
      app: payment-service          # payment-service 라벨을 가진 Pod에 적용
  ingress:                          # 인바운드 트래픽 규칙
  - fromEndpoints:                  # 출발지 정의
    - matchLabels:                  # 같은 네임스페이스 내 특정 서비스만 허용
        app: order-service          # order-service 라벨을 가진 Pod만 허용
    toPorts:                        # 목적지 포트 정의
    - ports:
      - port: "8080"                # 8080 포트 트래픽만 허용
        protocol: TCP               # TCP 프로토콜
      rules:                        # L7 규칙 정의 시작
        http:                       # HTTP 프로토콜에 대한 규칙
        - method: "POST"            # POST 메서드만 허용
          path: "/api/payments"     # /api/payments 경로만 허용
          headers:                  # 헤더 기반 필터링
          - 'Content-Type: application/json'  # JSON 요청만 허용

 

이 정책은 order-service에서 payment-service로의 결제 요청만 허용하고, 다른 모든 서비스의 접근은 차단합니다.


📌 실무 적용 사례 및 팁

✅ Kafka 정책 실무 적용 팁

  1. 토픽 네이밍 규칙 정립:
    • 마이크로서비스 접두사 사용 (예: order-, payment-)
    • 작업 유형 접미사 추가 (예: *-events, *-commands)
    • 이렇게 하면 정규식 패턴으로 정책 관리가 용이해집니다.
  2. API Key 조합 최적화:
    • 최소 권한의 원칙 적용: 필요한 API Key만 허용
    • 관련 API Key 그룹화: fetch, offsetfetch, offsetcommit은 함께 필요한 경우가 많음
  3. 성능 고려사항:
    • L7 Kafka 정책은 추가 오버헤드 발생 가능
    • 중요한 토픽에만 세밀한 정책 적용 검토

✅ DNS 정책 실무 적용 팁

  1. 도메인 그룹화:
    • 기능별 정책 분리 (예: 모니터링 도메인, API 도메인, 저장소 도메인)
    • 정책 관리 및 디버깅이 더 쉬워집니다.
  2. DNS 캐싱 고려:
    • DNS 응답이 캐시되면 Cilium은 새로운 응답을 볼 수 없음
    • TTL 값이 짧은 도메인을 사용하거나, 캐싱 비활성화 고려
  3. 와일드카드 사용 주의:
    • *.com과 같은 너무 광범위한 패턴은 보안 위험
    • 가능한 한 구체적인 패턴 사용 권장

✅ 정책 디버깅 및 트러블슈팅

  1. Hubble을 활용한 모니터링:
  2. hubble observe --protocol kafka # Kafka 트래픽 모니터링 hubble observe --protocol dns # DNS 트래픽 모니터링
  3. 정책 테스트 방법:
    • 먼저 모니터링 모드로 적용하여 트래픽 패턴 분석
    • 점진적으로 제한 정책 적용
  4. 일반적인 문제 해결:
    • DNS 정책에서는 DNS 조회는 성공해도 실제 연결이 차단될 수 있음
    • Kafka 정책에서 특정 API Key가 누락되면 예상치 못한 오류 발생 가능
    • 정책 적용 후 항상 전체 워크플로우 테스트 필요

📌 Summary

이번 글에서는 Cilium을 활용한 비HTTP 프로토콜의 L7 정책 구성 방법을 살펴보았습니다:

Cilium L7 프로토콜 필터링 개요

 

 

Kafka 정책의 주요 특징:

  • 토픽, API Key, 컨슈머 그룹 기반 접근 제어
  • 정규식 패턴 매칭을 통한 유연한 토픽 관리
  • 프로듀서/컨슈머/관리자 권한 분리

DNS 정책의 주요 특징:

  • toFQDNs 셀렉터를 사용한 도메인 기반 필터링
  • 와일드카드 및 정규식 패턴을 통한 유연한 도메인 관리
  • DNS 정책과 L7 HTTP 정책 조합으로 더 세밀한 제어

toEndpoints 활용:

  • 네임스페이스 간 통신 제어
  • 마이크로서비스 아키텍처에서의 서비스 간 통신 제어

실무 적용 시 고려사항:

  • 토픽/도메인 네이밍 규칙 정립
  • 성능과 보안 간의 균형
  • Hubble을 활용한 트래픽 모니터링 및 정책 검증
728x90