스터디/Cilium

[Cilium] Cilium Observability - Hubble 설치 및 CiliumNetworkPolicy 적용

안녕유지 2025. 7. 27. 05:50
Cloudnet Cilium 2주차 스터디를 진행하며 정리한 글입니다.


이번 포스팅에서는 Cilium Hubble에 대해 이해하는 포스팅을 작성하겠습니다.

Hubble

https://docs.cilium.io/en/stable/overview/intro/#intro

 

Cilium에서 Hubble은 네트워크 및 보안 가시성을 제공하는 분산 추적/관찰 도구입니다.

Cilium은 eBPF를 사용해 클러스터 내부 네트워크 흐름을 커널 수준에서 추적하고, Hubble은 이 정보를 수집, 분석, 시각화하는 역할을 합니다.

 

Hubble 특징

1. 서비스 간 통신 및 의존성 시각화

  • 어떤 서비스가 어떤 서비스와 통신하는지 확인
  • 서비스 간 통신 빈도 파악
  • 서비스 간 의존성 그래프 확인
  • 어떤 HTTP 요청이 오가는지 파악
  • 어떤 Kafka 토픽을 읽고 쓰는지 확인 (Cilium Kafka-aware 정책 설정 시)

2. 네트워크 모니터링 및 알림

  • 네트워크 통신 실패 여부 파악
  • 실패 원인 분석 (DNS 문제인지, 애플리케이션 문제인지, TCP 문제인지 등)
  • Layer 4 (TCP) 또는 Layer 7 (HTTP) 문제 식별
  • 최근 5분 간 DNS 해석 오류가 있었던 서비스 확인
  • TCP 연결이 끊긴 서비스, 연결 타임아웃 발생 서비스 식별
  • 응답 없는 TCP SYN 요청 비율 측정

3. 애플리케이션 수준 모니터링

  • HTTP 4xx, 5xx 응답률 측정 (특정 서비스 또는 클러스터 전체)
  • HTTP 요청-응답 간 95th / 99th percentile 지연 시간 측정
  • 가장 성능이 나쁜 서비스 식별
  • 서비스 간 HTTP 지연 시간 측정

4. 보안 관찰 기능 (Security Observability)

  • 네트워크 정책에 의해 차단된 연결 기록 추적
  • 클러스터 외부에서 접근한 서비스 식별
  • 특정 DNS 이름을 조회한 서비스 확인

 

Hubble 구성 요소

  • hubble-relay: 여러 노드에서 수집된 흐름을 중앙에서 집계해주는 gRPC 서버
  • hubble-ui: 시각화 대시보드 
  • hubble-cli: CLI로 흐름 확인
  • cilium-agent: 기본적으로 각 노드에서 흐름 이벤트 생성

 

Hubble 설치

현재 cilium 설치 시, hubble을 활성화시키지 않아서, cilium helm을 업그레이드 하며 hubble을 활성화하여 설치하도록 하겠습니다.

설치 했을 때, 기존에 생성되지 않았던 Hubble과 OpenMetrics 기반 메트릭이 활성화되어 있습니다. 

hubble-relay, hubble-server, cilium-ca 관련 TLS 인증서들도 생성 되었습니다. 

hubble-peer 서비스가 클러스터 IP 타입으로 생성되었고, 엔드포인트에 노드 3개가 각각 :4244로 Hubble gRPC 서버로 연결된 것을 확인할 수 있습니다. 

hubble-peer는 ClusterIP 서비스로, Cilium이 각 노드의 Hubble Server를 4244 포트로 서비스 형태로 묶은 것입니다. hubble-relay는 hubble-peer를 통해 각 노드의 Cilium으로 연결합니다.

root@k8s-ctr:~# helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
# 위의 설정만 적용해도 되나, 아래 설정은 편의를 위해 설정
--set hubble.ui.service.type=NodePort \
--set hubble.ui.service.nodePort=31234 \
--set hubble.export.static.enabled=true \
--set hubble.export.static.filePath=/var/run/cilium/hubble/events.log \
--set prometheus.enabled=true \
--set operator.prometheus.enabled=true \
--set hubble.metrics.enableOpenMetrics=true \
--set hubble.metrics.enabled="{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}"
Release "cilium" has been upgraded. Happy Helming!
NAME: cilium
LAST DEPLOYED: Sun Jul 27 05:39:42 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 2
TEST SUITE: None
NOTES:
You have successfully installed Cilium with Hubble Relay and Hubble UI.

Your release version is 1.17.6.

For any further help, visit https://docs.cilium.io/en/v1.17/gettinghelp

root@k8s-ctr:~# cilium config view | grep -i hubble
enable-hubble                                     true
enable-hubble-open-metrics                        true

root@k8s-ctr:~# kubectl get secret -n kube-system | grep -iE 'cilium-ca|hubble'
cilium-ca                      Opaque                          2      105s
hubble-relay-client-certs      kubernetes.io/tls               3      105s
hubble-server-certs            kubernetes.io/tls               3      104s


root@k8s-ctr:~# ss -tnlp | grep -iE 'cilium|hubble' | tee after.txt
LISTEN 0      4096        127.0.0.1:9234       0.0.0.0:*    users:(("cilium-operator",pid=5427,fd=9))
LISTEN 0      4096        127.0.0.1:34841      0.0.0.0:*    users:(("cilium-agent",pid=7363,fd=53))
LISTEN 0      4096        127.0.0.1:9891       0.0.0.0:*    users:(("cilium-operator",pid=5427,fd=6))
LISTEN 0      4096        127.0.0.1:9890       0.0.0.0:*    users:(("cilium-agent",pid=7363,fd=6))
LISTEN 0      4096        127.0.0.1:9879       0.0.0.0:*    users:(("cilium-agent",pid=7363,fd=59))
LISTEN 0      4096        127.0.0.1:9878       0.0.0.0:*    users:(("cilium-envoy",pid=5933,fd=27))
LISTEN 0      4096        127.0.0.1:9878       0.0.0.0:*    users:(("cilium-envoy",pid=5933,fd=26))
LISTEN 0      4096          0.0.0.0:9964       0.0.0.0:*    users:(("cilium-envoy",pid=5933,fd=25))
LISTEN 0      4096          0.0.0.0:9964       0.0.0.0:*    users:(("cilium-envoy",pid=5933,fd=24))
LISTEN 0      4096                *:4244             *:*    users:(("cilium-agent",pid=7363,fd=51)) # 추가됨
LISTEN 0      4096                *:9965             *:*    users:(("cilium-agent",pid=7363,fd=45)) # 추가됨
LISTEN 0      4096                *:9962             *:*    users:(("cilium-agent",pid=7363,fd=7)) # 추가됨
LISTEN 0      4096                *:9963             *:*    users:(("cilium-operator",pid=5427,fd=7))


root@k8s-ctr:~# kubectl get svc,ep -n kube-system hubble-peer
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
NAME                  TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/hubble-peer   ClusterIP   10.96.76.2   <none>        443/TCP   5m34s

NAME                    ENDPOINTS                                                     AGE
endpoints/hubble-peer   192.168.10.100:4244,192.168.10.101:4244,192.168.10.102:4244   5m34s


# hubble ui 웹 접속 주소 확인
root@k8s-ctr:~# NODEIP=$(ip -4 addr show eth1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
root@k8s-ctr:~# echo -e "http://$NODEIP:31234"
http://192.168.10.100:31234

 

 

 

Hubble 클라이언트를 설치하겠습니다.

root@k8s-ctr:~# HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
HUBBLE_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then HUBBLE_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}
sudo tar xzvfC hubble-linux-${HUBBLE_ARCH}.tar.gz /usr/local/bin

root@k8s-ctr:~# which hubble
/usr/local/bin/hubble

# hubble cli를 사용하려면 hubble relay 주소를 지정해야함
root@k8s-ctr:~# hubble status
failed getting status: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:4245: connect: connection refused"

# 포트포워딩
root@k8s-ctr:~# cilium hubble port-forward&
[1] 8366
root@k8s-ctr:~# ℹ️  Hubble Relay is available at 127.0.0.1:4245

root@k8s-ctr:~# ss -tnlp | grep 4245
LISTEN 0      4096        127.0.0.1:4245       0.0.0.0:*    users:(("cilium",pid=8366,fd=7))
LISTEN 0      4096            [::1]:4245          [::]:*    users:(("cilium",pid=8366,fd=8))

root@k8s-ctr:~# hubble status
Healthcheck (via localhost:4245): Ok
Current/Max Flows: 11,080/12,285 (90.19%)
Flows/s: 38.00
Connected Nodes: 3/3

root@k8s-ctr:~# hubble config view
basic-auth-password: ""
basic-auth-username: ""
config: /root/.config/hubble/config.yaml
debug: false
kube-context: ""
kube-namespace: kube-system
kubeconfig: ""
port-forward: false
port-forward-port: "4245"
request-timeout: 12s
server: localhost:4245
timeout: 5s
tls: false
tls-allow-insecure: false
tls-ca-cert-files: []
tls-client-cert-file: ""
tls-client-key-file: ""
tls-server-name: ""

 

hubble observe 명령어를 통해 트래픽을 확인해보겠습니다.

root@k8s-ctr:~# hubble observe
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) -> kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-endpoint FORWARDED (TCP Flags: ACK)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <- kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-stack FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:10.080: 10.0.2.15:60422 (host) -> kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-endpoint FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:10.081: 10.0.2.15:60422 (host) -> kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-endpoint FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:10.081: 10.0.2.15:60422 (host) <- kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-stack FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:10.081: 10.0.2.15:60422 (host) -> kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-endpoint FORWARDED (TCP Flags: ACK)
Jul 26 20:55:12.634: 192.168.10.102:49300 (host) -> 192.168.10.100:6443 (kube-apiserver) to-network FORWARDED (TCP Flags: ACK)
Jul 26 20:55:12.919: kube-system/hubble-ui-76d4965bb6-fk6kx:42022 (ID:49608) -> kube-system/hubble-relay-5dcd46f5c-2lfmr:4245 (ID:20599) to-endpoint FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:12.920: kube-system/hubble-ui-76d4965bb6-fk6kx:42022 (ID:49608) <- kube-system/hubble-relay-5dcd46f5c-2lfmr:4245 (ID:20599) to-network FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:14.420: 10.0.2.15:56230 (host) -> kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-endpoint FORWARDED (TCP Flags: ACK)
Jul 26 20:55:14.420: 10.0.2.15:56230 (host) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.420: 10.0.2.15:56230 (host) -> kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-endpoint FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:14.421: 10.0.2.15:56230 (host) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.421: 10.0.2.15:56230 (host) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.421: 10.0.2.15:56230 (host) <- kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-stack FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:14.421: 10.0.2.15:56230 (host) <- kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-stack FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:14.421: 10.0.2.15:56230 (host) -> kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-endpoint FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:14.862: 127.0.0.1:8090 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.862: 127.0.0.1:8090 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.862: 127.0.0.1:37496 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:37496 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:37496 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:37496 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:37496 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:8090 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:8090 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:15.031: kube-system/hubble-relay-5dcd46f5c-2lfmr:40558 (ID:20599) <- 192.168.10.102:4244 (host) to-endpoint FORWARDED (TCP Flags: ACK)
Jul 26 20:55:15.031: kube-system/hubble-relay-5dcd46f5c-2lfmr:40558 (ID:20599) -> 192.168.10.102:4244 (host) to-stack FORWARDED (TCP Flags: ACK)
Jul 26 20:55:15.475: 192.168.10.101:38498 (host) -> 192.168.10.100:6443 (kube-apiserver) to-network FORWARDED (TCP Flags: ACK)
Jul 26 20:55:15.911: 192.168.10.100:56393 (kube-apiserver) -> kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-endpoint FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:15.911: 192.168.10.100:56393 (kube-apiserver) <- kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-network FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:16.243: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.243: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.243: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.489: 127.0.0.1:42030 (world) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.489: 127.0.0.1:42030 (world) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.489: 127.0.0.1:42030 (world) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.489: 127.0.0.1:42030 (world) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)

 

아래 Demo 어플리케이션 배포 전, 단축키를 지정하겠습니다.

# cilium 파드 이름
export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-ctr -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD1=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w1  -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD2=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w2  -o jsonpath='{.items[0].metadata.name}')
echo $CILIUMPOD0 $CILIUMPOD1 $CILIUMPOD2

# 단축키(alias) 지정
alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium"
alias c1="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- cilium"
alias c2="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- cilium"

alias c0bpf="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- bpftool"
alias c1bpf="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- bpftool"
alias c2bpf="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- bpftool"

 

 

Hubble 확인을 위한 Demo 어플리케이션 배포

스타워즈에서 영감을 받은 예제에서는 deathstar, tiefighter, xwing의 세 가지 마이크로서비스 애플리케이션이 있습니다.

deathstar는 포트 80에서 HTTP 웹서비스를 실행하며, 이 서비스는 두 개의 포드 복제본에 걸쳐 deathstar에 대한 요청을 로드 밸런싱하는 Kubernetes 서비스로 노출됩니다.

deathstar 서비스는 제국의 우주선에 착륙 서비스를 제공하여 착륙 포트를 요청할 수 있도록 합니다.

tiefighter 포드는 일반적인 제국 선박의 착륙 요청 클라이언트 서비스를 나타내며, xwing은 동맹 선박의 유사한 서비스를 나타냅니다.

deathstar 착륙 서비스에 대한 접근 제어를 위한 다양한 보안 정책을 테스트할 수 있도록 존재합니다.

 

https://docs.cilium.io/en/stable/gettingstarted/demo/

 

root@k8s-ctr:~# kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.17.6/examples/minikube/http-sw-app.yaml
service/deathstar created
deployment.apps/deathstar created
pod/tiefighter created
pod/xwing created

root@k8s-ctr:~# kubectl get pod --show-labels
NAME                        READY   STATUS              RESTARTS   AGE   LABELS
deathstar-8c4c77fb7-bpd5j   0/1     ContainerCreating   0          11s   app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=8c4c77fb7
deathstar-8c4c77fb7-dlgkl   1/1     Running             0          11s   app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=8c4c77fb7
tiefighter                  0/1     ContainerCreating   0          12s   app.kubernetes.io/name=tiefighter,class=tiefighter,org=empire
xwing                       0/1     ContainerCreating   0          12s   app.kubernetes.io/name=xwing,class=xwing,org=alliance

# 자기 자신의 노드에 실행되고 있는 엔드포인트만 기록
root@k8s-ctr:~# c0 endpoint list
ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS (source:key[=value])                                                  IPv6   IPv4           STATUS
           ENFORCEMENT        ENFORCEMENT
355        Disabled           Disabled          1          k8s:node-role.kubernetes.io/control-plane                                                          ready
                                                           k8s:node.kubernetes.io/exclude-from-external-load-balancers
                                                           reserved:host
469        Disabled           Disabled          63813      k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system          172.20.0.129   ready
                                                           k8s:io.cilium.k8s.policy.cluster=default
                                                           k8s:io.cilium.k8s.policy.serviceaccount=coredns
                                                           k8s:io.kubernetes.pod.namespace=kube-system
                                                           k8s:k8s-app=kube-dns
2977       Disabled           Disabled          63813      k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system          172.20.0.84    ready
                                                           k8s:io.cilium.k8s.policy.cluster=default
                                                           k8s:io.cilium.k8s.policy.serviceaccount=coredns
                                                           k8s:io.kubernetes.pod.namespace=kube-system
                                                           k8s:k8s-app=kube-dns

 

 

deathstar 서비스의 관점에서 보면, org= empire 라벨이 부착된 선박만 연결하여 착륙을 요청할 수 있습니다. 별도 policy을 시행하지 않기 때문에 Xwing과 타이파이터 모두 착륙을 요청할 수 있습니다.

 

이 때, endpoint와 identity값의 차이를 알아보겠습니다.

endpoint는 Cilium에서 endpoint는 각 Pod 또는 네트워크 엔티티를 대표하는 객체입니다. 

identity는 라벨의 집합에 대해 Cilium이 부여하는 고유한 ID 숫자값입니다. 따라서, 동일한 라벨이 부여된 Pod는 동일한 identity를 가져야 합니다.

(하지만, 나는 왜 deathstar 라벨에 대해 identity가 다르게 나올까?;;;)

root@k8s-ctr:~# c1 endpoint list | grep -iE 'xwing|tiefighter|deathstar'
ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS (source:key[=value])                                                  IPv6   IPv4           STATUS
502        Disabled           Disabled          4101       k8s:app.kubernetes.io/name=xwing                                                    172.20.1.80    ready
                                                           k8s:class=xwing
1974       Disabled           Disabled          2752       k8s:app.kubernetes.io/name=deathstar                                                172.20.1.6     ready
                                                           k8s:class=deathstar                                         
root@k8s-ctr:~# c2 endpoint list | grep -iE 'xwing|tiefighter|deathstar'
ENDPOINT   POLICY (ingress)   POLICY (egress)   IDENTITY   LABELS (source:key[=value])                                                  IPv6   IPv4           STATUS
362        Disabled           Disabled          12397      k8s:app.kubernetes.io/name=deathstar                                                172.20.2.246   ready
                                                           k8s:class=deathstar
2507       Disabled           Disabled          10657      k8s:app.kubernetes.io/name=tiefighter                                               172.20.2.56    ready
                                                           k8s:class=tiefighter

 

 

 

L3, L4 Policy 적용하기

Cilium을 사용할 때는 IP 주소가 아니라 Pod에 붙은 라벨을 기반으로 네트워크 보안 정책을 정의합니다.

예를 들어, deathstar 서비스에는 org=empire 라벨이 붙은 pod만 접근하도록 제한할 수 있습니다. 이처럼 라벨 기반으로 특정 그룹만 통신을 허용하는 정책은 L3/L4 보안 정책이라고 부르며, IP나 포트 수준에서 제어가 가능합니다.

또한 Cilium은 stateful하게 연결을 추적하기 때문에, 정책에서 한 방향의 요청만 허용하면 응답 패킷은 자동으로 허용됩니다. 예를 들어 empire 소속이 deathstar에 요청을 보낼 수 있도록 허용했다면, deathstar의 응답은 별도 규칙 없이도 전달됩니다.

 

endpointSelector로 이 정책이 적용될 대상 pod가 deathstar 서비스이고, 접근을 허용한 pod 조건이 org=empire 라벨이 있고, L4 계층의 TCP 80 포트에 대해서 허용하는 규칙을 적용했습니다.

이 때, tiefighter는 해당 라벨이 존재하여 접근이 가능한 반면, xwing은 해당 라벨이 없어서 접근이 허용되지 않아 drop 된 것을 확인할 수 있습니다. 

# CiliumNetworkPolicy
## CiliumNetworkPolicys는 "endpointSelector"를 사용하여 팟 레이블에서 정책이 적용되는 소스와 목적지를 식별합니다. 
## 아래 정책은 TCP 포트 80에서 레이블(org=empire)이 있는 모든 팟에서 레이블(org=empire, class=deathstar)이 있는 데스스타 팟으로 전송되는 트래픽을 화이트리스트로 작성합니다.
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "rule1"
spec:
  description: "L3-L4 policy to restrict deathstar access to empire ships only"
  endpointSelector:
    matchLabels:
      org: empire
      class: deathstar
  ingress:
  - fromEndpoints:
    - matchLabels:
        org: empire
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
        
        
root@k8s-ctr:~# kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.17.6/examples/minikube/sw_l3_l4_policy.yaml
ciliumnetworkpolicy.cilium.io/rule1 created

root@k8s-ctr:~# kubectl get pod --show-labels
NAME                        READY   STATUS    RESTARTS   AGE   LABELS
deathstar-8c4c77fb7-w7cjr   1/1     Running   0          21m   app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=8c4c77fb7
deathstar-8c4c77fb7-zhrdx   1/1     Running   0          21m   app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=8c4c77fb7
tiefighter                  1/1     Running   0          21m   app.kubernetes.io/name=tiefighter,class=tiefighter,org=empire
xwing                       1/1     Running   0          21m   app.kubernetes.io/name=xwing,class=xwing,org=alliance


root@k8s-ctr:~# kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing --connect-timeout 2
command terminated with exit code 28

root@k8s-ctr:~# hubble observe -f --type drop
Jul 26 21:36:32.679: default/xwing:43520 (ID:4101) <> default/deathstar-8c4c77fb7-zhrdx:80 (ID:2752) Policy denied DROPPED (TCP Flags: SYN)
Jul 26 21:36:33.738: default/xwing:43520 (ID:4101) <> default/deathstar-8c4c77fb7-zhrdx:80 (ID:2752) Policy denied DROPPED (TCP Flags: SYN)

root@k8s-ctr:~# kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed

 

 

L7 Policy 적용하기

Cilium에서는 L7 정책 처리를 위해 Envoy 프록시가 자동으로 사이드카 없이 데몬셋 형태로 배포되며, 이를 통해 HTTP 수준의 정책 필터링을 수행합니다. 실제로는 cilium-envoy라는 데몬이 각 노드에 존재하며, Cilium이 정책에 따라 트래픽을 이 프록시에 전달해 L7 처리를 맡깁니다. 

 

 

앞선 L3/L4 정책에서는 tiefighter와 xwing에게 deathstar 서비스 전체에 대한 접근을 일괄 허용하거나 차단했지만, 실제 운영 환경에서는 이보다 더 세밀한 제어가 필요합니다. 특히 마이크로서비스 환경에서는 각 서비스가 수행해야 하는 HTTP 요청만 허용하고, 나머지는 모두 차단하는 식의 최소 권한 원칙이 중요합니다.

예를 들어, deathstar 서비스가 GET /exhaust-port 외에도 내부 유지보수용 API를 함께 노출하고 있다면, 단순한 L3/L4 제어만으로는 이를 구분해 막을 수 없기 때문에 이런 경우, 요청의 HTTP 메서드나 경로에 따라 허용 여부를 결정하는 L7 정책이 필요합니다.

 

 

# 기존 rule1 정책을 업데이트 해서 사용
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "rule1"
spec:
  description: "L7 policy to restrict access to specific HTTP call"
  endpointSelector:
    matchLabels:
      org: empire
      class: deathstar
  ingress:
  - fromEndpoints:
    - matchLabels:
        org: empire
    toPorts:
    - ports:
      - port: "80"
        protocol: TCP
      rules:
        http:
        - method: "POST"
          path: "/v1/request-landing"
          

root@k8s-ctr:~# kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.17.6/examples/minikube/sw_l3_l4_l7_policy.yaml
ciliumnetworkpolicy.cilium.io/rule1 configured

root@k8s-ctr:~# kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed

# 아까와 다르게 L7 layer엣 HTTP 호출에 대해 자세하게 남는다.
root@k8s-ctr:~# hubble observe -f --pod deathstar --protocol http
Jul 26 21:46:17.054: default/tiefighter:33516 (ID:10657) -> default/deathstar-8c4c77fb7-w7cjr:80 (ID:12397) http-request FORWARDED (HTTP/1.1 POST http://deathstar.default.svc.cluster.local/v1/request-landing)
Jul 26 21:46:17.070: default/tiefighter:33516 (ID:10657) <- default/deathstar-8c4c77fb7-w7cjr:80 (ID:12397) http-response FORWARDED (HTTP/1.1 200 22ms (POST http://deathstar.default.svc.cluster.local/v1/request-landing))