본문 바로가기
카테고리 없음

실리움(Cilium) 네트워크 정책 재정의: 올바른 Deny 사용법과 mTLS 인증 🔐

by gasbugs 2025. 10. 6.

안녕하세요! 쿠버네티스 네트워킹의 보안을 책임지고 계신 여러분, 기술의 세계에서는 배우고 수정하며 나아가는 것이 무엇보다 중요합니다. 이전 포스팅에서 실리움의 Deny 정책에 대해 잘못된 정보가 있었음을 인정하며, 올바른 내용으로 다시 찾아왔습니다.

오늘은 실리움 네트워크 정책의 정확한 거부(Deny) 규칙 사용법과, 한 단계 더 나아가 IP와 포트를 넘어 워크로드의 신원을 확인하는 mTLS 인증까지 깊이 있게 알아보겠습니다.

 

1. 기본 원칙: 여전히 유효한 'Default Deny' (허용 목록)

먼저, 기본 중의 기본인 'Default Deny' 원칙은 변함이 없습니다. 어떤 파드에 egress나 ingress 규칙이 적용되면, 명시적으로 허용된 트래픽을 제외한 모든 것은 차단됩니다. 이는 클러스터 보안의 가장 중요한 출발점입니다.

"초대 명단(egress/ingress 규칙)에 이름이 없으면 들어올 수 없습니다."

이 허용 목록 방식은 제로 트러스트 보안의 근간을 이룹니다.


2. 올바른 거부(Deny) 규칙: egressDeny 와 ingressDeny 🚫

이전 글에서 제가 action: Deny라는 잘못된 필드를 안내했습니다. 정확한 방법은 egressDeny와 ingressDeny라는 별도의 최상위 필드를 사용하는 것입니다.

이 규칙의 작동 방식은 매우 명확하고 강력합니다.

규칙 평가 순서: Deny 규칙이 Allow 규칙보다 항상 먼저 검사되고 우선순위를 가집니다.

  1. [1순위] Deny 규칙 확인: 파드에서 나가는 트래픽은 가장 먼저 egressDeny 규칙과 비교됩니다. 만약 일치하는 규칙이 있다면, 트래픽은 **즉시 차단(DENY)**됩니다. 더 이상 다른 규칙은 확인하지 않습니다.
  2. [2순위] Allow 규칙 확인: egressDeny 규칙에 해당하지 않는 트래픽은 그제야 egress (허용) 규칙과 비교됩니다. 일치하는 허용 규칙이 있다면 트래픽은 **허용(ALLOW)**됩니다.
  3. [최종] 기본 차단: 만약 Deny 규칙에도, Allow 규칙에도 해당하지 않는다면, 'Default Deny' 원칙에 따라 트래픽은 **최종적으로 차단(DENY)**됩니다.

예시: "모든 외부 통신은 허용하되, database 서비스로의 접근은 절대 금지"

YAML
 
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "correct-deny-database"
  namespace: "team-orange"
spec:
  endpointSelector:
    matchLabels:
      type: messenger
  
  # 2순위로 평가되는 '허용' 규칙
  egress:
    - toCIDR:
        - "0.0.0.0/0" # 모든 외부 IP 대역으로의 통신을 허용
  
  # 1순위로 평가되는 '거부' 규칙
  egressDeny:
    - toServices:
        - k8sService:
            serviceName: database
            namespace: team-orange

이 정책은 messenger 파드에 대해 다음과 같이 동작합니다.

  • 파드가 database 서비스로 통신을 시도하면, egressDeny 규칙에 먼저 매치되어 즉시 차단됩니다.
  • 파드가 구글 DNS(8.8.8.8) 같은 다른 외부 IP로 통신을 시도하면, egressDeny 규칙에는 해당하지 않으므로 egress 규칙으로 넘어갑니다. toCIDR: ["0.0.0.0/0"] 규칙에 해당하므로 허용됩니다.

3. 신원 확인의 시간: mTLS를 통한 상호 인증 (Authentication) 🛡️

지금까지 우리는 IP, 포트, 서비스 이름 등 네트워크 레벨(L3/L4)에서 트래픽을 제어했습니다. 하지만 해커가 IP를 스푸핑하거나, 허가된 파드에 침투하여 악의적인 통신을 시도한다면 어떻게 될까요?

이때 필요한 것이 바로 **"신원 인증"**입니다. 네트워크 경로가 올바른지를 넘어, **"통신을 시도하는 워크로드가 정말 우리가 신뢰하는 그 워크로드가 맞는가?"**를 확인하는 과정입니다. 실리움은 이를 **상호 TLS (mTLS)**를 통해 구현합니다.

쉬운 비유: 네트워크 정책이 **"초대 명단에 이름이 있는지 확인"**하는 것이라면, mTLS 인증은 **"그 사람이 맞는지 신분증 사진과 얼굴을 대조"**하는 것과 같습니다.

예시: client는 오직 인증된 server와만 통신 가능

client 파드가 server 파드와 통신하는 규칙에 mTLS 인증을 추가해 보겠습니다.

YAML
 
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "mtls-auth-policy"
  namespace: "default"
spec:
  endpointSelector:
    matchLabels:
      app: client
  egress:
    - toEndpoints:
        - matchLabels:
            app: server
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
      # --- 바로 이 부분이 신원 인증을 강제하는 부분입니다 ---
      authentication:
        mode: "required"
  • authentication: mode: "required": 이 설정이 추가되면, client 파드는 server 파드로 통신을 시도할 때 반드시 자신의 암호화된 신원(SPIFFE ID)을 제시해야 합니다.
  • server 파드 역시 client의 신원을 확인하고, 유효한 경우에만 통신을 수락합니다.
  • 만약 신원 확인에 실패한 위장된 파드가 server로 접근을 시도하면, 네트워크 경로가 허용되더라도 인증 단계에서 연결이 차단됩니다.

최종 정리: 다층 방어(Defense in Depth) 구축하기

오늘 우리는 두 가지 중요한 개념을 바로잡고 새로 배웠습니다.

  1. 올바른 Deny: 명시적 차단은 egressDeny / ingressDeny 필드를 사용하며, 이 규칙은 항상 Allow 규칙보다 우선합니다.
  2. 강력한 인증: authentication: mode: "required"를 통해 네트워크 정책에 신원 확인 계층(mTLS)을 추가하여 제로 트러스트 보안을 한층 강화할 수 있습니다.

단순히 네트워크 경로를 제어하는 것을 넘어, 워크로드의 신원까지 확인하는 다층 방어 전략을 통해 여러분의 쿠버네티스 클러스터를 더욱 안전하게 보호하시길 바랍니다.


Cilium, NetworkPolicy, Kubernetes, Security, CNI, EgressDeny, IngressDeny, mTLS, Authentication, SPIFFE, Zero Trust, DevSecOps, 실리움, 쿠버네티스, 보안