From f5c8a71698d0a7a16c68be220e49fe64bfee7f5c Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 15 Jan 2018 11:21:06 -0600 Subject: ABC-22: Plugin sandboxing for linux/amd64 (#8068) * plugin sandboxing * remove unused type * better symlink handling, better remounting, better test, whitespace fixes, and comment on the remounting * fix test compile error * big simplification for getting mount flags * mask statfs flags to the ones we're interested in --- plugin/rpcplugin/sandbox/seccomp_linux_test.go | 210 +++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 plugin/rpcplugin/sandbox/seccomp_linux_test.go (limited to 'plugin/rpcplugin/sandbox/seccomp_linux_test.go') diff --git a/plugin/rpcplugin/sandbox/seccomp_linux_test.go b/plugin/rpcplugin/sandbox/seccomp_linux_test.go new file mode 100644 index 000000000..46fe38fe0 --- /dev/null +++ b/plugin/rpcplugin/sandbox/seccomp_linux_test.go @@ -0,0 +1,210 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package sandbox + +import ( + "encoding/binary" + "syscall" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/bpf" +) + +func seccompData(nr int32, arch uint32, ip uint64, args ...uint64) []byte { + var buf [64]byte + binary.BigEndian.PutUint32(buf[0:], uint32(nr)) + binary.BigEndian.PutUint32(buf[4:], arch) + binary.BigEndian.PutUint64(buf[8:], ip) + for i := 0; i < 6 && i < len(args); i++ { + binary.BigEndian.PutUint64(buf[16+i*8:], args[i]) + } + return buf[:] +} + +func TestSeccompFilter(t *testing.T) { + for name, tc := range map[string]struct { + Filter []bpf.Instruction + Data []byte + Expected bool + }{ + "Allowed": { + Filter: SeccompFilter(0xf00, []SeccompSyscall{ + {Syscall: syscall.SYS_READ}, + {Syscall: syscall.SYS_WRITE}, + }), + Data: seccompData(syscall.SYS_READ, 0xf00, 0), + Expected: true, + }, + "AllFail": { + Filter: SeccompFilter(0xf00, []SeccompSyscall{ + { + Syscall: syscall.SYS_READ, + Any: []SeccompConditions{ + {All: []SeccompCondition{ + &SeccompArgHasAnyBit{Arg: 0, Mask: 2}, + &SeccompArgHasAnyBit{Arg: 1, Mask: 2}, + &SeccompArgHasAnyBit{Arg: 2, Mask: 2}, + &SeccompArgHasAnyBit{Arg: 3, Mask: 2}, + }}, + }, + }, + {Syscall: syscall.SYS_WRITE}, + }), + Data: seccompData(syscall.SYS_READ, 0xf00, 0, 1, 2, 3, 4), + Expected: false, + }, + "AllPass": { + Filter: SeccompFilter(0xf00, []SeccompSyscall{ + { + Syscall: syscall.SYS_READ, + Any: []SeccompConditions{ + {All: []SeccompCondition{ + &SeccompArgHasAnyBit{Arg: 0, Mask: 7}, + &SeccompArgHasAnyBit{Arg: 1, Mask: 7}, + &SeccompArgHasAnyBit{Arg: 2, Mask: 7}, + &SeccompArgHasAnyBit{Arg: 3, Mask: 7}, + }}, + }, + }, + {Syscall: syscall.SYS_WRITE}, + }), + Data: seccompData(syscall.SYS_READ, 0xf00, 0, 1, 2, 3, 4), + Expected: true, + }, + "AnyFail": { + Filter: SeccompFilter(0xf00, []SeccompSyscall{ + { + Syscall: syscall.SYS_READ, + Any: []SeccompConditions{ + {All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 0, Mask: 8}}}, + {All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 1, Mask: 8}}}, + {All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 2, Mask: 8}}}, + {All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 3, Mask: 8}}}, + }, + }, + {Syscall: syscall.SYS_WRITE}, + }), + Data: seccompData(syscall.SYS_READ, 0xf00, 0, 1, 2, 3, 4), + Expected: false, + }, + "AnyPass": { + Filter: SeccompFilter(0xf00, []SeccompSyscall{ + { + Syscall: syscall.SYS_READ, + Any: []SeccompConditions{ + {All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 0, Mask: 2}}}, + {All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 1, Mask: 2}}}, + {All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 2, Mask: 2}}}, + {All: []SeccompCondition{&SeccompArgHasAnyBit{Arg: 3, Mask: 2}}}, + }, + }, + {Syscall: syscall.SYS_WRITE}, + }), + Data: seccompData(syscall.SYS_READ, 0xf00, 0, 1, 2, 3, 4), + Expected: true, + }, + "BadArch": { + Filter: SeccompFilter(0xf00, []SeccompSyscall{ + {Syscall: syscall.SYS_READ}, + {Syscall: syscall.SYS_WRITE}, + }), + Data: seccompData(syscall.SYS_MOUNT, 0xf01, 0), + Expected: false, + }, + "BadSyscall": { + Filter: SeccompFilter(0xf00, []SeccompSyscall{ + {Syscall: syscall.SYS_READ}, + {Syscall: syscall.SYS_WRITE}, + }), + Data: seccompData(syscall.SYS_MOUNT, 0xf00, 0), + Expected: false, + }, + } { + t.Run(name, func(t *testing.T) { + vm, err := bpf.NewVM(tc.Filter) + require.NoError(t, err) + result, err := vm.Run(tc.Data) + require.NoError(t, err) + if tc.Expected { + assert.Equal(t, SECCOMP_RET_ALLOW, result) + } else { + assert.Equal(t, int(SECCOMP_RET_ERRNO|syscall.EPERM), result) + } + }) + } +} + +func TestSeccompFilter_Conditions(t *testing.T) { + for name, tc := range map[string]struct { + Condition SeccompCondition + Args []uint64 + Expected bool + }{ + "ArgHasAnyBitFail": { + Condition: SeccompArgHasAnyBit{Arg: 0, Mask: 0x0004}, + Args: []uint64{0x0400008000}, + Expected: false, + }, + "ArgHasAnyBitPass1": { + Condition: SeccompArgHasAnyBit{Arg: 0, Mask: 0x400000004}, + Args: []uint64{0x8000008004}, + Expected: true, + }, + "ArgHasAnyBitPass2": { + Condition: SeccompArgHasAnyBit{Arg: 0, Mask: 0x400000004}, + Args: []uint64{0x8400008000}, + Expected: true, + }, + "ArgHasNoBitsFail1": { + Condition: SeccompArgHasNoBits{Arg: 0, Mask: 0x1100000011}, + Args: []uint64{0x0000008007}, + Expected: false, + }, + "ArgHasNoBitsFail2": { + Condition: SeccompArgHasNoBits{Arg: 0, Mask: 0x1100000011}, + Args: []uint64{0x0700008000}, + Expected: false, + }, + "ArgHasNoBitsPass": { + Condition: SeccompArgHasNoBits{Arg: 0, Mask: 0x400000004}, + Args: []uint64{0x8000008000}, + Expected: true, + }, + "ArgEqualsPass": { + Condition: SeccompArgEquals{Arg: 0, Value: 0x123456789ABCDEF}, + Args: []uint64{0x123456789ABCDEF}, + Expected: true, + }, + "ArgEqualsFail1": { + Condition: SeccompArgEquals{Arg: 0, Value: 0x123456789ABCDEF}, + Args: []uint64{0x023456789ABCDEF}, + Expected: false, + }, + "ArgEqualsFail2": { + Condition: SeccompArgEquals{Arg: 0, Value: 0x123456789ABCDEF}, + Args: []uint64{0x123456789ABCDE0}, + Expected: false, + }, + } { + t.Run(name, func(t *testing.T) { + filter := SeccompFilter(0xf00, []SeccompSyscall{ + { + Syscall: 1, + Any: []SeccompConditions{{All: []SeccompCondition{tc.Condition}}}, + }, + }) + vm, err := bpf.NewVM(filter) + require.NoError(t, err) + result, err := vm.Run(seccompData(1, 0xf00, 0, tc.Args...)) + require.NoError(t, err) + if tc.Expected { + assert.Equal(t, SECCOMP_RET_ALLOW, result) + } else { + assert.Equal(t, int(SECCOMP_RET_ERRNO|syscall.EPERM), result) + } + }) + } +} -- cgit v1.2.3-1-g7c22