-
Notifications
You must be signed in to change notification settings - Fork 389
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add kprobe read/write e2e test #3374
base: main
Are you sure you want to change the base?
Changes from 1 commit
197242d
2d78430
9dadd59
6c1044d
3de0f92
e4db7ef
6762a45
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading the buffer from the user-space buffer is generally an unsafe pattern. There is no issue for the test, but I don't know if adding this anti-pattern to our code is a good idea. We also don't seem to validate the buffer so maybe it would maybe sense to remove this argument? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed args. |
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. similar here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed args. |
||
- index: 2 | ||
type: "size_t" | ||
selectors: | ||
- matchPIDs: | ||
- operator: NotIn | ||
followForks: true | ||
isNamespacePID: true | ||
values: | ||
- 1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is the matchPIDs filter needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I pasted from example write.yaml. |
||
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++ | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One thing to note is that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
return false, nil | ||
} | ||
|
||
func (k *kprobeCheker) FinalCheck(logger *logrus.Logger) error { | ||
if k.matches > 0 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed this |
||
return nil | ||
} | ||
return fmt.Errorf("kprobe checker failed, had %d matches", k.matches) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is probably from the skeleton test, so maybe it's worth removing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed this
2d78430