-
Notifications
You must be signed in to change notification settings - Fork 376
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Common Expression Language filter
Add Common Expression Language [^1] filter in GetEventRequest. See unit tests for sample usages. This is largely based on Hubble CEL expression filter [^2]. [^1]: https://cel.dev/ [^2]: cilium/cilium#32147 Signed-off-by: Michi Mutsuzaki <[email protected]>
- Loading branch information
1 parent
54e452c
commit 05e4231
Showing
11 changed files
with
733 additions
and
493 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
340 changes: 176 additions & 164 deletions
340
contrib/tetragon-rthooks/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.pb.go
Large diffs are not rendered by default.
Oops, something went wrong.
2 changes: 2 additions & 0 deletions
2
contrib/tetragon-rthooks/vendor/github.com/cilium/tetragon/api/v1/tetragon/events.proto
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Tetragon | ||
|
||
package filters | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"reflect" | ||
|
||
v1 "github.com/cilium/cilium/pkg/hubble/api/v1" | ||
hubbleFilters "github.com/cilium/cilium/pkg/hubble/filters" | ||
"github.com/cilium/tetragon/api/v1/tetragon" | ||
"github.com/cilium/tetragon/api/v1/tetragon/codegen/helpers" | ||
"github.com/google/cel-go/cel" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
// compile will parse and check an expression `expr` against a given | ||
// environment `env` and determine whether the resulting type of the expression | ||
// matches the `exprType` provided as input. | ||
// Copied from | ||
// https://github.com/google/cel-go/blob/338b3c80e688f7f44661d163c0dbc02eb120dcb7/codelab/solution/codelab.go#LL385C1-L399C2 | ||
// with modifications | ||
func compile(env *cel.Env, expr string, celType *cel.Type) (*cel.Ast, error) { | ||
ast, iss := env.Compile(expr) | ||
if iss.Err() != nil { | ||
return nil, iss.Err() | ||
} | ||
// Type-check the expression for correctness. | ||
checked, iss := env.Check(ast) | ||
// Report semantic errors, if present. | ||
if iss.Err() != nil { | ||
return nil, iss.Err() | ||
} | ||
if checked.OutputType() != celType { | ||
return nil, fmt.Errorf( | ||
"got %q, wanted %q result type", | ||
checked.OutputType(), celType) | ||
} | ||
return ast, nil | ||
} | ||
|
||
func (t *CELExpressionFilter) filterByCELExpression(ctx context.Context, log logrus.FieldLogger, exprs []string) (hubbleFilters.FilterFunc, error) { | ||
var programs []cel.Program | ||
for _, expr := range exprs { | ||
// we want filters to be boolean expressions, so check the type of the | ||
// expression before proceeding | ||
ast, err := compile(t.celEnv, expr, cel.BoolType) | ||
if err != nil { | ||
return nil, fmt.Errorf("error compiling CEL expression: %w", err) | ||
} | ||
|
||
prg, err := t.celEnv.Program(ast) | ||
if err != nil { | ||
return nil, fmt.Errorf("error building CEL program: %w", err) | ||
} | ||
programs = append(programs, prg) | ||
} | ||
|
||
return func(ev *v1.Event) bool { | ||
if ev == nil { | ||
return false | ||
} | ||
response, ok := ev.Event.(*tetragon.GetEventsResponse) | ||
if !ok { | ||
return false | ||
} | ||
for _, prg := range programs { | ||
out, _, err := prg.ContextEval(ctx, helpers.ProcessEventMap(response)) | ||
if err != nil { | ||
log.Errorf("error running CEL program %s", err) | ||
return false | ||
} | ||
|
||
v, err := out.ConvertToNative(t.boolType) | ||
if err != nil { | ||
log.Errorf("invalid conversion in CEL program: %s", err) | ||
return false | ||
} | ||
b, ok := v.(bool) | ||
if ok && b { | ||
return true | ||
} | ||
} | ||
return false | ||
}, nil | ||
} | ||
|
||
// CELExpressionFilter implements filtering based on CEL (common expression | ||
// language) expressions | ||
type CELExpressionFilter struct { | ||
log logrus.FieldLogger | ||
celEnv *cel.Env | ||
boolType reflect.Type | ||
} | ||
|
||
func NewCELExpressionFilter(log logrus.FieldLogger) *CELExpressionFilter { | ||
responseTypeMap := helpers.ResponseTypeMap() | ||
options := []cel.EnvOption{ | ||
cel.Container("tetragon"), | ||
} | ||
for key, val := range responseTypeMap { | ||
name := string(val.ProtoReflect().Descriptor().FullName()) | ||
options = append(options, cel.Variable(key, cel.ObjectType(name))) | ||
options = append(options, cel.Types(val)) | ||
} | ||
celEnv, err := cel.NewEnv(options...) | ||
if err != nil { | ||
panic(fmt.Sprintf("error creating CEL env %s", err)) | ||
} | ||
return &CELExpressionFilter{ | ||
log: log, | ||
celEnv: celEnv, | ||
boolType: reflect.TypeOf(false), | ||
} | ||
} | ||
|
||
// OnBuildFilter builds a CEL expression filter. | ||
func (t *CELExpressionFilter) OnBuildFilter(ctx context.Context, f *tetragon.Filter) ([]hubbleFilters.FilterFunc, error) { | ||
if exprs := f.GetCelExpression(); exprs != nil { | ||
filter, err := t.filterByCELExpression(ctx, t.log, exprs) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return []hubbleFilters.FilterFunc{filter}, nil | ||
} | ||
return []hubbleFilters.FilterFunc{}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Tetragon | ||
|
||
package filters | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
v1 "github.com/cilium/cilium/pkg/hubble/api/v1" | ||
"github.com/cilium/tetragon/api/v1/tetragon" | ||
"github.com/sirupsen/logrus" | ||
"github.com/stretchr/testify/assert" | ||
"google.golang.org/protobuf/types/known/wrapperspb" | ||
) | ||
|
||
func TestInvalidFilter(t *testing.T) { | ||
log := logrus.New() | ||
f := tetragon.Filter{CelExpression: []string{"process_exec.process.bad_field_name == 'curl'"}} | ||
celFilter := NewCELExpressionFilter(log) | ||
_, err := celFilter.OnBuildFilter(context.Background(), &f) | ||
assert.Error(t, err) | ||
} | ||
|
||
func TestProcessExecFilter(t *testing.T) { | ||
log := logrus.New() | ||
f := []*tetragon.Filter{{CelExpression: []string{"process_exec.process.pid > uint(1)"}}} | ||
fl, err := BuildFilterList(context.Background(), f, []OnBuildFilter{NewCELExpressionFilter(log)}) | ||
assert.NoError(t, err) | ||
ev := v1.Event{ | ||
Event: &tetragon.GetEventsResponse{ | ||
Event: &tetragon.GetEventsResponse_ProcessExec{ | ||
ProcessExec: &tetragon.ProcessExec{Process: &tetragon.Process{Pid: wrapperspb.UInt32(1)}}, | ||
}, | ||
}, | ||
} | ||
assert.False(t, fl.MatchOne(&ev)) | ||
ev = v1.Event{ | ||
Event: &tetragon.GetEventsResponse{ | ||
Event: &tetragon.GetEventsResponse_ProcessExec{ | ||
ProcessExec: &tetragon.ProcessExec{Process: &tetragon.Process{Pid: wrapperspb.UInt32(2)}}, | ||
}, | ||
}, | ||
} | ||
assert.True(t, fl.MatchOne(&ev)) | ||
} | ||
|
||
func TestProcessKprobeFilter(t *testing.T) { | ||
log := logrus.New() | ||
f := []*tetragon.Filter{{CelExpression: []string{"process_kprobe.function_name == 'security_file_permission'"}}} | ||
fl, err := BuildFilterList(context.Background(), f, []OnBuildFilter{NewCELExpressionFilter(log)}) | ||
assert.NoError(t, err) | ||
ev := v1.Event{ | ||
Event: &tetragon.GetEventsResponse{ | ||
Event: &tetragon.GetEventsResponse_ProcessKprobe{ | ||
ProcessKprobe: &tetragon.ProcessKprobe{FunctionName: "security_file_permission"}}, | ||
}, | ||
} | ||
assert.True(t, fl.MatchOne(&ev)) | ||
ev = v1.Event{ | ||
Event: &tetragon.GetEventsResponse{ | ||
Event: &tetragon.GetEventsResponse_ProcessKprobe{ | ||
ProcessKprobe: &tetragon.ProcessKprobe{FunctionName: "something_else"}}, | ||
}, | ||
} | ||
assert.False(t, fl.MatchOne(&ev)) | ||
} |
Oops, something went wrong.