Skip to content

Commit

Permalink
add kprobe read/write e2e test
Browse files Browse the repository at this point in the history
Signed-off-by: sat0ken <[email protected]>
  • Loading branch information
sat0ken committed Feb 7, 2025
1 parent cd762df commit 197242d
Showing 1 changed file with 254 additions and 0 deletions.
254 changes: 254 additions & 0 deletions tests/e2e/tests/kprobe/kprobe_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Tetragon

// This package contains a simple test skeleton that can be copied, pasted, and modified
// to create new Tetragon e2e tests.
package kprobe_test

import (
"context"
_ "embed"
"errors"
"fmt"
"github.com/cilium/tetragon/api/v1/tetragon"
"github.com/cilium/tetragon/tests/e2e/helpers/grpc"
"github.com/sirupsen/logrus"
"testing"
"time"

ec "github.com/cilium/tetragon/api/v1/tetragon/codegen/eventchecker"
"github.com/cilium/tetragon/tests/e2e/checker"
"github.com/cilium/tetragon/tests/e2e/helpers"
"github.com/cilium/tetragon/tests/e2e/runners"
"k8s.io/klog/v2"
"sigs.k8s.io/e2e-framework/pkg/envconf"
"sigs.k8s.io/e2e-framework/pkg/features"
)

// This holds our test environment which we get from calling runners.NewRunner().Setup()
var runner *runners.Runner

// The namespace where we want to spawn our pods
const namespace = "kprobe-test"

var (
// Basic Tetragon parameters
TetragonNamespace = "kube-system"
TetragonAppNameKey = "app.kubernetes.io/name"
TetragonAppNameVal = "tetragon"
TetragonContainer = "tetragon"
TetragonCLI = "tetra"
)

func TestMain(m *testing.M) {
runner = runners.NewRunner().Init()

// Here we ensure our test namespace doesn't already exist then create it.
runner.Setup(func(ctx context.Context, c *envconf.Config) (context.Context, error) {
ctx, _ = helpers.DeleteNamespace(namespace, true)(ctx, c)

ctx, err := helpers.CreateNamespace(namespace, true)(ctx, c)
if err != nil {
return ctx, fmt.Errorf("failed to create namespace: %w", err)
}

return ctx, nil
})

// Run the tests using the test runner.
runner.Run(m)
}

func TestKprobeTracingPolicy(t *testing.T) {
runner.SetupExport(t)

// Create an curl event checker with a limit or 10 events or 30 seconds, whichever comes first
checker := kprobeChecker().WithEventLimit(20).WithTimeLimit(30 * time.Second)

// Define test features here. These can be used to perform actions like:
// - Spawning an event checker and running checks
// - Modifying resources in the cluster
// - etc.

// This starts curlChecker and uses it to run event checks.
runEventChecker := features.New("Run Event Checks").
Assess("Run Event Checks",
checker.CheckWithFilters(
// allow list
30*time.Second,
[]*tetragon.Filter{{
EventSet: []tetragon.EventType{tetragon.EventType_PROCESS_KPROBE},
Namespace: []string{namespace},
}},
// deny list
[]*tetragon.Filter{},
)).Feature()

// This feature waits for curlChecker to start then runs a custom workload.
runWorkload := features.New("Run kprobe test").
Assess("Install policy", func(ctx context.Context, _ *testing.T, c *envconf.Config) context.Context {
ctx, err := helpers.LoadCRDString(namespace, kprobeReadWritePolicy, false)(ctx, c)
if err != nil {
klog.ErrorS(err, "failed to install policy")
t.Fail()
}
return ctx
}).
Assess("Wait for policy", func(ctx context.Context, _ *testing.T, _ *envconf.Config) context.Context {
if err := grpc.WaitForTracingPolicy(ctx, "sys-write"); err != nil {
klog.ErrorS(err, "failed to wait for policy")
t.Fail()
}
return ctx
}).
Assess("Wait for Checker", checker.Wait(30*time.Second)).
Assess("Start pods", func(ctx context.Context, _ *testing.T, c *envconf.Config) context.Context {
var err error
for _, pod := range []string{ubuntuReadPod, ubuntuWritePod} {
ctx, err = helpers.LoadCRDString(namespace, pod, true)(ctx, c)
if err != nil {
klog.ErrorS(err, "failed to load pod")
t.Fail()
}

}
return ctx
}).
Assess("Uninstall policy", func(ctx context.Context, _ *testing.T, c *envconf.Config) context.Context {
ctx, err := helpers.UnloadCRDString(namespace, kprobeReadWritePolicy, true)(ctx, c)
if err != nil {
klog.ErrorS(err, "failed to uninstall policy")
t.Fail()
}
return ctx
}).Feature()

runner.TestInParallel(t, runEventChecker, runWorkload)
}

const kprobeReadWritePolicy = `
apiVersion: cilium.io/v1alpha1
kind: TracingPolicyNamespaced
metadata:
name: "sys-read-write"
spec:
kprobes:
# write system call tracing
- call: "sys_write"
syscall: true
args:
- index: 0
type: "int"
- index: 1
type: "char_buf"
sizeArgIndex: 3
- index: 2
type: "size_t"
selectors:
- matchPIDs:
- operator: NotIn
followForks: true
isNamespacePID: true
values:
- 1
matchArgs:
- index: 0
operator: "Equal"
values:
- "1"
# read system call tracing
- call: "sys_read"
syscall: true
args:
- index: 0
type: "int"
- index: 1
type: "char_buf"
sizeArgIndex: 3
- index: 2
type: "size_t"
selectors:
- matchPIDs:
- operator: NotIn
followForks: true
isNamespacePID: true
values:
- 1
matchArgs:
- index: 0
operator: "Equal"
values:
- "0"
`

const ubuntuWritePod = `
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: ubuntu-write
name: ubuntu-write
spec:
containers:
- args:
- /usr/bin/echo
- hello
image: ubuntu
name: ubuntu-write
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
`

const ubuntuReadPod = `
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: ubuntu-read
name: ubuntu-read
spec:
containers:
- args:
- cat
- /etc/hostname
image: ubuntu
name: ubuntu-read
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
`

func kprobeChecker() *checker.RPCChecker {
return checker.NewRPCChecker(&kprobeCheker{}, "kprobe-checker")
}

type kprobeCheker struct {
matches int
}

func (k *kprobeCheker) NextEventCheck(event ec.Event, _ *logrus.Logger) (bool, error) {
// ignore other events
ev, ok := event.(*tetragon.ProcessKprobe)
if !ok {
return false, errors.New("not a process kprobe")
}

if ev.GetFunctionName() == "__x64_sys_write" && ev.GetProcess().GetBinary() == "/usr/bin/echo" {
k.matches++
}
if ev.GetFunctionName() == "__x64_sys_read" && ev.GetProcess().GetBinary() == "cat" {
k.matches++
}

return false, nil
}

func (k *kprobeCheker) FinalCheck(logger *logrus.Logger) error {
if k.matches > 0 {
return nil
}
return fmt.Errorf("kprobe checker failed, had %d matches", k.matches)
}

0 comments on commit 197242d

Please sign in to comment.