Skip to content

[CWS] Introduce scope_field for process scope #37709

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

Open
wants to merge 2 commits into
base: will/export-cgroup-write-event
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions pkg/security/probe/field_handlers_ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ func (fh *EBPFFieldHandlers) ResolveProcessCacheEntry(ev *model.Event, newEntryC
return ev.ProcessCacheEntry, true
}

// ResolveProcessCacheEntryFromPID queries the ProcessResolver to retrieve the ProcessContext of the provided PID
func (fh *EBPFFieldHandlers) ResolveProcessCacheEntryFromPID(pid uint32) *model.ProcessCacheEntry {
return fh.resolvers.ProcessResolver.Resolve(pid, pid, 0, true, nil)
}

// ResolveFilePath resolves the inode to a full path
func (fh *EBPFFieldHandlers) ResolveFilePath(ev *model.Event, f *model.FileEvent) string {
if !f.IsPathnameStrResolved && len(f.PathnameStr) == 0 {
Expand Down
6 changes: 6 additions & 0 deletions pkg/security/probe/field_handlers_ebpfless.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ func (fh *EBPFLessFieldHandlers) ResolveProcessCacheEntry(ev *model.Event, _ fun
return ev.ProcessCacheEntry, true
}

// ResolveProcessCacheEntryFromPID queries the ProcessResolver to retrieve the ProcessContext of the provided PID
func (fh *EBPFLessFieldHandlers) ResolveProcessCacheEntryFromPID(pid uint32) *model.ProcessCacheEntry {
// not supported yet, we're missing NSID
return nil
}

// ResolveFilePath resolves the inode to a full path
func (fh *EBPFLessFieldHandlers) ResolveFilePath(_ *model.Event, f *model.FileEvent) string {
return f.PathnameStr
Expand Down
5 changes: 5 additions & 0 deletions pkg/security/probe/field_handlers_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ func (fh *FieldHandlers) ResolveProcessCacheEntry(ev *model.Event, _ func(*model
return ev.ProcessCacheEntry, true
}

// ResolveProcessCacheEntryFromPID queries the ProcessResolver to retrieve the ProcessContext of the provided PID
func (fh *FieldHandlers) ResolveProcessCacheEntryFromPID(pid uint32) *ProcessCacheEntry {
return fh.resolvers.ProcessResolver.Resolve(pid)
}

// ResolveProcessCmdLineScrubbed returns a scrubbed version of the cmdline
func (fh *FieldHandlers) ResolveProcessCmdLineScrubbed(_ *model.Event, e *model.Process) string {
return fh.resolvers.ProcessResolver.GetProcessCmdLineScrubbed(e)
Expand Down
21 changes: 21 additions & 0 deletions pkg/security/secl/compiler/eval/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ type Context struct {
// iterator register cache. used to cache entry within a single rule evaluation
RegisterCache map[RegisterID]*RegisterCacheEntry

// Action context, used to cache data within the evaluation of a single action
scopeFieldEvaluator Evaluator

// rule register
Registers map[RegisterID]int

Expand Down Expand Up @@ -96,6 +99,24 @@ func (c *Context) Reset() {

// per eval
c.PerEvalReset()

// per action
c.PerActionReset()
}

// SetScopeFieldEvaluator sets the scope field evaluator during the evaluation of an action
func (c *Context) SetScopeFieldEvaluator(evaluator Evaluator) {
c.scopeFieldEvaluator = evaluator
}

// GetScopeFieldEvaluator returns the current scope field evaluator in context
func (c *Context) GetScopeFieldEvaluator() Evaluator {
return c.scopeFieldEvaluator
}

// PerActionReset the context
func (c *Context) PerActionReset() {
c.scopeFieldEvaluator = nil
}

// PerEvalReset the context
Expand Down
6 changes: 6 additions & 0 deletions pkg/security/secl/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ type AWSSecurityCredentials struct {
// BaseExtraFieldHandlers handlers not hold by any field
type BaseExtraFieldHandlers interface {
ResolveProcessCacheEntry(ev *Event, newEntryCb func(*ProcessCacheEntry, error)) (*ProcessCacheEntry, bool)
ResolveProcessCacheEntryFromPID(pid uint32) *ProcessCacheEntry
ResolveContainerContext(ev *Event) (*ContainerContext, bool)
}

Expand All @@ -621,6 +622,11 @@ func (dfh *FakeFieldHandlers) ResolveProcessCacheEntry(ev *Event, _ func(*Proces
return nil, false
}

// ResolveProcessCacheEntryFromPID stub implementation
func (fh *FakeFieldHandlers) ResolveProcessCacheEntryFromPID(pid uint32) *ProcessCacheEntry {
return GetPlaceholderProcessCacheEntry(pid, pid, false)
}

// ResolveContainerContext stub implementation
func (dfh *FakeFieldHandlers) ResolveContainerContext(_ *Event) (*ContainerContext, bool) {
return nil, false
Expand Down
8 changes: 8 additions & 0 deletions pkg/security/secl/model/model_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ func NewFakeEvent() *Event {
}
}

var processContextZero = ProcessCacheEntry{}

// GetPlaceholderProcessCacheEntry returns an empty process cache entry for failed process resolutions
func GetPlaceholderProcessCacheEntry(pid uint32) *ProcessCacheEntry {
processContextZero.Pid = pid
return &processContextZero
}

// ValidateField validates the value of a field
func (m *Model) ValidateField(field eval.Field, fieldValue eval.FieldValue) error {
if m.ExtraValidateFieldFnc != nil {
Expand Down
26 changes: 23 additions & 3 deletions pkg/security/secl/rules/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import (
// Action represents the action to take when a rule is triggered
// It can either come from policy a definition or be an internal callback
type Action struct {
Def *ActionDefinition
InternalCallback *InternalCallbackDefinition
FilterEvaluator *eval.RuleEvaluator
Def *ActionDefinition
InternalCallback *InternalCallbackDefinition
FilterEvaluator *eval.RuleEvaluator
ScopeFieldEvaluator eval.Evaluator
}

// Check returns an error if the action in invalid
Expand Down Expand Up @@ -59,6 +60,10 @@ func (a *ActionDefinition) Check(opts PolicyLoaderOpts) error {
if a.Set.Inherited && a.Set.Scope != "process" {
return fmt.Errorf("only variables scoped to process can be marked as inherited")
}

if len(a.Set.ScopeField) > 0 && a.Set.Scope != "process" {
return fmt.Errorf("only variables scoped to process can have a custom scope_field")
}
} else if a.Kill != nil {
if opts.DisableEnforcement {
a.Kill = nil
Expand Down Expand Up @@ -103,6 +108,21 @@ func (a *Action) CompileFilter(parsingContext *ast.ParsingContext, model eval.Mo
return nil
}

// CompileScopeField compiles the scope field
func (a *Action) CompileScopeField(model eval.Model) error {
if a.Def.Set == nil || len(a.Def.Set.ScopeField) == 0 {
return nil
}

evaluator, err := model.GetEvaluator(a.Def.Set.ScopeField, "", 0)
if err != nil {
return &ErrScopeField{Expression: a.Def.Set.ScopeField, Err: err}
}

a.ScopeFieldEvaluator = evaluator
return nil
}

// IsAccepted returns whether a filter is accepted and has to be executed
func (a *Action) IsAccepted(ctx *eval.Context) bool {
return a.FilterEvaluator == nil || a.FilterEvaluator.Eval(ctx)
Expand Down
10 changes: 10 additions & 0 deletions pkg/security/secl/rules/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ func (e ErrActionFilter) Error() string {
return fmt.Sprintf("filter `%s` error: %s", e.Expression, e.Err)
}

// ErrScopeField is return on scope field definition error
type ErrScopeField struct {
Expression string
Err error
}

func (e ErrScopeField) Error() string {
return fmt.Sprintf("scope_field `%s` error: %s", e.Expression, e.Err)
}

// ErrFieldNotAvailable is returned when a field is not available
type ErrFieldNotAvailable struct {
Field eval.Field
Expand Down
1 change: 1 addition & 0 deletions pkg/security/secl/rules/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ type SetDefinition struct {
Expression string `yaml:"expression" json:"expression,omitempty"`
Append bool `yaml:"append" json:"append,omitempty"`
Scope Scope `yaml:"scope" json:"scope,omitempty" jsonschema:"enum=process,enum=container,enum=cgroup"`
ScopeField string `yaml:"scope_field" json:"scope_field,omitempty"`
Size int `yaml:"size" json:"size,omitempty"`
TTL *HumanReadableDuration `yaml:"ttl" json:"ttl,omitempty"`
Private bool `yaml:"private" json:"private,omitempty"`
Expand Down
Loading