github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/profiles/seccomp/seccomp_test.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  package seccomp // import "github.com/docker/docker/profiles/seccomp"
     5  
     6  import (
     7  	"encoding/json"
     8  	"os"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/opencontainers/runtime-spec/specs-go"
    13  	"gotest.tools/v3/assert"
    14  )
    15  
    16  func TestLoadProfile(t *testing.T) {
    17  	f, err := os.ReadFile("fixtures/example.json")
    18  	if err != nil {
    19  		t.Fatal(err)
    20  	}
    21  	rs := createSpec()
    22  	p, err := LoadProfile(string(f), &rs)
    23  	if err != nil {
    24  		t.Fatal(err)
    25  	}
    26  	var expectedErrno uint = 12345
    27  	var expectedDefaultErrno uint = 1
    28  	expected := specs.LinuxSeccomp{
    29  		DefaultAction:   specs.ActErrno,
    30  		DefaultErrnoRet: &expectedDefaultErrno,
    31  		Syscalls: []specs.LinuxSyscall{
    32  			{
    33  				Names:  []string{"clone"},
    34  				Action: specs.ActAllow,
    35  				Args: []specs.LinuxSeccompArg{{
    36  					Index:    0,
    37  					Value:    2114060288,
    38  					ValueTwo: 0,
    39  					Op:       specs.OpMaskedEqual,
    40  				}},
    41  			},
    42  			{
    43  
    44  				Names:  []string{"open"},
    45  				Action: specs.ActAllow,
    46  				Args:   []specs.LinuxSeccompArg{},
    47  			},
    48  			{
    49  				Names:  []string{"close"},
    50  				Action: specs.ActAllow,
    51  				Args:   []specs.LinuxSeccompArg{},
    52  			},
    53  			{
    54  				Names:    []string{"syslog"},
    55  				Action:   specs.ActErrno,
    56  				ErrnoRet: &expectedErrno,
    57  				Args:     []specs.LinuxSeccompArg{},
    58  			},
    59  		},
    60  	}
    61  
    62  	assert.DeepEqual(t, expected, *p)
    63  }
    64  
    65  func TestLoadProfileWithDefaultErrnoRet(t *testing.T) {
    66  	var profile = []byte(`{
    67  "defaultAction": "SCMP_ACT_ERRNO",
    68  "defaultErrnoRet": 6
    69  }`)
    70  	rs := createSpec()
    71  	p, err := LoadProfile(string(profile), &rs)
    72  	if err != nil {
    73  		t.Fatal(err)
    74  	}
    75  
    76  	expectedErrnoRet := uint(6)
    77  	expected := specs.LinuxSeccomp{
    78  		DefaultAction:   specs.ActErrno,
    79  		DefaultErrnoRet: &expectedErrnoRet,
    80  	}
    81  
    82  	assert.DeepEqual(t, expected, *p)
    83  }
    84  
    85  func TestLoadProfileWithListenerPath(t *testing.T) {
    86  	var profile = []byte(`{
    87  "defaultAction": "SCMP_ACT_ERRNO",
    88  "listenerPath": "/var/run/seccompaget.sock",
    89  "listenerMetadata": "opaque-metadata"
    90  }`)
    91  	rs := createSpec()
    92  	p, err := LoadProfile(string(profile), &rs)
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  
    97  	expected := specs.LinuxSeccomp{
    98  		DefaultAction:    specs.ActErrno,
    99  		ListenerPath:     "/var/run/seccompaget.sock",
   100  		ListenerMetadata: "opaque-metadata",
   101  	}
   102  
   103  	assert.DeepEqual(t, expected, *p)
   104  }
   105  
   106  func TestLoadProfileWithFlag(t *testing.T) {
   107  	profile := `{"defaultAction": "SCMP_ACT_ERRNO", "flags": ["SECCOMP_FILTER_FLAG_SPEC_ALLOW", "SECCOMP_FILTER_FLAG_LOG"]}`
   108  	expected := specs.LinuxSeccomp{
   109  		DefaultAction: specs.ActErrno,
   110  		Flags:         []specs.LinuxSeccompFlag{"SECCOMP_FILTER_FLAG_SPEC_ALLOW", "SECCOMP_FILTER_FLAG_LOG"},
   111  	}
   112  	rs := createSpec()
   113  	p, err := LoadProfile(profile, &rs)
   114  	assert.NilError(t, err)
   115  	assert.DeepEqual(t, expected, *p)
   116  }
   117  
   118  // TestLoadProfileValidation tests that invalid profiles produce the correct error.
   119  func TestLoadProfileValidation(t *testing.T) {
   120  	tests := []struct {
   121  		doc      string
   122  		profile  string
   123  		expected string
   124  	}{
   125  		{
   126  			doc:      "conflicting architectures and archMap",
   127  			profile:  `{"defaultAction": "SCMP_ACT_ERRNO", "architectures": ["A", "B", "C"], "archMap": [{"architecture": "A", "subArchitectures": ["B", "C"]}]}`,
   128  			expected: `use either 'architectures' or 'archMap'`,
   129  		},
   130  		{
   131  			doc:      "conflicting syscall.name and syscall.names",
   132  			profile:  `{"defaultAction": "SCMP_ACT_ERRNO", "syscalls": [{"name": "accept", "names": ["accept"], "action": "SCMP_ACT_ALLOW"}]}`,
   133  			expected: `use either 'name' or 'names'`,
   134  		},
   135  	}
   136  	for _, tc := range tests {
   137  		tc := tc
   138  		rs := createSpec()
   139  		t.Run(tc.doc, func(t *testing.T) {
   140  			_, err := LoadProfile(tc.profile, &rs)
   141  			assert.ErrorContains(t, err, tc.expected)
   142  		})
   143  	}
   144  }
   145  
   146  // TestLoadLegacyProfile tests loading a seccomp profile in the old format
   147  // (before https://github.com/docker/docker/pull/24510)
   148  func TestLoadLegacyProfile(t *testing.T) {
   149  	f, err := os.ReadFile("fixtures/default-old-format.json")
   150  	if err != nil {
   151  		t.Fatal(err)
   152  	}
   153  	rs := createSpec()
   154  	p, err := LoadProfile(string(f), &rs)
   155  	assert.NilError(t, err)
   156  	assert.Equal(t, p.DefaultAction, specs.ActErrno)
   157  	assert.DeepEqual(t, p.Architectures, []specs.Arch{"SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"})
   158  	assert.Equal(t, len(p.Syscalls), 311)
   159  	expected := specs.LinuxSyscall{
   160  		Names:  []string{"accept"},
   161  		Action: specs.ActAllow,
   162  		Args:   []specs.LinuxSeccompArg{},
   163  	}
   164  	assert.DeepEqual(t, p.Syscalls[0], expected)
   165  }
   166  
   167  func TestLoadDefaultProfile(t *testing.T) {
   168  	f, err := os.ReadFile("default.json")
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  	rs := createSpec()
   173  	if _, err := LoadProfile(string(f), &rs); err != nil {
   174  		t.Fatal(err)
   175  	}
   176  }
   177  
   178  func TestUnmarshalDefaultProfile(t *testing.T) {
   179  	expected := DefaultProfile()
   180  	if expected == nil {
   181  		t.Skip("seccomp not supported")
   182  	}
   183  
   184  	f, err := os.ReadFile("default.json")
   185  	if err != nil {
   186  		t.Fatal(err)
   187  	}
   188  	var profile Seccomp
   189  	err = json.Unmarshal(f, &profile)
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  	assert.DeepEqual(t, expected.Architectures, profile.Architectures)
   194  	assert.DeepEqual(t, expected.ArchMap, profile.ArchMap)
   195  	assert.DeepEqual(t, expected.DefaultAction, profile.DefaultAction)
   196  	assert.DeepEqual(t, expected.Syscalls, profile.Syscalls)
   197  }
   198  
   199  func TestMarshalUnmarshalFilter(t *testing.T) {
   200  	t.Parallel()
   201  	tests := []struct {
   202  		in    string
   203  		out   string
   204  		error bool
   205  	}{
   206  		{in: `{"arches":["s390x"],"minKernel":3}`, error: true},
   207  		{in: `{"arches":["s390x"],"minKernel":3.12}`, error: true},
   208  		{in: `{"arches":["s390x"],"minKernel":true}`, error: true},
   209  		{in: `{"arches":["s390x"],"minKernel":"0.0"}`, error: true},
   210  		{in: `{"arches":["s390x"],"minKernel":"3"}`, error: true},
   211  		{in: `{"arches":["s390x"],"minKernel":".3"}`, error: true},
   212  		{in: `{"arches":["s390x"],"minKernel":"3."}`, error: true},
   213  		{in: `{"arches":["s390x"],"minKernel":"true"}`, error: true},
   214  		{in: `{"arches":["s390x"],"minKernel":"3.12.1\""}`, error: true},
   215  		{in: `{"arches":["s390x"],"minKernel":"4.15abc"}`, error: true},
   216  		{in: `{"arches":["s390x"],"minKernel":null}`, out: `{"arches":["s390x"]}`},
   217  		{in: `{"arches":["s390x"],"minKernel":""}`, out: `{"arches":["s390x"],"minKernel":""}`}, // FIXME: try to fix omitempty for this
   218  		{in: `{"arches":["s390x"],"minKernel":"0.5"}`, out: `{"arches":["s390x"],"minKernel":"0.5"}`},
   219  		{in: `{"arches":["s390x"],"minKernel":"0.50"}`, out: `{"arches":["s390x"],"minKernel":"0.50"}`},
   220  		{in: `{"arches":["s390x"],"minKernel":"5.0"}`, out: `{"arches":["s390x"],"minKernel":"5.0"}`},
   221  		{in: `{"arches":["s390x"],"minKernel":"50.0"}`, out: `{"arches":["s390x"],"minKernel":"50.0"}`},
   222  		{in: `{"arches":["s390x"],"minKernel":"4.15"}`, out: `{"arches":["s390x"],"minKernel":"4.15"}`},
   223  	}
   224  	for _, tc := range tests {
   225  		tc := tc
   226  		t.Run(tc.in, func(t *testing.T) {
   227  			var filter Filter
   228  			err := json.Unmarshal([]byte(tc.in), &filter)
   229  			if tc.error {
   230  				if err == nil {
   231  					t.Fatal("expected an error")
   232  				} else if !strings.Contains(err.Error(), "invalid kernel version") {
   233  					t.Fatal("unexpected error:", err)
   234  				}
   235  				return
   236  			}
   237  			if err != nil {
   238  				t.Fatal(err)
   239  			}
   240  			out, err := json.Marshal(filter)
   241  			if err != nil {
   242  				t.Fatal(err)
   243  			}
   244  			if string(out) != tc.out {
   245  				t.Fatalf("expected %s, got %s", tc.out, string(out))
   246  			}
   247  		})
   248  	}
   249  }
   250  
   251  func TestLoadConditional(t *testing.T) {
   252  	f, err := os.ReadFile("fixtures/conditional_include.json")
   253  	if err != nil {
   254  		t.Fatal(err)
   255  	}
   256  	tests := []struct {
   257  		doc      string
   258  		cap      string
   259  		expected []string
   260  	}{
   261  		{doc: "no caps", expected: []string{"chmod", "ptrace"}},
   262  		{doc: "with syslog", cap: "CAP_SYSLOG", expected: []string{"chmod", "syslog", "ptrace"}},
   263  		{doc: "no ptrace", cap: "CAP_SYS_ADMIN", expected: []string{"chmod"}},
   264  	}
   265  
   266  	for _, tc := range tests {
   267  		tc := tc
   268  		t.Run(tc.doc, func(t *testing.T) {
   269  			rs := createSpec(tc.cap)
   270  			p, err := LoadProfile(string(f), &rs)
   271  			if err != nil {
   272  				t.Fatal(err)
   273  			}
   274  			if len(p.Syscalls) != len(tc.expected) {
   275  				t.Fatalf("expected %d syscalls in profile, have %d", len(tc.expected), len(p.Syscalls))
   276  			}
   277  			for i, v := range p.Syscalls {
   278  				if v.Names[0] != tc.expected[i] {
   279  					t.Fatalf("expected %s syscall, have %s", tc.expected[i], v.Names[0])
   280  				}
   281  			}
   282  		})
   283  	}
   284  }
   285  
   286  // createSpec() creates a minimum spec for testing
   287  func createSpec(caps ...string) specs.Spec {
   288  	rs := specs.Spec{
   289  		Process: &specs.Process{
   290  			Capabilities: &specs.LinuxCapabilities{},
   291  		},
   292  	}
   293  	if caps != nil {
   294  		rs.Process.Capabilities.Bounding = append(rs.Process.Capabilities.Bounding, caps...)
   295  	}
   296  	return rs
   297  }