Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CSM] Fix syscall dirty flag after execve #27679

Merged
merged 4 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions pkg/security/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,16 +513,7 @@ func (c *RuntimeSecurityConfig) sanitizeRuntimeSecurityConfigActivityDump() erro
c.ActivityDumpTracedCgroupsCount = model.MaxTracedCgroupsCount
}

hasProfileStorageFormat := false
for _, format := range c.ActivityDumpLocalStorageFormats {
hasProfileStorageFormat = hasProfileStorageFormat || format == Profile
}

if c.SecurityProfileEnabled && !hasProfileStorageFormat {
return fmt.Errorf("'profile' storage format has to be enabled when using security profiles, got only formats: %v", c.ActivityDumpLocalStorageFormats)
}

if c.SecurityProfileEnabled && c.ActivityDumpLocalStorageDirectory != c.SecurityProfileDir {
if c.SecurityProfileEnabled && c.ActivityDumpEnabled && c.ActivityDumpLocalStorageDirectory != c.SecurityProfileDir {
return fmt.Errorf("activity dumps storage directory '%s' has to be the same than security profile storage directory '%s'", c.ActivityDumpLocalStorageDirectory, c.SecurityProfileDir)
}

Expand Down
6 changes: 2 additions & 4 deletions pkg/security/ebpf/c/include/events_definition.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,8 @@ struct syscall_monitor_event_t {
struct span_context_t span;
struct container_context_t container;

union {
struct syscall_monitor_entry_t syscalls;
long syscall_id;
} syscall_data;
u64 event_reason;
char syscalls[SYSCALL_ENCODING_TABLE_SIZE];
};

struct rename_event_t {
Expand Down
7 changes: 4 additions & 3 deletions pkg/security/ebpf/c/include/helpers/raw_syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ __attribute__((always_inline)) void send_or_skip_syscall_monitor_event(struct _t
shoud_send_event:
if (should_send > 0) {
// send an event now
event->syscall_data.syscalls = *entry;
event->event_reason = should_send;
bpf_probe_read(event->syscalls, sizeof(event->syscalls), entry->syscalls);

// reset the syscalls mask for the drift monitor type
if (syscall_monitor_type == SYSCALL_MONITOR_TYPE_DRIFT) {
Expand All @@ -97,14 +98,14 @@ __attribute__((always_inline)) void send_or_skip_syscall_monitor_event(struct _t
fill_span_context(&event->span);

// remove last_sent and dirty from the event size, we don't care about these fields
send_event_with_size_ptr(args, EVENT_SYSCALLS, event, offsetof(struct syscall_monitor_event_t, syscall_data) + SYSCALL_ENCODING_TABLE_SIZE);
send_event_ptr(args, EVENT_SYSCALLS, event);
}

key.syscall_key = EXECVE_SYSCALL_KEY;
if (is_syscall(&key)) {
// reset syscalls map for the new process
bpf_probe_read(&entry->syscalls[0], sizeof(entry->syscalls), &zero->syscalls[0]);
entry->dirty = 1;
entry->dirty = 0;
entry->last_sent = now;
}
key.syscall_key = EXIT_SYSCALL_KEY;
Expand Down
1 change: 1 addition & 0 deletions pkg/security/probe/probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type PlatformProbe interface {
DumpProcessCache(_ bool) (string, error)
AddDiscarderPushedCallback(_ DiscarderPushedCallback)
GetEventTags(_ string) []string
GetProfileManager() interface{}
}

// EventHandler represents a handler for events sent by the probe that needs access to all the fields in the SECL model
Expand Down
25 changes: 25 additions & 0 deletions pkg/security/probe/probe_ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ var (
}
)

var _ PlatformProbe = (*EBPFProbe)(nil)

// EBPFProbe defines a platform probe
type EBPFProbe struct {
Resolvers *resolvers.EBPFResolvers
Expand Down Expand Up @@ -149,6 +151,11 @@ type EBPFProbe struct {
onDemandRateLimiter *rate.Limiter
}

// GetProfileManager returns the Profile Managers
func (p *EBPFProbe) GetProfileManager() interface{} {
return p.profileManagers
}

func (p *EBPFProbe) detectKernelVersion() error {
kernelVersion, err := kernel.NewKernelVersion()
if err != nil {
Expand Down Expand Up @@ -1219,6 +1226,15 @@ func (p *EBPFProbe) updateProbes(ruleEventTypes []eval.EventType, needRawSyscall
}
}
}
// SecurityProfiles
if p.config.RuntimeSecurity.AnomalyDetectionEnabled {
for _, e := range p.profileManagers.GetAnomalyDetectionEventTypes() {
if e == model.SyscallsEventType {
activatedProbes = append(activatedProbes, probes.SyscallMonitorSelectors...)
break
}
}
}
}

// Print the list of unique probe identification IDs that are registered
Expand Down Expand Up @@ -1666,6 +1682,15 @@ func NewEBPFProbe(probe *Probe, config *config.Config, opts Opts, wmeta optional
}
}
}
if config.RuntimeSecurity.AnomalyDetectionEnabled {
for _, e := range config.RuntimeSecurity.AnomalyDetectionEventTypes {
if e == model.SyscallsEventType {
// Add syscall monitor probes
p.managerOptions.ActivatedProbes = append(p.managerOptions.ActivatedProbes, probes.SyscallMonitorSelectors...)
break
}
}
}

p.constantOffsets, err = p.GetOffsetConstants()
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions pkg/security/probe/probe_ebpfless.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ type EBPFLessProbe struct {
wg sync.WaitGroup
}

// GetProfileManager returns the Profile Managers
func (p *EBPFLessProbe) GetProfileManager() interface{} {
return nil
}

func (p *EBPFLessProbe) handleClientMsg(cl *client, msg *ebpfless.Message) {
switch msg.Type {
case ebpfless.MessageTypeHello:
Expand Down
5 changes: 5 additions & 0 deletions pkg/security/probe/probe_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ func (p *WindowsProbe) Init() error {
return p.initEtwFIM()
}

// GetProfileManager returns the Profile Managers
func (p *WindowsProbe) GetProfileManager() interface{} {
return nil
}

func (p *WindowsProbe) initEtwFIM() error {

if !p.config.RuntimeSecurity.FIMEnabled {
Expand Down
5 changes: 5 additions & 0 deletions pkg/security/probe/security_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ func (spm *SecurityProfileManagers) GetActivityDumpTracedEventTypes() []model.Ev
return spm.config.RuntimeSecurity.ActivityDumpTracedEventTypes
}

// GetAnomalyDetectionEventTypes returns the event types that may generate anomaly detections
func (spm *SecurityProfileManagers) GetAnomalyDetectionEventTypes() []model.EventType {
return spm.config.RuntimeSecurity.AnomalyDetectionEventTypes
}

// SnapshotTracedCgroups snapshots traced cgroups
func (spm *SecurityProfileManagers) SnapshotTracedCgroups() {
spm.activityDumpManager.SnapshotTracedCgroups()
Expand Down
24 changes: 24 additions & 0 deletions pkg/security/secl/model/consts_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,30 @@ const (
UpperLayer
)

// SyscallDriftEventReason describes why a syscall drift event was sent
type SyscallDriftEventReason uint64

const (
// SyscallMonitorPeriodReason means that the event was sent because the syscall cache entry was dirty for longer than syscall_monitor.period
SyscallMonitorPeriodReason SyscallDriftEventReason = iota + 1
// ExitReason means that the event was sent because a pid that was about to exit had a dirty cache entry
ExitReason
// ExecveReason means that the event was sent because an execve syscall was detected on a pid with a dirty cache entry
ExecveReason
)

func (r SyscallDriftEventReason) String() string {
switch r {
case SyscallMonitorPeriodReason:
return "MonitorPeriod"
case ExecveReason:
return "Execve"
case ExitReason:
return "Exit"
}
return "Unknown"
}

func initErrorConstants() {
for k, v := range errorConstants {
seclConstants[k] = &eval.IntEvaluator{Value: v}
Expand Down
11 changes: 2 additions & 9 deletions pkg/security/secl/model/model_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ type Event struct {
Exit ExitEvent `field:"exit" event:"exit"` // [7.38] [Process] A process was terminated
Syscalls SyscallsEvent `field:"-"`

// anomaly detection related events
AnomalyDetectionSyscallEvent AnomalyDetectionSyscallEvent `field:"-"`

// kernel events
SELinux SELinuxEvent `field:"selinux" event:"selinux"` // [7.30] [Kernel] An SELinux operation was run
BPF BPFEvent `field:"bpf" event:"bpf"` // [7.33] [Kernel] A BPF command was executed
Expand Down Expand Up @@ -647,12 +644,8 @@ type VethPairEvent struct {

// SyscallsEvent represents a syscalls event
type SyscallsEvent struct {
Syscalls []Syscall // 64 * 8 = 512 > 450, bytes should be enough to hold all 450 syscalls
}

// AnomalyDetectionSyscallEvent represents an anomaly detection for a syscall event
type AnomalyDetectionSyscallEvent struct {
SyscallID Syscall
EventReason SyscallDriftEventReason
Syscalls []Syscall // 64 * 8 = 512 > 450, bytes should be enough to hold all 450 syscalls
}

// PathKey identifies an entry in the dentry cache
Expand Down
18 changes: 5 additions & 13 deletions pkg/security/secl/model/unmarshallers_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1230,29 +1230,21 @@ func (e *BindEvent) UnmarshalBinary(data []byte) (int, error) {

// UnmarshalBinary unmarshalls a binary representation of itself
func (e *SyscallsEvent) UnmarshalBinary(data []byte) (int, error) {
if len(data) < 64 {
if len(data) < 72 {
return 0, ErrNotEnoughData
}

for i, b := range data[:64] {
e.EventReason = SyscallDriftEventReason(binary.NativeEndian.Uint64(data[0:8]))

for i, b := range data[8:72] {
// compute the ID of the syscall
for j := 0; j < 8; j++ {
if b&(1<<j) > 0 {
e.Syscalls = append(e.Syscalls, Syscall(i*8+j))
}
}
}
return 64, nil
}

// UnmarshalBinary unmarshalls a binary representation of itself
func (e *AnomalyDetectionSyscallEvent) UnmarshalBinary(data []byte) (int, error) {
if len(data) < 8 {
return 0, ErrNotEnoughData
}

e.SyscallID = Syscall(binary.NativeEndian.Uint64(data[0:8]))
return 8, nil
return 72, nil
}

// UnmarshalBinary unmarshalls a binary representation of itself
Expand Down
10 changes: 5 additions & 5 deletions pkg/security/secl/model/unmarshallers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ mainLoop:
func allSyscallsTest() syscallsEventTest {
all := syscallsEventTest{
name: "all_syscalls",
args: make([]byte, syscallsEventByteCount),
args: make([]byte, syscallsEventByteCount+8),
}

for i := 0; i < syscallsEventByteCount*8; i++ {
Expand All @@ -42,7 +42,7 @@ func allSyscallsTest() syscallsEventTest {
// should be tested in eBPF...
index := i / 8
bit := byte(1 << (i % 8))
all.args[index] |= bit
all.args[index+8] |= bit
}

return all
Expand All @@ -51,13 +51,13 @@ func allSyscallsTest() syscallsEventTest {
func oneSyscallTest(s Syscall) syscallsEventTest {
one := syscallsEventTest{
name: s.String(),
args: make([]byte, syscallsEventByteCount),
args: make([]byte, syscallsEventByteCount+8),
}

// should be tested in eBPF ...
index := s / 8
bit := byte(1 << (s % 8))
one.args[index] |= bit
one.args[index+8] |= bit

one.want = []Syscall{s}
return one
Expand All @@ -78,7 +78,7 @@ func TestSyscallsEvent_UnmarshalBinary(t *testing.T) {
},
{
name: "no_syscall",
args: make([]byte, 64),
args: make([]byte, 72),
},
allSyscallsTest(),
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/security/tests/module_tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,10 @@ func genTestConfigs(cfgDir string, opts testOpts) (*emconfig.Config, *secconfig.
runtimeSecurityEnabled = false
}

if opts.activityDumpSyscallMonitorPeriod == time.Duration(0) {
opts.activityDumpSyscallMonitorPeriod = 60 * time.Second
}

buffer := new(bytes.Buffer)
if err := tmpl.Execute(buffer, map[string]interface{}{
"TestPoliciesDir": cfgDir,
Expand All @@ -745,6 +749,7 @@ func genTestConfigs(cfgDir string, opts testOpts) (*emconfig.Config, *secconfig.
"ActivityDumpLocalStorageDirectory": opts.activityDumpLocalStorageDirectory,
"ActivityDumpLocalStorageCompression": opts.activityDumpLocalStorageCompression,
"ActivityDumpLocalStorageFormats": opts.activityDumpLocalStorageFormats,
"ActivityDumpSyscallMonitorPeriod": opts.activityDumpSyscallMonitorPeriod,
"EnableSecurityProfile": opts.enableSecurityProfile,
"SecurityProfileMaxImageTags": opts.securityProfileMaxImageTags,
"SecurityProfileDir": opts.securityProfileDir,
Expand Down
2 changes: 2 additions & 0 deletions pkg/security/tests/module_tester_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ runtime_security_config:
enabled: {{ .HostSBOMEnabled }}
activity_dump:
enabled: {{ .EnableActivityDump }}
syscall_monitor:
period: {{ .ActivityDumpSyscallMonitorPeriod }}
{{if .EnableActivityDump}}
rate_limiter: {{ .ActivityDumpRateLimiter }}
tag_rules:
Expand Down
Loading
Loading