From 8384df48643a0289e57c81e72ea1a88c7f2ed5b7 Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Thu, 20 Feb 2025 13:29:10 +0100 Subject: [PATCH] policyconf: policy mode test (override, enforcer) Add a second test for the policy mode test. This test the override and enforcer actions. Because they do not kill the process, the way we check for them is different than the sigkill test. For override, we just check the error that is printed in stdout. For enforcer, we check the enforcer map contents. Signed-off-by: Kornilios Kourtis --- pkg/policyconf/test/mode_test.go | 119 ++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/pkg/policyconf/test/mode_test.go b/pkg/policyconf/test/mode_test.go index e8920f61446..3dcec86cbc5 100644 --- a/pkg/policyconf/test/mode_test.go +++ b/pkg/policyconf/test/mode_test.go @@ -16,8 +16,8 @@ import ( "github.com/cilium/tetragon/pkg/logger" "github.com/cilium/tetragon/pkg/policyconf" "github.com/cilium/tetragon/pkg/reader/notify" - _ "github.com/cilium/tetragon/pkg/sensors/exec" // NB: needed so that the exec sensor can load the execve probe on its init - _ "github.com/cilium/tetragon/pkg/sensors/tracing" // NB: needed so that the exec tracing sensor can load its policy handlers on init + _ "github.com/cilium/tetragon/pkg/sensors/exec" // NB: needed so that the exec sensor can load the execve probe on its init + stracing "github.com/cilium/tetragon/pkg/sensors/tracing" // NB: needed so that the exec tracing sensor can load its policy handlers on init "github.com/cilium/tetragon/pkg/testutils" "github.com/cilium/tetragon/pkg/testutils/perfring" pft "github.com/cilium/tetragon/pkg/testutils/policyfilter/tester" @@ -118,3 +118,118 @@ func TestModeSigKill(t *testing.T) { policyconf.SetPolicyMode(tp, policyconf.EnforceMode) checkEnforce() } + +func TestModeEnforcer(t *testing.T) { + // NB: the policy below checks for both enforcer and override at the same time, which is why + // we need both (and, also, why fmod_ret is not enough) + if !bpf.HasSignalHelper() { + t.Skip("skipping test, bpf_send_signal helper not available") + } + if !bpf.HasOverrideHelper() { + t.Skip("skipping test, neither bpf_override_return not available") + } + + testutils.CaptureLog(t, logger.GetLogger().(*logrus.Logger)) + ctx, cancel := context.WithTimeout(context.Background(), tus.Conf().CmdWaitTime) + defer cancel() + + pft := pft.Start(t, ctx) + + polName := "tp-test" + polNamespace := "namespace" + tp := &tracingpolicy.GenericTracingPolicyNamespaced{ + Metadata: v1.ObjectMeta{ + Name: polName, + Namespace: polNamespace, + }, + Spec: v1alpha1.TracingPolicySpec{ + Enforcers: []v1alpha1.EnforcerSpec{{ + // NB: add another enforcer call so that we can just check the map + Calls: []string{"sys_lseek"}, + }}, + KProbes: []v1alpha1.KProbeSpec{{ + Call: "sys_getcpu", + Return: true, + Syscall: true, + ReturnArg: &v1alpha1.KProbeArg{ + Index: 0, + Type: "int", + }, + Selectors: []v1alpha1.KProbeSelector{{ + MatchActions: []v1alpha1.ActionSelector{ + {Action: "NotifyEnforcer", ArgSig: 9}, + {Action: "Override", ArgError: -1}, + }, + }}, + }}, + }, + } + + pft.AddPolicy(t, ctx, tp) + + pid := pft.ProgTester.Cmd.Process.Pid + + var cmdOut string + var cmdErr error + ops := func() { + cmdOut, cmdErr = pft.ProgTester.Command("getcpu") + t.Logf("command getcpu out:%q err:%v", cmdOut, cmdErr) + } + + checkEnforce := func() { + cnt := perfring.RunTestEventReduceCount(t, ctx, ops, perfring.FilterTestMessages, + func(x notify.Message) int { + if kprobe, ok := x.(*tracing.MsgGenericKprobeUnix); ok { + if strings.HasSuffix(kprobe.FuncName, "sys_getcpu") { + return 1 + } + return -1 + } + return 0 + }) + require.NoError(t, cmdErr) + require.Equal(t, cnt[1], 1, fmt.Sprintf("count=%v", cnt)) + enfDump, enfDumpErr := stracing.DumpEnforcerMap(polName, polNamespace) + require.NoError(t, enfDumpErr) + require.Len(t, enfDump, 1) + require.Contains(t, cmdOut, "operation not permitted") + for key, val := range enfDump { + require.Equal(t, pid, int(key.PidTgid>>32)) + require.Equal(t, val.Sig, int16(9)) + break + } + } + + resetEnforcerMap := func() { + err := stracing.ResetEnforcerMap(t, polName, polNamespace) + require.NoError(t, err) + } + + checkMonitor := func() { + cnt := perfring.RunTestEventReduceCount(t, ctx, ops, perfring.FilterTestMessages, + func(x notify.Message) int { + if kprobe, ok := x.(*tracing.MsgGenericKprobeUnix); ok { + if strings.HasSuffix(kprobe.FuncName, "sys_getcpu") { + return 1 + } + return -1 + } + return 0 + }) + require.NoError(t, cmdErr) + require.Equal(t, cnt[1], 1, fmt.Sprintf("count=%v", cnt)) + require.NotContains(t, cmdOut, "operation not permitted") + enfDump, enfDumpErr := stracing.DumpEnforcerMap(polName, polNamespace) + require.NoError(t, enfDumpErr) + require.Len(t, enfDump, 0) + } + + // finally, we can do the test + checkEnforce() + resetEnforcerMap() + policyconf.SetPolicyMode(tp, policyconf.MonitorMode) + checkMonitor() + resetEnforcerMap() + policyconf.SetPolicyMode(tp, policyconf.EnforceMode) + checkEnforce() +}