Cilium Study Security Network Policy
Intro
Cilium Security
Policy Enforcement — Cilium 1.18.1 documentation

- Cilium에서의 보안정책은 “세션 기반 프로토콜”에 대해 상태 저장 정책이 적용되어, 응답 패킷은 자동으로 허용됨.
- 정책이 A => B를 허용한다면, B에서 A로의 응답 패킷도 자동으로 허용
- 그러나 B가 A로의 연결을 시작하는 것은 자동으로 허용 X (양방향은 명시적 허용 필요)
- 보안 정책은 입력(수신) 또는 출력(송신)에서 적용
- ID 기반 보안정책을 사용 (다중 클러스터 환경도 포함)
- 기본 보안 정책
- 기본(Default) 동작 (= 정책이 없는 경우)은 “Allow All”
- 첫번째 정책 규칙이 적용되면 화이트리스트(WhiteList) 기반으로 동작해 화이트리스트에 없는 패킷은 Drop
- L4 정책도 동일
Cilium Envoy Proxy

Cilium은 클러스터의 네트워크 정책에 지정된 대로 HTTP 및 기타 L7 정책을 적용하기 위해 이 최소 배포를 호스트 프록시로 사용
- Cilium Proxy는 Cilium 이미지 내에 배포
- Cilium L7 기능(수신, 게이트웨이 API, L7 기능이 있는 네트워크 정책, L7 프로토콜 가시성)이 Kubernetes 클러스터에 활성화되거나 설치되면 Cilium 에이전트는 Cilium 에이전트 Pod 내에서 별도의 프로세스로 Envoy 프록시를 시작
- 모든 L7 요청을 프록시하는 역할
- [Cilium Envoy as DaemonSet]
- 또는
cilium-envoy라는 독립된 LifeCycle을 가진 DaemonSet으로 배포해서 사용가능
- 또는
Identity-Based
기존의 IP 주소 기반 접근 제어와 달리, 클러스터 내 모든 엔드포인트(예: Pod)에 고유한 Security Identity를 부여하고 이 Identity를 중심으로 네트워크 정책을 집행하는 방식
다음 특징을 가진다.
- Kubernetes Label 기반 : 각 Endpoint(Pod 등)이 가진 레이블(Label) 세트 기준으로 ID가 자동 부여
- IP 주소와 보안 완전 분리 : 기존 IP 주소 기반 접근 제어와 달리 Endpoint를 작업(Pod 생성/삭제 등)이 되면서 동적으로 라이프사이클이 변해도 레이블기반 Identity만 신경쓰는 방식이라 대규모 환경에서도 정책 관리, 유연성이 뛰어남
- ID는 클러스터 내에 유일한 ID로 구성되어야함.
Cilium이 Identity를 확인하는 순서
- Pod 혹은 Container가 생성되면, Cilium은 컨테이너 런타임에서 수신한 Event를 기반으로 네트워크 Pod 또는 Container를 나타내는 Endpoint(Cilium Endpoint)를 생성
- 생성된 Endpoint ID를 확인
- Pod 또는 Container의 Label이 변경될때마다 재확인하고 필요에 따라 수정
1# Cilium Endpoint 확인
2kubectl get ciliumendpoints.cilium.io -n kube-system
3NAME SECURITY IDENTITY ENDPOINT STATE IPV4 IPV6
4coredns-674b8bbfcf-7tm5s 31321 ready 172.20.0.132
5coredns-674b8bbfcf-np4mw 31321 ready 172.20.0.204
6hubble-relay-fdd49b976-lgr8q 19024 ready 172.20.0.170
7hubble-ui-655f947f96-rrkh8 10462 ready 172.20.0.159
8metrics-server-5dd7b49d79-44vww 14275 ready 172.20.0.229
9
10# Cilium ID 확인
11kubectl get ciliumidentities.cilium.io
12NAME NAMESPACE AGE
1310462 kube-system 25m
1412743 cilium-monitoring 25m
1514275 kube-system 25m
1619024 kube-system 25m
1731321 kube-system 25m
1832083 local-path-storage 25m
1950153 cilium-monitoring 25m
Q. 기동 중인 Pod에 Label 추가 후 Cilium ID(보안 Label)에 어떻게 반영되는지?
1# 현재 CoreDNS의 ID 확인
2kubectl exec -it -n kube-system ds/cilium -- cilium identity get 31321
331321 k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
4 k8s:io.cilium.k8s.policy.cluster=default
5 k8s:io.cilium.k8s.policy.serviceaccount=coredns
6 k8s:io.kubernetes.pod.namespace=kube-system
7 k8s:k8s-app=kube-dns
8
9# Label 변경(추가)
10kubectl label pods -n kube-system -l k8s-app=kube-dns study=8w
11
12# ID 재확인
13kubectl exec -it -n kube-system ds/cilium -- cilium identity get 31321
1431321 k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
15 k8s:io.cilium.k8s.policy.cluster=default
16 k8s:io.cilium.k8s.policy.serviceaccount=coredns
17 k8s:io.kubernetes.pod.namespace=kube-system
18 k8s:k8s-app=kube-dns
19
20# 신규 ID 생성됨
21kubectl exec -it -n kube-system ds/cilium -- cilium identity list
22...
2319722 k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
24 k8s:io.cilium.k8s.policy.cluster=default
25 k8s:io.cilium.k8s.policy.serviceaccount=coredns
26 k8s:io.kubernetes.pod.namespace=kube-system
27 k8s:k8s-app=kube-dns
28 k8s:study=8w
- Cilium은 Pod의 update event를 watch하므로, Label 변경 시 cilium endpoint가 ‘waiting-for-identity’ 상태로 전환되어 (새로운 Label Set에 맞는) 새로운 identity를 할당받음
- 따라서 Security labels와 관련된 네트워크 정책도 자동으로 재적용
- 대규모 클러스터에서 자주 labels를 변경하면 identity 할당이 빈번해져 성능 저하가 발생할 수 있으므로, identity-relevant labels를 제한하는 것이 권장
Special Identities
Cilium에서 관리하는 모든 엔드포인트에는 ID가 할당되는데, 관리하지 않는 네트워크 엔드포인트와의 통신을 허용하기 위해 이러한 엔드포인트를 나타내는 특수(Special) ID가 존재
- 특별히 예약된 ID에는
reserved문자열 접두사가 붙음
1kubectl exec -it -n kube-system ds/cilium -- cilium identity list
2ID LABELS
31 reserved:host
4 reserved:kube-apiserver
52 reserved:world
63 reserved:unmanaged
74 reserved:health
85 reserved:init
96 reserved:remote-node
107 reserved:kube-apiserver
11 reserved:remote-node
128 reserved:ingress
139 reserved:world-ipv4
1410 reserved:world-ipv6
158836 k8s:app.kubernetes.io/instance=metrics-server
16 k8s:app.kubernetes.io/name=metrics-server
17 k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
18 k8s:io.cilium.k8s.policy.cluster=default
19 k8s:io.cilium.k8s.policy.serviceaccount=metrics-server*
- 각
reservedID에 대한 설명Identity Numeric ID Description reserved:unknown0 The identity could not be derived. reserved:host1 The local host. Any traffic that originates from or is designated to one of the local host IPs. reserved:world2 Any network endpoint outside of the cluster reserved:unmanaged3 An endpoint that is not managed by Cilium, e.g. a Kubernetes pod that was launched before Cilium was installed. reserved:health4 This is health checking traffic generated by Cilium agents. reserved:init5 An endpoint for which the identity has not yet been resolved is assigned the init identity. This represents the phase of an endpoint in which some of the metadata required to derive the security identity is still missing. This is typically the case in the bootstrapping phase.The init identity is only allocated if the labels of the endpoint are not known at creation time. This can be the case for the Docker plugin. reserved:remote-node6 The collection of all remote cluster hosts. Any traffic that originates from or is designated to one of the IPs of any host in any connected cluster other than the local node. reserved:kube-apiserver7 Remote node(s) which have backend(s) serving the kube-apiserver running. reserved:ingress8 Given to the IPs used as the source address for connections from Ingress proxies.
Identity Management in the cluster

- ID는 전체 클러스터에서 유효 (Multi Cluster 환경에서도 ID 관련 레이블을 공유하는 경우 단일 ID를 가짐)
- 이를 위해서 클러스터 노드 간의 조정이 필요함.
- 엔드포인트 ID를 확인하는 작업은 **분산 키-값 저장소(**Key-Value Store)를 통해 수행
- 분산 Key-Value Store는 이전에 값이 확인되지 않는 경우 새로운 고유 식별자를 생성하고, 각 클러스터 노드는 ID관련 레이블 하위 집합을 생성해 Key-Value Store에 Query 후 ID값을 도출함.
- ID Management Mode : Cilium Agent (Default) 또는 Cilium Operator에 의해 CID(Cilium Identity)를 중앙 집중화 관리를 지원
Network Policy

Kubernetes Network Policy 대비 Cilium Network Policy가 어떤 이점을 가지고 있는지 표 형식으로 정리
| 항목 | Kubernetes Network Policy | Cilium Network Policy |
|---|---|---|
| 정책 형식 | 표준 NetworkPolicy 리소스 | NetworkPolicy 및 Cilium CustomResource (CiliumNetworkPolicy) |
| 지원 레이어 | L3, L4 (IP, Port) | L3, L4, L7 (HTTP, Kafka 등 애플리케이션 프로토콜 인식) |
| 정책 적용 범위 | 네임스페이스 Scope | 네임스페이스 또는 클러스터 전체(Global) 정책 |
| 정책 객체 확장성 | 제한적(커스텀 확장 불가, 표준에 한정) | 확장성 높음: DNS 기반, 동적 인식 등 다양한 조건 지원 |
| 엔진/기술 | 일반 CNI 플러그인(플러그인마다 구현 세부 상이) | eBPF 기반으로 리눅스 커널 직접 제어 |
| 정책 조건 | PodSelector, NamespaceSelector, IPBlock | 위 조건 모두 + EndPointSelector, DNS, L7규칙 등 다양 |
| 성능/가시성 | 표준 리눅스 커널 iptables 기반, 가시성 낮음 | eBPF 기반 고성능, 트래픽 가시성 Hubble 지원 |
| 클러스터/멀티클러스터 | 클러스터 내 정책만 지원 | 멀티클러스터 정책 지원(ClusterMesh) |
| 정책 충돌 | 네임스페이스 내 중첩 가능, 우선순위 개념 없음 | 네임스페이스/클러스터 스코프 우선순위 및 중첩 체계 지원 |
| 관리 및 생태계 통합 | kubectl 등 표준 툴 | kubectl, REST API, Cilium CLI, Hubble 등 통합 관리 |
Cilium Network Policy는 다음 큰 특징을 가지고 있다.
- L7 Layer 지원 : HTTP, Kafka, gRPC 등을 지원
- CiliumClusterwideNetworkPolicy는 클러스터 전체 정책을 지정할 수 있는 범위를 가짐.
- 확장성이 높음
toFQDNs및 DNS 기반 정책을 통해 IP가 아닌 도메인 이름 또는 패턴으로 네트워크 정책을 적용EndpointSelector는 기존 Kubernetes Label 뿐만 아니라, NodeSelector 등의 다양한 조건의 정책 대상을 지정할 수 있음.[Layer3 Example - Node Based] https://docs.cilium.io/en/stable/security/policy/language/#node-based
--enbale-node-selector-labels=true설정 활성화된 경우- Cilium Agent는 각 노드마다 다른 (Remote Node Scope를 가진) Local Security Identity를 가지게됨.
1# control plane 노드에 네트워크 정책 적용 2apiVersion: "cilium.io/v2" 3kind: CiliumNetworkPolicy 4metadata: 5 name: "to-prod-from-control-plane-nodes" 6spec: 7 endpointSelector: 8 matchLabels: 9 env: prod 10 ingress: 11 - fromNodes: 12 - matchLabels: 13 node-role.kubernetes.io/control-plane: ""
Policy Enforcement Modes
다음 3가지 정책 적용 모드가 있다.
- default (default) : Endpoint가 정책에 적용되기 전까지 제한 없는 네트워크 액세스를 갖는다.
- always :
enable-policy: always/ 특정 엔드포인트를 선택하는 규칙이 없더라도 모든 엔드포인트에 정책이 적용된다. - never : 규칙이 특정 엔드포인트를 선택하더라도 모든 엔드포인트에서 정책 적용이 비활성화된다. (= 모든 트래픽은 모든 수신과 송신에서 허용)
Endpoint default Policy
기본적으로 모든 송신 및 수신 트래픽은 모든 엔드포인트에 허용됩니다. 네트워크 정책에 의해 엔드포인트가 선택되면 명시적으로 허용된 트래픽만 허용되는 기본 거부 상태로 전환
상태는 방향별로 적용된다.
- 규칙이 엔드포인트를 선택 하고 해당 규칙에 수신 섹션이 있는 경우, 엔드포인트는 수신에 대해 기본 거부 모드로 전환
- 규칙이 엔드포인트를 선택 하고 규칙에 송신 섹션이 있는 경우, 엔드포인트는 송신에 대해 기본 거부 모드로 전환
flowchart TD
subgraph 기본["기본 상태"]
Start["모든 트래픽 허용"]
end
subgraph 방향별_동작["방향별 동작"]
direction TB
Ingress["수신 정책 적용"]
Egress["송신 정책 적용"]
Ingress --> IDeny["수신: 기본 거부 모드"]
Egress --> EDeny["송신: 기본 거부 모드"]
end
subgraph 결과["최종 상태"]
Final["정책에 명시된 트래픽만 허용"]
end
Start --> Ingress
Start --> Egress
IDeny --> Final
EDeny --> Final
classDef default fill:#f9f9f9,stroke:#333,stroke-width:2px,color:#000000
classDef action fill:#e1f5fe,stroke:#0288d1,stroke-width:2px,color:#000000
classDef result fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#000000
class Start default
class Ingress,Egress action
class IDeny,EDeny,Final result
Rule Basics
- endpointSelector / nodeSelector
- 정책 규칙이 적용될 엔드포인트 또는 노드를 선택
- ingress
- 엔드포인트에 들어오는 모든 네트워크 패킷에 적용해야 하는 규칙 목록
- egress
- 엔드포인트를 떠나는 모든 네트워크 패킷에 적용되어야 하는 규칙 목록
- labels
- 레이블은 규칙을 식별하는 데 사용됨
- Kubernetes를 통해 가져온 정책 규칙에는 NetworkPolicy 또는 CiliumNetworkPolicy 리소스 에 지정된 이름에 해당하는 레이블이 자동으로
io.cilium.k8s.policy.name=NAME지정
Network Policy Editor

- Cilium Network Policy를 사용하는 부분에서 잘못 사용하면 서비스에 큰 영향을 줄 수 있다. 예를 들어, 정책 적용전에 모든 트래픽이 허용되었지만, 정책 적용 후에는 명시적으로 허용되는 트래픽만 허용되는 부분을 놓치게된다면 실제 서비스에 네트워크 차단이 이뤄질 수 있기 마련이다.
- Isovalent의 Network Policy Editor 글을 참고하면 사용에 도움이 된다.
[Example] Layer 3 예시
https://docs.cilium.io/en/stable/security/policy/language/#layer-3-examples
Endpoint based
- 두 엔드포인트가 Cilium에 의해 관리되고 레이블이 할당되는 경우 관계를 설명하는 데 사용
- 장점 ) IP 주소가 정책에 인코딩되지 않고 정책이 주소 지정과 완전히 분리된다는 것
1# Simple Ingress 허용 2apiVersion: "cilium.io/v2" 3kind: CiliumNetworkPolicy 4metadata: 5 name: "l3-rule" 6spec: 7 endpointSelector: 8 matchLabels: 9 role: backend 10 ingress: 11 - fromEndpoints: 12 - matchLabels: 13 role: frontend 14 15# Allow All Endpoints 16apiVersion: "cilium.io/v2" 17kind: CiliumNetworkPolicy 18metadata: 19 name: "allow-all-to-victim" 20spec: 21 endpointSelector: 22 matchLabels: 23 role: victim 24 ingress: 25 - fromEndpoints: 26 - {}Service based
- Kubernetes의 Service를 이용
- 엔드포인트는 모든 백엔드 IP 주소를 포함하도록 자동으로 유지
1apiVersion: "cilium.io/v2" 2kind: CiliumNetworkPolicy 3metadata: 4 name: "service-rule" 5spec: 6 endpointSelector: 7 matchLabels: 8 id: app2 9 egress: 10 - toServices: 11 # Services may be referenced by namespace + name 12 - k8sService: 13 serviceName: myservice 14 namespace: default 15 # Services may be referenced by namespace + label selector 16 - k8sServiceSelector: 17 selector: 18 matchLabels: 19 env: staging 20 namespace: another-namespaceEntity based
- 엔티티는 IP 주소를 알지 못해도 분류할 수 있는 원격 peer를 설명하는데 사용
- 엔드포인트를 제공하는 로컬 호스트에 대한 연결이나 클러스터 외부에 대한 모든 연결이 포함
1# 레이블이 있는 모든 엔드포인트가 env=devkube-apiserver에 액세스하도록 허용 2apiVersion: "cilium.io/v2" 3kind: CiliumNetworkPolicy 4metadata: 5 name: "dev-to-kube-apiserver" 6spec: 7 endpointSelector: 8 matchLabels: 9 env: dev 10 egress: 11 - toEntities: 12 - kube-apiserverNode based
- 엔티티의 확장
- 노드는 고유한 ID를 가질 수 있으며, 이를 사용해 특정 노드에 대한 접금만 허용 혹은 차단
1apiVersion: "cilium.io/v2" 2kind: CiliumNetworkPolicy 3metadata: 4 name: "to-prod-from-control-plane-nodes" 5spec: 6 endpointSelector: 7 matchLabels: 8 env: prod 9 ingress: 10 - fromNodes: 11 - matchLabels: 12 node-role.kubernetes.io/control-plane: ""IP / CIDR based
- 원격 피어가 엔드포인트가 아닌 경우 사용
- 안정적인 IP 혹은 서브넷 할당이 필요해 최후의 수단으로 사용해야함.
1apiVersion: "cilium.io/v2" 2kind: CiliumNetworkPolicy 3metadata: 4 name: "cidr-rule" 5spec: 6 endpointSelector: 7 matchLabels: 8 app: myService 9 egress: 10 - toCIDR: 11 - 20.1.1.1/32 12 - toCIDRSet: 13 - cidr: 10.0.0.0/8 14 except: 15 - 10.96.0.0/12`DNS based
- Cilium이 관리되지 않는 엔드포인트에 사용
- DNS 쿼리를 통해 응답받은 IP 주소로 ‘IP / CIDR Based’와 유사한 방식으로 허용
- 원격 IP 변동이되는 환경에서 더 유용하게 사용할 수 있음.
- 도메인 이름과 IP 주소를 연결하기 위해 Cilium은 DNS 프록시를 사용하여 엔드포인트별 DNS 응답을 가로챔
- Cilium이 DNS 요청을 허용하는 L7 정책이 설정되어 있어야함 (
—enable-l7-proxy=true) - DNS Proxy는 각 Cilium agent에 제공되고, DNS 요청은 Cilium agent Pod의 가용성에 따라 달라짐
- 클러스터 내부 트래픽에 대해서는 ‘Service based’ 및 ‘Endpoints based’ 을 참고
- Cilium이 DNS 요청을 허용하는 L7 정책이 설정되어 있어야함 (
- DNS TTL은 준수
toFQDNs.matchName- 정확히 일치하는 도메인 주소를 입력
- 여러 개의 도메인을 별도의
matchName항목에 포함시킬 수 있음
toFQDNs.matchPattern- Wildcard를 고려해 패턴과 일치하는 도메인 주소를 입력 (a-z, 0-9, ., -)
1apiVersion: "cilium.io/v2" 2kind: CiliumNetworkPolicy 3metadata: 4 name: "to-fqdn" 5spec: 6 endpointSelector: 7 matchLabels: 8 app: test-app 9 egress: 10 - toEndpoints: 11 - matchLabels: 12 "k8s:io.kubernetes.pod.namespace": kube-system 13 "k8s:k8s-app": kube-dns 14 toPorts: 15 - ports: 16 - port: "53" 17 protocol: ANY 18 rules: 19 dns: 20 - matchPattern: "*" 21 - toFQDNs: 22 - matchName: "my-remote-service.com"
[Example] Layer 4 예시
https://docs.cilium.io/en/stable/security/policy/language/#layer-4-examples
- Layer 4는 Layer 3 정책과 함께 또는 독립적으로 지정 가능
- 엔드포인트가 특정 프로토콜(Protocol)을 사용해 특정 포트에서 패킷을 송출, 수신하는 엔드포인트의 기능을 제한
- 엔드포인트에 Layer 4 정책이 지정되지 않는 경우, ICMP를 포함한 모든 Layer 4의 포트 및 프로토콜에서 송수신할 수 있음.
- Layer 4 정책이 지정된 경우, 정책에서 허용하는 연결과 관련된 경우를 제외하고 차단
- Layer 4 정책은 서비스 포트 매핑(Port Mapping)이 적용된 후 포트에 적용
- Layer 4 정책은 필드를 사용해 송, 수신 모두에 지정 가능
[Layer 4] 예시 1 - app: myService 엔드포인트 송신 80(TCP) 포트 제한
1apiVersion: "cilium.io/v2"
2kind: CiliumNetworkPolicy
3metadata:
4 name: "l4-rule"
5spec:
6 endpointSelector:
7 matchLabels:
8 app: myService
9 egress:
10 - toPorts:
11 - ports:
12 - port: "80"
13 protocol: TCP
[Layer 4] 예시 2 - app: myService 엔드포인트에 송신 80 ~ 444(TCP) 포트 제한
1apiVersion: "cilium.io/v2"
2kind: CiliumNetworkPolicy
3metadata:
4 name: "l4-port-range-rule"
5spec:
6 endpointSelector:
7 matchLabels:
8 app: myService
9 egress:
10 - toPorts:
11 - ports:
12 - port: "80"
13 endPort: 444
14 protocol: TCP
[Layer 4] 예시 3 - app: myService 엔드포인트에서 송신 CIDR ‘192.0.2.0/24’ 80(TCP) 포트 제한
1apiVersion: "cilium.io/v2"
2kind: CiliumNetworkPolicy
3metadata:
4 name: "cidr-l4-rule"
5spec:
6 endpointSelector:
7 matchLabels:
8 role: myService
9 egress:
10 - toCIDR:
11 - 192.0.2.0/24
12 toPorts:
13 - ports:
14 - port: "80"
15 protocol: TCP
TLS SNI 제한
- 여러 웹사이트가 동일 서버에 공유 IP 주소로 호스팅되는 경우, TLS 프로토콜의 확장 기능인 서버 이름 표시(SNI)는 클라이언트가 접속하려는 웹사이트에 대한 올바른 SSL 인증서를 수신하도록 보장
- SNI를 사용하면 HTTP 연결이 설정될 때 Handshake 이후가 아닌, TLS Handshake 중 웹사이트의 호스트이름 또는 도메인 이름을 지정
- Cilium Network Policy를 사용해 엔드포인트가 TLS Handshake를 지정된 SNI 목록으로 제한할 수 있음
- SNI 정책은 ‘송신’ 레벨에서 구성되며 일반적으로 포트 정책과 함께 설정됨
- TLS SNI 정책을 설정하려면 L7 Proxy를 활성화 해야함.
1apiVersion: "cilium.io/v2"
2kind: CiliumNetworkPolicy
3metadata:
4 name: "l4-sni-rule"
5spec:
6 endpointSelector:
7 matchLabels:
8 app: myService
9 **egress**:
10 - toPorts:
11 - ports:
12 - port: "443"
13 protocol: TCP
14 **serverNames:** # SNI
15 **- one.one.one.one**
[Example] Layer 7 예시
https://docs.cilium.io/en/stable/security/policy/language/#layer-7-examples
Layer 7 정책 규칙은 Layer 4 예제 규칙을 포함되고 수신 및 송신에 대해 지정 가능
*번외) code level에서도
L7Rules구조체 정의는 l4.go에 포함되고 있다.
L7Rules구조는 프로토콜 별 필드의 열거형을 포함하는 기본 유형이다.1// L7Rules is a union of port level rule types. Mixing of different port 2// level rule types is disallowed, so exactly one of the following must be set. 3// If none are specified, then no additional port level rules are applied. 4type L7Rules struct { 5 // HTTP specific rules. 6 // 7 // +optional 8 HTTP []PortRuleHTTP `json:"http,omitempty"` 9 10 // Kafka-specific rules. 11 // 12 // +optional 13 Kafka []PortRuleKafka `json:"kafka,omitempty"` 14 15 // DNS-specific rules. 16 // 17 // +optional 18 DNS []PortRuleDNS `json:"dns,omitempty"` 19}- 구조체는 공용 구조체로 구현되었고 그래서 포트당 하나의 멤버 필드만 사용할 수 있다.
- 여러
toPorts규칙이 동일한PortProtocol을 선택하여 중복된 엔드포인트 목록을 선택하면, 같은 유형의 Layer 7 규칙이 결합(Overlapping)된다. - 유형이 다를 경우, 정책이 거부
각 멤버는 애플리케이션 프로토콜 규칙 목록으로 구성. 규칙 중 하나라도 일치하면 Layer 7 요청이 허용된다.
규칙이 지정되지 않으면 모든 트래픽이 허용
(Layer 4 > Layer 7) 정책에 계층 4 규칙이 지정되어 있고, 계층 7 규칙이 포함된 유사한 계층 4 규칙도 지정된 경우, 후자 규칙의 계층 7 부분은 효과 없음
레이어 3 및 레이어 4 정책과 달리 레이어 7 규칙을 위반해도 패킷 삭제가 발생하지 않습니다. 대신 가능한 경우 애플리케이션 프로토콜별 액세스 거부 메시지가 생성되어 반환됩니다(예: 정책을 위반하는 HTTP 요청에 대해 HTTP 403 액세스 거부가 다시 전송되거나 DNS 요청에 대해 DNS REFUSED 응답이 전송됨)
레이어 7 정책은 노드 로컬 Envoy 인스턴스를 통해 트래픽을 프록시하며, 이 인스턴스는 DaemonSet으로 배포되거나 에이전트 Pod에 내장됩니다.
HTTP
- Path
- 요청 경로와 일치하는 확장된 POSIX 정규 표현식
- 현재는 RFC 3986에 정의된 URL의 기존 “경로” 부분에서 허용되지 않는 문자를 포함할 수 있음
- 경로는
/로 시작해야함. 생략되거나 비어 있으면 모든 경로가 모두 허용
- Method
- 요청의 메서드와 일치하는 확장된 POSIX 정규식입니다(예
GET: ,POST,PUT,PATCH, …) - 생략한 경우 모든 메서드 허용
- 요청의 메서드와 일치하는 확장된 POSIX 정규식입니다(예
- Host
- 요청의 호스트 헤더와 일치하는 확장된 POSIX 정규식입니다(예:
foo.com) - 생략한 경우 호스트 헤더 값은 무시
- 요청의 호스트 헤더와 일치하는 확장된 POSIX 정규식입니다(예:
- Headers
- 요청에 있어야 하는 HTTP 헤더 목록
- 생략되거나 비어 있는 경우 헤더에 관계없이 요청이 허용
- 고급 헤더 매칭
HeaderMatches는 존재해야 하고 주어진 값과 일치해야 하는 HTTP 헤더 목록
[Layer 7] HTTP 예시 1 - /public Path 허용
1apiVersion: "cilium.io/v2"
2kind: CiliumNetworkPolicy
3metadata:
4 name: "rule1"
5spec:
6 description: "Allow HTTP GET /public from env=prod to app=service"
7 endpointSelector:
8 matchLabels:
9 app: service
10 ingress:
11 - fromEndpoints:
12 - matchLabels:
13 env: prod
14 toPorts:
15 - ports:
16 - port: "80"
17 protocol: TCP
18 **rules:
19 http:
20 - method: "GET"
21 path: "/public"**
[Layer 7] HTTP 예시 2 - Header(**X-My-Header: true)**가 설정된 경우 모든 GET /path1 및 PUT /path2 허용
1apiVersion: "cilium.io/v2"
2kind: CiliumNetworkPolicy
3metadata:
4 name: "l7-rule"
5spec:
6 endpointSelector:
7 matchLabels:
8 app: myService
9 ingress:
10 - toPorts:
11 - ports:
12 - port: '80'
13 protocol: TCP
14 **rules:
15 http:
16 - method: GET
17 path: "/path1$"
18 - method: PUT
19 path: "/path2$"
20 headers:
21 - 'X-My-Header: true'**
DNS Policy and IP Discovery
특정 DNS 쿼리 이름 또는 이름 패턴을 허용하거나 허용하지 않을 수 있습니다
DNS Proxy를 통해서 적용되며, L3 DNS based
toFQDNs규칙을 채우는데 사용되는 IP 주소를 수집하는부분에도 사용Layer 7 DNS 정책은 다른 Layer 3 규칙 없이도 적용할 수 있지만, Layer 7 규칙 (Layer 3 및 Layer 4 구성 요소 포함)이 있으면 다른 트래픽도 차단
[주의] kubernetes에서 DNS 정책을 적용할 때,
service.namespace.svc.cluster.local에 대한 쿼리는 명시적으로 허용해야한다. (matchPattern: *.*.svc.cluster.local.)[주의] 마찬가지로 FQDN을 완성하기 위해 DNS 검색 목록에 의존하는 쿼리는 전체가 허용해야한다.
- Service Name으로 쿼리하는 경우,
<Service Name>.namespace.svc.cluster.local.로 허용
- Service Name으로 쿼리하는 경우,
DNS 정책 적용
matchName: 정확히 일치하는 도메인에 대한 쿼리 허용matchPattern: 와일드카드를 고려하여 패턴과 일치하는 도메인에 대한 쿼리를 허용

1# org 2# namespace가 kube-system 이고, k8s-app이 kube-dns인 Endpoint 3# 4apiVersion: cilium.io/v2 5kind: CiliumNetworkPolicy 6metadata: 7 name: "tofqdn-dns-visibility" 8spec: 9 endpointSelector: 10 matchLabels: 11 any:org: alliance 12 egress: 13 - toEndpoints: 14 - matchLabels: 15 "k8s:io.kubernetes.pod.namespace": kube-system 16 "k8s:k8s-app": kube-dns 17 toPorts: 18 - ports: 19 - port: "53" 20 protocol: ANY 21 rules: 22 dns: 23 - matchName: "cilium.io" 24 - matchPattern: "*.cilium.io" 25 - matchPattern: "*.api.cilium.io" 26 27 - toFQDNs: 28 - matchName: "cilium.io" 29 - matchName: "sub.cilium.io" 30 - matchName: "service1.api.cilium.io" 31 - matchPattern: "special*service.api.cilium.io" 32 toPorts: 33 - ports: 34 - port: "80" 35 protocol: TCP
Obtaining DNS Data for use by toFQDNs
- Cilium Proxy를 거쳐 DNS 요청을 가로채 IP를 얻음.
- 가로채기 자체는 DNS 요청을 제어하는 별도의 정책 규칙이므로 별도로 지정해야함.
- DNS 응답은 TTL을 준수하여 Cilium Agent 내에 Caching됨.
- 애플리케이션에 대한 가로채기된 DNS 응답의 IP만 Cilium 정책 규칙에서 허용됨.
- 특정 도메인 이름에 대해 Cilium 인스턴스가 관리하는 모든 Pod에 대한 응답의 IP 정책에 따라 허용됨 (TTL 준수)
- DNS 프록시는 Wildcard L7 DNS
matchPattern규칙에 의해 허용된 응답에서 IPs를toFQDNs규칙에서 사용하기 위해 허용하는 유일한 방법입니다.

1# 다음 예제는 DNS 요청을 차단하지 않고 가로채기 방식으로 DNS 데이터를 수집합니다.
2# sub.cilium.io 및 모든 하위 도메인에 대한 L3 연결을 cilium.io허용 sub.cilium.io 합니다
3apiVersion: cilium.io/v2
4kind: CiliumNetworkPolicy
5metadata:
6 name: "tofqdn-dns-visibility"
7spec:
8 endpointSelector:
9 matchLabels:
10 any:org: alliance
11 egress:
12 - toEndpoints:
13 - matchLabels:
14 "k8s:io.kubernetes.pod.namespace": kube-system
15 "k8s:k8s-app": kube-dns
16 toPorts:
17 - ports:
18 - port: "53"
19 protocol: ANY
20 rules:
21 dns:
22 - matchPattern: "*"
23 - toFQDNs:
24 - matchName: "cilium.io"
25 - matchName: "sub.cilium.io"
26 - matchPattern: "*.sub.cilium.io"
Lab
[Lab1] Locking Down External Access with DNS-Based Policies
Cilium의 DNS 기반 정책은 DNS-IP 매핑 추적과 같은 복잡한 측면을 관리하는 동시에 액세스 제어를 쉽게 지정할 수 있는 메커니즘을 제공
아래 Lab에서는 다음 사항에 대해서 확인해봄
- DNS 기반 정책을 사용하여 클러스터 외부 서비스에 대한 이탈 액세스 제어
- 패턴(또는 와일드카드)을 사용하여 DNS 도메인 하위 집합을 허용 목록에 추가
- 외부 서비스 접근 제한을 위한 DNS, 포트 및 L7 규칙 결합
Demo Application
“Empire의 mediabot pod가 Empire의 git 저장소 관리를 위해 GitHub에 접근해야 하는 간단한 시나리오”
= Pod는 다른 외부 서비스에 접근은 할 수 있어야함.
1# hubble ui 에 pod name 표기를 위해 app labels 추가 >> 빼고 배포해보고 차이점을 확인해보자. 2cat << EOF | k apply -f - 3apiVersion: v1 4kind: Pod 5metadata: 6 name: mediabot 7 labels: 8 org: empire 9 class: mediabot 10 **app: mediabot** 11spec: 12 containers: 13 - name: mediabot 14 image: quay.io/cilium/**json-mock**:v1.3.8@sha256:5aad04835eda9025fe4561ad31be77fd55309af8158ca8663a72f6abb78c2603 15EOF 16 17kubectl wait pod/mediabot --for=condition=Ready 18 19# 확인 20kubectl exec -it -n kube-system ds/cilium -- cilium identity list 21 22# Hubble UI 23cilium hubble port-forward & 24open http://192.168.10.100:30003 # Open hubble UI 25 26# 외부 통신 확인 27**kubectl exec mediabot -- curl -I -s https://api.github.com | head -1** 28HTTP/2 200 29**kubectl exec mediabot -- curl -I -s --max-time 5 https://support.github.com | head -1** 30HTTP/2 302
[DNS Egress Policy] mediabot Pod가 api.github.com 에만 액세스하도록 허용
1cat << EOF | kubectl apply -f -
2apiVersion: "cilium.io/v2"
3kind: CiliumNetworkPolicy
4metadata:
5 name: "fqdn"
6spec:
7 endpointSelector:
8 matchLabels:
9 org: empire
10 class: mediabot
11 egress:
12 - toFQDNs:
13 - matchName: "api.github.com"
14 - toEndpoints:
15 - matchLabels:
16 "k8s:io.kubernetes.pod.namespace": kube-system
17 "k8s:k8s-app": kube-dns
18 toPorts:
19 - ports:
20 - port: "53"
21 protocol: ANY
22 rules:
23 dns:
24 - matchPattern: "*"
25EOF
26
27# 확인
28kubectl get cnp
29NAME AGE VALID
30fqdn 8s True
CNP 설정이후 Cilium 설정에서는 다음 값을 확인할 수 있다.
- Cilium Network Policy의 LabelSelector 정보
- Cilium Config에서는
dnsproxy-enable-transparent-mode: true설정으로 DNS Query에 대해서 Cilium이 가로채서 DNS Proxying 설정됨.
1# Cilium Policy Selector 확인
2kubectl exec -it -n kube-system ds/cilium -- cilium policy selectors
3SELECTOR LABELS USERS IDENTITIES
4&LabelSelector{MatchLabels:map[string]string{any.class: mediabot,any.org: empire,k8s.io.kubernetes.pod.namespace: default,},MatchExpressions:[]LabelSelectorRequirement{},} default/fqdn 1 6390
5
6# Cilium Config 확인
7cilium config view | grep -i dns
8dnsproxy-enable-transparent-mode true
9dnsproxy-socket-linger-timeout 10
10hubble-metrics dns drop tcp flow port-distribution icmp httpV2:exemplars=true;labelsContext=source_ip,source_namespace,source_workload,destination_ip,destination_namespace,destination_workload,traffic_direction
11tofqdns-dns-reject-response-code refused
12tofqdns-enable-dns-compression true
13tofqdns-endpoint-max-ip-per-hostname 1000
14tofqdns-idle-connection-grace-period 0s
15tofqdns-max-deferred-connection-deletes 10000
16tofqdns-preallocate-identities true
17tofqdns-proxy-response-max-delay 100ms
이전 테스트 처럼 api.github.com과 support.github.com에 curl 요청을 보내보면, api.github.com 요청만 정상적으로 forwarding된걸 확인할 수 있다.
1# forwarded
2kubectl exec mediabot -- curl -I -s https://api.github.com | head -1
3HTTP/2 200
4
5# dropped
6kubectl exec mediabot -- curl -I -s --max-time 5 https://support.github.com | head -1
7command terminated with exit code 28


그리고 추가적으로 Cilium Agent 내에서 go로 구현된 Lightweight Proxy가 DNS 쿼리 / 응답에 대한 가로채기를 진행하고 이를 hubble observe 를 통해서 흐름 로그를 확인할 수 있다.
- mediabot Pod에서 보낸 DNS 요청에 coredns가 api.github.com에 대한 DNS Query를 응답
- 아래의 값에서
DNS Answer TTL: 4294967295으로 과도하게 크게 설정된 TTL 값은 ‘캐싱 정책’에 의해 설정된것보다는 표준 DNS TTL 응답이 아닌 특별한 응답(없는 도메인 등)을 해석할 수 있다.
1cilium hubble port-forward&
2hubble observe --pod mediabot
3
4#
5Sep 7 14:19:15.615: default/mediabot:44740 (ID:6390) <- kube-system/coredns-674b8bbfcf-np4mw:53 (ID:19722) dns-response proxy FORWARDED (DNS Answer TTL: 4294967295 (Proxy api.github.com. AAAA))
6...
7Sep 7 14:19:15.618: default/mediabot:44740 (ID:6390) <- kube-system/coredns-674b8bbfcf-np4mw:53 (ID:19722) dns-response proxy FORWARDED (DNS Answer "20.200.245.245" TTL: 30 (Proxy api.github.com. A))
8...
9Sep 7 14:19:15.621: default/mediabot:44066 (ID:6390) -> api.github.com:443 (ID:16777217) policy-verdict:L3-Only EGRESS ALLOWED (TCP Flags: SYN)
Cilium에서 cilium fqdn cache list 라는 명령어로 FQDN(Full Qulified Domain Name)에 대한 Caching 정보를 확인할 수 있는데, 위 요청에서는 다음과 같이 확인되었다.
[사전 설정] Cilium Pod에 단축키 매핑
1## cilium 파드 이름 2export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-ctr -o jsonpath='{.items[0].metadata.name}') 3export CILIUMPOD1=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w1 -o jsonpath='{.items[0].metadata.name}') 4export CILIUMPOD2=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w2 -o jsonpath='{.items[0].metadata.name}') 5echo $CILIUMPOD0 $CILIUMPOD1 $CILIUMPOD2 6 7## 단축키(alias) 지정 8alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium" 9alias c1="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- cilium" 10alias c2="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- cilium"
1# 현재 mediabot이 실행중인 노드는 k8s-w1이다.
2k get pods -o wide
3NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
4mediabot 1/1 Running 0 78m 172.20.1.148 k8s-w1 <none> <none>
5
6# k8s-w1에서는 FQDN Cache List가 조회돔
7c1 fqdn cache list
8Endpoint Source FQDN TTL ExpirationTime IPs
92809 connection support.github.com. 0 2025-09-07T15:10:12.302Z 185.199.108.133
102809 connection support.github.com. 0 2025-09-07T15:10:12.302Z 185.199.109.133
112809 connection support.github.com. 0 2025-09-07T15:10:12.302Z 185.199.110.133
122809 connection support.github.com. 0 2025-09-07T15:10:12.302Z 185.199.111.133
132809 connection api.github.com. 0 2025-09-07T15:10:12.302Z 20.200.245.245
14
15# k8s-ctr, k8s-w2에서는 조회되지 않음
16c0 fqdn cache list
17c2 fqdn cache list