Cilium Study [1기] 8주차 Cilium Security & Tetragon
안녕하세요 이번 게시물에서는 Cilium Security & Tetragon 라는 주제에 대한 정보를 전달 드리고자 합니다.
1. 실습 환경 구성
실습 환경은 가장 기본적인 구성(Master Mode 1 EA, Worker Node 2 EA)으로 진행합니다.
curl.exe -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/8w/Vagrantfile
vagrant up
vargrant
2. Cilium Security 이론
Cilium Security
Cilium에서 제공하는 보안정책은 L3, L4, L7계층의 보안 정책을 적용할 수 있습니다.
L3 계층의 보안 정책은 주로 IP기반 정책이기 떄문에 kubernetes 와는 적합하지 않아 Identity 기반으로 보안 정책을 적용합니다.

Figure 2.1 Cilium L3 정책 예시
정책 적용 시 응답 패킷은 자동으로 허용되는 특징이 있습니다.
기본적으로 Cilium에서는 정책이 적용되지 않는 한 Any Open이 Default입니다.
L7 보안 정책을 수립하는 경우에는 Envoy Proxy를 이용하여 정책을 적용하게 됩니다.
IPSec, WireGuard같은 트래픽의 암호화 기능도 지원을 합니다.
IPSec은 BPF 호스트 라우팅에서는 작동하지 않고 터널당 단일 CPU 코어로 제한된 특징이 있으며, WireGuard 사용 시 VXLAN이나 Geneve와 같이 사용하는 경우 두번 캡슐화 된다는 특징이 있습니다.

Figure 2.2 Node간 통신 시 암호화 과정
Identity
Identity란 Cilum CNI에서 Pod를 생성하게되면 생성 시 연결된 Label 기반으로 생성되는 고유한 값을 말합니다.
EndPoint들이 동일한 Security Relevant Labels 사용 시 동일한 ID를 공유합니다.
# Identity 확인
kubectl get ciliumendpoints.cilium.io -n kube-system
# NAME SECURITY IDENTITY ENDPOINT STATE IPV4 IPV6
# coredns-674b8bbfcf-5ztwh 21925 ready 172.20.0.16
# coredns-674b8bbfcf-nfr9n 21925 ready 172.20.0.176
# hubble-relay-fdd49b976-x8mgm 22042 ready 172.20.0.70
# hubble-ui-655f947f96-jkvnt 14764 ready 172.20.0.251
# metrics-server-5dd7b49d79-crbs5 21898 ready 172.20.0.3
Cilumidentites의 정보를 세부적으로 확인하면 다음과 같이 정보를 확인 할 수 있습니다.
kubectl get ciliumidentities.cilium.io 21925 -o yaml | yq
# {
# "apiVersion": "cilium.io/v2",
# "kind": "CiliumIdentity",
# "metadata": {
# "creationTimestamp": "2025-09-06T07:05:17Z",
# "generation": 1,
# "labels": {
# "io.kubernetes.pod.namespace": "kube-system"
# },
# "name": "21925",
# "resourceVersion": "828",
# "uid": "600d078d-786b-4f41-9a8a-df11295678c0"
# },
# "security-labels": {
# "k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name": "kube-system",
# "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"
# }
# }
Identity는 Pod 나 Container가 시작되면, Container Runtime에서 수신한 이벤트를 기반으로 EndPoint를 생성합니다.
그리고 Label이 변경될 때마다 ID가 재확인이 되고 필요에 따라 자동으로 수정됩니다.
해당 내용을 확인해보기 위해 CoreDNS에 Label을 추가하여 확인해 보도록 하겠습니다.
kubectl label pods -n kube-system -l k8s-app=kube-dns study=8w
kubectl get ciliumendpoints.cilium.io -n kube-system
# NAME SECURITY IDENTITY ENDPOINT STATE IPV4 IPV6
# coredns-674b8bbfcf-5ztwh 21925 waiting-for-identity 172.20.0.16
# coredns-674b8bbfcf-nfr9n 21925 waiting-for-identity 172.20.0.176
# hubble-relay-fdd49b976-x8mgm 22042 ready 172.20.0.70
# hubble-ui-655f947f96-jkvnt 14764 ready 172.20.0.251
# metrics-server-5dd7b49d79-crbs5 21898 ready 172.20.0.3
kubectl get ciliumendpoints.cilium.io -n kube-system
# NAME SECURITY IDENTITY ENDPOINT STATE IPV4 IPV6
# coredns-674b8bbfcf-5ztwh 4308 ready 172.20.0.16
# coredns-674b8bbfcf-nfr9n 4308 ready 172.20.0.176
Cilium은 pod update 이벤트를 watch하므로, labels 변경 시 endpoint가 waiting-for-identity 상태로 전환되어 새로운 identity를 할당받고 Security Labels와 관련된 네트워크 정책도 자동으로 재적용됩니다.
대규모 클러스터에서는 Label을 변경하게 되면 identity 할당이 빈번해져 성능 저하가 발생하기때문에 identity-relevan labels를 제한하는 것을 권장합니다.
Cilium에서 관리하는 모든 엔드포인트에는 ID가 할당되지만 Cilium에서 관리하지 않는 Network EndPoint와 통신을 허용하기위한 특수 ID가 존재합니다.
특수 ID는 reserved 라는 문자열 접두사가 붙습니다.
kubectl exec -it -n kube-system ds/cilium -- cilium identity list
# ID LABELS
# 1 reserved:host
# reserved:kube-apiserver
# 2 reserved:world
# 3 reserved:unmanaged
# 4 reserved:health
# 5 reserved:init
# 6 reserved:remote-node
# ...
Identtiy 관리는 Cilium Agent에서 관리를 했지만 최근에는 Cilum Operator에서 관리하는 방식을 제공하고 있습니다(Beta)
Operator에서 관리를 하게 되면 여러 Agent가 동시에 Identity를 생성시 중앙에서 통제를 할 수 있어 CID중복이 감소할 수 있어 네트워크 정책의 신뢰성과 확장성을 향상 시킬 수 있습니다.
Network Policy
Cilium에서 제공하는 Network Policy는 kubernetes에서 기본적으로 제공하는 Network Policy와 다르게 3~7계층에서 모두 송 수신에 대한 정책을 지원합니다.

Figure 2.3 Cilium vs Kubernetes
클러스터 전체 정책은 CiliumClusterwideNetworkPolicy 으로 적용을 하고 Namespace별로 적용 시에는 CiliumNetworkPolicy 정책을 사용해서 적용합니다.
Network Policy 적용 시 Ingress, Egress, EndPoint, Label과 같은 요소들로 정책을 적용 합니다.
L3 정책을 실제로 적용 해 보겠습니다.
# 테스트를 위한 리소스 생성
apiVersion: v1
kind: Namespace
metadata:
name: cnp-test
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: client
namespace: cnp-test
spec:
replicas: 1
selector:
matchLabels:
app: client
template:
metadata:
labels:
app: client
spec:
containers:
- name: netshoot
image: nicolaka/netshoot:latest
command: ["sleep", "infinity"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
namespace: cnp-test
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
template:
metadata:
labels:
app: httpbin
spec:
containers:
- name: httpbin
image: kennethreitz/httpbin
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
namespace: cnp-test
spec:
selector:
app: httpbin
ports:
- name: http
port: 80
targetPort: 80
테스트 결과를 확실하게 확인하기 위해서 NameSpace 전체에 기본 차단을 적용 하고 L3 정책에서 적용 가능한 범위만 트래픽을 허용하는 방식으로 진행하겠습니다.
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: allow-dns-to-kube-dns
namespace: cnp-test
spec:
endpointSelector:
matchLabels:
app: client
egress:
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
"k8s:k8s-app": kube-dns
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"
EndPoint 기반으로 정책을 적용하는 사례입니다.
app=client에서 appl=httpbin으로 TCP/80만 허용하는 정책입니다.
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: ep-allow-client-to-httpbin
namespace: cnp-test
spec:
endpointSelector:
matchLabels:
app: client
egress:
- toEndpoints:
- matchLabels:
app: httpbin
toPorts:
- ports:
- port: "80"
protocol: TCP
kubectl -n cnp-test exec deploy/client -it -- curl -sS http://httpbin.cnp-test:80/get
# {
# "args": {},
# "headers": {
# "Accept": "*/*",
# "Host": "httpbin.cnp-test",
# "User-Agent": "curl/8.14.1"
# },
# "origin": "172.20.1.176",
# "url": "http://httpbin.cnp-test/get"
# }
kubectl -n cnp-test exec deploy/client -it -- curl -sS -m 3 http://httpbin.cnp-test:81 || echo "blocked as expected"
# curl: (28) Connection timed out after 3001 milliseconds
# command terminated with exit code 28
# blocked as expected
Service 기반으로 정책을 적용하는 사례입니다.
내부 Service 호출 시에는 toService를 사용하며, 이름이나 Label Collector를 이용해서 Service를 참조합니다.
client → httpbin Service 경유 트래픽은 허용되며, 다른 Service/Pod IP로 직접 접근은 차단합니다.
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: svc-egress-to-httpbin
namespace: cnp-test
spec:
endpointSelector:
matchLabels:
app: client
egress:
- toServices:
- k8sService:
serviceName: httpbin
namespace: cnp-test
kubectl -n cnp-test exec deploy/client -it -- curl -sS http://httpbin.cnp-test:80/get
# {
# "args": {},
# "headers": {
# "Accept": "*/*",
# "Host": "httpbin.cnp-test",
# "User-Agent": "curl/8.14.1"
# },
# "origin": "172.20.1.176",
# "url": "http://httpbin.cnp-test/get"
# }
3. Clium Security 실습
Locking Down External Access with DNS-Based Policies
이번 실습을 통해서 다음과 같은 정보를 알아보고자 합니다.
- DNS 기반 정책을 사용하여 클러스터 외부 서비스에 대한 이탈 액세스 제어
- 패턴(또는 와일드카드)을 사용하여 DNS 도메인 하위 집합을 허용 목록에 추가
- 외부 서비스 접근 제한을 위한 DNS, 포트 및 L7 규칙 결합
Empire의 mediabot pod가 Empire의 git 저장소 관리를 위해 GitHub에 접근해야 하는 간단한 시나리오 입니다.
cat << EOF > dns-sw-app.yaml
apiVersion: v1
kind: Pod
metadata:
name: mediabot
labels:
org: empire
class: mediabot
app: mediabot
spec:
containers:
- name: mediabot
image: quay.io/cilium/json-mock:v1.3.8@sha256:5aad04835eda9025fe4561ad31be77fd55309af8158ca8663a72f6abb78c2603
EOF
kubectl apply -f dns-sw-app.yaml
kubectl exec mediabot -- curl -I -s https://api.github.com | head -1
# HTTP/2 200

Figure 3.1 Hubble UI을 이용한 시나리오 확인
mediabot Pod가 api.github.com에만 액세스하도록 허용해보겠습니다.
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "fqdn"
spec:
endpointSelector:
matchLabels:
org: empire
class: mediabot
egress:
- toFQDNs:
- matchName: "api.github.com"
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
"k8s:k8s-app": kube-dns
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"
EOF
cilium config view | grep -i dns
# dnsproxy-enable-transparent-mode true
# dnsproxy-socket-linger-timeout 10
# hubble-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
kubectl exec mediabot -- curl -I -s --max-time 5 https://support.github.com | head -1
# command terminated with exit code 28
Network Policy가 적용된 경우, 목적지의 FQDN이 표현되는 것을 Hubble UI를 통해 확인할 수 있습니다.


Figure 3.2 Hubble UI을 이용한 Network Policy 확인
hubble로 확인 시 lightweight proxy가 DNS 쿼리 및 응답을 가로 채는 것을 알 수 있습니다.
cilium hubble port-forward&
hubble observe --pod mediabot
# Sep 6 10:35:11.979: default/mediabot (ID:3024) <> kube-system/kube-dns:53 (world) pre-xlate-fwd TRACED (UDP)
# Sep 6 10:35:11.979: default/mediabot (ID:3024) <> kube-system/coredns-674b8bbfcf-nfr9n:53 (ID:4308) post-xlate-fwd TRANSLATED (UDP)
# Sep 6 10:35:11.979: default/mediabot:57255 (ID:3024) -> kube-system/coredns-674b8bbfcf-nfr9n:53 (ID:4308) policy-verdict:L3-L4 EGRESS ALLOWED (UDP)
# Sep 6 10:35:11.979: default/mediabot:57255 (ID:3024) -> kube-system/coredns-674b8bbfcf-nfr9n:53 (ID:4308) to-proxy FORWARDED (UDP)
# Sep 6 10:35:11.987: default/mediabot:57255 (ID:3024) <> 192.168.10.102 (host) pre-xlate-rev TRACED (UDP)
# Sep 6 10:35:11.989: default/mediabot:57255 (ID:3024) -> kube-system/coredns-674b8bbfcf-nfr9n:53 (ID:4308) to-endpoint FORWARDED (UDP)
# Sep 6 10:35:11.989: default/mediabot:57255 (ID:3024) <> 192.168.10.102 (host) pre-xlate-rev TRACED (UDP)
이번에는 모든 GitHub 하위 도메인(예: 패턴)에 액세스하는 정책 실습을 진행해보겠습니다.
## 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"
kubectl delete cnp fqdn
c0 fqdn cache clean -f
c1 fqdn cache clean -f
c2 fqdn cache clean -f
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.18.1/examples/kubernetes-dns/dns-pattern.yaml
# *.github.com 통신 확인
kubectl exec mediabot -- curl -I -s https://support.github.com | head -1
# HTTP/2 302
kubectl exec mediabot -- curl -I -s --max-time 5 https://github.com | head -1
# command terminated with exit code 28
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "fqdn"
spec:
endpointSelector:
matchLabels:
org: empire
class: mediabot
egress:
- toFQDNs:
- matchName: "*.github.com"
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
"k8s:k8s-app": kube-dns
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"

Figure 3.3 Hubble UI을 이용한 Network Policy 확인(2)
마지막으로 DNS + Port 조합으로도 정책을 적용할 수 있습니다.
kubectl delete cnp fqdn
c0 fqdn cache clean -f
c1 fqdn cache clean -f
c2 fqdn cache clean -f
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.18.1/examples/kubernetes-dns/dns-port.yaml
# HTTP/HTTPS 통신 확인
kubectl exec mediabot -- curl -I -s https://support.github.com | head -1
# HTTP/2 302
kubectl exec mediabot -- curl -I -s --max-time 5 http://support.github.com | head -1
# command terminated with exit code 28
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "fqdn"
spec:
endpointSelector:
matchLabels:
org: empire
class: mediabot
egress:
- toFQDNs:
- matchPattern: "*.github.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
"k8s:k8s-app": kube-dns
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"
WireGuard 설정 및 실습 : 터널 모드는 두 번 캡슐화 확인
해당 실습을 진행하기 전 실습 Application을 먼저 배포하겠습니다.
실습 시 기본적으로 Linux Kernel 5.6 이상 버전이 필요합니다.
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: webpod
spec:
replicas: 2
selector:
matchLabels:
app: webpod
template:
metadata:
labels:
app: webpod
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- sample-app
topologyKey: "kubernetes.io/hostname"
containers:
- name: webpod
image: traefik/whoami
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: webpod
labels:
app: webpod
spec:
selector:
app: webpod
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
EOF
# k8s-ctr 노드에 curl-pod 파드 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: curl-pod
labels:
app: curl
spec:
nodeName: k8s-ctr
containers:
- name: curl
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
```
```bash
# 설정
helm upgrade cilium cilium/cilium --version 1.18.1 --namespace kube-system --reuse-values \
--set encryption.enabled=true --set encryption.type=wireguard
# 재시작
kubectl -n kube-system rollout restart ds/cilium
# 설치 확인
cilium config view | grep -i wireguard
# enable-wireguard true
# wireguard-persistent-keepalive 0s
# k8s-ctr Node에 생성된 인터페이스 확인
ip -d -c addr show cilium_wg0
# 24: cilium_wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default
# link/none promiscuity 0 allmulti 0 minmtu 0 maxmtu 2147483552
# wireguard numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536
# wg show 명령어로 wireGuard 상태 확인
wg show
# interface: cilium_wg0
# ....
# curl 호출
kubectl exec -it curl-pod -- curl webpod
# tcpdump확인
tcpdump -eni any udp port 51871
# 20:40:46.840868 eth1 Out ifindex 3 08:00:27:04:81:24 ethertype IPv4 (0x0800), length 196: 192.168.10.100.51871 > 192.168.10.101.51871: UDP, length 148
# 20:40:46.846307 eth1 In ifindex 3 08:00:27:32:9e:97 ethertype IPv4 (0x0800), length 140: 192.168.10.101.51871 > 192.168.10.100.51871: UDP, length 92
# 20:40:46.848194 eth1 Out ifindex 3 08:00:27:04:81:24 ethertype IPv4 (0x0800), length 144: 192.168.10.100.51871 > 192.168.10.101.51871: UDP, length 96
Cilium TLS Interception 실습
Cilium TLS Interception이란 Cilium을 사용하여 TLS 암호화 연결을 투명하게 검사 것을 말합니다.
이 검사를 수행하게되면, 클라이언트 간 통신이 TLS로 보호되는 연결에서도 Cilium API 인식 가시성 및 정책을 작동시킬 수 있게됩니다.
SDS 방식을 사용하여 TLS 검사를 수행합니다.
SDS 방식은 Cilium은 구성된 시크릿 네임스페이스에서 관련 시크릿을 읽고, 핵심 Envoy SDS API를 사용하여 Envoy에 노출합니다.
Envoy는 Ingress 또는 Gateway API 구성에 대한 시크릿을 찾는 것과 같은 방식으로 NPDS에 대한 SDS 시크릿을 찾습니다
이 벙법을 사용하게되면 Envoy의 Secret 저장의 중복을 막을 수 있어 NPDS방식보다 더 효율적입니다.
실습을 통해 더 자세한 내용을 확인해보겠습니다.
cat << EOF > tls-config.yaml
tls:
readSecretsOnlyFromSecretsNamespace: true
secretsNamespace:
name: cilium-secrets # This setting is optional, as it is the default
secretSync:
enabled: true
EOF
helm upgrade cilium cilium/cilium --version 1.18.1 --namespace kube-system --reuse-values \
-f tls-config.yaml
kubectl -n kube-system rollout restart deploy/cilium-operator
kubectl -n kube-system rollout restart ds/cilium
cilium config view | grep -i secret
# enable-ingress-secrets-sync true
# enable-policy-secrets-sync true
# ingress-secrets-namespace cilium-secrets
# policy-secrets-namespace cilium-secrets
# policy-secrets-only-from-secrets-namespace true
실습 과정은 다음과 같습니다.
- TLS 가로채기를 활성화하기 위해 내부 인증 기관(CA)과 해당 CA가 서명한 관련 인증서를 생성
- DNS 기반 정책 규칙을 사용하여 가로챌 트래픽을 선택하려면 Cilium 네트워크 정책을 사용
- cilium monitor를 사용하여 HTTP 요청의 세부 사항을 검사

Figure 3.5 tls-visibility 설명
# 내부 인증 기관(CA) 생성
# 비밀번호는 qwe123으로 통일
openssl genrsa -des3 -out myCA.key 2048
openssl req -x509 -new -nodes -key myCA.key -sha256 -days 1825 -out myCA.crt
openssl x509 -in myCA.crt -noout -text
# 지정된 DNS 이름에 대한 개인 키 및 인증서 서명 요청 생성
openssl genrsa -out internal-httpbin.key 2048
openssl req -new -key internal-httpbin.key -out internal-httpbin.csr
# 내부 CA 개인 키를 사용하여 httpbin.org에 대한 서명된 인증서를 생성합니다 internal-httpbin.crt
openssl x509 -req -days 360 -in internal-httpbin.csr -CA myCA.crt -CAkey myCA.key -CAcreateserial -out internal-httpbin.crt -sha256
openssl x509 -in internal-httpbin.crt -noout -text
.....
# Issuer: C = KR, ST = Seoul, L = Seoul, O = cloudneta, OU = study, CN = cloudneta.net
# Validity
# Not Before: Sep 6 12:31:56 2025 GMT
# Not After : Sep 1 12:31:56 2026 GMT
# Subject: C = KR, ST = Seoul, L = Seoul, O = cloudneta, OU = study, CN = httpbin.org
.....
# 다음으로 대상 서비스에 대한 개인 키와 서명된 인증서를 모두 포함하는 Kubernetes Secret 생성
kubectl create secret tls httpbin-tls-data -n kube-system --cert=internal-httpbin.crt --key=internal-httpbin.key
# 클라이언트 포드 내에서 신뢰할 수 있는 CA로 내부 CA 추가
kubectl cp myCA.crt default/mediabot:/usr/local/share/ca-certificates/myCA.crt
kubectl exec mediabot -- update-ca-certificates
# Cilium에 신뢰할 수 있는 CA 목록 제공
kubectl cp default/mediabot:/etc/ssl/certs/ca-certificates.crt ca-certificates.crt
kubectl create secret generic tls-orig-data -n kube-system --from-file=ca.crt=./ca-certificates.crt
# 정책 적용 전 호출
kubectl exec -it mediabot -- curl -sL 'https://httpbin.org/headers' -v
# * Server certificate:
# * subject: CN=httpbin.org
# * start date: Jul 20 00:00:00 2025 GMT
# * expire date: Aug 17 23:59:59 2026 GMT
# * subjectAltName: host "httpbin.org" matched cert's "httpbin.org"
# * issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M03
# * SSL certificate verify ok.
# 정책 적용
kubectl create -f https://raw.githubusercontent.com/cilium/cilium/1.18.1/examples/kubernetes-tls-inspection/l7-visibility-tls.yaml
# 정책 적용 후 호출
kubectl exec -it mediabot -- curl -sL 'https://httpbin.org/headers' -v
# * Server certificate:
# * subject: C=KR; ST=Seoul; L=Seoul; O=cloudneta; OU=study; CN=httpbin.org
# * start date: Sep 6 12:31:56 2025 GMT
# * expire date: Sep 1 12:31:56 2026 GMT
# * common name: httpbin.org (matched)
# * issuer: C=KR; ST=Seoul; L=Seoul; O=cloudneta; OU=study; CN=cloudneta.net
# * SSL certificate verify ok.
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l7-visibility-tls"
spec:
description: L7 policy with TLS
endpointSelector:
matchLabels:
org: empire
class: mediabot
egress:
- toFQDNs:
- matchName: "httpbin.org"
toPorts:
- ports:
- port: "443"
protocol: "TCP"
terminatingTLS:
secret:
namespace: "kube-system"
name: "httpbin-tls-data"
originatingTLS:
secret:
namespace: "kube-system"
name: "tls-orig-data"
rules:
http:
- {}
- toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*"

Figure 3.6 가로채기 확인
4. Tetragon
✅ Tetragon이란
Tetragon은 Linux Kernel Layer에서 발생하는 이벤트를 탐지 및 통제하는 역할을 하는 도구입니다.
주로 프로세스 실행 이벤트, 시스템 콜 활동, I/O 활동 및 파일 접근과 관련된 이벤트 감지 및 통제를 수행합니다.
Tetragon은 Kernel의 eBPF에 정책과 필터링을 직접 적용하기 때문에 Context 전환 및 WakeUp을 방지하여, 리소스 사용량을 절감합니다.
Figure 4.1 Tetragon설명
Tetragon 설치
helm repo add cilium https://helm.cilium.io
helm repo update
helm install tetragon cilium/tetragon -n kube-system
kubectl rollout status -n kube-system ds/tetragon -w
# Demo Application 배포
kubectl create -f https://raw.githubusercontent.com/cilium/cilium/v1.18.1/examples/minikube/http-sw-app.yaml
실행 모니터링
Tetragon을 이용하여 시스템의 모든 실행을 추적하는 예제를 실습해보겠습니다.


Figure 4.2 Tetragon 추적 설명
POD=$(kubectl -n kube-system get pods -l 'app.kubernetes.io/name=tetragon' -o name --field-selector spec.nodeName=$(kubectl get pod xwing -o jsonpath='{.spec.nodeName}'))
kubectl exec -ti -n kube-system $POD -c tetragon -- tetra getevents -o compact --pods xwing
# 🚀 process default/xwing /usr/bin/bash -c "curl https://ebpf.io/applications/#tetragon"
# 🚀 process default/xwing /usr/bin/curl https://ebpf.io/applications/#tetragon
# 💥 exit default/xwing /usr/bin/curl https://ebpf.io/applications/#tetragon 0
# 🚀 process default/xwing /usr/bin/bash -c "curl https://httpbin.org"
# 🚀 process default/xwing /usr/bin/curl https://httpbin.org
# 💥 exit default/xwing /usr/bin/curl https://httpbin.org 0
# 🚀 process default/xwing /usr/bin/bash -c "cat /etc/passwd"
# 🚀 process default/xwing /usr/bin/cat /etc/passwd
# 💥 exit default/xwing /usr/bin/cat /etc/passwd 0
# 터미널2에서 xwing pod 시스템 수행
kubectl exec -ti xwing -- bash -c 'curl https://ebpf.io/applications/#tetragon'
kubectl exec -ti xwing -- bash -c 'curl https://httpbin.org'
kubectl exec -ti xwing -- bash -c 'cat /etc/passwd'
파일 접속 모니터링
모니터링 시 추적 정책을 추가해서 실습해보겠습니다.

Figure 4.3 파일 접속 모니터링 개요
kubectl apply -f https://raw.githubusercontent.com/cilium/tetragon/main/examples/quickstart/file_monitoring.yaml
# /etc/passwd 파일 cat 명령어 수행시 추적
kubectl exec -ti xwing -- bash -c 'cat /etc/passwd'
# 📚 read default/xwing /usr/bin/cat /etc/shadow
# 📚 read default/xwing /usr/bin/cat /etc/shadow
# 📚 read default/xwing /usr/bin/cat /etc/shadow
# 📚 read default/xwing /usr/bin/cat /etc/shadow
# 💥 exit default/xwing /usr/bin/cat /etc/shadow 0
# 파일쓰기 수행 시 추적
kubectl exec -ti xwing -- bash -c 'echo foo >> /etc/bar'
# 📝 write default/xwing /usr/bin/bash /etc/bar
# 📝 write default/xwing /usr/bin/bash /etc/bar
# 💥 exit default/xwing /usr/bin/bash -c "echo foo >> /etc/bar" 0
파일 액세스 제한 적용
민감한 파일이 읽히지 않도록 적용이 가능하며, 적용 시에는 은 애플리케이션에 작업 블록을 추가 SIGKILL하고 작업 시 오류를 반환합니다.
기존 정책과 다른점은 다음과 같은 블록이 추가되어있다는 점입니다.
matchActions:
- action: Sigkill
kubectl apply -f https://raw.githubusercontent.com/cilium/tetragon/main/examples/quickstart/file_monitoring_enforce.yaml
# 민감파일 읽기 작업 수행
kubectl exec -ti xwing -- bash -c 'cat /etc/shadow'
# 🚀 process default/xwing /usr/bin/bash -c "cat /etc/shadow"
# 🚀 process default/xwing /usr/bin/cat /etc/shadow
# 📚 read default/xwing /usr/bin/cat /etc/shadow
# 📚 read default/xwing /usr/bin/cat /etc/shadow
# 💥 exit default/xwing /usr/bin/cat /etc/shadow SIGKILL
'DevOps > Study' 카테고리의 다른 글
| MinIO Study 1주차 MinIO On k8s (0) | 2025.09.13 |
|---|---|
| MinIO Study 1주차 MinIO 소개 (0) | 2025.09.13 |
| Cilium Study [1기] 7주차 Cilium Performance & Tunning (1) | 2025.08.31 |
| Cilium Study [1기] 7주차 K8S Performance & Tunning (1) | 2025.08.31 |
| Cilium Study [1기] 6주차 2/2 ServiceMesh (0) | 2025.08.23 |