seccomp: Seccomp: embed oci-spec LinuxSeccomp, add support for seccomp flags

This patch, similar to d92739713c, 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 <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2021-07-16 17:55:04 +02:00
parent bfd4b64600
commit 0ef7e727d2
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
4 changed files with 32 additions and 22 deletions

View File

@ -740,8 +740,10 @@ func DefaultProfile() *Seccomp {
} }
return &Seccomp{ return &Seccomp{
DefaultAction: specs.ActErrno, LinuxSeccomp: specs.LinuxSeccomp{
ArchMap: arches(), DefaultAction: specs.ActErrno,
Syscalls: syscalls, },
ArchMap: arches(),
Syscalls: syscalls,
} }
} }

View File

@ -10,15 +10,12 @@ import (
) )
// Seccomp represents the config for a seccomp profile for syscall restriction. // 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 { type Seccomp struct {
DefaultAction specs.LinuxSeccompAction `json:"defaultAction"` specs.LinuxSeccomp
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"`
// ArchMap contains a list of Architectures and Sub-architectures for the // ArchMap contains a list of Architectures and Sub-architectures for the
// profile. When generating the profile, this list is expanded to a // profile. When generating the profile, this list is expanded to a

View File

@ -82,18 +82,22 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error)
return nil, nil return nil, nil
} }
newConfig := &specs.LinuxSeccomp{}
if len(config.Architectures) != 0 && len(config.ArchMap) != 0 { 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'") 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.LinuxSeccomp.Syscalls) != 0 {
if len(config.Architectures) != 0 { // The Seccomp type overrides the LinuxSeccomp.Syscalls field,
newConfig.Architectures = config.Architectures // 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 { if seccompArch, ok := nativeToSeccomp[arch]; ok {
for _, a := range config.ArchMap { for _, a := range config.ArchMap {
if a.Arch == seccompArch { 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: Loop:
// Convert Syscall to OCI runtimes-spec specs.LinuxSyscall after filtering them. // Convert Syscall to OCI runtimes-spec specs.LinuxSyscall after filtering them.
for _, call := range config.Syscalls { for _, call := range config.Syscalls {

View File

@ -100,6 +100,18 @@ func TestLoadProfileWithListenerPath(t *testing.T) {
assert.DeepEqual(t, expected, *p) 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. // TestLoadProfileValidation tests that invalid profiles produce the correct error.
func TestLoadProfileValidation(t *testing.T) { func TestLoadProfileValidation(t *testing.T) {
tests := []struct { tests := []struct {