1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/profiles/seccomp/seccomp_test.go
Sebastiaan van Stijn 0ef7e727d2
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>
2021-07-17 15:57:54 +02:00

294 lines
8.1 KiB
Go

// +build linux
package seccomp // import "github.com/docker/docker/profiles/seccomp"
import (
"encoding/json"
"io/ioutil"
"strings"
"testing"
"github.com/opencontainers/runtime-spec/specs-go"
"gotest.tools/v3/assert"
)
func TestLoadProfile(t *testing.T) {
f, err := ioutil.ReadFile("fixtures/example.json")
if err != nil {
t.Fatal(err)
}
rs := createSpec()
p, err := LoadProfile(string(f), &rs)
if err != nil {
t.Fatal(err)
}
var expectedErrno uint = 12345
expected := specs.LinuxSeccomp{
DefaultAction: specs.ActErrno,
Syscalls: []specs.LinuxSyscall{
{
Names: []string{"clone"},
Action: specs.ActAllow,
Args: []specs.LinuxSeccompArg{{
Index: 0,
Value: 2114060288,
ValueTwo: 0,
Op: specs.OpMaskedEqual,
}},
},
{
Names: []string{"open"},
Action: specs.ActAllow,
Args: []specs.LinuxSeccompArg{},
},
{
Names: []string{"close"},
Action: specs.ActAllow,
Args: []specs.LinuxSeccompArg{},
},
{
Names: []string{"syslog"},
Action: specs.ActErrno,
ErrnoRet: &expectedErrno,
Args: []specs.LinuxSeccompArg{},
},
},
}
assert.DeepEqual(t, expected, *p)
}
func TestLoadProfileWithDefaultErrnoRet(t *testing.T) {
var profile = []byte(`{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 6
}`)
rs := createSpec()
p, err := LoadProfile(string(profile), &rs)
if err != nil {
t.Fatal(err)
}
expectedErrnoRet := uint(6)
expected := specs.LinuxSeccomp{
DefaultAction: specs.ActErrno,
DefaultErrnoRet: &expectedErrnoRet,
}
assert.DeepEqual(t, expected, *p)
}
func TestLoadProfileWithListenerPath(t *testing.T) {
var profile = []byte(`{
"defaultAction": "SCMP_ACT_ERRNO",
"listenerPath": "/var/run/seccompaget.sock",
"listenerMetadata": "opaque-metadata"
}`)
rs := createSpec()
p, err := LoadProfile(string(profile), &rs)
if err != nil {
t.Fatal(err)
}
expected := specs.LinuxSeccomp{
DefaultAction: specs.ActErrno,
ListenerPath: "/var/run/seccompaget.sock",
ListenerMetadata: "opaque-metadata",
}
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 {
doc string
profile string
expected string
}{
{
doc: "conflicting architectures and archMap",
profile: `{"defaultAction": "SCMP_ACT_ERRNO", "architectures": ["A", "B", "C"], "archMap": [{"architecture": "A", "subArchitectures": ["B", "C"]}]}`,
expected: `use either 'architectures' or 'archMap'`,
},
{
doc: "conflicting syscall.name and syscall.names",
profile: `{"defaultAction": "SCMP_ACT_ERRNO", "syscalls": [{"name": "accept", "names": ["accept"], "action": "SCMP_ACT_ALLOW"}]}`,
expected: `use either 'name' or 'names'`,
},
}
for _, tc := range tests {
tc := tc
rs := createSpec()
t.Run(tc.doc, func(t *testing.T) {
_, err := LoadProfile(tc.profile, &rs)
assert.ErrorContains(t, err, tc.expected)
})
}
}
// TestLoadLegacyProfile tests loading a seccomp profile in the old format
// (before https://github.com/docker/docker/pull/24510)
func TestLoadLegacyProfile(t *testing.T) {
f, err := ioutil.ReadFile("fixtures/default-old-format.json")
if err != nil {
t.Fatal(err)
}
rs := createSpec()
p, err := LoadProfile(string(f), &rs)
assert.NilError(t, err)
assert.Equal(t, p.DefaultAction, specs.ActErrno)
assert.DeepEqual(t, p.Architectures, []specs.Arch{"SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"})
assert.Equal(t, len(p.Syscalls), 311)
expected := specs.LinuxSyscall{
Names: []string{"accept"},
Action: specs.ActAllow,
Args: []specs.LinuxSeccompArg{},
}
assert.DeepEqual(t, p.Syscalls[0], expected)
}
func TestLoadDefaultProfile(t *testing.T) {
f, err := ioutil.ReadFile("default.json")
if err != nil {
t.Fatal(err)
}
rs := createSpec()
if _, err := LoadProfile(string(f), &rs); err != nil {
t.Fatal(err)
}
}
func TestUnmarshalDefaultProfile(t *testing.T) {
expected := DefaultProfile()
if expected == nil {
t.Skip("seccomp not supported")
}
f, err := ioutil.ReadFile("default.json")
if err != nil {
t.Fatal(err)
}
var profile Seccomp
err = json.Unmarshal(f, &profile)
if err != nil {
t.Fatal(err)
}
assert.DeepEqual(t, expected.Architectures, profile.Architectures)
assert.DeepEqual(t, expected.ArchMap, profile.ArchMap)
assert.DeepEqual(t, expected.DefaultAction, profile.DefaultAction)
assert.DeepEqual(t, expected.Syscalls, profile.Syscalls)
}
func TestMarshalUnmarshalFilter(t *testing.T) {
t.Parallel()
tests := []struct {
in string
out string
error bool
}{
{in: `{"arches":["s390x"],"minKernel":3}`, error: true},
{in: `{"arches":["s390x"],"minKernel":3.12}`, error: true},
{in: `{"arches":["s390x"],"minKernel":true}`, error: true},
{in: `{"arches":["s390x"],"minKernel":"0.0"}`, error: true},
{in: `{"arches":["s390x"],"minKernel":"3"}`, error: true},
{in: `{"arches":["s390x"],"minKernel":".3"}`, error: true},
{in: `{"arches":["s390x"],"minKernel":"3."}`, error: true},
{in: `{"arches":["s390x"],"minKernel":"true"}`, error: true},
{in: `{"arches":["s390x"],"minKernel":"3.12.1\""}`, error: true},
{in: `{"arches":["s390x"],"minKernel":"4.15abc"}`, error: true},
{in: `{"arches":["s390x"],"minKernel":null}`, out: `{"arches":["s390x"]}`},
{in: `{"arches":["s390x"],"minKernel":""}`, out: `{"arches":["s390x"],"minKernel":""}`}, // FIXME: try to fix omitempty for this
{in: `{"arches":["s390x"],"minKernel":"0.5"}`, out: `{"arches":["s390x"],"minKernel":"0.5"}`},
{in: `{"arches":["s390x"],"minKernel":"0.50"}`, out: `{"arches":["s390x"],"minKernel":"0.50"}`},
{in: `{"arches":["s390x"],"minKernel":"5.0"}`, out: `{"arches":["s390x"],"minKernel":"5.0"}`},
{in: `{"arches":["s390x"],"minKernel":"50.0"}`, out: `{"arches":["s390x"],"minKernel":"50.0"}`},
{in: `{"arches":["s390x"],"minKernel":"4.15"}`, out: `{"arches":["s390x"],"minKernel":"4.15"}`},
}
for _, tc := range tests {
tc := tc
t.Run(tc.in, func(t *testing.T) {
var filter Filter
err := json.Unmarshal([]byte(tc.in), &filter)
if tc.error {
if err == nil {
t.Fatal("expected an error")
} else if !strings.Contains(err.Error(), "invalid kernel version") {
t.Fatal("unexpected error:", err)
}
return
}
if err != nil {
t.Fatal(err)
}
out, err := json.Marshal(filter)
if err != nil {
t.Fatal(err)
}
if string(out) != tc.out {
t.Fatalf("expected %s, got %s", tc.out, string(out))
}
})
}
}
func TestLoadConditional(t *testing.T) {
f, err := ioutil.ReadFile("fixtures/conditional_include.json")
if err != nil {
t.Fatal(err)
}
tests := []struct {
doc string
cap string
expected []string
}{
{doc: "no caps", expected: []string{"chmod", "ptrace"}},
{doc: "with syslog", cap: "CAP_SYSLOG", expected: []string{"chmod", "syslog", "ptrace"}},
{doc: "no ptrace", cap: "CAP_SYS_ADMIN", expected: []string{"chmod"}},
}
for _, tc := range tests {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
rs := createSpec(tc.cap)
p, err := LoadProfile(string(f), &rs)
if err != nil {
t.Fatal(err)
}
if len(p.Syscalls) != len(tc.expected) {
t.Fatalf("expected %d syscalls in profile, have %d", len(tc.expected), len(p.Syscalls))
}
for i, v := range p.Syscalls {
if v.Names[0] != tc.expected[i] {
t.Fatalf("expected %s syscall, have %s", tc.expected[i], v.Names[0])
}
}
})
}
}
// createSpec() creates a minimum spec for testing
func createSpec(caps ...string) specs.Spec {
rs := specs.Spec{
Process: &specs.Process{
Capabilities: &specs.LinuxCapabilities{},
},
}
if caps != nil {
rs.Process.Capabilities.Bounding = append(rs.Process.Capabilities.Bounding, caps...)
}
return rs
}