mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Seccomp filters: Add method to uniquely identify the seccomp configuration.
This adds a `Key()` method on `config.Options` which uniquely identifies the ingredients that go into the seccomp filter configuration. This is necessary for precompiling seccomp filters. At runtime, precompiled seccomp programs will be matched according to their config key. PiperOrigin-RevId: 583492673
This commit is contained in:
committed by
gVisor bot
parent
51318e8a91
commit
2ef56fee94
@@ -17,6 +17,9 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/seccomp"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/accel"
|
||||
@@ -36,6 +39,24 @@ type Options struct {
|
||||
ControllerFD int
|
||||
}
|
||||
|
||||
// ConfigKey returns a unique string representing this set of options.
|
||||
// This is used for matching a set of `Options` at seccomp precompile
|
||||
// time with the same set of `Options` at runtime.
|
||||
// As such, it should encompass all fields that change the structure of
|
||||
// the seccomp rules, but should not encompass fields that are only known
|
||||
// at runtime (e.g. `ControllerFD`).
|
||||
func (opt Options) ConfigKey() string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf("Platform=%q ", opt.Platform.ConfigKey()))
|
||||
sb.WriteString(fmt.Sprintf("HostNetwork=%t ", opt.HostNetwork))
|
||||
sb.WriteString(fmt.Sprintf("HostNetworkRawSockets=%t ", opt.HostNetworkRawSockets))
|
||||
sb.WriteString(fmt.Sprintf("HostFilesystem=%t ", opt.HostFilesystem))
|
||||
sb.WriteString(fmt.Sprintf("ProfileEnable=%t ", opt.ProfileEnable))
|
||||
sb.WriteString(fmt.Sprintf("NVProxy=%t ", opt.NVProxy))
|
||||
sb.WriteString(fmt.Sprintf("TPUProxy=%t ", opt.TPUProxy))
|
||||
return strings.TrimSpace(sb.String())
|
||||
}
|
||||
|
||||
// Warnings returns a set of warnings that may be useful to display to the
|
||||
// user when the given options are used.
|
||||
func Warnings(opt Options) []string {
|
||||
|
||||
@@ -16,6 +16,7 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -74,3 +75,87 @@ func TestIoctlFirstArgumentIsNonNegativeFD(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestOptionsConfigKey verifies the behavior of `Options.ConfigKey`.
|
||||
func TestOptionsConfigKey(t *testing.T) {
|
||||
// mutateFn mutates the value of a specific Options field.
|
||||
type mutateFn func(opt *Options)
|
||||
|
||||
defaultOpt := Options{
|
||||
Platform: (&systrap.Systrap{}).SeccompInfo(),
|
||||
}
|
||||
|
||||
// Map of `Options` struct field names mapped to a function to mutate them.
|
||||
// This should only contain fields which influence the configuration;
|
||||
// calling the mutation function of these should change the value of
|
||||
// `Options.Key`.
|
||||
var configFields = map[string]mutateFn{
|
||||
"Platform": func(opt *Options) {
|
||||
if defaultOpt.Platform.ConfigKey() == opt.Platform.ConfigKey() {
|
||||
opt.Platform = (&kvm.KVM{}).SeccompInfo()
|
||||
} else {
|
||||
opt.Platform = (&systrap.Systrap{}).SeccompInfo()
|
||||
}
|
||||
},
|
||||
"HostNetwork": func(opt *Options) { opt.HostNetwork = !opt.HostNetwork },
|
||||
"HostNetworkRawSockets": func(opt *Options) { opt.HostNetworkRawSockets = !opt.HostNetworkRawSockets },
|
||||
"HostFilesystem": func(opt *Options) { opt.HostFilesystem = !opt.HostFilesystem },
|
||||
"ProfileEnable": func(opt *Options) { opt.ProfileEnable = !opt.ProfileEnable },
|
||||
"NVProxy": func(opt *Options) { opt.NVProxy = !opt.NVProxy },
|
||||
"TPUProxy": func(opt *Options) { opt.TPUProxy = !opt.TPUProxy },
|
||||
}
|
||||
|
||||
// Map of `Options` struct field names mapped to a function to mutate them.
|
||||
// This should only contain fields which are used as variables during
|
||||
// filter generation; calling the mutation function of these should *not*
|
||||
// change the value of `Options.Key`.
|
||||
var varsFields = map[string]mutateFn{
|
||||
"ControllerFD": func(opt *Options) { opt.ControllerFD++ },
|
||||
}
|
||||
|
||||
t.Run("fields are exhaustive", func(t *testing.T) {
|
||||
for i := 0; i < reflect.ValueOf(defaultOpt).NumField(); i++ {
|
||||
f := reflect.TypeOf(defaultOpt).Field(i)
|
||||
found := false
|
||||
for name := range configFields {
|
||||
if f.Name == name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
for name := range varsFields {
|
||||
if f.Name == name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("field `Options.%s` is not known to TestOptionsKey; please add it", f.Name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("mutating config fields causes ConfigKey to change", func(t *testing.T) {
|
||||
opt := defaultOpt // Make a copy, as we're about to mutate it.
|
||||
key := opt.ConfigKey()
|
||||
for name, mutateFn := range configFields {
|
||||
mutateFn(&opt)
|
||||
newKey := opt.ConfigKey()
|
||||
if key == newKey {
|
||||
t.Fatalf("mutating config field %q did not cause the ConfigKey to change: %q", name, key)
|
||||
}
|
||||
key = newKey
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("mutating vars fields does not cause ConfigKey to change", func(t *testing.T) {
|
||||
opt := defaultOpt // Make a copy, as we're about to mutate it.
|
||||
key := opt.ConfigKey()
|
||||
for name, mutateFn := range varsFields {
|
||||
mutateFn(&opt)
|
||||
if newKey := opt.ConfigKey(); key != newKey {
|
||||
t.Fatalf("mutating var field %q caused the ConfigKey to change: %q -> %q", name, key, newKey)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user