1// +build linux,cgo,seccomp 2 3package integration 4 5import ( 6 "strings" 7 "syscall" 8 "testing" 9 10 "github.com/opencontainers/runc/libcontainer" 11 "github.com/opencontainers/runc/libcontainer/configs" 12 libseccomp "github.com/seccomp/libseccomp-golang" 13) 14 15func TestSeccompDenyGetcwd(t *testing.T) { 16 if testing.Short() { 17 return 18 } 19 20 rootfs, err := newRootfs() 21 if err != nil { 22 t.Fatal(err) 23 } 24 defer remove(rootfs) 25 26 config := newTemplateConfig(rootfs) 27 config.Seccomp = &configs.Seccomp{ 28 DefaultAction: configs.Allow, 29 Syscalls: []*configs.Syscall{ 30 { 31 Name: "getcwd", 32 Action: configs.Errno, 33 }, 34 }, 35 } 36 37 container, err := newContainer(config) 38 if err != nil { 39 t.Fatal(err) 40 } 41 defer container.Destroy() 42 43 buffers := newStdBuffers() 44 pwd := &libcontainer.Process{ 45 Cwd: "/", 46 Args: []string{"pwd"}, 47 Env: standardEnvironment, 48 Stdin: buffers.Stdin, 49 Stdout: buffers.Stdout, 50 Stderr: buffers.Stderr, 51 Init: true, 52 } 53 54 err = container.Run(pwd) 55 if err != nil { 56 t.Fatal(err) 57 } 58 ps, err := pwd.Wait() 59 if err == nil { 60 t.Fatal("Expecting error (negative return code); instead exited cleanly!") 61 } 62 63 var exitCode int 64 status := ps.Sys().(syscall.WaitStatus) 65 if status.Exited() { 66 exitCode = status.ExitStatus() 67 } else if status.Signaled() { 68 exitCode = -int(status.Signal()) 69 } else { 70 t.Fatalf("Unrecognized exit reason!") 71 } 72 73 if exitCode == 0 { 74 t.Fatalf("Getcwd should fail with negative exit code, instead got %d!", exitCode) 75 } 76 77 expected := "pwd: getcwd: Operation not permitted" 78 actual := strings.Trim(buffers.Stderr.String(), "\n") 79 if actual != expected { 80 t.Fatalf("Expected output %s but got %s\n", expected, actual) 81 } 82} 83 84func TestSeccompPermitWriteConditional(t *testing.T) { 85 if testing.Short() { 86 return 87 } 88 89 rootfs, err := newRootfs() 90 if err != nil { 91 t.Fatal(err) 92 } 93 defer remove(rootfs) 94 95 config := newTemplateConfig(rootfs) 96 config.Seccomp = &configs.Seccomp{ 97 DefaultAction: configs.Allow, 98 Syscalls: []*configs.Syscall{ 99 { 100 Name: "write", 101 Action: configs.Errno, 102 Args: []*configs.Arg{ 103 { 104 Index: 0, 105 Value: 2, 106 Op: configs.EqualTo, 107 }, 108 }, 109 }, 110 }, 111 } 112 113 container, err := newContainer(config) 114 if err != nil { 115 t.Fatal(err) 116 } 117 defer container.Destroy() 118 119 buffers := newStdBuffers() 120 dmesg := &libcontainer.Process{ 121 Cwd: "/", 122 Args: []string{"busybox", "ls", "/"}, 123 Env: standardEnvironment, 124 Stdin: buffers.Stdin, 125 Stdout: buffers.Stdout, 126 Stderr: buffers.Stderr, 127 Init: true, 128 } 129 130 err = container.Run(dmesg) 131 if err != nil { 132 t.Fatal(err) 133 } 134 if _, err := dmesg.Wait(); err != nil { 135 t.Fatalf("%s: %s", err, buffers.Stderr) 136 } 137} 138 139func TestSeccompDenyWriteConditional(t *testing.T) { 140 if testing.Short() { 141 return 142 } 143 144 // Only test if library version is v2.2.1 or higher 145 // Conditional filtering will always error in v2.2.0 and lower 146 major, minor, micro := libseccomp.GetLibraryVersion() 147 if (major == 2 && minor < 2) || (major == 2 && minor == 2 && micro < 1) { 148 return 149 } 150 151 rootfs, err := newRootfs() 152 if err != nil { 153 t.Fatal(err) 154 } 155 defer remove(rootfs) 156 157 config := newTemplateConfig(rootfs) 158 config.Seccomp = &configs.Seccomp{ 159 DefaultAction: configs.Allow, 160 Syscalls: []*configs.Syscall{ 161 { 162 Name: "write", 163 Action: configs.Errno, 164 Args: []*configs.Arg{ 165 { 166 Index: 0, 167 Value: 2, 168 Op: configs.EqualTo, 169 }, 170 }, 171 }, 172 }, 173 } 174 175 container, err := newContainer(config) 176 if err != nil { 177 t.Fatal(err) 178 } 179 defer container.Destroy() 180 181 buffers := newStdBuffers() 182 dmesg := &libcontainer.Process{ 183 Cwd: "/", 184 Args: []string{"busybox", "ls", "does_not_exist"}, 185 Env: standardEnvironment, 186 Stdin: buffers.Stdin, 187 Stdout: buffers.Stdout, 188 Stderr: buffers.Stderr, 189 Init: true, 190 } 191 192 err = container.Run(dmesg) 193 if err != nil { 194 t.Fatal(err) 195 } 196 197 ps, err := dmesg.Wait() 198 if err == nil { 199 t.Fatal("Expecting negative return, instead got 0!") 200 } 201 202 var exitCode int 203 status := ps.Sys().(syscall.WaitStatus) 204 if status.Exited() { 205 exitCode = status.ExitStatus() 206 } else if status.Signaled() { 207 exitCode = -int(status.Signal()) 208 } else { 209 t.Fatalf("Unrecognized exit reason!") 210 } 211 212 if exitCode == 0 { 213 t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode) 214 } 215 216 // We're denying write to stderr, so we expect an empty buffer 217 expected := "" 218 actual := strings.Trim(buffers.Stderr.String(), "\n") 219 if actual != expected { 220 t.Fatalf("Expected output %s but got %s\n", expected, actual) 221 } 222} 223 224func TestSeccompPermitWriteMultipleConditions(t *testing.T) { 225 if testing.Short() { 226 return 227 } 228 229 rootfs, err := newRootfs() 230 if err != nil { 231 t.Fatal(err) 232 } 233 defer remove(rootfs) 234 235 config := newTemplateConfig(rootfs) 236 config.Seccomp = &configs.Seccomp{ 237 DefaultAction: configs.Allow, 238 Syscalls: []*configs.Syscall{ 239 { 240 Name: "write", 241 Action: configs.Errno, 242 Args: []*configs.Arg{ 243 { 244 Index: 0, 245 Value: 2, 246 Op: configs.EqualTo, 247 }, 248 { 249 Index: 2, 250 Value: 0, 251 Op: configs.NotEqualTo, 252 }, 253 }, 254 }, 255 }, 256 } 257 258 buffers, exitCode, err := runContainer(config, "", "ls", "/") 259 if err != nil { 260 t.Fatalf("%s: %s", buffers, err) 261 } 262 if exitCode != 0 { 263 t.Fatalf("exit code not 0. code %d buffers %s", exitCode, buffers) 264 } 265 // We don't need to verify the actual thing printed 266 // Just that something was written to stdout 267 if len(buffers.Stdout.String()) == 0 { 268 t.Fatalf("Nothing was written to stdout, write call failed!\n") 269 } 270} 271 272func TestSeccompDenyWriteMultipleConditions(t *testing.T) { 273 if testing.Short() { 274 return 275 } 276 277 // Only test if library version is v2.2.1 or higher 278 // Conditional filtering will always error in v2.2.0 and lower 279 major, minor, micro := libseccomp.GetLibraryVersion() 280 if (major == 2 && minor < 2) || (major == 2 && minor == 2 && micro < 1) { 281 return 282 } 283 284 rootfs, err := newRootfs() 285 if err != nil { 286 t.Fatal(err) 287 } 288 defer remove(rootfs) 289 290 config := newTemplateConfig(rootfs) 291 config.Seccomp = &configs.Seccomp{ 292 DefaultAction: configs.Allow, 293 Syscalls: []*configs.Syscall{ 294 { 295 Name: "write", 296 Action: configs.Errno, 297 Args: []*configs.Arg{ 298 { 299 Index: 0, 300 Value: 2, 301 Op: configs.EqualTo, 302 }, 303 { 304 Index: 2, 305 Value: 0, 306 Op: configs.NotEqualTo, 307 }, 308 }, 309 }, 310 }, 311 } 312 313 buffers, exitCode, err := runContainer(config, "", "ls", "/does_not_exist") 314 if err == nil { 315 t.Fatalf("Expecting error return, instead got 0") 316 } 317 if exitCode == 0 { 318 t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode) 319 } 320 321 expected := "" 322 actual := strings.Trim(buffers.Stderr.String(), "\n") 323 if actual != expected { 324 t.Fatalf("Expected output %s but got %s\n", expected, actual) 325 } 326} 327 328func TestSeccompMultipleConditionSameArgDeniesStdout(t *testing.T) { 329 if testing.Short() { 330 return 331 } 332 333 rootfs, err := newRootfs() 334 if err != nil { 335 t.Fatal(err) 336 } 337 defer remove(rootfs) 338 339 // Prevent writing to both stdout and stderr 340 config := newTemplateConfig(rootfs) 341 config.Seccomp = &configs.Seccomp{ 342 DefaultAction: configs.Allow, 343 Syscalls: []*configs.Syscall{ 344 { 345 Name: "write", 346 Action: configs.Errno, 347 Args: []*configs.Arg{ 348 { 349 Index: 0, 350 Value: 1, 351 Op: configs.EqualTo, 352 }, 353 { 354 Index: 0, 355 Value: 2, 356 Op: configs.EqualTo, 357 }, 358 }, 359 }, 360 }, 361 } 362 363 buffers, exitCode, err := runContainer(config, "", "ls", "/") 364 if err != nil { 365 t.Fatalf("%s: %s", buffers, err) 366 } 367 if exitCode != 0 { 368 t.Fatalf("exit code not 0. code %d buffers %s", exitCode, buffers) 369 } 370 // Verify that nothing was printed 371 if len(buffers.Stdout.String()) != 0 { 372 t.Fatalf("Something was written to stdout, write call succeeded!\n") 373 } 374} 375 376func TestSeccompMultipleConditionSameArgDeniesStderr(t *testing.T) { 377 if testing.Short() { 378 return 379 } 380 381 rootfs, err := newRootfs() 382 if err != nil { 383 t.Fatal(err) 384 } 385 defer remove(rootfs) 386 387 // Prevent writing to both stdout and stderr 388 config := newTemplateConfig(rootfs) 389 config.Seccomp = &configs.Seccomp{ 390 DefaultAction: configs.Allow, 391 Syscalls: []*configs.Syscall{ 392 { 393 Name: "write", 394 Action: configs.Errno, 395 Args: []*configs.Arg{ 396 { 397 Index: 0, 398 Value: 1, 399 Op: configs.EqualTo, 400 }, 401 { 402 Index: 0, 403 Value: 2, 404 Op: configs.EqualTo, 405 }, 406 }, 407 }, 408 }, 409 } 410 411 buffers, exitCode, err := runContainer(config, "", "ls", "/does_not_exist") 412 if err == nil { 413 t.Fatalf("Expecting error return, instead got 0") 414 } 415 if exitCode == 0 { 416 t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode) 417 } 418 // Verify nothing was printed 419 if len(buffers.Stderr.String()) != 0 { 420 t.Fatalf("Something was written to stderr, write call succeeded!\n") 421 } 422} 423