해당 포스팅은 현재 재직중인 회사에 관련이 없고, 개인 역량 개발을 위한 스터디 자료로 활용할 예정입니다.
Tetragon
Tetragon를 사용하여 Kubernetes 클러스터의 보안 및 프로세스 모니터링을 수행하는 방법에 대해 간단히 알아보자.
Tetragon이란 무엇인가?
그림 출처: https://isovalent.com/blog/post/2022-05-16-tetragon/
Tetragon은 eBPF(extended Berkeley Packet Filter)를 기반으로 하는 강력한 보안 및 모니터링 도구이다. eBPF는 커널 레벨에서 실행되는 프로그램으로, 시스템 성능에 최소한의 영향을 주면서 실시간으로 데이터를 수집하고 처리할 수 있다. Tetragon은 이를 활용하여 Kubernetes 환경에서 네트워크 트래픽, 시스템 호출, 컨테이너 활동 등을 모니터링하고 분석할 수 있다.
Tetragon은 반드시 Cilium이 있어야 하나?
Tetragon은 eBPF를 기반으로 한 강력한 보안 및 모니터링 도구로, Kubernetes 환경에서의 데이터 수집 및 분석에 최적화되어 있다. 그러나 Tetragon을 사용하기 위해 반드시 Cilium이 필요하지는 않다.
Cilium은 Kubernetes 클러스터에서 네트워크 정책을 관리하고, eBPF를 활용하여 성능을 최적화하는 CNI(컨테이너 네트워크 인터페이스) 플러그인이다. Tetragon은 Cilium과 함께 사용할 때 그 기능이 더욱 강화되지만, Cilium 없이도 Tetragon의 기본적인 기능을 활용할 수 있다.
Cilium을 사용하지 않는 경우, Tetragon은 여전히 eBPF를 통해 시스템 호출, 네트워크 트래픽, 컨테이너 활동 등을 모니터링할 수 있다. 그러나 Cilium을 사용하면 Tetragon의 성능과 통합성이 향상되며, 보다 정교한 네트워크 정책을 적용할 수 있는 장점이 있다.
결론적으로, Tetragon은 Cilium과 함께 사용할 때 최상의 성능을 발휘하지만, Cilium 없이도 유용하게 사용할 수 있는 도구이다. 사용자의 필요와 환경에 따라 적절한 선택을 하는 것이 중요하다.
Tetragon 설치 및 설정
요구 사항 및 테스트 환경
- Kubernetes 클러스터 : Kind 1.31.0
- Helm 3
- Kubectl
- jq
Kind 설치
Tetragon가 정상적으로 작동하려면 호스트 /proc 파일 시스템에 대한 액세스 권한이 있어야 한다. Kind 클러스터는 다음 명령을 통해 일 노드 클러스터를 사용한다고 가정한다.
cat <<EOF > kind-config.yaml
apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
nodes:
- role: control-plane
extraMounts:
- hostPath: /proc
containerPath: /procHost
EOF
kind create cluster --config kind-config.yaml
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.31.0) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Tetragon 설치
기본적으로 Tetragon은 이벤트 로그의 노이즈를 줄이기 위해 kube-system 이벤트를 필터링한다. 그리고 위에서 이야기 했던 것처럼 proc
호스트 패스를 클러스터 내 패스로 설정하여 helm
으로 배포한다. 설치가 완료되면 Tetragon이 Kubernetes 클러스터에 배포되고, eBPF 기반의 모니터링이 시작된다.
EXTRA_HELM_FLAGS=(--set tetragon.hostProcPath=/procHost)
helm repo add cilium https://helm.cilium.io
helm repo update
helm install tetragon ${EXTRA_HELM_FLAGS[@]} cilium/tetragon -n kube-system
kubectl rollout status -n kube-system ds/tetragon -w
"cilium" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "cilium" chart repository
Update Complete. ⎈Happy Helming!⎈
NAME: tetragon
LAST DEPLOYED: Sun Oct 27 00:56:55 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
Waiting for daemon set "tetragon" rollout to finish: 0 of 1 updated pods are available...
daemon set "tetragon" successfully rolled out
샘플 앱 배포
Tetragon을 탐색하기 위해 샘플 워크로드가 필요하다. 여기서는 Cilium의 데모로 주로 사용되는 애플리케이션 StarWars
을 사용할 예정이다.
kubectl create -f https://raw.githubusercontent.com/cilium/cilium/v1.15.3/examples/minikube/http-sw-app.yaml
배포된 리소스는 다음과 유사할 것이다.
kubectl get pods
NAME READY STATUS RESTARTS AGE
deathstar-bf77cddc9-nz5sc 1/1 Running 0 2m51s
deathstar-bf77cddc9-sd2s8 1/1 Running 0 2m51s
tiefighter 1/1 Running 0 2m51s
xwing 1/1 Running 0 2m51s
Tetragon 데몬 세트가 준비되고 파드가 실행 중인 상태가 되면, 노드에서 이벤트 수신을 시작할 수 있다. 바로 프로세스 실행을 모니터링할 수 있고, Tetragon은 일치하는 이벤트를 JSON 형식으로 내보낸다. 다음 명령을 통해 로그를 확인 할 수 있다.
kubectl logs -n kube-system -l app.kubernetes.io/name=tetragon -c export-stdout -f | jq
{
"process_exec": {
"process": {
"exec_id": "a2luZC1jb250cm9sLXBsYW5lOjc5MTM2NDIxODQwMjoxMjU5Mg==",
"pid": 12592,
"uid": 0,
"cwd": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/e1216138dabb7071aa17679a52b05f9cad566ffbf89f0a090c385fc0af644242/rootfs",
"binary": "/kind/bin/mount-product-files.sh",
"arguments": "/kind/bin/mount-product-files.sh",
"flags": "execve clone",
"start_time": "2024-10-26T16:02:01.948430838Z",
"auid": 4294967295,
"pod": {
"namespace": "default",
"name": "xwing",
"container": {
"id": "containerd://e1216138dabb7071aa17679a52b05f9cad566ffbf89f0a090c385fc0af644242",
"name": "spaceship",
"image": {
"id": "quay.io/cilium/json-mock@sha256:5aad04835eda9025fe4561ad31be77fd55309af8158ca8663a72f6abb78c2603",
"name": "sha256:56b43d7e51feffe637c2837a8c70da02be98a51099533f288c78fa369f5c6991"
},
"start_time": "2024-10-26T16:02:01Z",
"pid": 7
},
"pod_labels": {
"app.kubernetes.io/name": "xwing",
"class": "xwing",
"org": "alliance"
},
"workload": "xwing",
"workload_kind": "Pod"
},
"docker": "e1216138dabb7071aa17679a52b05f9",
"parent_exec_id": "a2luZC1jb250cm9sLXBsYW5lOjc5MTM1ODgxNDg2MToxMjU4Ng==",
"tid": 12592
},
"parent": {
"exec_id": "a2luZC1jb250cm9sLXBsYW5lOjc5MTM1ODgxNDg2MToxMjU4Ng==",
"pid": 12586,
"uid": 0,
"cwd": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/6b1f9e099116975f55078e5ff88ffd9cef985d871c1c1ff4c7af8e85398f2557",
"binary": "/usr/local/sbin/runc",
"arguments": "--root /run/containerd/runc/k8s.io --log /run/containerd/io.containerd.runtime.v2.task/k8s.io/e1216138dabb7071aa17679a52b05f9cad566ffbf89f0a090c385fc0af644242/log.json --log-format json --systemd-cgroup create --bundle /run/containerd/io.containerd.runtime.v2.task/k8s.io/e1216138dabb7071aa17679a52b05f9cad566ffbf89f0a090c385fc0af644242 --pid-file /run/containerd/io.containerd.runtime.v2.task/k8s.io/e1216138dabb7071aa17679a52b05f9cad566ffbf89f0a090c385fc0af644242/init.pid e1216138dabb7071aa17679a52b05f9cad566ffbf89f0a090c385fc0af644242",
"flags": "execve",
"start_time": "2024-10-26T16:02:01.943027380Z",
"auid": 4294967295,
"parent_exec_id": "a2luZC1jb250cm9sLXBsYW5lOjc5MTMzMTE2MDIzNjoxMjU3Ng==",
"refcnt": 1,
"tid": 12586
}
},
"node_name": "kind-control-plane",
"time": "2024-10-26T16:02:01.948430629Z"
}
...
위 로그는 Tetragon이 클러스터에서 실행 중인 프로세스에 대한 정보를 JSON 형식으로 출력한 것이다.
Tetragon을 사용하여 클러스터 내에서 실행되는 모든 프로세스를 모니터링할 수 있다. 해당 json 로그는 Tetragon이 프로세스 실행을 모니터링하고 있으며, 각 프로세스와 관련된 상세 정보(컨테이너, 파드, 부모 프로세스)를 제공한다. 이를 통해 보안 및 성능 모니터링을 수행할 수 있는것이 Tetragon의 기본 동작 방식이다.
Tetragon 사용 예시
tetra CLI 사용
Tetragon CLI인 tetra
은 pod, host, namespace 또는 process별로 이벤트를 필터링하는 데 유용하다. tetra CLI는 GitHub 릴리스 페이지에서 다운로드할 수 있다. CLI를 실행할 워크스테이션에 Go가 설치되어 있는 경우에는, 다음 명령을 사용하여 다운로드하고 설치할 수 있다.
GOOS=$(go env GOOS)
GOARCH=$(go env GOARCH)
curl -L --remote-name-all https://github.com/cilium/tetragon/releases/latest/download/tetra-${GOOS}-${GOARCH}.tar.gz{,.sha256sum}
sha256sum --check tetra-${GOOS}-${GOARCH}.tar.gz.sha256sum
sudo tar -C /usr/local/bin -xzvf tetra-${GOOS}-${GOARCH}.tar.gz
rm tetra-${GOOS}-${GOARCH}.tar.gz{,.sha256sum}
tetra CLI가 설치된 후 JSON 출력을 tetra getevents에 전달하여 -o compact
옵션을 통해 가독성 있게 이벤트를 확인 할 수 있다. 만약 특정 네임스페이스의 이벤트만 확인하는 등의 별도의 플래그를 통해 필터링 할 수 있다.
kubectl logs -n kube-system ds/tetragon -c export-stdout -f | tetra getevents -o compact
🚀 process default/xwing /kind/bin/mount-product-files.sh /kind/bin/mount-product-files.sh
🚀 process default/xwing /usr/bin/jq -r .bundle
💥 exit default/xwing /usr/bin/jq -r .bundle 0
🚀 process default/xwing /usr/bin/cp /kind/product_uuid /run/containerd/io.containerd.runtime.v2.task/k8s.io/e1216138dabb7071aa17679a52b05f9cad566ffbf89f0a090c385fc0af644242/rootfs/
💥 exit default/xwing /usr/bin/cp /kind/product_uuid /run/containerd/io.containerd.runtime.v2.task/k8s.io/e1216138dabb7071aa17679a52b05f9cad566ffbf89f0a090c385fc0af644242/rootfs/ 0
💥 exit default/xwing /kind/bin/mount-product-files.sh /kind/bin/mount-product-files.sh 0
🚀 process default/xwing /usr/bin/bash /run.sh
🚀 process default/xwing /usr/bin/tini -- /usr/local/bin/json-server --host --port 80 --watch /default.json --middlewares /middleware.js
🚀 process default/xwing /usr/local/bin/json-server node /usr/local/bin/json-server --host --port 80 --watch /default.json --middlewares /middleware.js
🚀 process default/xwing /usr/local/bin/node /usr/local/bin/json-server --host --port 80 --watch /default.json --middlewares /middleware.js
파일 시스템 액세스 추적 및 네트워크 트래픽 모니터링
TracingPolicy
는 커널 이벤트에 대한 실시간 필터를 쉽게 설정할 수 있는 사용자 정의 리소스이다. TracingPolicy
는 시스템 호출이 수행되면 등록된 정책에 일치될 경우 해당 작업을 트리거 한다. 처음 테스트를 진행할 내용은 특정 파드내에서 curl을 사용해서 외부로 접속하는 경우 발생하는 이벤트를 감지하는 것을 구현해본 예시이다.
먼저 두개의 정책을 적용한다. 하나씩 살펴보자.
kubectl apply -f https://raw.githubusercontent.com/cilium/tetragon/refs/heads/main/examples/tracingpolicy/sys_write_follow_fd_prefix.yaml
해당 정책(sys_write_follow_fd_prefix
)은 파일이 오픈되거나 읽고, 쓰기가 발생하는 이벤트를 확인하는 정책이다. fd_install
함수는 새로운 파일 디스크립터가 생성될 때 호출된다. 이 함수는 파일 디스크립터 테이블에 새로운 파일 디스크립터를 등록하고, 해당 파일 디스크립터가 가리키는 파일 객체를 설정한다. 즉, 프로세스가 파일을 열 때, 커널은 fd_install
을 통해 파일 디스크립터를 할당하고, 이 디스크립터를 통해 이후에 파일에 접근할 수 있도록 한다.
sys_read
함수는 파일 디스크립터를 통해 파일에서 데이터를 읽는 시스템 호출이다. 이 함수는 user가 지정한 파일 디스크립터를 사용하여 해당 파일에서 데이터를 읽어오는 역할을 한다. sys_read는 파일 디스크립터가 유효한지 확인하고, 해당 파일의 내용을 userspace으로 복사한다.
즉, fd_install
이 파일 디스크립터를 설정한 후, user는 이 디스크립터를 통해 sys_read를 호출하여 파일의 내용을 읽을 수 있게 된다. 이 두 함수는 파일 I/O 작업의 기본적인 흐름에서 서로 연결되어 있다.
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: "sys-read-follow-prefix"
spec:
kprobes:
- call: "fd_install"
syscall: false
return: false
args:
- index: 0
type: int
- index: 1
type: "file"
selectors:
- matchPIDs:
- operator: NotIn
followForks: true
isNamespacePID: true
values:
- 1
matchArgs:
- index: 1
operator: "Prefix"
values:
- "/etc/"
matchActions:
- action: FollowFD
argFd: 0
argName: 1
- call: "sys_close"
syscall: true
args:
- index: 0
type: "int"
selectors:
- matchActions:
- action: UnfollowFD
argFd: 0
argName: 0
- call: "sys_read"
syscall: true
args:
- index: 0
type: "fd"
- index: 1
type: "char_buf"
returnCopy: true
- index: 2
type: "size_t"
- call: "sys_write"
syscall: true
args:
- index: 0
type: "fd"
- index: 1
type: "char_buf"
sizeArgIndex: 3
- index: 2
type: "size_t"
네트워크 트래픽 모니터링
kubectl apply -f https://raw.githubusercontent.com/cilium/tetragon/refs/heads/main/examples/tracingpolicy/tcp-connect.yaml
해당 정책은 네트워크 트래픽을 모니터링한다. 정책 내용을 살펴보면 tcp_connect
, tcp_close
등의 네트워크 기반 시스템 콜이 발생하는 것을 모니터링 하게 된다. 이를 통해 파드 내에서 어떤 네트워크 요청이 발생했는지 추적할 수 있다.
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: "connect"
spec:
kprobes:
- call: "tcp_connect"
syscall: false
args:
- index: 0
type: "sock"
- call: "tcp_close"
syscall: false
args:
- index: 0
type: "sock"
- call: "tcp_sendmsg"
syscall: false
args:
- index: 0
type: "sock"
- index: 2
type: int
해당 이벤트 등을 감지하기 위해 tetra getevents -o compact
CLI명령을 실행해 놓자.
kubectl logs -n kube-system ds/tetragon -c export-stdout -f | tetra getevents -o compact --namespaces default --pods xwing
ㄴ```
이제 `xwing` 파드에서 curl 명령으로 외부로 통신하는 테스트를 진행해본다.
```bash
kubectl exec -it xwing -- curl google.com
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
아까 걸어놨던 tetra getevents -o compact
CLI명령을 확인해보면 다음과 유사한 결과를 확인할 수 있다.
🚀 process default/xwing /usr/bin/curl google.com
📬 open default/xwing /usr/bin/curl /etc/ld.so.cache
📪 close default/xwing /usr/bin/curl
...
📪 close default/xwing /usr/bin/curl
📬 open default/xwing /usr/bin/curl /etc/ssl/openssl.cnf
📚 read default/xwing /usr/bin/curl /etc/ssl/openssl.cnf 4096 bytes
📚 read default/xwing /usr/bin/curl /etc/ssl/openssl.cnf 4096 bytes
...
📪 close default/xwing /usr/bin/curl
📪 close default/xwing /usr/bin/curl
📬 open default/xwing /usr/bin/curl /etc/nsswitch.conf
📚 read default/xwing /usr/bin/curl /etc/nsswitch.conf 4096 bytes
📚 read default/xwing /usr/bin/curl /etc/nsswitch.conf 4096 bytes
📪 close default/xwing /usr/bin/curl
📬 open default/xwing /usr/bin/curl /etc/passwd
📚 read default/xwing /usr/bin/curl /etc/passwd 4096 bytes
📪 close default/xwing /usr/bin/curl
📪 close default/xwing /usr/bin/curl
📪 close default/xwing /usr/bin/curl
📪 close default/xwing /usr/bin/curl
📬 open default/xwing /usr/bin/curl /etc/host.conf
📚 read default/xwing /usr/bin/curl /etc/host.conf 4096 bytes
📚 read default/xwing /usr/bin/curl /etc/host.conf 4096 bytes
📪 close default/xwing /usr/bin/curl
📬 open default/xwing /usr/bin/curl /etc/resolv.conf
📚 read default/xwing /usr/bin/curl /etc/resolv.conf 4096 bytes
📚 read default/xwing /usr/bin/curl /etc/resolv.conf 4096 bytes
📪 close default/xwing /usr/bin/curl
📬 open default/xwing /usr/bin/curl /etc/hosts
📚 read default/xwing /usr/bin/curl /etc/hosts 4096 bytes
📚 read default/xwing /usr/bin/curl /etc/hosts 4096 bytes
📪 close default/xwing /usr/bin/curl
...
📪 close default/xwing /usr/bin/curl
📬 open default/xwing /usr/bin/curl /etc/gai.conf
📚 read default/xwing /usr/bin/curl /etc/gai.conf 4096 bytes
📚 read default/xwing /usr/bin/curl /etc/gai.conf 4096 bytes
📪 close default/xwing /usr/bin/curl
...
📪 close default/xwing /usr/bin/curl
🔌 connect default/xwing /usr/bin/curl tcp 10.244.0.9:34362 -> 142.250.207.110:80
📤 sendmsg default/xwing /usr/bin/curl tcp 10.244.0.9:34362 -> 142.250.207.110:80 bytes 74
📪 close default/xwing /usr/bin/curl
📪 close default/xwing /usr/bin/curl
📪 close default/xwing /usr/bin/curl
🧹 close default/xwing /usr/bin/curl tcp 10.244.0.9:34362 -> 142.250.207.110:80
💥 exit default/xwing /usr/bin/curl google.com 0
🚀 process default/xwing /usr/bin/sh
📬 open default/xwing /usr/bin/sh /etc/ld.so.cache
📪 close default/xwing /usr/bin/sh
📪 close default/xwing /usr/bin/sh
📪 close default/xwing /usr/bin/sh
📪 close default/xwing /usr/bin/sh
💥 exit default/xwing /usr/bin/sh 130
위 로그를 통해 Tetragon이 클러스터 내에서 실행 중인 curl 프로세스에 대한 상세한 이벤트를 기록한 것을 알 수 있다.
- 프로세스 실행: curl 명령이 google.com에 대한 요청 실행
- 파일 접근: curl 프로세스가 여러 파일을 열고 읽는 과정 확인. /etc/ld.so.cache, /etc/ssl/openssl.cnf, /etc/nsswitch.conf, /etc/passwd, /etc/host.conf, /etc/resolv.conf, /etc/hosts, /etc/gai.conf 등의 파일이 열리고 읽힌 것으로, 이는 curl이 외부 요청을 처리하기 위해 필요한 설정 파일이나 라이브러리를 로드하는 과정이다.
- 네트워크 연결: curl이 tcp 연결을 통해 10.244.0.9:34362에서 142.250.207.110:80으로 연결을 시도한 것을 확인할 수 있다.
- 데이터 전송: sendmsg 이벤트는 curl이 데이터를 전송했음을 나타낸다.
- 프로세스 종료: curl 프로세스가 성공적으로 종료되었음을 나타내는 exit 이벤트가 기록되어 있다. 종료 코드
0
은 정상 종료를 의미한다.
결론
Tetragon과 eBPF를 사용하면 Kubernetes 환경에서 강력한 보안 및 모니터링 기능을 구현할 수 있다. Tetragon은 시스템 호출을 추적하고, 프로세스별로 호출 횟수를 기록한다. 이러한 이벤트는 커널 내에서 직접 모니터링되므로 이러한 호출 추적 작업에 약간의 오버헤드만이 추가되어, Falco와 같은 도구보다는 리소스 사용 측면에서는 유리한 점은 존재한다.
Tetragon의 단점은 관찰, 추적된 프로세스를 kill 할 수 있는 조치가 후행적이라는 것이다. 이벤트가 일어나고 있고 이전에 예방하는 방식은 아니다라는 사실을 이해해야 한다. 하지만 커널 레벨에서 이벤트를 필터링하고 이에 대한 조치를 취할 수 있는 정책을 수립하고 만드는 측면에서는 블랙리스트 방식으로 하는 차단 방식에 비해 유연성을 어느 정도 보장해줄 수 있는 것은 강력해 보인다.
해당 포스팅은 현재 재직중인 회사에 관련이 없고, 개인 역량 개발을 위한 스터디 자료로 활용할 예정입니다.