네트워크 정책

네트워크 정책 (Network Policies)

TCP, UDP, SCTP 프로토콜에 대해 IP 주소나 포트 수준에서 트래픽 흐름을 제어하려면, 클러스터의 특정 애플리케이션에 쿠버네티스 NetworkPolicy를 사용하는 것을 고려해 볼 수 있어요. NetworkPolicy는 애플리케이션 중심 구조로, 파드가 네트워크를 통해 다양한 네트워크 "엔티티"와(여기서 "엔티티"라는 단어는 쿠버네티스에서 특정 의미를 가진 "엔드포인트"나 "서비스" 같은 더 흔한 용어의 중복을 피하기 위해 사용해요) 어떻게 통신하도록 허용되는지 지정하게 해줘요. NetworkPolicy는 한쪽 또는 양쪽 끝에 파드가 있는 연결에 적용되며, 다른 연결에는 관련이 없어요.

파드가 통신할 수 있는 엔티티는 다음 세 가지 식별자의 조합으로 식별돼요:

  • 허용된 다른 파드(예외: 파드는 자신에 대한 접근을 차단할 수 없어요)
  • 허용된 네임스페이스
  • IP 블록(예외: 파드가 실행되는 노드와의 트래픽은 파드·노드의 IP 주소와 무관하게 항상 허용돼요)

파드 기반 또는 네임스페이스 기반 NetworkPolicy를 정의할 때 셀렉터를 사용해 셀렉터와 일치하는 파드에 대한 트래픽이 무엇인지 지정해요.

한편 IP 기반 NetworkPolicy를 만들 때는 IP 블록(CIDR 범위)에 기반한 정책을 정의해요.

출처: 문서

본문

사전 요구 사항 (Prerequisites)

네트워크 정책은 네트워크 플러그인이 구현해요. 네트워크 정책을 사용하려면 NetworkPolicy를 지원하는 네트워킹 솔루션을 사용해야 해요. 그것을 구현하는 컨트롤러 없이 NetworkPolicy 리소스를 만드는 것은 아무 효과가 없어요.

두 종류의 파드 격리

파드에는 두 가지 격리가 있어요: 이그레스(egress) 격리와 인그레스(ingress) 격리예요. 이들은 어떤 연결이 수립될 수 있는지에 관한 것이에요. 여기서 "격리"는 절대적이지 않으며 "몇 가지 제한이 적용된다"는 의미예요. 대안인 "$방향에 대해 비격리(non-isolated for $direction)" 는 해당 방향에 제한이 적용되지 않음을 의미해요. 두 종류의 격리(여부)는 독립적으로 선언되며, 한 파드에서 다른 파드로의 연결에 모두 관련돼요.

기본적으로 파드는 이그레스에 대해 비격리이며, 모든 아웃바운드 연결이 허용돼요. 파드를 선택하고 policyTypes 에 "Egress"가 있는 NetworkPolicy가 있으면 파드는 이그레스에 대해 격리돼요. 그런 정책이 이그레스에 대해 파드에 적용된다고 말해요. 파드가 이그레스에 대해 격리될 때, 파드에서 허용되는 유일한 연결은 이그레스에 대해 파드에 적용되는 일부 NetworkPolicy의 egress 목록이 허용하는 연결이에요. 허용된 연결에 대한 응답 트래픽도 암시적으로 허용돼요. 이 egress 목록들의 효과는 덧셈적으로 결합돼요.

기본적으로 파드는 인그레스에 대해 비격리이며, 모든 인바운드 연결이 허용돼요. 파드를 선택하고 policyTypes 에 "Ingress"가 있는 NetworkPolicy가 있으면 파드는 인그레스에 대해 격리돼요. 그런 정책이 인그레스에 대해 파드에 적용된다고 말해요. 파드가 인그레스에 대해 격리될 때, 파드로 허용되는 유일한 연결은 파드의 노드에서 온 것과 인그레스에 대해 파드에 적용되는 일부 NetworkPolicy의 ingress 목록이 허용하는 것이에요. 허용된 연결에 대한 응답 트래픽도 암시적으로 허용돼요. 이 ingress 목록들의 효과는 덧셈적으로 결합돼요.

네트워크 정책은 충돌하지 않으며 덧셈적이에요. 주어진 방향에 대해 특정 파드에 한 개 이상의 정책이 적용되면, 그 방향에서 그 파드로 허용되는 연결은 적용 가능한 정책이 허용하는 것의 합집합이에요. 따라서 평가 순서는 정책 결과에 영향을 미치지 않아요.

소스 파드에서 대상 파드로의 연결이 허용되려면 소스 파드의 이그레스 정책과 대상 파드의 인그레스 정책 둘 다 그 연결을 허용해야 해요. 어느 한쪽이 연결을 허용하지 않으면 그 연결은 일어나지 않아요.

NetworkPolicy 리소스

리소스에 대한 전체 정의는 NetworkPolicy 참조를 참고해요.

예시 NetworkPolicy는 다음과 같을 수 있어요:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

참고:

필수 필드: 다른 모든 쿠버네티스 구성과 마찬가지로 NetworkPolicy는 apiVersion, kind, metadata 필드가 필요해요. 구성 파일 작업에 대한 일반 정보는 Pod가 ConfigMap을 사용하도록 구성하기객체 관리를 참고해요.

spec: NetworkPolicy spec 은 주어진 네임스페이스에서 특정 네트워크 정책을 정의하는 데 필요한 모든 정보를 가져요.

podSelector: 각 NetworkPolicy는 정책이 적용되는 파드 그룹을 선택하는 podSelector 를 포함해요. 예시 정책은 "role=db" 라벨이 있는 파드를 선택해요. 빈 podSelector 는 네임스페이스의 모든 파드를 선택해요.

policyTypes: 각 NetworkPolicy는 Ingress, Egress, 또는 둘 다 포함할 수 있는 policyTypes 목록을 포함해요. policyTypes 필드는 주어진 정책이 선택된 파드로의 인그레스 트래픽, 선택된 파드로부터의 이그레스 트래픽, 또는 둘 다에 적용되는지 여부를 나타내요. NetworkPolicy에서 policyTypes 가 지정되지 않으면 기본적으로 Ingress 가 항상 설정되고, NetworkPolicy에 이그레스 규칙이 있으면 Egress 가 설정돼요.

ingress: 각 NetworkPolicy는 허용된 ingress 규칙 목록을 포함할 수 있어요. 각 규칙은 fromports 섹션 둘 다와 일치하는 트래픽을 허용해요. 예시 정책은 단일 규칙을 포함하며, 단일 포트에서 ipBlock 로 지정된 첫 번째, namespaceSelector 로 두 번째, podSelector 로 세 번째의 세 소스 중 하나에서 오는 트래픽과 일치해요.

egress: 각 NetworkPolicy는 허용된 egress 규칙 목록을 포함할 수 있어요. 각 규칙은 toports 섹션 둘 다와 일치하는 트래픽을 허용해요. 예시 정책은 단일 규칙을 포함하며, 10.0.0.0/24 의 어떤 대상에도 단일 포트에서 트래픽을 일치시켜요.

그래서 예시 NetworkPolicy는:

더 많은 예시는 네트워크 정책 선언 워크스루를 참고해요.

to와 from 셀렉터의 동작

ingress from 섹션 또는 egress to 섹션에 지정할 수 있는 셀렉터는 네 가지 종류가 있어요:

podSelector: 이것은 NetworkPolicy와 같은 네임스페이스에서 인그레스 소스 또는 이그레스 대상으로 허용되어야 하는 특정 파드를 선택해요.

namespaceSelector: 이것은 모든 파드가 인그레스 소스 또는 이그레스 대상으로 허용되어야 하는 특정 네임스페이스를 선택해요.

namespaceSelector podSelector: namespaceSelectorpodSelector 둘 다를 지정하는 단일 to/from 항목은 특정 네임스페이스 안의 특정 파드를 선택해요. 올바른 YAML 문법을 사용하도록 주의하세요. 예:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

이 정책은 user=alice 라벨이 있는 네임스페이스에서 role=client 라벨이 있는 파드로부터의 연결을 허용하는 단일 from 요소를 포함해요. 하지만 다음 정책은 다릅니다:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

이것은 from 배열에 두 요소를 포함하며, 로컬 네임스페이스에서 role=client 라벨이 있는 파드 또는 user=alice 라벨이 있는 어떤 네임스페이스의 어떤 파드에서든 연결을 허용해요.

확실하지 않을 때는 kubectl describe 를 사용해 쿠버네티스가 정책을 어떻게 해석했는지 확인해요.

ipBlock: 이것은 인그레스 소스 또는 이그레스 대상으로 허용할 특정 IP CIDR 범위를 선택해요. 파드 IP는 일시적이고 예측 불가능하므로 클러스터 외부 IP여야 해요.

클러스터 인그레스·이그레스 메커니즘은 종종 패킷의 소스 또는 대상 IP를 다시 쓰는 것을 요구해요. 이런 일이 발생하는 경우, NetworkPolicy 처리 전인지 후인지 정의되지 않으며 네트워크 플러그인, 클라우드 제공자, Service 구현 등의 조합에 따라 동작이 달라질 수 있어요.

인그레스의 경우 이는 어떤 경우에는 실제 원래 소스 IP에 기반해 수신 패킷을 필터링할 수 있지만, 다른 경우에는 NetworkPolicy가 작용하는 "소스 IP" 가 LoadBalancer 나 파드의 노드의 IP일 수 있음을 의미해요.

이그레스의 경우 이는 클러스터 외부 IP로 다시 쓰여지는 Service IP에 대한 파드의 연결이 ipBlock 기반 정책의 적용을 받을 수도 있고 받지 않을 수도 있음을 의미해요.

기본 정책 (Default policies)

기본적으로 네임스페이스에 정책이 없으면 해당 네임스페이스의 파드에 대한 모든 인그레스·이그레스 트래픽이 허용돼요. 다음 예시들은 그 네임스페이스의 기본 동작을 변경하게 해줘요.

모든 인그레스 트래픽 기본 거부

모든 파드를 선택하지만 그 파드에 어떤 인그레스 트래픽도 허용하지 않는 NetworkPolicy를 만들어 네임스페이스에 대한 "기본" 인그레스 격리 정책을 만들 수 있어요.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

이것은 다른 NetworkPolicy로 선택되지 않은 파드도 인그레스에 대해 격리되도록 보장해요. 이 정책은 어떤 파드의 이그레스 격리에는 영향을 미치지 않아요.

모든 인그레스 트래픽 허용

네임스페이스의 모든 파드에 대한 모든 수신 연결을 허용하려면 그것을 명시적으로 허용하는 정책을 만들 수 있어요.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

이 정책이 있으면 추가 정책은 그 파드에 대한 어떤 수신 연결도 거부되게 할 수 없어요. 이 정책은 어떤 파드의 이그레스 격리에는 영향을 미치지 않아요.

모든 이그레스 트래픽 기본 거부

모든 파드를 선택하지만 그 파드에서 어떤 이그레스 트래픽도 허용하지 않는 NetworkPolicy를 만들어 네임스페이스에 대한 "기본" 이그레스 격리 정책을 만들 수 있어요.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

이것은 다른 NetworkPolicy로 선택되지 않은 파드도 이그레스 트래픽이 허용되지 않도록 보장해요. 이 정책은 어떤 파드의 인그레스 격리 동작도 변경하지 않아요.

주의:

모든 이그레스 트래픽 허용

네임스페이스의 모든 파드에서 모든 연결을 허용하려면 그 네임스페이스 파드의 모든 발신 연결을 명시적으로 허용하는 정책을 만들 수 있어요.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

이 정책이 있으면 추가 정책은 그 파드에서 어떤 발신 연결도 거부되게 할 수 없어요. 이 정책은 어떤 파드에 대한 인그레스 격리에는 영향을 미치지 않아요.

모든 인그레스·이그레스 트래픽 기본 거부

그 네임스페이스에 다음 NetworkPolicy를 만들어 모든 인그레스 및 이그레스 트래픽을 방지하는 "기본" 정책을 만들 수 있어요.

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

이것은 다른 NetworkPolicy로 선택되지 않은 파드도 인그레스·이그레스 트래픽이 허용되지 않도록 보장해요.

네트워크 트래픽 필터링

NetworkPolicy는 4계층 연결(TCP, UDP, 선택적으로 SCTP)에 대해 정의돼요. 다른 모든 프로토콜의 경우 네트워크 플러그인에 따라 동작이 다를 수 있어요.

참고:

deny all 네트워크 정책이 정의되면 TCP, UDP, SCTP 연결만 거부하는 것이 보장돼요. ARP나 ICMP 같은 다른 프로토콜의 경우 동작이 정의되지 않아요. 허용 규칙에도 동일하게 적용돼요. 특정 파드가 인그레스 소스나 이그레스 대상으로 허용되면 (예를 들어) ICMP 패킷에 무슨 일이 일어나는지 정의되지 않아요. ICMP 같은 프로토콜은 일부 네트워크 플러그인에서 허용되고 다른 곳에서는 거부될 수 있어요.

포트 범위 대상 지정

NetworkPolicy를 작성할 때 단일 포트 대신 포트 범위를 대상으로 지정할 수 있어요.

이는 다음 예시처럼 endPort 필드를 사용해 달성할 수 있어요:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-port-egress
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      ports:
        - protocol: TCP
          port: 32000
          endPort: 32768

위 규칙은 default 네임스페이스에서 role=db 라벨이 있는 어떤 파드든 대상 포트가 32000과 32768 사이인 경우 TCP로 10.0.0.0/24 범위 내의 어떤 IP와도 통신할 수 있게 허용해요.

이 필드를 사용할 때 다음 제한이 적용돼요:

  • endPort 필드는 port 필드보다 크거나 같아야 해요.
  • endPortport 도 정의된 경우에만 정의할 수 있어요.
  • 두 포트 모두 숫자여야 해요.

참고:

라벨로 여러 네임스페이스 대상 지정

이 시나리오에서 Egress NetworkPolicy는 라벨 이름으로 둘 이상의 네임스페이스를 대상으로 지정해요. 이를 위해 대상 네임스페이스에 라벨을 붙여야 해요. 예:

kubectl label namespace frontend namespace=frontend
kubectl label namespace backend namespace=backend

NetworkPolicy 문서의 namespaceSelector 아래에 라벨을 추가해요. 예:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: egress-namespaces
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchExpressions:
        - key: namespace
          operator: In
          values: ["frontend", "backend"]

참고:

이름으로 네임스페이스 대상 지정

쿠버네티스 제어 플레인은 모든 네임스페이스에 불변 라벨 kubernetes.io/metadata.name 을 설정하며, 라벨의 값은 네임스페이스 이름이에요.

NetworkPolicy는 어떤 객체 필드로도 이름으로 네임스페이스를 대상으로 지정할 수 없지만, 표준화된 라벨을 사용해 특정 네임스페이스를 대상으로 지정할 수 있어요.

파드 수명주기

참고:

새 NetworkPolicy 객체가 만들어지면 네트워크 플러그인이 새 객체를 처리하는 데 시간이 걸릴 수 있어요. NetworkPolicy의 영향을 받는 파드가 네트워크 플러그인이 NetworkPolicy 처리를 완료하기 전에 생성되면, 그 파드는 보호되지 않은 채 시작되어 NetworkPolicy 처리가 완료될 때 격리 규칙이 적용될 수 있어요.

NetworkPolicy가 네트워크 플러그인으로 처리되면,

만들어진 모든 NetworkPolicy는 결국 네트워크 플러그인이 처리하지만, 쿠버네티스 API에서 정확히 언제 그런 일이 일어나는지 알 수 있는 방법은 없어요.

따라서 파드는 예상과 다른 네트워크 연결로 시작되는 것에 대해 탄력적이어야 해요. 파드가 시작되기 전에 특정 목적지에 도달할 수 있도록 보장해야 한다면 init container를 사용해 kubelet이 앱 컨테이너를 시작하기 전에 그 목적지가 도달 가능할 때까지 기다리게 할 수 있어요.

모든 NetworkPolicy는 결국 선택된 모든 파드에 적용될 거예요. 네트워크 플러그인이 NetworkPolicy를 분산 방식으로 구현할 수 있기 때문에, 파드가 처음 생성될 때나 파드·정책이 변경될 때 파드가 네트워크 정책에 대해 약간 일관성 없는 보기를 볼 수 있어요. 예를 들어 Node 1의 Pod A와 Node 2의 Pod B에 모두 도달할 수 있어야 하는 새로 생성된 파드는 Pod A에는 즉시 도달할 수 있지만 몇 초 후까지 Pod B에는 도달할 수 없을 수 있어요.

NetworkPolicy와 hostNetwork 파드

hostNetwork 파드에 대한 NetworkPolicy 동작은 정의되지 않지만 두 가지 가능성으로 제한되어야 해요:

  • 네트워크 플러그인이 hostNetwork 파드 트래픽을 다른 모든 트래픽과 구분할 수 있으며(같은 노드의 서로 다른 hostNetwork 파드 트래픽을 구분하는 것 포함), pod-network 파드에 하는 것과 같이 hostNetwork 파드에 NetworkPolicy를 적용해요.
  • 네트워크 플러그인이 hostNetwork 파드 트래픽을 제대로 구분할 수 없어 podSelectornamespaceSelector 를 일치시킬 때 hostNetwork 파드를 무시해요. hostNetwork 파드로/부터의 트래픽은 노드 IP로/부터의 다른 모든 트래픽과 동일하게 취급돼요. (이것이 가장 흔한 구현이에요.)

이것은 다음 경우에 적용돼요

동시에 hostNetwork 파드는 자신이 있는 노드와 같은 IP 주소를 가지므로, 그 연결은 노드 연결로 취급돼요. 예를 들어 ipBlock 규칙으로 hostNetwork 파드에서 트래픽을 허용할 수 있어요.

네트워크 정책으로 할 수 없는 것(아직은)

쿠버네티스 1.37 기준으로 다음 기능은 NetworkPolicy API에 존재하지 않지만, 운영 체제 컴포넌트(예: SELinux, OpenVSwitch, IPTables 등)나 7계층 기술(Ingress 컨트롤러, Service Mesh 구현) 또는 admission 컨트롤러를 사용해 해결책을 구현할 수 있을지도 몰라요. 쿠버네티스 네트워크 보안이 처음이라면 다음 User Story는 (아직) NetworkPolicy API로 구현할 수 없다는 점을 알아 둘 가치가 있어요.

  • 내부 클러스터 트래픽을 공통 게이트웨이로 강제하기(이것은 서비스 메시나 다른 프록시로 가장 잘 제공될 수 있어요).
  • TLS 관련 어떤 것(이것은 서비스 메시나 ingress 컨트롤러를 사용해요).
  • 노드별 정책(CIDR 표기법을 사용할 수 있지만 쿠버네티스 정체성으로 특별히 노드를 대상으로 지정할 수는 없어요).
  • 이름으로 서비스 대상 지정(라벨로 파드나 네임스페이스를 대상으로 지정할 수 있으며, 종종 실행 가능한 해결책이에요).
  • 서드파티가 충족하는 "정책 요청" 생성·관리.
  • 모든 네임스페이스나 파드에 적용되는 기본 정책(이를 할 수 있는 일부 서드파티 쿠버네티스 배포와 프로젝트가 있어요).
  • 고급 정책 조회와 도달 가능성 도구.
  • 네트워크 보안 이벤트(예: 차단되거나 수락된 연결) 로깅 기능.
  • 명시적으로 거부하는 정책 기능(현재 NetworkPolicy 모델은 기본 거부이며 허용 규칙만 추가할 수 있어요).
  • 루프백 또는 수신 호스트 트래픽 방지(파드는 현재 localhost 접근을 차단할 수 없고, 자신이 상주하는 노드로부터의 접근을 차단할 수 있는 능력도 없어요).

기존 연결에 대한 NetworkPolicy의 영향

기존 연결에 적용되는 NetworkPolicy 집합이 변경되면(정책 변경 또는 정책이 선택한 네임스페이스/파드의 관련 라벨이 기존 연결 중간에 변경돼서 발생할 수 있음) 그 변경이 해당 기존 연결에 영향을 미칠지는 구현이 정의해요. 예: 이전에 허용된 연결을 거부하게 하는 정책이 생성되면, 기본 네트워크 플러그인 구현이 새 정책이 기존 연결을 닫을지 정의하는 책임을 져요. 기존 연결에 영향을 줄 수 있는 방식으로 정책/파드/네임스페이스를 수정하지 않는 것이 권장돼요.

더 알아보기 (Learn more)