diff --git a/README.md b/README.md index 5459ae3..3423fce 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,10 @@ to capture. There are a few parameters for this plugin: -| Flag | Description | -|------------------------|---------------------------------------| -| `-ns` or `--namespace` | The namespace scope of the target Pod | +| Flag | Description | +|------------------------|----------------------------------------------------------------| +| `-ns` or `--namespace` | The namespace scope of the target Pod | +| `--ebpf` | Use eBPF probe instead of kernel module for capturing syscalls | Aditionally, all the flags for the `sysdig` cli tool are supported. You can diff --git a/kubectl-capture b/kubectl-capture index b63b437..d66f3c1 100755 --- a/kubectl-capture +++ b/kubectl-capture @@ -10,6 +10,7 @@ capture_duration="120" now=$(date +%s) capture_pod="" sysdig_params="" +ebpf="0" function main() { parse_arguments "$@" @@ -31,6 +32,9 @@ function parse_arguments() { capture_duration=$2 shift ;; + --ebpf) + ebpf="1" + ;; -w|--write=*|-z|--compress|-pc|-pk|-pm|-print=*|-S|--summary) # Do not allow changes on these parameters echo $0: $1: skipping parameter for Sysdig>&2 @@ -59,6 +63,7 @@ function usage_and_die() { Usage: kubectl capture POD [-ns NAMESPACE] [sysdig options] Options: -ns | --namespace The namespace where the target pod lives (default: default) + --ebpf Launch capture pod with eBPF probe instead of kernel module EOF exit $1 } @@ -70,6 +75,32 @@ function start_capture() { exit 1 fi + if [[ "${ebpf}" -eq "1" ]];then + build_capture_pod_for_ebpf + else + build_capture_pod + fi + + kubectl apply -f capture-pod.yaml > /dev/null 2>&1 + rm capture-pod.yaml + + echo "Sysdig is starting to capture system calls:" + echo "" + echo "Node: ${node}" + echo "Pod: ${pod}" + echo "Duration: ${capture_duration} seconds" + echo "Parameters for Sysdig: ${sysdig_params}" + echo "" + + wait_until_finished + + kubectl cp ${capture_pod}:/${capture_pod}.scap.gz ${capture_pod}.scap.gz > /dev/null 2>&1 + kubectl delete pod ${capture_pod} > /dev/null 2>&1 + echo "The capture has been downloaded to your hard disk at:" + echo "${PWD}/${capture_pod}.scap.gz" +} + +function build_capture_pod() { cat << EOF > capture-pod.yaml apiVersion: v1 kind: Pod @@ -136,24 +167,85 @@ spec: path: /usr nodeName: ${node} EOF +} - kubectl apply -f capture-pod.yaml > /dev/null 2>&1 - rm capture-pod.yaml - - echo "Sysdig is starting to capture system calls:" - echo "" - echo "Node: ${node}" - echo "Pod: ${pod}" - echo "Duration: ${capture_duration} seconds" - echo "Parameters for Sysdig: ${sysdig_params}" - echo "" - - wait_until_finished - - kubectl cp ${capture_pod}:/${capture_pod}.scap.gz ${capture_pod}.scap.gz > /dev/null 2>&1 - kubectl delete pod ${capture_pod} > /dev/null 2>&1 - echo "The capture has been downloaded to your hard disk at:" - echo "${PWD}/${capture_pod}.scap.gz" +function build_capture_pod_for_ebpf() { + cat << EOF > capture-pod.yaml +apiVersion: v1 +kind: Pod +metadata: + name: "${capture_pod}" +spec: + hostNetwork: true + containers: + - name: capturer + image: sysdig/sysdig + args: + - /bin/bash + - "-c" + - "echo '* Capturing system calls'; sysdig ${sysdig_params}; touch /.finished; trap 'exit 0' TERM; sleep infinity & wait \$!" + imagePullPolicy: IfNotPresent + securityContext: + privileged: true + env: + - name: SYSDIG_BPF_PROBE + value: + resources: + requests: + cpu: 100m + memory: 64Mi + limits: + cpu: 100m + memory: 128Mi + volumeMounts: + - mountPath: /host/var/run/docker.sock + name: docker-socket + - mountPath: /host/dev + name: dev-fs + - mountPath: /host/proc + name: proc-fs + readOnly: true + - mountPath: /host/boot + name: boot-fs + readOnly: true + - mountPath: /host/lib/modules + name: lib-modules + readOnly: true + - mountPath: /host/usr + name: usr-fs + readOnly: true + - mountPath: /dev/shm + name: dshm + - mountPath: /host/etc + name: etc-fs + readOnly: true + volumes: + - name: dshm + emptyDir: + medium: Memory + - name: docker-socket + hostPath: + path: /var/run/docker.sock + - name: dev-fs + hostPath: + path: /dev + - name: proc-fs + hostPath: + path: /proc + - name: boot-fs + hostPath: + path: /boot + - name: lib-modules + hostPath: + path: /lib/modules + - name: usr-fs + hostPath: + path: /usr + - name: etc-fs + hostPath: + path: /etc + nodeName: ${node} +EOF } function wait_until_finished() { diff --git a/test/kubectl-capture.bats b/test/kubectl-capture.bats index 06bbf4b..e9064e6 100644 --- a/test/kubectl-capture.bats +++ b/test/kubectl-capture.bats @@ -9,12 +9,25 @@ kubectl delete deployment nginx [ "$status" -eq 0 ] + [ -f ${lines[-1]} ] +} + +@test "it does a capture using ebpf" { + kubectl create deployment nginx-ebpf --image=nginx + POD=$(kubectl get pod | grep nginx-ebpf | cut -f1 -d" ") + + run ./kubectl-capture $POD --ebpf -M 5 + + kubectl delete deployment nginx-ebpf + + [ "$status" -eq 0 ] + [ -f ${lines[-1]} ] } @test "when pod is inside a namespace it does a capture" { kubectl create namespace scope - kubectl -n scope create deployment nginx --image=nginx - POD=$(kubectl -n scope get pod | grep nginx | cut -f1 -d" ") + kubectl -n scope create deployment nginx-namespace --image=nginx + POD=$(kubectl -n scope get pod | grep nginx-namespace | cut -f1 -d" ") run ./kubectl-capture $POD -M 5 -ns scope