mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add trace session option to ignore errors
This allows a configuration file with newer trace points and fields to be used with an older runsc without failing (just skips the new trace points and fields). Updates #4805 PiperOrigin-RevId: 487091892
This commit is contained in:
committed by
gVisor bot
parent
6db3ed9186
commit
c57f7a79eb
@@ -45,12 +45,14 @@ go_test(
|
||||
name = "seccheck_test",
|
||||
size = "small",
|
||||
srcs = [
|
||||
"config_test.go",
|
||||
"metadata_test.go",
|
||||
"seccheck_test.go",
|
||||
],
|
||||
library = ":seccheck",
|
||||
deps = [
|
||||
"//pkg/context",
|
||||
"//pkg/fd",
|
||||
"//pkg/sentry/seccheck/points:points_go_proto",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -39,6 +39,13 @@ type SessionConfig struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
// Points is the set of points to enable in this session.
|
||||
Points []PointConfig `json:"points,omitempty"`
|
||||
// IgnoreMissing skips point and optional/context fields not found. This can
|
||||
// be used to apply a single configuration file with newer points/fields with
|
||||
// older versions which do not have them yet. Note that it may hide typos in
|
||||
// the configuration.
|
||||
//
|
||||
// This field does NOT apply to sinks.
|
||||
IgnoreMissing bool `json:"ignore_missing,omitempty"`
|
||||
// Sinks are the sinks that will process the points enabled above.
|
||||
Sinks []SinkConfig `json:"sinks,omitempty"`
|
||||
}
|
||||
@@ -93,17 +100,21 @@ func Create(conf *SessionConfig, force bool) error {
|
||||
for _, ptConfig := range conf.Points {
|
||||
desc, err := findPointDesc(ptConfig.Name)
|
||||
if err != nil {
|
||||
if conf.IgnoreMissing {
|
||||
log.Warningf("Skipping point %q: %v", ptConfig.Name, err)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
req := PointReq{Pt: desc.ID}
|
||||
|
||||
mask, err := setFields(ptConfig.OptionalFields, desc.OptionalFields)
|
||||
mask, err := setFields(ptConfig.OptionalFields, desc.OptionalFields, conf.IgnoreMissing)
|
||||
if err != nil {
|
||||
return fmt.Errorf("configuring point %q: %w", ptConfig.Name, err)
|
||||
}
|
||||
req.Fields.Local = mask
|
||||
|
||||
mask, err = setFields(ptConfig.ContextFields, desc.ContextFields)
|
||||
mask, err = setFields(ptConfig.ContextFields, desc.ContextFields, conf.IgnoreMissing)
|
||||
if err != nil {
|
||||
return fmt.Errorf("configuring point %q: %w", ptConfig.Name, err)
|
||||
}
|
||||
@@ -212,11 +223,15 @@ func findField(name string, fields []FieldDesc) (FieldDesc, error) {
|
||||
return FieldDesc{}, fmt.Errorf("field %q not found", name)
|
||||
}
|
||||
|
||||
func setFields(names []string, fields []FieldDesc) (FieldMask, error) {
|
||||
func setFields(names []string, fields []FieldDesc, ignoreMissing bool) (FieldMask, error) {
|
||||
fm := FieldMask{}
|
||||
for _, name := range names {
|
||||
desc, err := findField(name, fields)
|
||||
if err != nil {
|
||||
if ignoreMissing {
|
||||
log.Warningf("Skipping field %q: %v", name, err)
|
||||
continue
|
||||
}
|
||||
return FieldMask{}, err
|
||||
}
|
||||
fm.Add(desc.ID)
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright 2022 The gVisor Authors.
|
||||
//
|
||||
// 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 seccheck
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLifecycle(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
conf SessionConfig
|
||||
}{
|
||||
{
|
||||
name: "all-fields",
|
||||
conf: SessionConfig{
|
||||
Name: "Default",
|
||||
Points: []PointConfig{
|
||||
{
|
||||
Name: "syscall/sysno/0/enter",
|
||||
},
|
||||
{
|
||||
Name: "syscall/openat/enter",
|
||||
OptionalFields: []string{"fd_path"},
|
||||
},
|
||||
{
|
||||
Name: "syscall/sysno/1/enter",
|
||||
ContextFields: []string{"time"},
|
||||
},
|
||||
{
|
||||
Name: "syscall/openat/enter",
|
||||
OptionalFields: []string{"fd_path"},
|
||||
ContextFields: []string{"time"},
|
||||
},
|
||||
},
|
||||
Sinks: []SinkConfig{
|
||||
{Name: "test-sink"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no-sink",
|
||||
conf: SessionConfig{
|
||||
Name: "Default",
|
||||
Points: []PointConfig{
|
||||
{Name: "syscall/sysno/0/enter"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no-points",
|
||||
conf: SessionConfig{
|
||||
Name: "Default",
|
||||
Sinks: []SinkConfig{
|
||||
{Name: "test-sink"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignore-errors",
|
||||
conf: SessionConfig{
|
||||
Name: "Default",
|
||||
IgnoreMissing: true,
|
||||
Points: []PointConfig{
|
||||
{
|
||||
Name: "foobar",
|
||||
},
|
||||
{
|
||||
Name: "syscall/sysno/1/enter",
|
||||
ContextFields: []string{"foobar"},
|
||||
},
|
||||
{
|
||||
Name: "syscall/openat/enter",
|
||||
ContextFields: []string{"foobar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if err := Create(&tc.conf, false); err != nil {
|
||||
t.Errorf("Create(): %v", err)
|
||||
}
|
||||
|
||||
var got []SessionConfig
|
||||
List(&got)
|
||||
if len(got) != 1 {
|
||||
t.Errorf("only one session should exist, got: %d", len(got))
|
||||
} else {
|
||||
if got[0].Name != tc.conf.Name {
|
||||
t.Errorf("wrong name, want: %q, got: %q", tc.conf.Name, got[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
if err := Delete(tc.conf.Name); err != nil {
|
||||
t.Errorf("Delete(%q): %v", tc.conf.Name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailure(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
conf SessionConfig
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "point",
|
||||
err: `point "foobar" not found`,
|
||||
conf: SessionConfig{
|
||||
Name: "Default",
|
||||
Points: []PointConfig{
|
||||
{Name: "foobar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "optional-field",
|
||||
err: `field "foobar" not found`,
|
||||
conf: SessionConfig{
|
||||
Name: "Default",
|
||||
Points: []PointConfig{
|
||||
{
|
||||
Name: "syscall/openat/enter",
|
||||
OptionalFields: []string{"foobar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "context-field",
|
||||
err: `field "foobar" not found`,
|
||||
conf: SessionConfig{
|
||||
Name: "Default",
|
||||
Points: []PointConfig{
|
||||
{
|
||||
Name: "syscall/sysno/1/enter",
|
||||
ContextFields: []string{"foobar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sink",
|
||||
err: `sink "foobar" not found`,
|
||||
conf: SessionConfig{
|
||||
Name: "Default",
|
||||
Sinks: []SinkConfig{
|
||||
{Name: "foobar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sink-ignore-missing",
|
||||
err: `sink "foobar" not found`,
|
||||
conf: SessionConfig{
|
||||
Name: "Default",
|
||||
IgnoreMissing: true,
|
||||
Sinks: []SinkConfig{
|
||||
{Name: "foobar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := Create(&tc.conf, false)
|
||||
if err == nil {
|
||||
_ = Delete(tc.conf.Name)
|
||||
t.Fatal("Create() should have failed")
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.err) {
|
||||
t.Errorf("invalid error, want: %q, got: %q", tc.err, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -19,15 +19,29 @@ import (
|
||||
"testing"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fd"
|
||||
pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterSink(SinkDesc{
|
||||
Name: "test-sink",
|
||||
New: newTestSink,
|
||||
})
|
||||
}
|
||||
|
||||
type testSink struct {
|
||||
SinkDefaults
|
||||
|
||||
onClone func(ctx context.Context, fields FieldSet, info *pb.CloneInfo) error
|
||||
}
|
||||
|
||||
var _ Sink = (*testSink)(nil)
|
||||
|
||||
func newTestSink(_ map[string]any, _ *fd.FD) (Sink, error) {
|
||||
return &testSink{}, nil
|
||||
}
|
||||
|
||||
// Name implements Sink.Name.
|
||||
func (c *testSink) Name() string {
|
||||
return "test-sink"
|
||||
|
||||
Reference in New Issue
Block a user