diff --git a/internal/bootstrap/bootstrap_linux.go b/internal/bootstrap/bootstrap_linux.go index 7a25fd788..5208dab0c 100644 --- a/internal/bootstrap/bootstrap_linux.go +++ b/internal/bootstrap/bootstrap_linux.go @@ -95,6 +95,7 @@ func NewApp(cfg *config.Config, options ...Option) (*App, error) { var engine *rules.Engine var rs *config.RulesCompileResult + var plan *filter.ApproverPlan if cfg.Filters != nil && cfg.Filters.Rules.Enabled && !cfg.ForwardMode && !cfg.IsCaptureSet() && !cfg.IsFilamentSet() { engine = rules.NewEngine(psnap, cfg) var err error @@ -102,6 +103,7 @@ func NewApp(cfg *config.Config, options ...Option) (*App, error) { if err != nil { return nil, err } + plan = engine.ApproverPlan() if rs != nil { log.Infof("rules compile summary: %s", rs) } @@ -111,7 +113,7 @@ func NewApp(cfg *config.Config, options ...Option) (*App, error) { return &App{ config: cfg, - evs: NewEventSourceControl(psnap, cfg, rs), + evs: NewEventSourceControl(psnap, cfg, rs, plan), engine: engine, psnap: psnap, signals: sigs, diff --git a/internal/bootstrap/source_linux.go b/internal/bootstrap/source_linux.go index c341c9b55..69765e6d9 100644 --- a/internal/bootstrap/source_linux.go +++ b/internal/bootstrap/source_linux.go @@ -39,8 +39,9 @@ func NewEventSourceControl( psnap ps.Snapshotter, cfg *config.Config, compiler *config.RulesCompileResult, + plan *filter.ApproverPlan, ) *EventSourceControl { - return &EventSourceControl{evs: libebpf.NewEventSource(psnap, cfg, compiler)} + return &EventSourceControl{evs: libebpf.NewEventSource(psnap, cfg, compiler, plan)} } func (s *EventSourceControl) Open(cfg *config.Config) error { diff --git a/internal/ebpf/approver.go b/internal/ebpf/approver.go new file mode 100644 index 000000000..854494964 --- /dev/null +++ b/internal/ebpf/approver.go @@ -0,0 +1,257 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "fmt" + + "github.com/cilium/ebpf" + "github.com/rabbitstack/fibratus/internal/ebpf/bpf" + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/filter" +) + +func (l *loader) populateApprovers(plan *filter.ApproverPlan) error { + if l == nil || l.exec == nil { + return fmt.Errorf("approver maps are not loaded") + } + if plan == nil { + plan = filter.AllowAllPlan() + } + + cur, err := l.approverGen() + if err != nil { + return err + } + inactive := uint32(1) - cur + if err := l.clearGeneration(inactive); err != nil { + return fmt.Errorf("clearing inactive approver generation: %w", err) + } + if err := l.writePlan(inactive, plan); err != nil { + return err + } + // The retired generation is left populated on purpose. A program that read + // the old generation before the flip still resolves its lookups against it; + // wiping it here would turn those in-flight events into drops. The next + // reload clears it before reusing it. + if err := l.exec.ApproverGen.Put(uint32(0), inactive); err != nil { + return fmt.Errorf("flipping approver generation: %w", err) + } + return nil +} + +func (l *loader) approverGen() (uint32, error) { + var key, gen uint32 + if err := l.exec.ApproverGen.Lookup(&key, &gen); err != nil { + return 0, fmt.Errorf("reading approver generation: %w", err) + } + return gen & 1, nil +} + +func (l *loader) approverRejects() uint64 { + var key uint32 + var n uint64 + if l.exec == nil || l.exec.ApproverReject == nil { + return 0 + } + _ = l.exec.ApproverReject.Lookup(&key, &n) + return n +} + +func (l *loader) writePlan(gen uint32, plan *filter.ApproverPlan) error { + for i := uint32(0); i < approverTypeMax; i++ { + key := gen*approverTypeMax + i + typ := event.Type(i) + pol := plan.Policy(typ) + mode := policyMode(pol, typ) + if err := l.exec.ApproverMode.Put(&key, &mode); err != nil { + return fmt.Errorf("writing approver mode for type %d: %w", i, err) + } + if mode == 0 { + continue + } + if err := l.writePolicy(gen, typ, pol); err != nil { + return err + } + } + return nil +} + +func policyMode(pol filter.TypePolicy, typ event.Type) uint8 { + if filter.AlwaysAllowed(typ) || !typ.Exists() || pol.DefaultAllow { + return 0 + } + var mode uint8 + if pol.RequirePID { + mode |= approverReqPID + } + if pol.RequireFilename { + mode |= approverReqFile + } + // The kernel only parses a destination port out of the stashed sockaddr for + // connect. Enforcing a port on any other type would compare against zero and + // drop everything, so those types keep the rest of their policy instead. + if pol.RequirePort && typ == event.Connect { + mode |= approverReqPort + } + return mode +} + +func (l *loader) writePolicy(gen uint32, typ event.Type, pol filter.TypePolicy) error { + id := uint32(typ) + present := uint8(1) + if pol.RequirePID { + for _, pid := range pol.PIDs { + key := bpf.ExecveApproverPidKey{Gen: gen, Type: id, Pid: pid} + if err := l.exec.ApproverPid.Put(&key, &present); err != nil { + return fmt.Errorf("writing pid approver: %w", err) + } + } + } + if pol.RequirePort && typ == event.Connect { + for _, port := range pol.Ports { + key := bpf.ExecveApproverPortKey{Gen: gen, Type: id, Port: port} + if err := l.exec.ApproverPort.Put(&key, &present); err != nil { + return fmt.Errorf("writing port approver: %w", err) + } + } + } + if pol.RequireFilename { + for _, path := range pol.FileExact { + key := bpf.ExecveApproverFileKey{Gen: gen, Type: id} + copy(key.Path[:], path) + if err := l.exec.ApproverFileEq.Put(&key, &present); err != nil { + return fmt.Errorf("writing file approver: %w", err) + } + } + pre := l.prefixMap(gen) + if pre == nil { + return fmt.Errorf("missing filename prefix map for generation %d", gen) + } + for _, prefix := range pol.FilePrefix { + if prefix == "" || len(prefix) > len(bpf.ExecveApproverLpmKey{}.Path) { + continue + } + key := bpf.ExecveApproverLpmKey{Prefixlen: uint32(len(prefix) * 8)} + copy(key.Path[:], prefix) + if err := pre.Put(&key, &present); err != nil { + return fmt.Errorf("writing file prefix approver: %w", err) + } + } + } + return nil +} + +func (l *loader) prefixMap(gen uint32) *ebpf.Map { + if gen == 0 { + return l.exec.ApproverFilePre0 + } + return l.exec.ApproverFilePre1 +} + +func (l *loader) clearGeneration(gen uint32) error { + if err := deleteHashGen(l.exec.ApproverPid, gen, func(k bpf.ExecveApproverPidKey) uint32 { return k.Gen }); err != nil { + return err + } + if err := deleteHashGen(l.exec.ApproverPort, gen, func(k bpf.ExecveApproverPortKey) uint32 { return k.Gen }); err != nil { + return err + } + if err := deleteHashGen(l.exec.ApproverFileEq, gen, func(k bpf.ExecveApproverFileKey) uint32 { return k.Gen }); err != nil { + return err + } + if err := deleteAll(l.prefixMap(gen)); err != nil { + return err + } + for i := uint32(0); i < approverTypeMax; i++ { + key := gen*approverTypeMax + i + var mode uint8 + if err := l.exec.ApproverMode.Put(&key, &mode); err != nil { + return err + } + } + return nil +} + +func deleteHashGen[K any](m *ebpf.Map, gen uint32, genOf func(K) uint32) error { + if m == nil { + return fmt.Errorf("missing approver map") + } + var key K + var val uint8 + stale := make([]K, 0) + iter := m.Iterate() + for iter.Next(&key, &val) { + if genOf(key) == gen { + stale = append(stale, key) + } + } + if err := iter.Err(); err != nil { + return err + } + for i := range stale { + if err := m.Delete(&stale[i]); err != nil { + return err + } + } + return nil +} + +func deleteAll(m *ebpf.Map) error { + if m == nil { + return fmt.Errorf("missing approver map") + } + var key bpf.ExecveApproverLpmKey + var val uint8 + stale := make([]bpf.ExecveApproverLpmKey, 0) + iter := m.Iterate() + for iter.Next(&key, &val) { + stale = append(stale, key) + } + if err := iter.Err(); err != nil { + return err + } + for i := range stale { + if err := m.Delete(&stale[i]); err != nil { + return err + } + } + return nil +} + +func sharedMaps(exec *bpf.ExecveObjects) map[string]*ebpf.Map { + return map[string]*ebpf.Map{ + eventsMapName: exec.Events, + dropCountMapName: exec.DropCount, + scratchMapName: exec.Scratch, + scratchHeapMapName: exec.ScratchHeap, + enabledMapName: exec.Enabled, + approverGenMapName: exec.ApproverGen, + approverModeMapName: exec.ApproverMode, + approverPIDMapName: exec.ApproverPid, + approverPortMapName: exec.ApproverPort, + approverFileEqMapName: exec.ApproverFileEq, + approverFilePre0Name: exec.ApproverFilePre0, + approverFilePre1Name: exec.ApproverFilePre1, + approverFileHeapName: exec.ApproverFileHeap, + approverLPMHeapName: exec.ApproverLpmHeap, + approverRejectMapName: exec.ApproverReject, + } +} diff --git a/internal/ebpf/approver_integration_test.go b/internal/ebpf/approver_integration_test.go new file mode 100644 index 000000000..a29af5e27 --- /dev/null +++ b/internal/ebpf/approver_integration_test.go @@ -0,0 +1,189 @@ +//go:build linux && ebpf_integration + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/event/params" + "github.com/rabbitstack/fibratus/pkg/filter" + "github.com/rabbitstack/fibratus/pkg/ps" + "github.com/stretchr/testify/require" +) + +// TestApproverDecisionsMatchLoadedPrograms exercises the compiled BPF logic +// rather than the Go model in ApproverPlan.Allows. The two are separate +// implementations of the same predicate, so the soundness property is only +// worth as much as this test. +func TestApproverDecisionsMatchLoadedPrograms(t *testing.T) { + dir := t.TempDir() + allowed := filepath.Join(dir, "allowed.txt") + require.NoError(t, os.WriteFile(allowed, []byte("x"), 0o600)) + + // Establish first that the capture path reports this exact open with no + // approver installed. If that fails, the problem is the enter/exit + // correlation rather than the prefilter, and the second phase would + // otherwise blame the approver for it. + baseline := watchOpens(t, nil, allowed) + require.True(t, baseline.sawAllowed, + "capture never reported the open with no approver installed; pid=%d selfOpens=%d saw %q", + os.Getpid(), baseline.selfOpens, baseline.seen) + + // Written as a matches pattern rather than startswith so the rewrite into + // the LPM trie is covered too. + plan := filter.PlanFromFilter(mustCompile(t, + "evt.name = 'openat' and file.path matches '"+dir+"/*'")) + got := watchOpens(t, plan, allowed) + + require.True(t, got.sawAllowed, + "openat matching the approved pattern never reached userspace; wanted %q, selfOpens=%d saw %q, rejects %d -> %d", + allowed, got.selfOpens, got.seen, got.rejectsBefore, got.rejectsAfter) + require.False(t, got.sawDenied, "openat outside the approved pattern reached userspace") + require.Greater(t, got.rejectsAfter, got.rejectsBefore, "approver reject counter did not advance") +} + +type openWatch struct { + sawAllowed bool + sawDenied bool + seen []string + selfOpens int + rejectsBefore uint64 + rejectsAfter uint64 +} + +const deniedPath = "/etc/hostname" + +// watchOpens repeatedly opens allowed and a denied path while draining events, +// returning what reached userspace. Correlating sys_enter_openat with its exit +// goes through a bounded LRU map, so on a loaded machine any single open can +// lose its scratch entry and arrive with no path at all. Retrying keeps the +// assertions about the approver rather than about that. +func watchOpens(t *testing.T, plan *filter.ApproverPlan, allowed string) openWatch { + t.Helper() + cfg := testConfig() + es := NewEventSource(ps.NewSnapshotter(), cfg, nil, plan).(*EventSource) + require.NoError(t, es.Open(cfg)) + defer func() { _ = es.Close() }() + + w := openWatch{seen: make([]string, 0, 16), rejectsBefore: es.loader.approverRejects()} + + attempts := time.NewTicker(200 * time.Millisecond) + defer attempts.Stop() + touch := func() { openFromChild(t, allowed) } + touch() + + deadline := time.Now().Add(20 * time.Second) + for time.Now().Before(deadline) && !w.sawAllowed { + select { + case evt := <-es.Events(): + if evt.Type != event.Openat { + continue + } + path := evt.GetParamAsString(params.FilePath) + if evt.PID == uint64(os.Getpid()) { + w.selfOpens++ + } + if len(w.seen) < cap(w.seen) { + w.seen = append(w.seen, fmt.Sprintf("pid=%d sys=%d %s", + evt.PID, evt.GetParamAsUint32(params.SyscallID), path)) + } + switch path { + case allowed: + w.sawAllowed = true + case deniedPath: + w.sawDenied = true + } + case err := <-es.Errors(): + t.Fatalf("event source error: %v", err) + case <-attempts.C: + touch() + } + } + w.rejectsAfter = es.loader.approverRejects() + return w +} + +// openFromChild opens the two paths from a short-lived child rather than from +// the test process. Syscall tracepoints did not report this process's own opens +// on every runner, while child processes are reported reliably, which is also +// how the other privileged tests here drive syscalls. +func openFromChild(t *testing.T, allowed string) { + t.Helper() + cmd := exec.Command("/bin/sh", "-c", "cat -- \"$1\" >/dev/null 2>&1; cat -- \"$2\" >/dev/null 2>&1", + "sh", allowed, deniedPath) + _ = cmd.Run() +} + +func TestApproverGenerationFlipAndFailedReload(t *testing.T) { + ldr, err := loadCollections() + if err != nil { + t.Fatalf("loading collections: %v", err) + } + t.Cleanup(func() { _ = ldr.Close() }) + + plan := filter.PlanFromFilter(mustCompile(t, "evt.name = 'openat' and file.path startswith '/tmp'")) + require.NoError(t, ldr.populateApprovers(plan)) + gen, err := ldr.approverGen() + require.NoError(t, err) + require.Equal(t, uint32(1), gen) + + require.NoError(t, ldr.populateApprovers(plan)) + gen, err = ldr.approverGen() + require.NoError(t, err) + require.Equal(t, uint32(0), gen) + + oversized := filter.BuildApproverPlan(manyPrefixFilters(t, 300)) + require.Error(t, ldr.populateApprovers(oversized)) + still, err := ldr.approverGen() + require.NoError(t, err) + require.Equal(t, gen, still) +} + +func mustCompile(t *testing.T, expr string) filter.Filter { + t.Helper() + f := filter.New(expr, testConfig()) + require.NoError(t, f.Compile()) + return f +} + +func manyPrefixFilters(t *testing.T, n int) []filter.Filter { + t.Helper() + out := make([]filter.Filter, 0, n) + for i := 0; i < n; i++ { + expr := "evt.name = 'openat' and file.path startswith '" + "/t" + itoa3(i) + "'" + out = append(out, mustCompile(t, expr)) + } + return out +} + +func itoa3(n int) string { + return string([]byte{ + byte('0' + (n/100)%10), + byte('0' + (n/10)%10), + byte('0' + n%10), + }) +} diff --git a/internal/ebpf/approver_test.go b/internal/ebpf/approver_test.go new file mode 100644 index 000000000..36ad5eb23 --- /dev/null +++ b/internal/ebpf/approver_test.go @@ -0,0 +1,125 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "testing" + + "github.com/rabbitstack/fibratus/internal/ebpf/bpf" + "github.com/rabbitstack/fibratus/pkg/config" + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/filter" + "github.com/rabbitstack/fibratus/pkg/ps" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSetFilterStoresBeforeOpen(t *testing.T) { + cfg := testConfig() + es := NewEventSource(ps.NewSnapshotter(), cfg, nil, nil).(*EventSource) + f := filter.New("evt.name = 'openat' and file.path startswith '/tmp'", cfg) + require.NoError(t, f.Compile()) + es.SetFilter(f) + require.NotNil(t, es.cliPlan) + pol := es.cliPlan.Policy(event.Openat) + require.False(t, pol.DefaultAllow) + assert.True(t, pol.RequireFilename) + assert.Nil(t, es.loader) +} + +func TestReloadApproversWithoutMapsIsNoop(t *testing.T) { + es := NewEventSource(ps.NewSnapshotter(), testConfig(), nil, nil).(*EventSource) + require.NoError(t, es.reloadApprovers()) +} + +func TestPolicyModeKeepsProcessState(t *testing.T) { + pol := filter.TypePolicy{RequireFilename: true, FilePrefix: []string{"/tmp"}} + assert.Equal(t, uint8(0), policyMode(pol, event.Execve)) + assert.Equal(t, uint8(0), policyMode(pol, event.Exit)) + assert.Equal(t, uint8(0), policyMode(pol, event.Clone)) + assert.Equal(t, uint8(approverReqFile), policyMode(pol, event.Openat)) +} + +func TestModeArrayMatchesGeneratedMap(t *testing.T) { + spec, err := bpf.LoadExecve() + require.NoError(t, err) + require.Equal(t, uint32(approverTypeMax*2), spec.Maps[approverModeMapName].MaxEntries, + "approverTypeMax must mirror EVT_TYPE_MAX in c/common/approvers.h") +} + +func BenchmarkApproverReduction(b *testing.B) { + cfg := &config.Config{ + EventSource: config.EventSourceConfig{EnableFileIOEvents: true}, + Filters: &config.Filters{}, + } + f := filter.New("evt.name = 'openat' and file.path startswith '/tmp'", cfg) + if err := f.Compile(); err != nil { + b.Fatal(err) + } + plan := filter.PlanFromFilter(f) + paths := []string{"/tmp/a", "/tmp/b", "/var/log/syslog", "/etc/passwd", "/usr/bin/ls"} + kept := 0 + b.ResetTimer() + for i := 0; i < b.N; i++ { + s := filter.Sample{Type: event.Openat, Filename: paths[i%len(paths)]} + if plan.Allows(s) { + kept++ + } + } + b.StopTimer() + if b.N > 0 { + b.ReportMetric(float64(b.N-kept)/float64(b.N), "drop_ratio") + } +} + +func TestApproverSoundnessTable(t *testing.T) { + cfg := testConfig() + cases := []struct { + expr string + keep filter.Sample + drop filter.Sample + }{ + { + expr: "evt.name = 'openat' and file.path startswith '/tmp'", + keep: filter.Sample{Type: event.Openat, Filename: "/tmp/x"}, + drop: filter.Sample{Type: event.Openat, Filename: "/etc/passwd"}, + }, + { + expr: "evt.name = 'connect' and net.dport in (80, 443)", + keep: filter.Sample{Type: event.Connect, Port: 443}, + drop: filter.Sample{Type: event.Connect, Port: 22}, + }, + { + expr: "evt.name = 'kill' and ps.pid = 9", + keep: filter.Sample{Type: event.Kill, PID: 9}, + drop: filter.Sample{Type: event.Kill, PID: 1}, + }, + } + for _, tt := range cases { + t.Run(tt.expr, func(t *testing.T) { + f := filter.New(tt.expr, cfg) + require.NoError(t, f.Compile()) + plan := filter.PlanFromFilter(f) + assert.True(t, plan.Allows(tt.keep), tt.expr) + assert.False(t, plan.Allows(tt.drop), tt.expr) + }) + } +} diff --git a/internal/ebpf/bpf/clone_bpfeb.go b/internal/ebpf/bpf/clone_bpfeb.go index e8366cd2a..d3d15e461 100644 --- a/internal/ebpf/bpf/clone_bpfeb.go +++ b/internal/ebpf/bpf/clone_bpfeb.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type CloneApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type CloneApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type CloneApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type CloneApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type CloneScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -83,11 +111,21 @@ type CloneProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type CloneMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // CloneVariableSpecs contains global variables before they are loaded into the kernel. @@ -116,15 +154,35 @@ func (o *CloneObjects) Close() error { // // It can be passed to LoadCloneObjects or ebpf.CollectionSpec.LoadAndAssign. type CloneMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *CloneMaps) Close() error { return _CloneClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/clone_bpfeb.o b/internal/ebpf/bpf/clone_bpfeb.o index b8237d893..34882e98c 100644 Binary files a/internal/ebpf/bpf/clone_bpfeb.o and b/internal/ebpf/bpf/clone_bpfeb.o differ diff --git a/internal/ebpf/bpf/clone_bpfel.go b/internal/ebpf/bpf/clone_bpfel.go index 7df15faff..dcf3c9a19 100644 --- a/internal/ebpf/bpf/clone_bpfel.go +++ b/internal/ebpf/bpf/clone_bpfel.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type CloneApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type CloneApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type CloneApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type CloneApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type CloneScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -83,11 +111,21 @@ type CloneProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type CloneMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // CloneVariableSpecs contains global variables before they are loaded into the kernel. @@ -116,15 +154,35 @@ func (o *CloneObjects) Close() error { // // It can be passed to LoadCloneObjects or ebpf.CollectionSpec.LoadAndAssign. type CloneMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *CloneMaps) Close() error { return _CloneClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/clone_bpfel.o b/internal/ebpf/bpf/clone_bpfel.o index 237b36968..8d659e946 100644 Binary files a/internal/ebpf/bpf/clone_bpfel.o and b/internal/ebpf/bpf/clone_bpfel.o differ diff --git a/internal/ebpf/bpf/ctl_bpfeb.go b/internal/ebpf/bpf/ctl_bpfeb.go index 16ff6f0b1..4ca490886 100644 --- a/internal/ebpf/bpf/ctl_bpfeb.go +++ b/internal/ebpf/bpf/ctl_bpfeb.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type CtlApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type CtlApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type CtlApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type CtlApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type CtlScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -75,11 +103,21 @@ type CtlProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type CtlMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // CtlVariableSpecs contains global variables before they are loaded into the kernel. @@ -108,15 +146,35 @@ func (o *CtlObjects) Close() error { // // It can be passed to LoadCtlObjects or ebpf.CollectionSpec.LoadAndAssign. type CtlMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *CtlMaps) Close() error { return _CtlClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/ctl_bpfeb.o b/internal/ebpf/bpf/ctl_bpfeb.o index 63c85584a..0769585e8 100644 Binary files a/internal/ebpf/bpf/ctl_bpfeb.o and b/internal/ebpf/bpf/ctl_bpfeb.o differ diff --git a/internal/ebpf/bpf/ctl_bpfel.go b/internal/ebpf/bpf/ctl_bpfel.go index dc85991c4..74590e969 100644 --- a/internal/ebpf/bpf/ctl_bpfel.go +++ b/internal/ebpf/bpf/ctl_bpfel.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type CtlApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type CtlApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type CtlApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type CtlApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type CtlScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -75,11 +103,21 @@ type CtlProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type CtlMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // CtlVariableSpecs contains global variables before they are loaded into the kernel. @@ -108,15 +146,35 @@ func (o *CtlObjects) Close() error { // // It can be passed to LoadCtlObjects or ebpf.CollectionSpec.LoadAndAssign. type CtlMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *CtlMaps) Close() error { return _CtlClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/ctl_bpfel.o b/internal/ebpf/bpf/ctl_bpfel.o index 84c760c08..c61f26b2b 100644 Binary files a/internal/ebpf/bpf/ctl_bpfel.o and b/internal/ebpf/bpf/ctl_bpfel.o differ diff --git a/internal/ebpf/bpf/execve_bpfeb.go b/internal/ebpf/bpf/execve_bpfeb.go index 9ed97d93b..515b4d48e 100644 --- a/internal/ebpf/bpf/execve_bpfeb.go +++ b/internal/ebpf/bpf/execve_bpfeb.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type ExecveApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type ExecveApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type ExecveApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type ExecveApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type ExecveScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -78,11 +106,21 @@ type ExecveProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type ExecveMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // ExecveVariableSpecs contains global variables before they are loaded into the kernel. @@ -111,15 +149,35 @@ func (o *ExecveObjects) Close() error { // // It can be passed to LoadExecveObjects or ebpf.CollectionSpec.LoadAndAssign. type ExecveMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *ExecveMaps) Close() error { return _ExecveClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/execve_bpfeb.o b/internal/ebpf/bpf/execve_bpfeb.o index 3a22c38b0..02e3398c1 100644 Binary files a/internal/ebpf/bpf/execve_bpfeb.o and b/internal/ebpf/bpf/execve_bpfeb.o differ diff --git a/internal/ebpf/bpf/execve_bpfel.go b/internal/ebpf/bpf/execve_bpfel.go index 1b73d6db5..d463e5a10 100644 --- a/internal/ebpf/bpf/execve_bpfel.go +++ b/internal/ebpf/bpf/execve_bpfel.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type ExecveApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type ExecveApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type ExecveApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type ExecveApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type ExecveScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -78,11 +106,21 @@ type ExecveProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type ExecveMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // ExecveVariableSpecs contains global variables before they are loaded into the kernel. @@ -111,15 +149,35 @@ func (o *ExecveObjects) Close() error { // // It can be passed to LoadExecveObjects or ebpf.CollectionSpec.LoadAndAssign. type ExecveMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *ExecveMaps) Close() error { return _ExecveClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/execve_bpfel.o b/internal/ebpf/bpf/execve_bpfel.o index 300111f3a..5e46cdabc 100644 Binary files a/internal/ebpf/bpf/execve_bpfel.o and b/internal/ebpf/bpf/execve_bpfel.o differ diff --git a/internal/ebpf/bpf/exit_bpfeb.go b/internal/ebpf/bpf/exit_bpfeb.go index 48d5ce17b..ce9bfc931 100644 --- a/internal/ebpf/bpf/exit_bpfeb.go +++ b/internal/ebpf/bpf/exit_bpfeb.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type ExitApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type ExitApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type ExitApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type ExitApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type ExitScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -75,11 +103,21 @@ type ExitProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type ExitMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // ExitVariableSpecs contains global variables before they are loaded into the kernel. @@ -108,15 +146,35 @@ func (o *ExitObjects) Close() error { // // It can be passed to LoadExitObjects or ebpf.CollectionSpec.LoadAndAssign. type ExitMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *ExitMaps) Close() error { return _ExitClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/exit_bpfeb.o b/internal/ebpf/bpf/exit_bpfeb.o index 4ddda18cc..cc57f9c81 100644 Binary files a/internal/ebpf/bpf/exit_bpfeb.o and b/internal/ebpf/bpf/exit_bpfeb.o differ diff --git a/internal/ebpf/bpf/exit_bpfel.go b/internal/ebpf/bpf/exit_bpfel.go index 146c15056..98c83f8cd 100644 --- a/internal/ebpf/bpf/exit_bpfel.go +++ b/internal/ebpf/bpf/exit_bpfel.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type ExitApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type ExitApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type ExitApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type ExitApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type ExitScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -75,11 +103,21 @@ type ExitProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type ExitMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // ExitVariableSpecs contains global variables before they are loaded into the kernel. @@ -108,15 +146,35 @@ func (o *ExitObjects) Close() error { // // It can be passed to LoadExitObjects or ebpf.CollectionSpec.LoadAndAssign. type ExitMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *ExitMaps) Close() error { return _ExitClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/exit_bpfel.o b/internal/ebpf/bpf/exit_bpfel.o index 92dbc6818..6471cbdd2 100644 Binary files a/internal/ebpf/bpf/exit_bpfel.o and b/internal/ebpf/bpf/exit_bpfel.o differ diff --git a/internal/ebpf/bpf/file_bpfeb.go b/internal/ebpf/bpf/file_bpfeb.go index aa41f2094..28c624010 100644 --- a/internal/ebpf/bpf/file_bpfeb.go +++ b/internal/ebpf/bpf/file_bpfeb.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type FileApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type FileApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type FileApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type FileApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type FileScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -90,11 +118,21 @@ type FileProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type FileMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // FileVariableSpecs contains global variables before they are loaded into the kernel. @@ -123,15 +161,35 @@ func (o *FileObjects) Close() error { // // It can be passed to LoadFileObjects or ebpf.CollectionSpec.LoadAndAssign. type FileMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *FileMaps) Close() error { return _FileClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/file_bpfeb.o b/internal/ebpf/bpf/file_bpfeb.o index 2ff042363..46747d3c1 100644 Binary files a/internal/ebpf/bpf/file_bpfeb.o and b/internal/ebpf/bpf/file_bpfeb.o differ diff --git a/internal/ebpf/bpf/file_bpfel.go b/internal/ebpf/bpf/file_bpfel.go index 1d702b970..f66f09b8d 100644 --- a/internal/ebpf/bpf/file_bpfel.go +++ b/internal/ebpf/bpf/file_bpfel.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type FileApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type FileApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type FileApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type FileApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type FileScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -90,11 +118,21 @@ type FileProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type FileMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // FileVariableSpecs contains global variables before they are loaded into the kernel. @@ -123,15 +161,35 @@ func (o *FileObjects) Close() error { // // It can be passed to LoadFileObjects or ebpf.CollectionSpec.LoadAndAssign. type FileMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *FileMaps) Close() error { return _FileClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/file_bpfel.o b/internal/ebpf/bpf/file_bpfel.o index 42919ea6b..610f1b10e 100644 Binary files a/internal/ebpf/bpf/file_bpfel.o and b/internal/ebpf/bpf/file_bpfel.o differ diff --git a/internal/ebpf/bpf/mem_bpfeb.go b/internal/ebpf/bpf/mem_bpfeb.go index e5a1a1c02..5f8e03f39 100644 --- a/internal/ebpf/bpf/mem_bpfeb.go +++ b/internal/ebpf/bpf/mem_bpfeb.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type MemApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type MemApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type MemApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type MemApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type MemScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -80,11 +108,21 @@ type MemProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type MemMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // MemVariableSpecs contains global variables before they are loaded into the kernel. @@ -113,15 +151,35 @@ func (o *MemObjects) Close() error { // // It can be passed to LoadMemObjects or ebpf.CollectionSpec.LoadAndAssign. type MemMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *MemMaps) Close() error { return _MemClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/mem_bpfeb.o b/internal/ebpf/bpf/mem_bpfeb.o index 789506c53..b7514d2e1 100644 Binary files a/internal/ebpf/bpf/mem_bpfeb.o and b/internal/ebpf/bpf/mem_bpfeb.o differ diff --git a/internal/ebpf/bpf/mem_bpfel.go b/internal/ebpf/bpf/mem_bpfel.go index 7a8d011c2..e807e14ec 100644 --- a/internal/ebpf/bpf/mem_bpfel.go +++ b/internal/ebpf/bpf/mem_bpfel.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type MemApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type MemApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type MemApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type MemApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type MemScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -80,11 +108,21 @@ type MemProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type MemMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // MemVariableSpecs contains global variables before they are loaded into the kernel. @@ -113,15 +151,35 @@ func (o *MemObjects) Close() error { // // It can be passed to LoadMemObjects or ebpf.CollectionSpec.LoadAndAssign. type MemMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *MemMaps) Close() error { return _MemClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/mem_bpfel.o b/internal/ebpf/bpf/mem_bpfel.o index 5d167155c..dae3f656b 100644 Binary files a/internal/ebpf/bpf/mem_bpfel.o and b/internal/ebpf/bpf/mem_bpfel.o differ diff --git a/internal/ebpf/bpf/net_bpfeb.go b/internal/ebpf/bpf/net_bpfeb.go index 6f3c05914..c83567465 100644 --- a/internal/ebpf/bpf/net_bpfeb.go +++ b/internal/ebpf/bpf/net_bpfeb.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type NetApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type NetApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type NetApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type NetApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type NetScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -80,11 +108,21 @@ type NetProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type NetMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // NetVariableSpecs contains global variables before they are loaded into the kernel. @@ -113,15 +151,35 @@ func (o *NetObjects) Close() error { // // It can be passed to LoadNetObjects or ebpf.CollectionSpec.LoadAndAssign. type NetMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *NetMaps) Close() error { return _NetClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/net_bpfeb.o b/internal/ebpf/bpf/net_bpfeb.o index e33bd9ac6..e8851dbd5 100644 Binary files a/internal/ebpf/bpf/net_bpfeb.o and b/internal/ebpf/bpf/net_bpfeb.o differ diff --git a/internal/ebpf/bpf/net_bpfel.go b/internal/ebpf/bpf/net_bpfel.go index 95f7f27d6..8e87c33a6 100644 --- a/internal/ebpf/bpf/net_bpfel.go +++ b/internal/ebpf/bpf/net_bpfel.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type NetApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type NetApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type NetApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type NetApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type NetScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -80,11 +108,21 @@ type NetProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type NetMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // NetVariableSpecs contains global variables before they are loaded into the kernel. @@ -113,15 +151,35 @@ func (o *NetObjects) Close() error { // // It can be passed to LoadNetObjects or ebpf.CollectionSpec.LoadAndAssign. type NetMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *NetMaps) Close() error { return _NetClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/net_bpfel.o b/internal/ebpf/bpf/net_bpfel.o index 6757c2dda..9b8be1756 100644 Binary files a/internal/ebpf/bpf/net_bpfel.o and b/internal/ebpf/bpf/net_bpfel.o differ diff --git a/internal/ebpf/bpf/prociter_bpfeb.go b/internal/ebpf/bpf/prociter_bpfeb.go index 1f27cf6de..7c35590d7 100644 --- a/internal/ebpf/bpf/prociter_bpfeb.go +++ b/internal/ebpf/bpf/prociter_bpfeb.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type ProciterApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type ProciterApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type ProciterApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type ProciterApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type ProciterScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -75,11 +103,21 @@ type ProciterProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type ProciterMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // ProciterVariableSpecs contains global variables before they are loaded into the kernel. @@ -108,15 +146,35 @@ func (o *ProciterObjects) Close() error { // // It can be passed to LoadProciterObjects or ebpf.CollectionSpec.LoadAndAssign. type ProciterMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *ProciterMaps) Close() error { return _ProciterClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/prociter_bpfeb.o b/internal/ebpf/bpf/prociter_bpfeb.o index e93d91e49..541573a5d 100644 Binary files a/internal/ebpf/bpf/prociter_bpfeb.o and b/internal/ebpf/bpf/prociter_bpfeb.o differ diff --git a/internal/ebpf/bpf/prociter_bpfel.go b/internal/ebpf/bpf/prociter_bpfel.go index 7fc5ac494..fada2c274 100644 --- a/internal/ebpf/bpf/prociter_bpfel.go +++ b/internal/ebpf/bpf/prociter_bpfel.go @@ -13,6 +13,34 @@ import ( "github.com/cilium/ebpf" ) +type ProciterApproverFileKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Path [256]uint8 +} + +type ProciterApproverLpmKey struct { + _ structs.HostLayout + Prefixlen uint32 + Path [256]uint8 +} + +type ProciterApproverPidKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Pid uint64 +} + +type ProciterApproverPortKey struct { + _ structs.HostLayout + Gen uint32 + Type uint32 + Port uint16 + Pad uint16 +} + type ProciterScratchValue struct { _ structs.HostLayout Arg0 uint64 @@ -75,11 +103,21 @@ type ProciterProgramSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type ProciterMapSpecs struct { - DropCount *ebpf.MapSpec `ebpf:"drop_count"` - Enabled *ebpf.MapSpec `ebpf:"enabled"` - Events *ebpf.MapSpec `ebpf:"events"` - Scratch *ebpf.MapSpec `ebpf:"scratch"` - ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.MapSpec `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.MapSpec `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.MapSpec `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.MapSpec `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.MapSpec `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.MapSpec `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.MapSpec `ebpf:"approver_mode"` + ApproverPid *ebpf.MapSpec `ebpf:"approver_pid"` + ApproverPort *ebpf.MapSpec `ebpf:"approver_port"` + ApproverReject *ebpf.MapSpec `ebpf:"approver_reject"` + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Enabled *ebpf.MapSpec `ebpf:"enabled"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` + ScratchHeap *ebpf.MapSpec `ebpf:"scratch_heap"` } // ProciterVariableSpecs contains global variables before they are loaded into the kernel. @@ -108,15 +146,35 @@ func (o *ProciterObjects) Close() error { // // It can be passed to LoadProciterObjects or ebpf.CollectionSpec.LoadAndAssign. type ProciterMaps struct { - DropCount *ebpf.Map `ebpf:"drop_count"` - Enabled *ebpf.Map `ebpf:"enabled"` - Events *ebpf.Map `ebpf:"events"` - Scratch *ebpf.Map `ebpf:"scratch"` - ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` + ApproverFileEq *ebpf.Map `ebpf:"approver_file_eq"` + ApproverFileHeap *ebpf.Map `ebpf:"approver_file_heap"` + ApproverFilePre0 *ebpf.Map `ebpf:"approver_file_pre_0"` + ApproverFilePre1 *ebpf.Map `ebpf:"approver_file_pre_1"` + ApproverGen *ebpf.Map `ebpf:"approver_gen"` + ApproverLpmHeap *ebpf.Map `ebpf:"approver_lpm_heap"` + ApproverMode *ebpf.Map `ebpf:"approver_mode"` + ApproverPid *ebpf.Map `ebpf:"approver_pid"` + ApproverPort *ebpf.Map `ebpf:"approver_port"` + ApproverReject *ebpf.Map `ebpf:"approver_reject"` + DropCount *ebpf.Map `ebpf:"drop_count"` + Enabled *ebpf.Map `ebpf:"enabled"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` + ScratchHeap *ebpf.Map `ebpf:"scratch_heap"` } func (m *ProciterMaps) Close() error { return _ProciterClose( + m.ApproverFileEq, + m.ApproverFileHeap, + m.ApproverFilePre0, + m.ApproverFilePre1, + m.ApproverGen, + m.ApproverLpmHeap, + m.ApproverMode, + m.ApproverPid, + m.ApproverPort, + m.ApproverReject, m.DropCount, m.Enabled, m.Events, diff --git a/internal/ebpf/bpf/prociter_bpfel.o b/internal/ebpf/bpf/prociter_bpfel.o index c582ec5f2..50f08fef8 100644 Binary files a/internal/ebpf/bpf/prociter_bpfel.o and b/internal/ebpf/bpf/prociter_bpfel.o differ diff --git a/internal/ebpf/c/common/approvers.h b/internal/ebpf/c/common/approvers.h new file mode 100644 index 000000000..c9496b3f8 --- /dev/null +++ b/internal/ebpf/c/common/approvers.h @@ -0,0 +1,249 @@ +/* Conservative in-kernel approvers. Default-allow unless userspace + * populated a proven superset of equality/list/prefix predicates. + */ + +#pragma once + +#ifndef BPF_F_NO_PREALLOC +#define BPF_F_NO_PREALLOC (1U << 0) +#endif + +#define APPR_REQ_PID (1u << 0) +#define APPR_REQ_FILE (1u << 1) +#define APPR_REQ_PORT (1u << 2) + +#define APPR_GENS 2 +#define APPR_MODE_MAX (EVT_TYPE_MAX * APPR_GENS) + +/* There is no in-kernel glob matcher. A backtracking matcher forks a verifier + * path at every byte comparison, and even two patterns over 64 steps exhausts + * the 1M instruction budget. Userspace instead rewrites the patterns it can + * prove equivalent to a prefix into the LPM trie below, and leaves the rest + * default-allow. + */ + +struct approver_pid_key { + u32 gen; + u32 type; + u64 pid; +}; + +struct approver_port_key { + u32 gen; + u32 type; + u16 port; + u16 pad; +}; + +struct approver_file_key { + u32 gen; + u32 type; + u8 path[EVT_FILENAME_LEN]; +}; + +struct approver_lpm_key { + u32 prefixlen; + u8 path[EVT_FILENAME_LEN]; +}; + + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, u32); + __type(value, u32); +} approver_gen SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, APPR_MODE_MAX); + __type(key, u32); + __type(value, u8); +} approver_mode SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct approver_pid_key); + __type(value, u8); +} approver_pid SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct approver_port_key); + __type(value, u8); +} approver_port SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 1024); + __type(key, struct approver_file_key); + __type(value, u8); +} approver_file_eq SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LPM_TRIE); + __uint(max_entries, 256); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, struct approver_lpm_key); + __type(value, u8); +} approver_file_pre_0 SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LPM_TRIE); + __uint(max_entries, 256); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, struct approver_lpm_key); + __type(value, u8); +} approver_file_pre_1 SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); + __type(key, u32); + __type(value, struct approver_file_key); +} approver_file_heap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); + __type(key, u32); + __type(value, struct approver_lpm_key); +} approver_lpm_heap SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, u32); + __type(value, u64); +} approver_reject SEC(".maps"); + + +static __always_inline void account_approver_reject(void) +{ + u32 key = 0; + u64 *count; + + count = bpf_map_lookup_elem(&approver_reject, &key); + if (!count) + return; + __sync_fetch_and_add(count, 1); +} + +static __always_inline u32 approver_generation(void) +{ + u32 key = 0; + u32 *gen; + + gen = bpf_map_lookup_elem(&approver_gen, &key); + if (!gen) + return 0; + return *gen & 1; +} + +static __always_inline u16 sockaddr_dport(const u8 *aux) +{ + u16 family; + u16 port; + + if (!aux) + return 0; + family = (u16)aux[0] | ((u16)aux[1] << 8); + if (family != 2 && family != 10) + return 0; + port = ((u16)aux[2] << 8) | aux[3]; + return port; +} + +static __always_inline int stash_filename(const u8 *filename) +{ + struct approver_file_key *exact; + u32 zero = 0; + + exact = bpf_map_lookup_elem(&approver_file_heap, &zero); + if (!exact) + return 0; + __builtin_memset(exact, 0, sizeof(*exact)); + if (filename) + __builtin_memcpy(exact->path, filename, EVT_FILENAME_LEN); + return 1; +} + +static __always_inline int approver_file_match(u32 gen, u32 type) +{ + struct approver_file_key *exact; + struct approver_lpm_key *lpm; + u32 zero = 0; + + exact = bpf_map_lookup_elem(&approver_file_heap, &zero); + if (exact) { + exact->gen = gen; + exact->type = type; + if (bpf_map_lookup_elem(&approver_file_eq, exact)) + return 1; + lpm = bpf_map_lookup_elem(&approver_lpm_heap, &zero); + if (lpm) { + __builtin_memset(lpm, 0, sizeof(*lpm)); + lpm->prefixlen = EVT_FILENAME_LEN * 8; + __builtin_memcpy(lpm->path, exact->path, EVT_FILENAME_LEN); + if (gen) { + if (bpf_map_lookup_elem(&approver_file_pre_1, lpm)) + return 1; + } else if (bpf_map_lookup_elem(&approver_file_pre_0, lpm)) { + return 1; + } + } + } + return 0; +} + +static __always_inline int event_approved(u32 type, u16 port, u32 truncated) +{ + u32 gen; + u32 mode_key; + u8 *mode; + struct approver_pid_key pkey = {}; + struct approver_port_key rkey = {}; + + if (type == EVT_TYPE_SNAPSHOT || type == EVT_TYPE_EXECVE || + type == EVT_TYPE_EXIT || type == EVT_TYPE_CLONE) + return 1; + if (type >= EVT_TYPE_MAX) + return 1; + + gen = approver_generation(); + mode_key = gen * EVT_TYPE_MAX + type; + if (mode_key >= APPR_MODE_MAX) + return 1; + mode = bpf_map_lookup_elem(&approver_mode, &mode_key); + if (!mode || *mode == 0) + return 1; + + if (*mode & APPR_REQ_PID) { + pkey.gen = gen; + pkey.type = type; + /* The init-namespace tgid, which is also what fill_current_ids puts on + * the event, so a ps.pid predicate compares like with like. + */ + pkey.pid = bpf_get_current_pid_tgid() >> 32; + if (!bpf_map_lookup_elem(&approver_pid, &pkey)) + return 0; + } + + if (*mode & APPR_REQ_FILE) { + if (!(truncated & TRUNC_FILENAME) && + !approver_file_match(gen, type)) + return 0; + } + + if (*mode & APPR_REQ_PORT) { + rkey.gen = gen; + rkey.type = type; + rkey.port = port; + if (!bpf_map_lookup_elem(&approver_port, &rkey)) + return 0; + } + + return 1; +} diff --git a/internal/ebpf/c/common/events.h b/internal/ebpf/c/common/events.h index 185eea59c..e9df5bc41 100644 --- a/internal/ebpf/c/common/events.h +++ b/internal/ebpf/c/common/events.h @@ -138,6 +138,8 @@ struct { __type(value, u8); } enabled SEC(".maps"); +#include "approvers.h" + static __always_inline void account_drop(void) { u32 key = 0; @@ -256,12 +258,29 @@ static __always_inline int submit_from_scratch(u32 type, long ret, u32 syscall_i u64 key = bpf_get_current_pid_tgid(); struct scratch_value *val; struct syscall_event *e; + u16 port = 0; + u32 truncated = 0; if (!type_enabled(type)) { bpf_map_delete_elem(&scratch, &key); return 0; } + val = bpf_map_lookup_elem(&scratch, &key); + if (val) { + truncated = val->truncated; + if (type == EVT_TYPE_CONNECT) + port = sockaddr_dport(val->aux); + stash_filename(val->filename); + } else { + stash_filename(NULL); + } + if (!event_approved(type, port, truncated)) { + account_approver_reject(); + bpf_map_delete_elem(&scratch, &key); + return 0; + } + e = reserve_event(); if (!e) { bpf_map_delete_elem(&scratch, &key); @@ -318,6 +337,11 @@ static __always_inline int submit_args(u32 type, long ret, u32 syscall_id, if (!type_enabled(type)) return 0; + stash_filename(NULL); + if (!event_approved(type, 0, 0)) { + account_approver_reject(); + return 0; + } e = reserve_event(); if (!e) diff --git a/internal/ebpf/consumer.go b/internal/ebpf/consumer.go index ea5ea037b..36d466d6f 100644 --- a/internal/ebpf/consumer.go +++ b/internal/ebpf/consumer.go @@ -42,6 +42,7 @@ var ( lateSnapshots = expvar.NewInt("ebpf.startup.snapshot.late") enrichmentMiss = expvar.NewInt("ebpf.enrichment.miss") ringbufDrops = expvar.NewInt("ebpf.ringbuf.drops") + approverDrops = expvar.NewInt("ebpf.approver.drops") ) func applyProcessState(psnap ps.Snapshotter, evt *event.Event) { diff --git a/internal/ebpf/doc.go b/internal/ebpf/doc.go index d283bd6b7..97119d250 100644 --- a/internal/ebpf/doc.go +++ b/internal/ebpf/doc.go @@ -19,6 +19,8 @@ // Package ebpf hosts the Linux eBPF instrumentation backend. The event // source loads CO-RE programs, captures process, file, network, memory, // and process-control syscalls, and reconciles live events against an -// iter/task baseline. The spike subdirectory remains as a feasibility -// prototype. +// iter/task baseline. Optional in-kernel approvers drop events that cannot +// match compiled rules or the CLI filter, using only positive equality, +// list, and prefix predicates. The spike subdirectory remains as a +// feasibility prototype. package ebpf diff --git a/internal/ebpf/loader.go b/internal/ebpf/loader.go index 7ea3dc4f9..cb7ea0af1 100644 --- a/internal/ebpf/loader.go +++ b/internal/ebpf/loader.go @@ -95,13 +95,7 @@ func loadCollections() (*loader, error) { return nil, fmt.Errorf("loading execve objects: %w", err) } - replacements := map[string]*ebpf.Map{ - eventsMapName: execObjs.Events, - dropCountMapName: execObjs.DropCount, - scratchMapName: execObjs.Scratch, - scratchHeapMapName: execObjs.ScratchHeap, - enabledMapName: execObjs.Enabled, - } + replacements := sharedMaps(&execObjs) opts := &ebpf.CollectionOptions{MapReplacements: replacements} var exitObjs bpf.ExitObjects diff --git a/internal/ebpf/maps.go b/internal/ebpf/maps.go index c07a9de62..51f129b46 100644 --- a/internal/ebpf/maps.go +++ b/internal/ebpf/maps.go @@ -21,9 +21,28 @@ package ebpf const ( - eventsMapName = "events" - dropCountMapName = "drop_count" - scratchMapName = "scratch" - scratchHeapMapName = "scratch_heap" - enabledMapName = "enabled" + eventsMapName = "events" + dropCountMapName = "drop_count" + scratchMapName = "scratch" + scratchHeapMapName = "scratch_heap" + enabledMapName = "enabled" + approverGenMapName = "approver_gen" + approverModeMapName = "approver_mode" + approverPIDMapName = "approver_pid" + approverPortMapName = "approver_port" + approverFileEqMapName = "approver_file_eq" + approverFilePre0Name = "approver_file_pre_0" + approverFilePre1Name = "approver_file_pre_1" + approverFileHeapName = "approver_file_heap" + approverLPMHeapName = "approver_lpm_heap" + approverRejectMapName = "approver_reject" +) + +// Mirrors APPR_REQ_* and EVT_TYPE_MAX in c/common/approvers.h. Both sides index +// the mode array as generation*approverTypeMax+type, so the values must agree. +const ( + approverReqPID = 1 << 0 + approverReqFile = 1 << 1 + approverReqPort = 1 << 2 + approverTypeMax = 32 ) diff --git a/internal/ebpf/source.go b/internal/ebpf/source.go index e692659f1..c88a093ba 100644 --- a/internal/ebpf/source.go +++ b/internal/ebpf/source.go @@ -50,8 +50,11 @@ type EventSource struct { evts chan *event.Event errs chan error - filter filter.Filter - listeners []event.Listener + filter filter.Filter + rulesPlan *filter.ApproverPlan + cliPlan *filter.ApproverPlan + approverMu sync.Mutex + listeners []event.Listener loader *loader reader *ringReader @@ -67,9 +70,7 @@ type EventSource struct { } // NewEventSource constructs the Linux eBPF event source. -func NewEventSource(psnap ps.Snapshotter, cfg *config.Config, _ *config.RulesCompileResult) source.EventSource { - // Startup replay pushes queued events before the aggregator starts - // consuming, so the channel must be able to absorb a full pending queue. +func NewEventSource(psnap ps.Snapshotter, cfg *config.Config, _ *config.RulesCompileResult, plan *filter.ApproverPlan) source.EventSource { evts := make(chan *event.Event, defaultPendingCap) return &EventSource{ psnap: psnap, @@ -82,6 +83,7 @@ func NewEventSource(psnap ps.Snapshotter, cfg *config.Config, _ *config.RulesCom pendingCap: defaultPendingCap, stop: make(chan struct{}), done: make(chan struct{}), + rulesPlan: plan, } } @@ -111,8 +113,12 @@ func (e *EventSource) Open(cfg *config.Config) error { if err := ldr.setEnabledTypes(&e.config.EventSource); err != nil { _ = ldr.Close() + e.loader = nil return err } + if err := e.reloadApprovers(); err != nil { + log.Warnf("approver population failed, default-allow: %v", err) + } rd, err := newRingReader(ldr.eventsMap()) if err != nil { @@ -135,11 +141,21 @@ func (e *EventSource) Open(cfg *config.Config) error { time.Sleep(startupDrain) e.finishBaseline() - ringbufDrops.Add(int64(ldr.dropCount())) + e.sampleCounters() log.Infof("eBPF process source is live; %s", e.startupSummary()) return nil } +// sampleCounters mirrors the cumulative kernel-side counters into expvars. The +// maps hold running totals, so this reads them rather than accumulating. +func (e *EventSource) sampleCounters() { + if e.loader == nil { + return + } + ringbufDrops.Set(int64(e.loader.dropCount())) + approverDrops.Set(int64(e.loader.approverRejects())) +} + func (e *EventSource) Close() error { var err error e.once.Do(func() { @@ -149,6 +165,7 @@ func (e *EventSource) Close() error { _ = e.reader.Close() } if e.loader != nil { + e.sampleCounters() err = e.loader.Close() } if started { @@ -168,7 +185,24 @@ func (e *EventSource) Errors() <-chan error { return e.errs } func (e *EventSource) Events() <-chan *event.Event { return e.q.Events() } -func (e *EventSource) SetFilter(f filter.Filter) { e.filter = f } +func (e *EventSource) SetFilter(f filter.Filter) { + e.filter = f + e.approverMu.Lock() + e.cliPlan = filter.PlanFromFilter(f) + e.approverMu.Unlock() + if err := e.reloadApprovers(); err != nil { + log.Warnf("approver reload failed, keeping previous policy: %v", err) + } +} + +func (e *EventSource) reloadApprovers() error { + e.approverMu.Lock() + defer e.approverMu.Unlock() + if e.loader == nil { + return nil + } + return e.loader.populateApprovers(filter.Union(e.rulesPlan, e.cliPlan)) +} func (e *EventSource) RegisterEventListener(lis event.Listener) { e.listeners = append(e.listeners, lis) @@ -310,6 +344,6 @@ func (e *EventSource) dispatch(evt *event.Event) { } func (e *EventSource) startupSummary() string { - return fmt.Sprintf("pending_queued=%d pending_dropped=%d replay_applied=%d snapshot_upserts=%d", - pendingQueued.Value(), pendingDropped.Value(), replayApplied.Value(), snapshotUpserts.Value()) + return fmt.Sprintf("pending_queued=%d pending_dropped=%d replay_applied=%d snapshot_upserts=%d approver_drops=%d", + pendingQueued.Value(), pendingDropped.Value(), replayApplied.Value(), snapshotUpserts.Value(), approverDrops.Value()) } diff --git a/internal/ebpf/source_integration_test.go b/internal/ebpf/source_integration_test.go index f43c1feac..59e5b654b 100644 --- a/internal/ebpf/source_integration_test.go +++ b/internal/ebpf/source_integration_test.go @@ -34,7 +34,7 @@ import ( func TestLiveProcessSource(t *testing.T) { cfg := testConfig() - es := NewEventSource(ps.NewSnapshotter(), cfg, nil).(*EventSource) + es := NewEventSource(ps.NewSnapshotter(), cfg, nil, nil).(*EventSource) if err := es.Open(cfg); err != nil { t.Fatalf("opening process source: %v", err) } @@ -86,7 +86,7 @@ func execLookPath() string { func TestLiveSyscallTelemetry(t *testing.T) { cfg := testConfig() - es := NewEventSource(ps.NewSnapshotter(), cfg, nil).(*EventSource) + es := NewEventSource(ps.NewSnapshotter(), cfg, nil, nil).(*EventSource) if err := es.Open(cfg); err != nil { t.Fatalf("opening process source: %v", err) } diff --git a/internal/ebpf/source_test.go b/internal/ebpf/source_test.go index 7d01d2d1a..3503c79b6 100644 --- a/internal/ebpf/source_test.go +++ b/internal/ebpf/source_test.go @@ -36,12 +36,13 @@ func testConfig() *config.Config { cfg.EventSource.EnableNetEvents = true cfg.EventSource.EnableMemEvents = true cfg.EventSource.Init() + cfg.Filters = &config.Filters{} return cfg } func TestStartupReplayAndLateSnapshot(t *testing.T) { cfg := testConfig() - es := NewEventSource(ps.NewSnapshotter(), cfg, nil).(*EventSource) + es := NewEventSource(ps.NewSnapshotter(), cfg, nil, nil).(*EventSource) hot := sampleExecve() hot.PID = 42 @@ -91,7 +92,7 @@ func TestStartupReplayAndLateSnapshot(t *testing.T) { func TestPIDReuseKeepsDistinctUUIDs(t *testing.T) { cfg := testConfig() - es := NewEventSource(ps.NewSnapshotter(), cfg, nil).(*EventSource) + es := NewEventSource(ps.NewSnapshotter(), cfg, nil, nil).(*EventSource) es.live.Store(true) first := sampleExecve() diff --git a/pkg/filter/approver.go b/pkg/filter/approver.go new file mode 100644 index 000000000..453bdb94e --- /dev/null +++ b/pkg/filter/approver.go @@ -0,0 +1,640 @@ +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package filter + +import ( + "slices" + "strconv" + "strings" + + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/filter/fields" + "github.com/rabbitstack/fibratus/pkg/filter/ql" +) + +// ApproverProvider is an optional filter capability that exposes a +// conservative, kernel-safe extraction of positive predicates. Filters +// that do not implement it are treated as unextractable, which forces +// default-allow for every event type they could match. +type ApproverProvider interface { + ApproverExtraction() Extraction +} + +// Extraction is the conservative predicate set taken from one compiled +// filter. Unsupported is set when the AST contains a shape that cannot +// be proven as a superset of matches (OR, NOT, inequality, regex, range, +// function, bound field, or sequence). +type Extraction struct { + Unsupported bool + Types []event.Type + TypesScoped bool + PIDs []uint64 + FileExact []string + FilePrefix []string + Ports []uint16 +} + +// TypePolicy is the in-kernel prefilter for one event type. DefaultAllow +// means userspace must see every event of that type. Otherwise an event +// must satisfy every required field, matching the union of allowed values. +type TypePolicy struct { + DefaultAllow bool + RequirePID bool + RequireFilename bool + RequirePort bool + PIDs []uint64 + FileExact []string + FilePrefix []string + Ports []uint16 +} + +// ApproverPlan is a platform-neutral description of per-type prefilters. +// It is derived from compiled filters and is not stored on RulesCompileResult, +// which remains a usage summary. +type ApproverPlan struct { + policies map[event.Type]TypePolicy +} + +// Sample is the subset of event fields the in-kernel approver can observe. +type Sample struct { + Type event.Type + PID uint64 + Filename string + Port uint16 + Truncated bool +} + +// AllowAllPlan returns a plan that never drops events. +func AllowAllPlan() *ApproverPlan { + return &ApproverPlan{policies: map[event.Type]TypePolicy{}} +} + +// PlanFromFilter builds a plan from a single compiled filter, typically the CLI +// expression supplied through SetFilter. +func PlanFromFilter(f Filter) *ApproverPlan { + if f == nil { + return AllowAllPlan() + } + return BuildApproverPlan([]Filter{f}) +} + +// BuildApproverPlan unions extractable predicates across filters. A type is +// default-allow when any applicable filter is unextractable, when no filter +// applies, or when the applicable filters do not share a required field. +func BuildApproverPlan(filters []Filter) *ApproverPlan { + plan := AllowAllPlan() + if len(filters) == 0 { + return plan + } + + byType := make(map[event.Type][]Extraction) + blocked := make(map[event.Type]bool) + + for _, f := range filters { + provider, ok := f.(ApproverProvider) + if !ok { + for _, typ := range event.All() { + blocked[typ] = true + } + continue + } + ex := provider.ApproverExtraction() + types := scopedTypes(ex) + if ex.Unsupported { + for _, typ := range types { + blocked[typ] = true + } + continue + } + for _, typ := range types { + byType[typ] = append(byType[typ], ex) + } + } + + for _, typ := range event.All() { + if blocked[typ] || len(byType[typ]) == 0 { + plan.policies[typ] = TypePolicy{DefaultAllow: true} + continue + } + plan.policies[typ] = policyFromClauses(byType[typ]) + } + return plan +} + +// Union combines plans by intersecting required fields and unioning their +// allowed values. Any default-allow policy wins, which is the safe direction. +func Union(plans ...*ApproverPlan) *ApproverPlan { + var out *ApproverPlan + for _, p := range plans { + if p == nil { + continue + } + if out == nil { + out = clonePlan(p) + continue + } + for _, typ := range event.All() { + out.policies[typ] = unionPolicy(out.Policy(typ), p.Policy(typ)) + } + } + if out == nil { + return AllowAllPlan() + } + return out +} + +// Policy returns the prefilter for an event type. Missing types default-allow. +func (p *ApproverPlan) Policy(t event.Type) TypePolicy { + if p == nil || p.policies == nil { + return TypePolicy{DefaultAllow: true} + } + pol, ok := p.policies[t] + if !ok { + return TypePolicy{DefaultAllow: true} + } + return pol +} + +// Allows reports whether the kernel prefilter would keep the sample. A true +// userspace match must always produce true here; false positives are allowed. +func (p *ApproverPlan) Allows(s Sample) bool { + if AlwaysAllowed(s.Type) { + return true + } + pol := p.Policy(s.Type) + if pol.DefaultAllow { + return true + } + if pol.RequirePID && !slices.Contains(pol.PIDs, s.PID) { + return false + } + if pol.RequireFilename && !s.Truncated && !matchFilename(pol, s.Filename) { + return false + } + if pol.RequirePort && !slices.Contains(pol.Ports, s.Port) { + return false + } + return true +} + +var _ ApproverProvider = (*filter)(nil) + +func (f *filter) ApproverExtraction() Extraction { + if f.seq != nil { + // Sequence matching is stateful: an early stage has to reach userspace + // for a later one to ever fire, so no stage can be prefiltered. Scope + // the block to the event names and categories in the filter's string + // fields, which is the same set rules.Engine indexes the sequence by, + // so the approver and the engine cannot disagree about which types can + // reach the rule. + ex := Extraction{Unsupported: true, TypesScoped: true} + ex.Types = typesFromStringFields(f.stringFields) + return ex + } + return extractFromExpr(f.expr) +} + +// typesFromStringFields mirrors the event name and category indexing performed +// by rules.Engine.Compile. +func typesFromStringFields(stringFields map[fields.Field][]string) []event.Type { + var types []event.Type + for _, name := range stringFields[fields.EvtName] { + types = append(types, event.NameToTypes(name)...) + } + for _, name := range stringFields[fields.EvtCategory] { + if !event.IsCategoryKnown(name) { + continue + } + cat := event.Category(name) + for _, typ := range event.All() { + if typ.Category() == cat { + types = append(types, typ) + } + } + } + return uniqueTypes(types) +} + +func extractFromExpr(root ql.Node) Extraction { + var ex Extraction + if root == nil { + ex.Unsupported = true + return ex + } + ql.WalkFunc(root, func(n ql.Node) { + switch node := n.(type) { + case *ql.NotExpr, *ql.Function: + ex.Unsupported = true + case *ql.BinaryExpr: + extractBinary(&ex, node) + } + }) + ex.Types = uniqueTypes(ex.Types) + ex.PIDs = uniqueUint64(ex.PIDs) + ex.FileExact = uniqueStrings(ex.FileExact) + ex.FilePrefix = uniqueStrings(ex.FilePrefix) + ex.Ports = uniqueUint16(ex.Ports) + return ex +} + +func extractBinary(ex *Extraction, expr *ql.BinaryExpr) { + switch expr.Op { + case ql.And: + return + case ql.Or: + ex.Unsupported = true + return + } + + lhs, ok := expr.LHS.(*ql.FieldLiteral) + if !ok { + ex.Unsupported = true + return + } + if _, ok := expr.RHS.(*ql.BoundFieldLiteral); ok { + ex.Unsupported = true + return + } + + switch lhs.Field { + case fields.EvtName: + extractEventNames(ex, expr) + case fields.EvtCategory: + extractCategories(ex, expr) + case fields.EvtPID, fields.PsPid: + extractPIDs(ex, expr) + case fields.FilePath: + extractFilenames(ex, expr) + case fields.NetDport: + extractPorts(ex, expr) + default: + // ps.name is deliberately absent. The only process name the kernel can + // read cheaply is the live task comm, while ps.name is snapshot state + // captured at execve. They diverge under prctl(PR_SET_NAME) and when + // the snapshot falls back to an executable basename longer than comm's + // 16 bytes, either of which would drop a matching event. + ex.Unsupported = true + } +} + +func extractEventNames(ex *Extraction, expr *ql.BinaryExpr) { + if expr.Op != ql.Eq && expr.Op != ql.In { + ex.Unsupported = true + return + } + names, ok := rhsStrings(expr.RHS) + if !ok { + ex.Unsupported = true + return + } + ex.TypesScoped = true + for _, name := range names { + typ := event.NameToType(name) + if typ == event.UnknownType { + continue + } + ex.Types = append(ex.Types, typ) + } +} + +func extractCategories(ex *Extraction, expr *ql.BinaryExpr) { + if expr.Op != ql.Eq && expr.Op != ql.In { + ex.Unsupported = true + return + } + names, ok := rhsStrings(expr.RHS) + if !ok { + ex.Unsupported = true + return + } + ex.TypesScoped = true + for _, name := range names { + if !event.IsCategoryKnown(name) { + continue + } + cat := event.Category(name) + for _, typ := range event.All() { + if typ.Category() == cat { + ex.Types = append(ex.Types, typ) + } + } + } +} + +func extractPIDs(ex *Extraction, expr *ql.BinaryExpr) { + if expr.Op != ql.Eq && expr.Op != ql.In { + ex.Unsupported = true + return + } + nums, ok := rhsUint64(expr.RHS) + if !ok { + ex.Unsupported = true + return + } + ex.PIDs = append(ex.PIDs, nums...) +} + +func extractFilenames(ex *Extraction, expr *ql.BinaryExpr) { + switch expr.Op { + case ql.Eq, ql.In: + names, ok := rhsStrings(expr.RHS) + if !ok { + ex.Unsupported = true + return + } + ex.FileExact = append(ex.FileExact, names...) + case ql.Startswith: + names, ok := rhsStrings(expr.RHS) + if !ok { + ex.Unsupported = true + return + } + for _, name := range names { + if name == "" { + continue + } + ex.FilePrefix = append(ex.FilePrefix, name) + } + case ql.Matches: + names, ok := rhsStrings(expr.RHS) + if !ok { + ex.Unsupported = true + return + } + for _, name := range names { + literal, prefix, ok := globAsPrefix(name) + if !ok { + ex.Unsupported = true + return + } + switch { + case literal != "": + ex.FileExact = append(ex.FileExact, literal) + case prefix != "": + ex.FilePrefix = append(ex.FilePrefix, prefix) + } + } + default: + // imatches stays out: case folding the path in the kernel would have to + // agree with unicode.ToLower on every rune userspace folds. + ex.Unsupported = true + } +} + +func extractPorts(ex *Extraction, expr *ql.BinaryExpr) { + if expr.Op != ql.Eq && expr.Op != ql.In { + ex.Unsupported = true + return + } + nums, ok := rhsUint64(expr.RHS) + if !ok { + ex.Unsupported = true + return + } + for _, n := range nums { + if n > 65535 { + ex.Unsupported = true + return + } + ex.Ports = append(ex.Ports, uint16(n)) + } +} + +// scopedTypes resolves which event types an extraction speaks for. An +// extraction that names no event type at all covers every type, which is right +// for a CLI filter because the source evaluates it against every event. Rules +// are not in that position: rules.Engine discards a rule without an event name +// or category, so the compiler keeps unscoped rules out of the plan entirely +// rather than letting their predicates constrain anything here. +func scopedTypes(ex Extraction) []event.Type { + if len(ex.Types) > 0 { + return uniqueTypes(ex.Types) + } + if ex.TypesScoped { + return nil + } + return event.All() +} + +func policyFromClauses(clauses []Extraction) TypePolicy { + reqPID, reqFile, reqPort := true, true, true + for _, c := range clauses { + if len(c.PIDs) == 0 { + reqPID = false + } + if len(c.FileExact)+len(c.FilePrefix) == 0 { + reqFile = false + } + if len(c.Ports) == 0 { + reqPort = false + } + } + if !reqPID && !reqFile && !reqPort { + return TypePolicy{DefaultAllow: true} + } + pol := TypePolicy{ + RequirePID: reqPID, + RequireFilename: reqFile, + RequirePort: reqPort, + } + for _, c := range clauses { + if reqPID { + pol.PIDs = append(pol.PIDs, c.PIDs...) + } + if reqFile { + pol.FileExact = append(pol.FileExact, c.FileExact...) + pol.FilePrefix = append(pol.FilePrefix, c.FilePrefix...) + } + if reqPort { + pol.Ports = append(pol.Ports, c.Ports...) + } + } + pol.PIDs = uniqueUint64(pol.PIDs) + pol.FileExact = uniqueStrings(pol.FileExact) + pol.FilePrefix = uniqueStrings(pol.FilePrefix) + pol.Ports = uniqueUint16(pol.Ports) + return pol +} + +func unionPolicy(a, b TypePolicy) TypePolicy { + if a.DefaultAllow || b.DefaultAllow { + return TypePolicy{DefaultAllow: true} + } + reqPID := a.RequirePID && b.RequirePID + reqFile := a.RequireFilename && b.RequireFilename + reqPort := a.RequirePort && b.RequirePort + if !reqPID && !reqFile && !reqPort { + return TypePolicy{DefaultAllow: true} + } + pol := TypePolicy{ + RequirePID: reqPID, + RequireFilename: reqFile, + RequirePort: reqPort, + } + if reqPID { + pol.PIDs = uniqueUint64(append(append([]uint64{}, a.PIDs...), b.PIDs...)) + } + if reqFile { + pol.FileExact = uniqueStrings(append(append([]string{}, a.FileExact...), b.FileExact...)) + pol.FilePrefix = uniqueStrings(append(append([]string{}, a.FilePrefix...), b.FilePrefix...)) + } + if reqPort { + pol.Ports = uniqueUint16(append(append([]uint16{}, a.Ports...), b.Ports...)) + } + return pol +} + +func clonePlan(p *ApproverPlan) *ApproverPlan { + out := AllowAllPlan() + for typ, pol := range p.policies { + out.policies[typ] = pol + } + return out +} + +// globAsPrefix rewrites a matches pattern into the exact or prefix form the +// kernel can already evaluate. There is no in-kernel glob matcher because a +// backtracking one is not verifiable, so only patterns provably equivalent to +// one of those two forms are accepted: +// +// /etc/passwd -> exact, no wildcard at all +// /tmp/* -> prefix, since '*' spans every remaining byte including '/' +// * -> vacuous, matches everything and constrains nothing +// +// A '*' anywhere but the end, or any '?', reports false and leaves the event +// type default-allow. Callers must pass the pattern through collapseStars so +// /tmp/** reaches here as /tmp/*. +func globAsPrefix(pattern string) (literal, prefix string, ok bool) { + pattern = collapseStars(pattern) + if strings.ContainsRune(pattern, '?') { + return "", "", false + } + switch strings.Count(pattern, "*") { + case 0: + return pattern, "", true + case 1: + if !strings.HasSuffix(pattern, "*") { + return "", "", false + } + return "", strings.TrimSuffix(pattern, "*"), true + default: + return "", "", false + } +} + +// collapseStars rewrites runs of '*' as a single '*'. The two are equivalent to +// matchCaseSensitive, which only ever remembers the most recent star position. +func collapseStars(pattern string) string { + if !strings.Contains(pattern, "**") { + return pattern + } + var b strings.Builder + b.Grow(len(pattern)) + var prevStar bool + for i := range len(pattern) { + c := pattern[i] + if c == '*' && prevStar { + continue + } + prevStar = c == '*' + b.WriteByte(c) + } + return b.String() +} + +func matchFilename(pol TypePolicy, path string) bool { + if slices.Contains(pol.FileExact, path) { + return true + } + for _, prefix := range pol.FilePrefix { + if strings.HasPrefix(path, prefix) { + return true + } + } + return false +} + +func rhsStrings(n ql.Node) ([]string, bool) { + switch v := n.(type) { + case *ql.StringLiteral: + return []string{v.Value}, true + case *ql.ListLiteral: + return v.Values, true + } + return nil, false +} + +func rhsUint64(n ql.Node) ([]uint64, bool) { + switch v := n.(type) { + case *ql.IntegerLiteral: + if v.Value < 0 { + return nil, false + } + return []uint64{uint64(v.Value)}, true + case *ql.UnsignedLiteral: + return []uint64{v.Value}, true + case *ql.StringLiteral: + n, err := strconv.ParseUint(v.Value, 10, 64) + if err != nil { + return nil, false + } + return []uint64{n}, true + case *ql.ListLiteral: + out := make([]uint64, 0, len(v.Values)) + for _, s := range v.Values { + n, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return nil, false + } + out = append(out, n) + } + return out, true + } + return nil, false +} + +func uniqueTypes(in []event.Type) []event.Type { + return unique(in, func(a, b event.Type) bool { return a == b }) +} + +func uniqueUint64(in []uint64) []uint64 { + return unique(in, func(a, b uint64) bool { return a == b }) +} + +func uniqueUint16(in []uint16) []uint16 { + return unique(in, func(a, b uint16) bool { return a == b }) +} + +func uniqueStrings(in []string) []string { + return unique(in, func(a, b string) bool { return a == b }) +} + +func unique[T any](in []T, eq func(T, T) bool) []T { + if len(in) == 0 { + return nil + } + out := make([]T, 0, len(in)) + for _, v := range in { + if slices.ContainsFunc(out, func(e T) bool { return eq(e, v) }) { + continue + } + out = append(out, v) + } + return out +} diff --git a/pkg/filter/approver_fuzz_linux_test.go b/pkg/filter/approver_fuzz_linux_test.go new file mode 100644 index 000000000..664bed6da --- /dev/null +++ b/pkg/filter/approver_fuzz_linux_test.go @@ -0,0 +1,120 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package filter + +import ( + "testing" + + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/event/params" + pstypes "github.com/rabbitstack/fibratus/pkg/ps/types" + "github.com/stretchr/testify/require" +) + +// fuzzExprs mixes extractable shapes with shapes that must fall back to +// default-allow, so the property holds across both paths. +var fuzzExprs = []string{ + "evt.name = 'openat' and file.path startswith '/tmp'", + "evt.name = 'openat' and file.path = '/etc/passwd'", + "evt.name = 'openat' and file.path in ('/tmp/a', '/tmp/b') and ps.pid = 42", + "evt.name in ('openat', 'connect') and ps.pid in (1, 42)", + "evt.category = 'file' and file.path startswith '/var'", + "evt.name = 'openat' and file.path matches '/tmp/*'", + "evt.name = 'openat' and file.path matches ('/tmp/*', '/var/tmp/**')", + "evt.name = 'openat' and file.path matches '/etc/passwd'", + "evt.name = 'openat' and file.path matches '/tmp/*/evil'", + "evt.name = 'openat' and file.path matches '*.so'", + "evt.name = 'openat' and file.path matches '*'", + "evt.name = 'openat' and file.path imatches '/TMP/*'", + "evt.name = 'connect' and net.dport in (80, 443)", + "evt.name = 'connect' and net.dport = 443 and ps.pid = 42", + "evt.name = 'kill' and ps.pid = 42", + "evt.name = 'openat' and file.path != '/tmp/x'", + "evt.name = 'openat' and not (file.path = '/tmp/x')", + "evt.name = 'openat' and (ps.pid = 1 or file.path = '/tmp/x')", + "evt.name = 'openat' and ps.name = 'bash'", + "file.path startswith '/tmp'", + "ps.pid = 42", +} + +var fuzzTypes = []event.Type{ + event.Openat, event.Connect, event.Kill, event.Execve, event.Mmap, event.Accept, +} + +func fuzzEvent(typ event.Type, pid uint64, path string, port uint16) *event.Event { + evt := &event.Event{ + Type: typ, + Category: typ.Category(), + Name: typ.String(), + PID: pid, + PS: &pstypes.PS{PID: pid, Name: "fuzz", Exe: "/bin/fuzz"}, + Params: event.Params{}, + } + switch typ { + case event.Openat, event.Unlink, event.Rename: + evt.Params[params.FilePath] = &event.Param{Name: params.FilePath, Type: params.Path, Value: path} + case event.Connect: + evt.Params[params.NetDport] = &event.Param{Name: params.NetDport, Type: params.Port, Value: port} + case event.Accept: + evt.Params[params.NetSport] = &event.Param{Name: params.NetSport, Type: params.Port, Value: port} + } + return evt +} + +// FuzzApproverNoFalseNegatives is the core soundness property: whatever the +// userspace filter matches, the in-kernel prefilter must also keep. +func FuzzApproverNoFalseNegatives(f *testing.F) { + f.Add(0, 0, uint64(42), "/tmp/payload", uint16(443)) + f.Add(2, 1, uint64(1), "/var/tmp/x", uint16(80)) + f.Add(5, 3, uint64(0), "/etc/passwd", uint16(22)) + f.Add(8, 2, uint64(4242), "", uint16(0)) + + compiled := make([]Filter, len(fuzzExprs)) + plans := make([]*ApproverPlan, len(fuzzExprs)) + for i, expr := range fuzzExprs { + compiled[i] = mustCompileApprover(f, expr) + plans[i] = PlanFromFilter(compiled[i]) + } + + f.Fuzz(func(t *testing.T, exprIdx, typeIdx int, pid uint64, path string, port uint16) { + i := abs(exprIdx) % len(compiled) + typ := fuzzTypes[abs(typeIdx)%len(fuzzTypes)] + + evt := fuzzEvent(typ, pid, path, port) + if !compiled[i].Eval(evt) { + return + } + sample := Sample{Type: typ, PID: pid, Port: port} + if typ == event.Openat || typ == event.Unlink || typ == event.Rename { + sample.Filename = path + } + require.True(t, plans[i].Allows(sample), + "prefilter dropped a userspace match: expr=%q type=%s pid=%d path=%q port=%d", + fuzzExprs[i], typ, pid, path, port) + }) +} + +func abs(n int) int { + if n < 0 { + return -n - 1 + } + return n +} diff --git a/pkg/filter/approver_linux.go b/pkg/filter/approver_linux.go new file mode 100644 index 000000000..3d1f80037 --- /dev/null +++ b/pkg/filter/approver_linux.go @@ -0,0 +1,36 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package filter + +import "github.com/rabbitstack/fibratus/pkg/event" + +// AlwaysAllowed reports event types the process snapshotter consumes. Dropping +// one in the kernel would desynchronize process identity from live tasks, so no +// prefilter is ever installed for them regardless of what the rules ask for. +// Mirrored by the leading check in event_approved in c/common/approvers.h. +func AlwaysAllowed(t event.Type) bool { + switch t { + case event.Execve, event.Exit, event.Clone, event.UnknownType: + return true + default: + return false + } +} diff --git a/pkg/filter/approver_linux_test.go b/pkg/filter/approver_linux_test.go new file mode 100644 index 000000000..e970c24cf --- /dev/null +++ b/pkg/filter/approver_linux_test.go @@ -0,0 +1,206 @@ +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package filter + +import ( + "testing" + + "github.com/rabbitstack/fibratus/pkg/config" + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/util/wildcard" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func compileApprover(t *testing.T, expr string) Filter { + t.Helper() + return mustCompileApprover(t, expr) +} + +func mustCompileApprover(tb testing.TB, expr string) Filter { + tb.Helper() + f := New(expr, &config.Config{ + EventSource: config.EventSourceConfig{ + EnableFileIOEvents: true, + EnableNetEvents: true, + EnableMemEvents: true, + }, + Filters: &config.Filters{}, + }) + require.NoError(tb, f.Compile(), expr) + return f +} + +func TestExtractEqualityListPrefix(t *testing.T) { + f := compileApprover(t, "evt.name = 'openat' and file.path startswith '/tmp' and ps.pid in (1, 42)") + ex := f.(ApproverProvider).ApproverExtraction() + require.False(t, ex.Unsupported) + assert.Equal(t, []event.Type{event.Openat}, ex.Types) + assert.ElementsMatch(t, []uint64{1, 42}, ex.PIDs) + assert.Equal(t, []string{"/tmp"}, ex.FilePrefix) +} + +func TestExtractPortList(t *testing.T) { + f := compileApprover(t, "evt.name = 'connect' and net.dport in (80, 443)") + ex := f.(ApproverProvider).ApproverExtraction() + require.False(t, ex.Unsupported) + assert.Equal(t, []uint16{80, 443}, ex.Ports) +} + +func TestExtractUnsupportedShapesDefaultAllow(t *testing.T) { + for _, expr := range []string{ + "evt.name = 'openat' and file.path != '/etc/passwd'", + "evt.name = 'openat' and not (file.path = '/etc/passwd')", + "evt.name = 'openat' and (ps.pid = 1 or file.path = '/tmp/x')", + "evt.name = 'openat' and ps.pid > 1", + "evt.name = 'openat' and lower(file.path) = '/tmp/x'", + } { + plan := PlanFromFilter(compileApprover(t, expr)) + assert.True(t, plan.Policy(event.Openat).DefaultAllow, expr) + } + + seqPlan := PlanFromFilter(compileApprover(t, `sequence +|evt.name = 'execve'| +|evt.name = 'connect'|`)) + assert.True(t, seqPlan.Policy(event.Execve).DefaultAllow) + assert.True(t, seqPlan.Policy(event.Connect).DefaultAllow) +} + +// A list of literal paths needs no wildcard machinery: every element is an +// exact key, so the whole condition resolves in the kernel's hash lookup. +func TestExtractFilePathList(t *testing.T) { + f := compileApprover(t, "evt.name = 'openat' and file.path in ('/etc/passwd', '/etc/shadow')") + ex := f.(ApproverProvider).ApproverExtraction() + require.False(t, ex.Unsupported) + assert.Equal(t, []string{"/etc/passwd", "/etc/shadow"}, ex.FileExact) + assert.Empty(t, ex.FilePrefix) + + plan := PlanFromFilter(f) + pol := plan.Policy(event.Openat) + require.False(t, pol.DefaultAllow) + assert.True(t, pol.RequireFilename) + assert.True(t, plan.Allows(Sample{Type: event.Openat, Filename: "/etc/passwd"})) + assert.True(t, plan.Allows(Sample{Type: event.Openat, Filename: "/etc/shadow"})) + assert.False(t, plan.Allows(Sample{Type: event.Openat, Filename: "/etc/hosts"})) +} + +// ps.name is snapshot state while the kernel can only read the live task comm, +// so a process name predicate must never narrow the in-kernel prefilter. +func TestProcessNameIsNotExtractable(t *testing.T) { + plan := PlanFromFilter(compileApprover(t, "evt.name = 'openat' and ps.name = 'bash'")) + assert.True(t, plan.Policy(event.Openat).DefaultAllow) + + mixed := PlanFromFilter(compileApprover(t, "evt.name = 'openat' and ps.name = 'bash' and file.path startswith '/tmp'")) + assert.True(t, mixed.Policy(event.Openat).DefaultAllow) +} + +func TestExtractMatchesTrailingStar(t *testing.T) { + f := compileApprover(t, "evt.name = 'openat' and file.path matches ('/tmp/*', '/var/tmp/*')") + ex := f.(ApproverProvider).ApproverExtraction() + require.False(t, ex.Unsupported) + assert.Equal(t, []string{"/tmp/", "/var/tmp/"}, ex.FilePrefix) + + plan := PlanFromFilter(f) + assert.True(t, plan.Allows(Sample{Type: event.Openat, Filename: "/tmp/dropper"})) + assert.True(t, plan.Allows(Sample{Type: event.Openat, Filename: "/tmp/a/b/c"})) + assert.True(t, plan.Allows(Sample{Type: event.Openat, Filename: "/var/tmp/x"})) + assert.False(t, plan.Allows(Sample{Type: event.Openat, Filename: "/etc/passwd"})) +} + +// A trailing '*' spans every remaining byte, separators included, so rewriting +// it to a prefix is exact rather than merely conservative. +func TestTrailingStarRewriteMatchesOperator(t *testing.T) { + paths := []string{"/tmp/x", "/tmp/a/b/c", "/tmp/", "/tmpfoo", "/etc/passwd", ""} + for _, pattern := range []string{"/tmp/*", "/tmp/**", "/etc/passwd", "*"} { + literal, prefix, ok := globAsPrefix(pattern) + require.True(t, ok, pattern) + pol := TypePolicy{RequireFilename: true} + switch { + case literal != "": + pol.FileExact = []string{literal} + case prefix != "": + pol.FilePrefix = []string{prefix} + default: + continue // '*' constrains nothing + } + for _, path := range paths { + assert.Equal(t, wildcard.Match(pattern, path, true), matchFilename(pol, path), + "pattern=%q path=%q", pattern, path) + } + } +} + +// Patterns that are not reducible to a prefix have no kernel equivalent, so +// they must widen to default-allow rather than be approximated. +func TestNonPrefixGlobsAreDefaultAllow(t *testing.T) { + for _, expr := range []string{ + "evt.name = 'openat' and file.path matches '/tmp/*/evil'", + "evt.name = 'openat' and file.path matches '*.so'", + "evt.name = 'openat' and file.path matches '/tmp/?vil'", + "evt.name = 'openat' and file.path matches ('/tmp/*', '*.so')", + "evt.name = 'openat' and file.path imatches '/TMP/*'", + } { + plan := PlanFromFilter(compileApprover(t, expr)) + assert.True(t, plan.Policy(event.Openat).DefaultAllow, expr) + } +} + +func TestVacuousStarDoesNotConstrain(t *testing.T) { + plan := PlanFromFilter(compileApprover(t, "evt.name = 'openat' and file.path matches '*'")) + assert.True(t, plan.Policy(event.Openat).DefaultAllow) +} + +func TestBuildPlanIntersectsRequiredFields(t *testing.T) { + pid := compileApprover(t, "evt.name = 'openat' and ps.pid = 7") + path := compileApprover(t, "evt.name = 'openat' and file.path startswith '/tmp'") + mixed := BuildApproverPlan([]Filter{pid, path}) + assert.True(t, mixed.Policy(event.Openat).DefaultAllow, "different required fields cannot be ANDed across rules") + + a := compileApprover(t, "evt.name = 'openat' and file.path startswith '/tmp'") + b := compileApprover(t, "evt.name = 'openat' and file.path startswith '/var'") + union := BuildApproverPlan([]Filter{a, b}) + pol := union.Policy(event.Openat) + require.False(t, pol.DefaultAllow) + assert.True(t, pol.RequireFilename) + assert.ElementsMatch(t, []string{"/tmp", "/var"}, pol.FilePrefix) +} + +func TestAllowsNeverFalseNegative(t *testing.T) { + f := compileApprover(t, "evt.name = 'openat' and file.path startswith '/tmp' and ps.pid = 42") + plan := PlanFromFilter(f) + + keep := Sample{Type: event.Openat, PID: 42, Filename: "/tmp/payload"} + drop := Sample{Type: event.Openat, PID: 7, Filename: "/etc/passwd"} + assert.True(t, plan.Allows(keep)) + assert.False(t, plan.Allows(drop)) + assert.True(t, plan.Allows(Sample{Type: event.Openat, PID: 42, Filename: "/tmp", Truncated: true})) +} + +func TestUnionCLIAndRules(t *testing.T) { + rules := PlanFromFilter(compileApprover(t, "evt.name = 'connect' and net.dport in (80, 443)")) + cli := PlanFromFilter(compileApprover(t, "evt.name = 'connect' and net.dport = 443")) + combined := Union(rules, cli) + pol := combined.Policy(event.Connect) + require.False(t, pol.DefaultAllow) + assert.True(t, pol.RequirePort) + assert.ElementsMatch(t, []uint16{80, 443}, pol.Ports) + assert.True(t, combined.Allows(Sample{Type: event.Connect, Port: 80})) + assert.True(t, combined.Allows(Sample{Type: event.Connect, Port: 443})) + assert.False(t, combined.Allows(Sample{Type: event.Connect, Port: 22})) +} diff --git a/pkg/filter/approver_windows.go b/pkg/filter/approver_windows.go new file mode 100644 index 000000000..36f674911 --- /dev/null +++ b/pkg/filter/approver_windows.go @@ -0,0 +1,27 @@ +//go:build windows + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package filter + +import "github.com/rabbitstack/fibratus/pkg/event" + +// AlwaysAllowed has no Windows counterpart because the ETW source does its own +// approver work in internal/etw/approvers. +func AlwaysAllowed(event.Type) bool { return false } diff --git a/pkg/rules/compiler.go b/pkg/rules/compiler.go index 668e13078..151ec6000 100644 --- a/pkg/rules/compiler.go +++ b/pkg/rules/compiler.go @@ -61,6 +61,7 @@ type compiler struct { psnap ps.Snapshotter config *config.Config approvers config.Approvers + plan *filter.ApproverPlan } func newCompiler(psnap ps.Snapshotter, cfg *config.Config) *compiler { @@ -151,6 +152,7 @@ func (c *compiler) compile() (map[*config.FilterConfig]filter.Filter, *config.Ru } if len(filters) == 0 { + c.plan = filter.AllowAllPlan() return filters, nil, nil } @@ -159,9 +161,34 @@ func (c *compiler) compile() (map[*config.FilterConfig]filter.Filter, *config.Ru r.Approvers = c.approvers } + providers := make([]filter.Filter, 0, len(filters)) + for _, fltr := range filters { + if !isApproverScoped(fltr) { + continue + } + providers = append(providers, fltr) + } + c.plan = filter.BuildApproverPlan(providers) + return filters, r, nil } +func isApproverScoped(f filter.Filter) bool { + for name := range f.GetStringFields() { + if name == fields.EvtName || name == fields.EvtCategory { + return true + } + } + return false +} + +func (c *compiler) ApproverPlan() *filter.ApproverPlan { + if c == nil || c.plan == nil { + return filter.AllowAllPlan() + } + return c.plan +} + func (c *compiler) visitApproverPredicates(node ql.Node) { walk := func(n ql.Node) { expr, ok := n.(*ql.BinaryExpr) diff --git a/pkg/rules/compiler_linux_test.go b/pkg/rules/compiler_linux_test.go index fdb2ba7ce..351280417 100644 --- a/pkg/rules/compiler_linux_test.go +++ b/pkg/rules/compiler_linux_test.go @@ -26,6 +26,7 @@ import ( "testing" "github.com/rabbitstack/fibratus/pkg/config" + "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/ps" "github.com/stretchr/testify/require" ) @@ -51,9 +52,35 @@ condition: evt.name = 'execve' and ps.name = 'bash' require.True(t, result.HasProcEvents) } +func TestCompilerExtractableApproverPlan(t *testing.T) { + rule := filepath.Join(t.TempDir(), "openat.yml") + require.NoError(t, os.WriteFile(rule, []byte(`name: Temporary file open +id: 7c1c2d3e-4f5a-6789-abcd-ef0123456789 +version: 1.0.0 +min-engine-version: 3.0.0 +condition: evt.name = 'openat' and file.path startswith '/tmp' +`), 0o600)) + + cfg := &config.Config{ + EventSource: config.EventSourceConfig{EnableFileIOEvents: true}, + Filters: &config.Filters{ + Rules: config.Rules{FromPaths: []string{rule}}, + }, + } + c := newCompiler(ps.NewSnapshotter(), cfg) + _, result, err := c.compile() + require.NoError(t, err) + require.NotNil(t, result) + pol := c.ApproverPlan().Policy(event.Openat) + require.False(t, pol.DefaultAllow) + require.True(t, pol.RequireFilename) + require.Equal(t, []string{"/tmp"}, pol.FilePrefix) +} + func TestCompilerShippedLinuxRules(t *testing.T) { cfg := newLinuxConfig(filepath.Join("..", "..", "rules", "linux", "*.yml")) - filters, result, err := newCompiler(ps.NewSnapshotter(), cfg).compile() + c := newCompiler(ps.NewSnapshotter(), cfg) + filters, result, err := c.compile() require.NoError(t, err) require.NotEmpty(t, filters) require.NotNil(t, result) @@ -66,6 +93,45 @@ func TestCompilerShippedLinuxRules(t *testing.T) { require.True(t, f.HasLabel(label), "%s is missing the %s label", f.Name, label) } } + + plan := c.ApproverPlan() + require.True(t, plan.Policy(event.Execve).DefaultAllow) + require.True(t, plan.Policy(event.Connect).DefaultAllow) + require.True(t, plan.Policy(event.Ptrace).DefaultAllow) + require.True(t, plan.Policy(event.Kill).DefaultAllow) +} + +// The approver scopes a sequence rule to the event names and categories in its +// string fields, on the assumption that the engine indexes the sequence by that +// same set and so never evaluates a stage against any other type. Pin it here: +// if indexing ever widens, the approver would under-block and drop events a +// sequence stage needs. +func TestEngineIndexesSequenceByStringFieldTypes(t *testing.T) { + rule := filepath.Join(t.TempDir(), "sequence.yml") + require.NoError(t, os.WriteFile(rule, []byte(`name: Execve then unscoped stage +id: 8e2f1a5b-3c4d-4e6f-8a9b-0c1d2e3f4a5b +version: 1.0.0 +min-engine-version: 3.0.0 +condition: > + sequence + maxspan 1m + |evt.name = 'execve'| + |ps.pid = 5| +`), 0o600)) + + cfg := newLinuxConfig(rule) + e := NewEngine(ps.NewSnapshotter(), cfg) + _, err := e.Compile() + require.NoError(t, err) + + require.Len(t, e.filters.types[event.Execve], 1, "sequence must be indexed under execve") + for _, typ := range event.All() { + if typ == event.Execve { + continue + } + require.Empty(t, e.filters.types[typ], "sequence must not be indexed under %s", typ) + } + require.Empty(t, e.filters.categories, "sequence names no category") } func TestCompilerSharedSemanticFixtures(t *testing.T) { diff --git a/pkg/rules/engine.go b/pkg/rules/engine.go index c5a20afcd..e1aef4f70 100644 --- a/pkg/rules/engine.go +++ b/pkg/rules/engine.go @@ -146,6 +146,16 @@ func NewEngine(psnap ps.Snapshotter, config *config.Config) *Engine { return e } +// ApproverPlan returns the conservative in-kernel prefilter derived from the +// compiled ruleset. RulesCompileResult stays a usage summary and is not used +// as predicate data. +func (e *Engine) ApproverPlan() *filter.ApproverPlan { + if e == nil || e.compiler == nil { + return filter.AllowAllPlan() + } + return e.compiler.ApproverPlan() +} + func (e *Engine) gcSequences() { for { <-e.scavenger.C