From 0ef7e727d2c4d82fe28053e076470ff8630825e3 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 16 Jul 2021 17:55:04 +0200 Subject: [PATCH] seccomp: Seccomp: embed oci-spec LinuxSeccomp, add support for seccomp flags This patch, similar to d92739713c633c155c0f3d8065c8278b1d8a44e7, embeds the `LinuxSeccomp` type of the runtime-spec, so that we can support all options provided by the spec, and decorates it with our own fields. With this, profiles can make use of the recently added "Flags" field, to specify flags that must be passed to seccomp(2) when installing the filter. Signed-off-by: Sebastiaan van Stijn --- profiles/seccomp/default_linux.go | 8 +++++--- profiles/seccomp/seccomp.go | 13 +++++-------- profiles/seccomp/seccomp_linux.go | 21 ++++++++++----------- profiles/seccomp/seccomp_test.go | 12 ++++++++++++ 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/profiles/seccomp/default_linux.go b/profiles/seccomp/default_linux.go index 1c2471553f..5fa5fe59d2 100644 --- a/profiles/seccomp/default_linux.go +++ b/profiles/seccomp/default_linux.go @@ -740,8 +740,10 @@ func DefaultProfile() *Seccomp { } return &Seccomp{ - DefaultAction: specs.ActErrno, - ArchMap: arches(), - Syscalls: syscalls, + LinuxSeccomp: specs.LinuxSeccomp{ + DefaultAction: specs.ActErrno, + }, + ArchMap: arches(), + Syscalls: syscalls, } } diff --git a/profiles/seccomp/seccomp.go b/profiles/seccomp/seccomp.go index d48d35a9a2..45b1e4f76e 100644 --- a/profiles/seccomp/seccomp.go +++ b/profiles/seccomp/seccomp.go @@ -10,15 +10,12 @@ import ( ) // Seccomp represents the config for a seccomp profile for syscall restriction. +// It is used to marshal/unmarshal the JSON profiles as accepted by docker, and +// extends the runtime-spec's specs.LinuxSeccomp, overriding some fields to +// provide the ability to define conditional rules based on the host's kernel +// version, architecture, and the container's capabilities. type Seccomp struct { - DefaultAction specs.LinuxSeccompAction `json:"defaultAction"` - DefaultErrnoRet *uint `json:"defaultErrnoRet,omitempty"` - ListenerPath string `json:"listenerPath,omitempty"` - ListenerMetadata string `json:"listenerMetadata,omitempty"` - - // Architectures is kept to maintain backward compatibility with the old - // seccomp profile. - Architectures []specs.Arch `json:"architectures,omitempty"` + specs.LinuxSeccomp // ArchMap contains a list of Architectures and Sub-architectures for the // profile. When generating the profile, this list is expanded to a diff --git a/profiles/seccomp/seccomp_linux.go b/profiles/seccomp/seccomp_linux.go index b57734ae35..4d8fed68c6 100644 --- a/profiles/seccomp/seccomp_linux.go +++ b/profiles/seccomp/seccomp_linux.go @@ -82,18 +82,22 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error) return nil, nil } - newConfig := &specs.LinuxSeccomp{} - if len(config.Architectures) != 0 && len(config.ArchMap) != 0 { return nil, errors.New("both 'architectures' and 'archMap' are specified in the seccomp profile, use either 'architectures' or 'archMap'") } - // if config.Architectures == 0 then libseccomp will figure out the architecture to use - if len(config.Architectures) != 0 { - newConfig.Architectures = config.Architectures + if len(config.LinuxSeccomp.Syscalls) != 0 { + // The Seccomp type overrides the LinuxSeccomp.Syscalls field, + // so 'this should never happen' when loaded from JSON, but could + // happen if someone constructs the Config from source. + return nil, errors.New("the LinuxSeccomp.Syscalls field should be empty") } - arch := goToNative[runtime.GOARCH] + var ( + // Copy all common / standard properties to the output profile + newConfig = &config.LinuxSeccomp + arch = goToNative[runtime.GOARCH] + ) if seccompArch, ok := nativeToSeccomp[arch]; ok { for _, a := range config.ArchMap { if a.Arch == seccompArch { @@ -104,11 +108,6 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error) } } - newConfig.DefaultAction = config.DefaultAction - newConfig.DefaultErrnoRet = config.DefaultErrnoRet - newConfig.ListenerPath = config.ListenerPath - newConfig.ListenerMetadata = config.ListenerMetadata - Loop: // Convert Syscall to OCI runtimes-spec specs.LinuxSyscall after filtering them. for _, call := range config.Syscalls { diff --git a/profiles/seccomp/seccomp_test.go b/profiles/seccomp/seccomp_test.go index 0d3d39852c..eb4b95cc45 100644 --- a/profiles/seccomp/seccomp_test.go +++ b/profiles/seccomp/seccomp_test.go @@ -100,6 +100,18 @@ func TestLoadProfileWithListenerPath(t *testing.T) { assert.DeepEqual(t, expected, *p) } +func TestLoadProfileWithFlag(t *testing.T) { + profile := `{"defaultAction": "SCMP_ACT_ERRNO", "flags": ["SECCOMP_FILTER_FLAG_SPEC_ALLOW", "SECCOMP_FILTER_FLAG_LOG"]}` + expected := specs.LinuxSeccomp{ + DefaultAction: specs.ActErrno, + Flags: []specs.LinuxSeccompFlag{"SECCOMP_FILTER_FLAG_SPEC_ALLOW", "SECCOMP_FILTER_FLAG_LOG"}, + } + rs := createSpec() + p, err := LoadProfile(profile, &rs) + assert.NilError(t, err) + assert.DeepEqual(t, expected, *p) +} + // TestLoadProfileValidation tests that invalid profiles produce the correct error. func TestLoadProfileValidation(t *testing.T) { tests := []struct {