Skip to content

Commit

Permalink
feat: add event manager
Browse files Browse the repository at this point in the history
The event manager manages the state (disabled/enabled) of events
globably on tracee.
  • Loading branch information
josedonizetti committed Sep 19, 2023
1 parent ecf3e5e commit a74f957
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 15 deletions.
4 changes: 2 additions & 2 deletions pkg/ebpf/events_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,8 +571,8 @@ func (t *Tracee) sinkEvents(ctx context.Context, in <-chan *trace.Event) <-chan
}

// Is the rule disabled for the matched policies?
if !t.policyManager.IsRuleEnabled(event.MatchedPoliciesUser, events.ID(event.EventID)) {
logger.Debugw("event dropped because matched rule disabled", "event", event.EventName)
if !t.policyManager.IsEnabled(event.MatchedPoliciesUser, events.ID(event.EventID)) {
logger.Debugw("event dropped because it is not enabled", "event", event.EventName)
t.eventsPool.Put(event)
continue
}
Expand Down
102 changes: 92 additions & 10 deletions pkg/ebpf/policy_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,129 @@ import (
// policyManager is a thread-safe struct that manages the enabled policies for each rule
type policyManager struct {
mutex sync.Mutex
rules map[events.ID]uint64
rules map[events.ID]*eventState
}

// eventState is a struct that holds the state of a given event
type eventState struct {
policyMask uint64
enabled bool
}

func newPolicyManager() *policyManager {
return &policyManager{
mutex: sync.Mutex{},
rules: make(map[events.ID]uint64),
rules: make(map[events.ID]*eventState),
}
}

// IsEnabled tests if a event, or a policy per event is enabled (in the future it will also check if a policy is enabled)
// TODO: add metrics about an event being enabled/disabled, or a policy being enabled/disabled?
func (pm *policyManager) IsEnabled(matchedPolicies uint64, ruleId events.ID) bool {
pm.mutex.Lock()
defer pm.mutex.Unlock()

if !pm.isEventEnabled(ruleId) {
return false
}

return pm.isRuleEnabled(matchedPolicies, ruleId)
}

// IsRuleEnabled returns true if a given event policy is enabled for a given rule
func (pm *policyManager) IsRuleEnabled(matchedPolicies uint64, ruleId events.ID) bool {
pm.mutex.Lock()
defer pm.mutex.Unlock()

policyMask, ok := pm.rules[ruleId]
return pm.isRuleEnabled(matchedPolicies, ruleId)
}

// not synchronized, use IsRuleEnabled instead
func (pm *policyManager) isRuleEnabled(matchedPolicies uint64, ruleId events.ID) bool {
state, ok := pm.rules[ruleId]
if !ok {
return false
}

return state.policyMask&matchedPolicies != 0
}

// IsEventEnabled returns true if a given event policy is enabled for a given rule
func (pm *policyManager) IsEventEnabled(evenId events.ID) bool {
pm.mutex.Lock()
defer pm.mutex.Unlock()

return pm.isEventEnabled(evenId)
}

// not synchronized, use IsEventEnabled instead
func (pm *policyManager) isEventEnabled(evenId events.ID) bool {
state, ok := pm.rules[evenId]
if !ok {
return false
}

return policyMask&matchedPolicies != 0
return state.enabled
}

// EnableRule enables a rule for a given event policy
func (pm *policyManager) EnableRule(policyId int, ruleId events.ID) {
pm.mutex.Lock()
defer pm.mutex.Unlock()

policyMask := pm.rules[ruleId]
utils.SetBit(&policyMask, uint(policyId))
state, ok := pm.rules[ruleId]
if !ok {
// if you you enabling/disabling a rule for an event that
// was not enabled/disabled, we assume the event should be enabled
state = &eventState{enabled: true}
}

utils.SetBit(&state.policyMask, uint(policyId))

pm.rules[ruleId] = policyMask
pm.rules[ruleId] = state
}

// DisableRule disables a rule for a given event policy
func (pm *policyManager) DisableRule(policyId int, ruleId events.ID) {
pm.mutex.Lock()
defer pm.mutex.Unlock()

policyMask := pm.rules[ruleId]
utils.ClearBit(&policyMask, uint(policyId))
state, ok := pm.rules[ruleId]
if !ok {
// if you you enabling/disabling a rule for an event that
// was not enabled/disabled, we assume the event should be enabled
state = &eventState{enabled: true}
}

utils.ClearBit(&state.policyMask, uint(policyId))

pm.rules[ruleId] = state
}

// EnableEvent enables a given event
func (pm *policyManager) EnableEvent(eventId events.ID) {
pm.mutex.Lock()
defer pm.mutex.Unlock()

state, ok := pm.rules[eventId]
if !ok {
pm.rules[eventId] = &eventState{enabled: true}
return
}

state.enabled = true
}

// DisableEvent disables a given event
func (pm *policyManager) DisableEvent(eventId events.ID) {
pm.mutex.Lock()
defer pm.mutex.Unlock()

state, ok := pm.rules[eventId]
if !ok {
pm.rules[eventId] = &eventState{enabled: false}
return
}

pm.rules[ruleId] = policyMask
state.enabled = false
}
104 changes: 101 additions & 3 deletions pkg/ebpf/policy_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (
"github.com/aquasecurity/tracee/pkg/policy"
)

func TestEventsManagerEnableRule(t *testing.T) {
func TestPolicyManagerEnableRule(t *testing.T) {
policyManager := newPolicyManager()

policyManager.EnableEvent(events.SecurityBPF)

policy1Mached := uint64(0b10)
policy2Mached := uint64(0b100)
policy1And2Mached := uint64(0b110)
Expand All @@ -28,14 +30,17 @@ func TestEventsManagerEnableRule(t *testing.T) {
assert.True(t, policyManager.IsRuleEnabled(policy1And2Mached, events.SecurityBPF))

policyManager.EnableRule(2, events.SecurityBPF)

assert.True(t, policyManager.IsRuleEnabled(policy1Mached, events.SecurityBPF))
assert.True(t, policyManager.IsRuleEnabled(policy2Mached, events.SecurityBPF))
assert.True(t, policyManager.IsRuleEnabled(policy1And2Mached, events.SecurityBPF))
}

func TestEventsManagerDisableRule(t *testing.T) {
func TestPolicyManagerDisableRule(t *testing.T) {
policyManager := newPolicyManager()

policyManager.EnableEvent(events.SecurityBPF)

policy1Mached := uint64(0b10)
policy2Mached := uint64(0b100)
policy1And2Mached := uint64(0b110)
Expand All @@ -53,7 +58,7 @@ func TestEventsManagerDisableRule(t *testing.T) {
assert.False(t, policyManager.IsRuleEnabled(policy1And2Mached, events.SecurityBPF))
}

func TestEventsManagerEnableAndDisableConcurrent(t *testing.T) {
func TestPolicyManagerEnableAndDisableRuleConcurrent(t *testing.T) {
eventsToEnable := []events.ID{
events.SecurityBPF,
events.SchedGetPriorityMax,
Expand Down Expand Up @@ -105,3 +110,96 @@ func TestEventsManagerEnableAndDisableConcurrent(t *testing.T) {
}
}
}

func TestPolicyManagerEnableEvent(t *testing.T) {
policyManager := newPolicyManager()

assert.False(t, policyManager.isEventEnabled(events.SecurityBPF))
assert.False(t, policyManager.isEventEnabled(events.SecurityFileOpen))
assert.False(t, policyManager.isEventEnabled(events.SecuritySocketAccept))

policyManager.EnableEvent(events.SecurityBPF)
policyManager.EnableEvent(events.SecurityFileOpen)
policyManager.EnableEvent(events.SecuritySocketAccept)

assert.True(t, policyManager.isEventEnabled(events.SecurityBPF))
assert.True(t, policyManager.isEventEnabled(events.SecurityFileOpen))
assert.True(t, policyManager.isEventEnabled(events.SecuritySocketAccept))
}

func TestPolicyManagerDisableEvent(t *testing.T) {
policyManager := newPolicyManager()

policyManager.EnableEvent(events.SecurityBPF)
policyManager.EnableEvent(events.SecurityFileOpen)
policyManager.EnableEvent(events.SecuritySocketAccept)

assert.True(t, policyManager.IsEventEnabled(events.SecurityBPF))
assert.True(t, policyManager.IsEventEnabled(events.SecurityFileOpen))
assert.True(t, policyManager.IsEventEnabled(events.SecuritySocketAccept))

policyManager.DisableEvent(events.SecurityBPF)
policyManager.DisableEvent(events.SecurityFileOpen)

assert.False(t, policyManager.IsEventEnabled(events.SecurityBPF))
assert.False(t, policyManager.IsEventEnabled(events.SecurityFileOpen))
assert.True(t, policyManager.IsEventEnabled(events.SecuritySocketAccept))
}

func TestPolicyManagerEnableAndDisableEventConcurrent(t *testing.T) {
eventsToEnable := []events.ID{
events.SecurityBPF,
events.SchedGetPriorityMax,
events.SchedProcessExec,
events.SchedProcessExit,
events.Ptrace,
}

eventsToDisable := []events.ID{
events.SecurityBPFMap,
events.Openat2,
events.SchedProcessFork,
events.MagicWrite,
events.FileModification,
}

policyManager := newPolicyManager()

// activate events
for _, e := range eventsToDisable {
policyManager.EnableEvent(e)
}

var wg sync.WaitGroup

wg.Add(1)
go func() {
for i := 0; i < policy.MaxPolicies; i++ {
for _, e := range eventsToEnable {
policyManager.EnableEvent(e)
}
}
wg.Done()
}()

wg.Add(1)
go func() {
for i := 0; i < policy.MaxPolicies; i++ {
for _, e := range eventsToDisable {
policyManager.DisableEvent(e)
}
}
wg.Done()
}()

wg.Wait()

for i := 0; i < policy.MaxPolicies; i++ {
for _, e := range eventsToEnable {
assert.True(t, policyManager.IsEventEnabled(e))
}
for _, e := range eventsToDisable {
assert.False(t, policyManager.IsEventEnabled(e))
}
}
}
23 changes: 23 additions & 0 deletions pkg/ebpf/tracee.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ func New(cfg config.Config) (*Tracee, error) {
utils.SetBit(&emit, uint(p.ID))
t.eventsState[e] = events.EventState{Submit: submit, Emit: emit}

policyManager.EnableEvent(e)
policyManager.EnableRule(p.ID, e)
}
}
Expand Down Expand Up @@ -1773,6 +1774,28 @@ func (t *Tracee) Unsubscribe(s *streams.Stream) {
t.streamsManager.Unsubscribe(s)
}

func (t *Tracee) EnableEvent(eventName string) error {
id, found := events.Core.GetDefinitionIDByName(eventName)
if !found {
return errfmt.Errorf("error event not found: %s", eventName)
}

t.policyManager.EnableEvent(id)

return nil
}

func (t *Tracee) DisableEvent(eventName string) error {
id, found := events.Core.GetDefinitionIDByName(eventName)
if !found {
return errfmt.Errorf("error event not found: %s", eventName)
}

t.policyManager.DisableEvent(id)

return nil
}

// EnableRule enables a rule in the specified policies
func (t *Tracee) EnableRule(policyNames []string, ruleId string) error {
eventID, found := events.Core.GetDefinitionIDByName(ruleId)
Expand Down

0 comments on commit a74f957

Please sign in to comment.