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 {