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 }