github.com/moby/docker@v26.1.3+incompatible/integration-cli/docker_cli_run_unix_test.go (about) 1 //go:build !windows 2 3 package main 4 5 import ( 6 "bufio" 7 "encoding/json" 8 "fmt" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "regexp" 13 "runtime" 14 "strconv" 15 "strings" 16 "syscall" 17 "testing" 18 "time" 19 20 "github.com/creack/pty" 21 "github.com/docker/docker/client" 22 "github.com/docker/docker/integration-cli/cli" 23 "github.com/docker/docker/integration-cli/cli/build" 24 "github.com/docker/docker/pkg/parsers" 25 "github.com/docker/docker/pkg/sysinfo" 26 "github.com/docker/docker/testutil" 27 "github.com/moby/sys/mount" 28 "gotest.tools/v3/assert" 29 "gotest.tools/v3/icmd" 30 ) 31 32 // #6509 33 func (s *DockerCLIRunSuite) TestRunRedirectStdout(c *testing.T) { 34 checkRedirect := func(command string) { 35 _, tty, err := pty.Open() 36 assert.Assert(c, err == nil, "Could not open pty") 37 cmd := exec.Command("sh", "-c", command) 38 cmd.Stdin = tty 39 cmd.Stdout = tty 40 cmd.Stderr = tty 41 assert.NilError(c, cmd.Start()) 42 ch := make(chan error, 1) 43 go func() { 44 ch <- cmd.Wait() 45 close(ch) 46 }() 47 48 select { 49 case <-time.After(10 * time.Second): 50 c.Fatal("command timeout") 51 case err := <-ch: 52 assert.Assert(c, err == nil, "wait err") 53 } 54 } 55 56 checkRedirect(dockerBinary + " run -i busybox cat /etc/passwd | grep -q root") 57 checkRedirect(dockerBinary + " run busybox cat /etc/passwd | grep -q root") 58 } 59 60 // Test recursive bind mount works by default 61 func (s *DockerCLIRunSuite) TestRunWithVolumesIsRecursive(c *testing.T) { 62 // /tmp gets permission denied 63 testRequires(c, NotUserNamespace, testEnv.IsLocalDaemon) 64 tmpDir, err := os.MkdirTemp("", "docker_recursive_mount_test") 65 assert.NilError(c, err) 66 67 defer os.RemoveAll(tmpDir) 68 69 // Create a temporary tmpfs mount. 70 tmpfsDir := filepath.Join(tmpDir, "tmpfs") 71 assert.Assert(c, os.MkdirAll(tmpfsDir, 0o777) == nil, "failed to mkdir at %s", tmpfsDir) 72 assert.Assert(c, mount.Mount("tmpfs", tmpfsDir, "tmpfs", "") == nil, "failed to create a tmpfs mount at %s", tmpfsDir) 73 defer mount.Unmount(tmpfsDir) 74 75 f, err := os.CreateTemp(tmpfsDir, "touch-me") 76 assert.NilError(c, err) 77 defer f.Close() 78 79 out := cli.DockerCmd(c, "run", "--name", "test-data", "--volume", fmt.Sprintf("%s:/tmp:ro", tmpDir), "busybox:latest", "ls", "/tmp/tmpfs").Combined() 80 assert.Assert(c, strings.Contains(out, filepath.Base(f.Name())), "Recursive bind mount test failed. Expected file not found") 81 } 82 83 func (s *DockerCLIRunSuite) TestRunDeviceDirectory(c *testing.T) { 84 testRequires(c, DaemonIsLinux, NotUserNamespace) 85 if _, err := os.Stat("/dev/snd"); err != nil { 86 c.Skip("Host does not have /dev/snd") 87 } 88 89 out := cli.DockerCmd(c, "run", "--device", "/dev/snd:/dev/snd", "busybox", "sh", "-c", "ls /dev/snd/").Combined() 90 assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "timer"), "expected output /dev/snd/timer") 91 out = cli.DockerCmd(c, "run", "--device", "/dev/snd:/dev/othersnd", "busybox", "sh", "-c", "ls /dev/othersnd/").Combined() 92 assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "seq"), "expected output /dev/othersnd/seq") 93 } 94 95 // TestRunAttachDetach checks attaching and detaching with the default escape sequence. 96 func (s *DockerCLIRunSuite) TestRunAttachDetach(c *testing.T) { 97 const name = "attach-detach" 98 99 cli.DockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") 100 101 cmd := exec.Command(dockerBinary, "attach", name) 102 stdout, err := cmd.StdoutPipe() 103 assert.NilError(c, err) 104 cpty, tty, err := pty.Open() 105 assert.NilError(c, err) 106 defer cpty.Close() 107 cmd.Stdin = tty 108 assert.NilError(c, cmd.Start()) 109 cli.WaitRun(c, name) 110 111 _, err = cpty.Write([]byte("hello\n")) 112 assert.NilError(c, err) 113 114 out, err := bufio.NewReader(stdout).ReadString('\n') 115 assert.NilError(c, err) 116 assert.Equal(c, strings.TrimSpace(out), "hello") 117 118 // escape sequence 119 _, err = cpty.Write([]byte{16}) 120 assert.NilError(c, err) 121 time.Sleep(100 * time.Millisecond) 122 _, err = cpty.Write([]byte{17}) 123 assert.NilError(c, err) 124 125 ch := make(chan struct{}, 1) 126 go func() { 127 cmd.Wait() 128 ch <- struct{}{} 129 }() 130 131 select { 132 case <-ch: 133 case <-time.After(10 * time.Second): 134 c.Fatal("timed out waiting for container to exit") 135 } 136 137 running := inspectField(c, name, "State.Running") 138 assert.Equal(c, running, "true", "expected container to still be running") 139 140 out = cli.DockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c), "-f", "container="+name).Stdout() 141 // attach and detach event should be monitored 142 assert.Assert(c, strings.Contains(out, "attach")) 143 assert.Assert(c, strings.Contains(out, "detach")) 144 } 145 146 // TestRunAttachDetachFromFlag checks attaching and detaching with the escape sequence specified via flags. 147 func (s *DockerCLIRunSuite) TestRunAttachDetachFromFlag(c *testing.T) { 148 const name = "attach-detach" 149 keyCtrlA := []byte{1} 150 keyA := []byte{97} 151 152 cli.DockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") 153 154 cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-a,a", name) 155 stdout, err := cmd.StdoutPipe() 156 if err != nil { 157 c.Fatal(err) 158 } 159 cpty, tty, err := pty.Open() 160 if err != nil { 161 c.Fatal(err) 162 } 163 defer cpty.Close() 164 cmd.Stdin = tty 165 if err := cmd.Start(); err != nil { 166 c.Fatal(err) 167 } 168 cli.WaitRun(c, name) 169 170 if _, err := cpty.Write([]byte("hello\n")); err != nil { 171 c.Fatal(err) 172 } 173 174 out, err := bufio.NewReader(stdout).ReadString('\n') 175 if err != nil { 176 c.Fatal(err) 177 } 178 if strings.TrimSpace(out) != "hello" { 179 c.Fatalf("expected 'hello', got %q", out) 180 } 181 182 // escape sequence 183 if _, err := cpty.Write(keyCtrlA); err != nil { 184 c.Fatal(err) 185 } 186 time.Sleep(100 * time.Millisecond) 187 if _, err := cpty.Write(keyA); err != nil { 188 c.Fatal(err) 189 } 190 191 ch := make(chan struct{}, 1) 192 go func() { 193 cmd.Wait() 194 ch <- struct{}{} 195 }() 196 197 select { 198 case <-ch: 199 case <-time.After(10 * time.Second): 200 c.Fatal("timed out waiting for container to exit") 201 } 202 203 running := inspectField(c, name, "State.Running") 204 assert.Equal(c, running, "true", "expected container to still be running") 205 } 206 207 // TestRunAttachDetachFromInvalidFlag checks attaching and detaching with the escape sequence specified via flags. 208 func (s *DockerCLIRunSuite) TestRunAttachDetachFromInvalidFlag(c *testing.T) { 209 const name = "attach-detach" 210 cli.DockerCmd(c, "run", "--name", name, "-itd", "busybox", "top") 211 cli.WaitRun(c, name) 212 213 // specify an invalid detach key, container will ignore it and use default 214 cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-A,a", name) 215 stdout, err := cmd.StdoutPipe() 216 if err != nil { 217 c.Fatal(err) 218 } 219 cpty, tty, err := pty.Open() 220 if err != nil { 221 c.Fatal(err) 222 } 223 defer cpty.Close() 224 cmd.Stdin = tty 225 if err := cmd.Start(); err != nil { 226 c.Fatal(err) 227 } 228 go cmd.Wait() 229 230 bufReader := bufio.NewReader(stdout) 231 out, err := bufReader.ReadString('\n') 232 if err != nil { 233 c.Fatal(err) 234 } 235 // it should print a warning to indicate the detach key flag is invalid 236 errStr := "Invalid detach keys (ctrl-A,a) provided" 237 assert.Equal(c, strings.TrimSpace(out), errStr) 238 } 239 240 // TestRunAttachDetachFromConfig checks attaching and detaching with the escape sequence specified via config file. 241 func (s *DockerCLIRunSuite) TestRunAttachDetachFromConfig(c *testing.T) { 242 keyCtrlA := []byte{1} 243 keyA := []byte{97} 244 245 // Setup config 246 tmpDir, err := os.MkdirTemp("", "fake-home") 247 assert.NilError(c, err) 248 defer os.RemoveAll(tmpDir) 249 250 dotDocker := filepath.Join(tmpDir, ".docker") 251 os.Mkdir(dotDocker, 0o600) 252 tmpCfg := filepath.Join(dotDocker, "config.json") 253 254 if runtime.GOOS == "windows" { 255 c.Setenv("USERPROFILE", tmpDir) 256 } else { 257 c.Setenv("HOME", tmpDir) 258 } 259 260 data := `{ 261 "detachKeys": "ctrl-a,a" 262 }` 263 264 err = os.WriteFile(tmpCfg, []byte(data), 0o600) 265 assert.NilError(c, err) 266 267 // Then do the work 268 const name = "attach-detach" 269 cli.DockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") 270 271 cmd := exec.Command(dockerBinary, "attach", name) 272 stdout, err := cmd.StdoutPipe() 273 if err != nil { 274 c.Fatal(err) 275 } 276 cpty, tty, err := pty.Open() 277 if err != nil { 278 c.Fatal(err) 279 } 280 defer cpty.Close() 281 cmd.Stdin = tty 282 if err := cmd.Start(); err != nil { 283 c.Fatal(err) 284 } 285 cli.WaitRun(c, name) 286 287 if _, err := cpty.Write([]byte("hello\n")); err != nil { 288 c.Fatal(err) 289 } 290 291 out, err := bufio.NewReader(stdout).ReadString('\n') 292 if err != nil { 293 c.Fatal(err) 294 } 295 if strings.TrimSpace(out) != "hello" { 296 c.Fatalf("expected 'hello', got %q", out) 297 } 298 299 // escape sequence 300 if _, err := cpty.Write(keyCtrlA); err != nil { 301 c.Fatal(err) 302 } 303 time.Sleep(100 * time.Millisecond) 304 if _, err := cpty.Write(keyA); err != nil { 305 c.Fatal(err) 306 } 307 308 ch := make(chan struct{}, 1) 309 go func() { 310 cmd.Wait() 311 ch <- struct{}{} 312 }() 313 314 select { 315 case <-ch: 316 case <-time.After(10 * time.Second): 317 c.Fatal("timed out waiting for container to exit") 318 } 319 320 running := inspectField(c, name, "State.Running") 321 assert.Equal(c, running, "true", "expected container to still be running") 322 } 323 324 // TestRunAttachDetachKeysOverrideConfig checks attaching and detaching with the detach flags, making sure it overrides config file 325 func (s *DockerCLIRunSuite) TestRunAttachDetachKeysOverrideConfig(c *testing.T) { 326 keyCtrlA := []byte{1} 327 keyA := []byte{97} 328 329 // Setup config 330 tmpDir, err := os.MkdirTemp("", "fake-home") 331 assert.NilError(c, err) 332 defer os.RemoveAll(tmpDir) 333 334 dotDocker := filepath.Join(tmpDir, ".docker") 335 os.Mkdir(dotDocker, 0o600) 336 tmpCfg := filepath.Join(dotDocker, "config.json") 337 338 if runtime.GOOS == "windows" { 339 c.Setenv("USERPROFILE", tmpDir) 340 } else { 341 c.Setenv("HOME", tmpDir) 342 } 343 344 data := `{ 345 "detachKeys": "ctrl-e,e" 346 }` 347 348 err = os.WriteFile(tmpCfg, []byte(data), 0o600) 349 assert.NilError(c, err) 350 351 // Then do the work 352 const name = "attach-detach" 353 cli.DockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") 354 355 cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-a,a", name) 356 stdout, err := cmd.StdoutPipe() 357 if err != nil { 358 c.Fatal(err) 359 } 360 cpty, tty, err := pty.Open() 361 if err != nil { 362 c.Fatal(err) 363 } 364 defer cpty.Close() 365 cmd.Stdin = tty 366 if err := cmd.Start(); err != nil { 367 c.Fatal(err) 368 } 369 cli.WaitRun(c, name) 370 371 if _, err := cpty.Write([]byte("hello\n")); err != nil { 372 c.Fatal(err) 373 } 374 375 out, err := bufio.NewReader(stdout).ReadString('\n') 376 if err != nil { 377 c.Fatal(err) 378 } 379 if strings.TrimSpace(out) != "hello" { 380 c.Fatalf("expected 'hello', got %q", out) 381 } 382 383 // escape sequence 384 if _, err := cpty.Write(keyCtrlA); err != nil { 385 c.Fatal(err) 386 } 387 time.Sleep(100 * time.Millisecond) 388 if _, err := cpty.Write(keyA); err != nil { 389 c.Fatal(err) 390 } 391 392 ch := make(chan struct{}, 1) 393 go func() { 394 cmd.Wait() 395 ch <- struct{}{} 396 }() 397 398 select { 399 case <-ch: 400 case <-time.After(10 * time.Second): 401 c.Fatal("timed out waiting for container to exit") 402 } 403 404 running := inspectField(c, name, "State.Running") 405 assert.Equal(c, running, "true", "expected container to still be running") 406 } 407 408 func (s *DockerCLIRunSuite) TestRunAttachInvalidDetachKeySequencePreserved(c *testing.T) { 409 const name = "attach-detach" 410 keyA := []byte{97} 411 keyB := []byte{98} 412 413 cli.DockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat") 414 415 cmd := exec.Command(dockerBinary, "attach", "--detach-keys=a,b,c", name) 416 stdout, err := cmd.StdoutPipe() 417 if err != nil { 418 c.Fatal(err) 419 } 420 cpty, tty, err := pty.Open() 421 if err != nil { 422 c.Fatal(err) 423 } 424 defer cpty.Close() 425 cmd.Stdin = tty 426 if err := cmd.Start(); err != nil { 427 c.Fatal(err) 428 } 429 go cmd.Wait() 430 cli.WaitRun(c, name) 431 432 // Invalid escape sequence aba, should print aba in output 433 if _, err := cpty.Write(keyA); err != nil { 434 c.Fatal(err) 435 } 436 time.Sleep(100 * time.Millisecond) 437 if _, err := cpty.Write(keyB); err != nil { 438 c.Fatal(err) 439 } 440 time.Sleep(100 * time.Millisecond) 441 if _, err := cpty.Write(keyA); err != nil { 442 c.Fatal(err) 443 } 444 time.Sleep(100 * time.Millisecond) 445 if _, err := cpty.Write([]byte("\n")); err != nil { 446 c.Fatal(err) 447 } 448 449 out, err := bufio.NewReader(stdout).ReadString('\n') 450 if err != nil { 451 c.Fatal(err) 452 } 453 if strings.TrimSpace(out) != "aba" { 454 c.Fatalf("expected 'aba', got %q", out) 455 } 456 } 457 458 // "test" should be printed 459 func (s *DockerCLIRunSuite) TestRunWithCPUQuota(c *testing.T) { 460 testRequires(c, cpuCfsQuota) 461 462 const file = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us" 463 out := cli.DockerCmd(c, "run", "--cpu-quota", "8000", "--name", "test", "busybox", "cat", file).Combined() 464 assert.Equal(c, strings.TrimSpace(out), "8000") 465 466 out = inspectField(c, "test", "HostConfig.CpuQuota") 467 assert.Equal(c, out, "8000", "setting the CPU CFS quota failed") 468 } 469 470 func (s *DockerCLIRunSuite) TestRunWithCpuPeriod(c *testing.T) { 471 testRequires(c, cpuCfsPeriod) 472 473 const file = "/sys/fs/cgroup/cpu/cpu.cfs_period_us" 474 out := cli.DockerCmd(c, "run", "--cpu-period", "50000", "--name", "test", "busybox", "cat", file).Combined() 475 assert.Equal(c, strings.TrimSpace(out), "50000") 476 477 out = cli.DockerCmd(c, "run", "--cpu-period", "0", "busybox", "cat", file).Combined() 478 assert.Equal(c, strings.TrimSpace(out), "100000") 479 480 out = inspectField(c, "test", "HostConfig.CpuPeriod") 481 assert.Equal(c, out, "50000", "setting the CPU CFS period failed") 482 } 483 484 func (s *DockerCLIRunSuite) TestRunWithInvalidCpuPeriod(c *testing.T) { 485 testRequires(c, cpuCfsPeriod) 486 out, _, err := dockerCmdWithError("run", "--cpu-period", "900", "busybox", "true") 487 assert.ErrorContains(c, err, "") 488 expected := "CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)" 489 assert.Assert(c, strings.Contains(out, expected)) 490 491 out, _, err = dockerCmdWithError("run", "--cpu-period", "2000000", "busybox", "true") 492 assert.ErrorContains(c, err, "") 493 assert.Assert(c, strings.Contains(out, expected)) 494 495 out, _, err = dockerCmdWithError("run", "--cpu-period", "-3", "busybox", "true") 496 assert.ErrorContains(c, err, "") 497 assert.Assert(c, strings.Contains(out, expected)) 498 } 499 500 func (s *DockerCLIRunSuite) TestRunWithCPUShares(c *testing.T) { 501 testRequires(c, cpuShare) 502 503 const file = "/sys/fs/cgroup/cpu/cpu.shares" 504 out := cli.DockerCmd(c, "run", "--cpu-shares", "1000", "--name", "test", "busybox", "cat", file).Combined() 505 assert.Equal(c, strings.TrimSpace(out), "1000") 506 507 out = inspectField(c, "test", "HostConfig.CPUShares") 508 assert.Equal(c, out, "1000") 509 } 510 511 // "test" should be printed 512 func (s *DockerCLIRunSuite) TestRunEchoStdoutWithCPUSharesAndMemoryLimit(c *testing.T) { 513 testRequires(c, cpuShare) 514 testRequires(c, memoryLimitSupport) 515 cli.DockerCmd(c, "run", "--cpu-shares", "1000", "-m", "32m", "busybox", "echo", "test").Assert(c, icmd.Expected{ 516 Out: "test\n", 517 }) 518 } 519 520 func (s *DockerCLIRunSuite) TestRunWithCpusetCpus(c *testing.T) { 521 testRequires(c, cgroupCpuset) 522 523 const file = "/sys/fs/cgroup/cpuset/cpuset.cpus" 524 out := cli.DockerCmd(c, "run", "--cpuset-cpus", "0", "--name", "test", "busybox", "cat", file).Combined() 525 assert.Equal(c, strings.TrimSpace(out), "0") 526 527 out = inspectField(c, "test", "HostConfig.CpusetCpus") 528 assert.Equal(c, out, "0") 529 } 530 531 func (s *DockerCLIRunSuite) TestRunWithCpusetMems(c *testing.T) { 532 testRequires(c, cgroupCpuset) 533 534 const file = "/sys/fs/cgroup/cpuset/cpuset.mems" 535 out := cli.DockerCmd(c, "run", "--cpuset-mems", "0", "--name", "test", "busybox", "cat", file).Combined() 536 assert.Equal(c, strings.TrimSpace(out), "0") 537 538 out = inspectField(c, "test", "HostConfig.CpusetMems") 539 assert.Equal(c, out, "0") 540 } 541 542 func (s *DockerCLIRunSuite) TestRunWithBlkioWeight(c *testing.T) { 543 testRequires(c, blkioWeight) 544 545 const file = "/sys/fs/cgroup/blkio/blkio.weight" 546 out := cli.DockerCmd(c, "run", "--blkio-weight", "300", "--name", "test", "busybox", "cat", file).Combined() 547 assert.Equal(c, strings.TrimSpace(out), "300") 548 549 out = inspectField(c, "test", "HostConfig.BlkioWeight") 550 assert.Equal(c, out, "300") 551 } 552 553 func (s *DockerCLIRunSuite) TestRunWithInvalidBlkioWeight(c *testing.T) { 554 testRequires(c, blkioWeight) 555 out, _, err := dockerCmdWithError("run", "--blkio-weight", "5", "busybox", "true") 556 assert.ErrorContains(c, err, "", out) 557 expected := "Range of blkio weight is from 10 to 1000" 558 assert.Assert(c, strings.Contains(out, expected)) 559 } 560 561 func (s *DockerCLIRunSuite) TestRunWithInvalidPathforBlkioWeightDevice(c *testing.T) { 562 testRequires(c, blkioWeight) 563 out, _, err := dockerCmdWithError("run", "--blkio-weight-device", "/dev/sdX:100", "busybox", "true") 564 assert.ErrorContains(c, err, "", out) 565 } 566 567 func (s *DockerCLIRunSuite) TestRunWithInvalidPathforBlkioDeviceReadBps(c *testing.T) { 568 testRequires(c, blkioWeight) 569 out, _, err := dockerCmdWithError("run", "--device-read-bps", "/dev/sdX:500", "busybox", "true") 570 assert.ErrorContains(c, err, "", out) 571 } 572 573 func (s *DockerCLIRunSuite) TestRunWithInvalidPathforBlkioDeviceWriteBps(c *testing.T) { 574 testRequires(c, blkioWeight) 575 out, _, err := dockerCmdWithError("run", "--device-write-bps", "/dev/sdX:500", "busybox", "true") 576 assert.ErrorContains(c, err, "", out) 577 } 578 579 func (s *DockerCLIRunSuite) TestRunWithInvalidPathforBlkioDeviceReadIOps(c *testing.T) { 580 testRequires(c, blkioWeight) 581 out, _, err := dockerCmdWithError("run", "--device-read-iops", "/dev/sdX:500", "busybox", "true") 582 assert.ErrorContains(c, err, "", out) 583 } 584 585 func (s *DockerCLIRunSuite) TestRunWithInvalidPathforBlkioDeviceWriteIOps(c *testing.T) { 586 testRequires(c, blkioWeight) 587 out, _, err := dockerCmdWithError("run", "--device-write-iops", "/dev/sdX:500", "busybox", "true") 588 assert.ErrorContains(c, err, "", out) 589 } 590 591 func (s *DockerCLIRunSuite) TestRunOOMExitCode(c *testing.T) { 592 testRequires(c, memoryLimitSupport, swapMemorySupport, NotPpc64le) 593 errChan := make(chan error, 1) 594 go func() { 595 defer close(errChan) 596 // memory limit lower than 8MB will raise an error of "device or resource busy" from docker-runc. 597 out, exitCode, _ := dockerCmdWithError("run", "-m", "8MB", "busybox", "sh", "-c", "x=a; while true; do x=$x$x$x$x; done") 598 if expected := 137; exitCode != expected { 599 errChan <- fmt.Errorf("wrong exit code for OOM container: expected %d, got %d (output: %q)", expected, exitCode, out) 600 } 601 }() 602 603 select { 604 case err := <-errChan: 605 assert.NilError(c, err) 606 case <-time.After(600 * time.Second): 607 c.Fatal("Timeout waiting for container to die on OOM") 608 } 609 } 610 611 func (s *DockerCLIRunSuite) TestRunWithMemoryLimit(c *testing.T) { 612 testRequires(c, memoryLimitSupport) 613 614 const file = "/sys/fs/cgroup/memory/memory.limit_in_bytes" 615 cli.DockerCmd(c, "run", "-m", "32M", "--name", "test", "busybox", "cat", file).Assert(c, icmd.Expected{ 616 Out: "33554432", 617 }) 618 cli.InspectCmd(c, "test", cli.Format(".HostConfig.Memory")).Assert(c, icmd.Expected{ 619 Out: "33554432", 620 }) 621 } 622 623 // TestRunWithoutMemoryswapLimit sets memory limit and disables swap 624 // memory limit, this means the processes in the container can use 625 // 16M memory and as much swap memory as they need (if the host 626 // supports swap memory). 627 func (s *DockerCLIRunSuite) TestRunWithoutMemoryswapLimit(c *testing.T) { 628 testRequires(c, DaemonIsLinux) 629 testRequires(c, memoryLimitSupport) 630 testRequires(c, swapMemorySupport) 631 cli.DockerCmd(c, "run", "-m", "32m", "--memory-swap", "-1", "busybox", "true") 632 } 633 634 func (s *DockerCLIRunSuite) TestRunWithSwappiness(c *testing.T) { 635 testRequires(c, memorySwappinessSupport) 636 const file = "/sys/fs/cgroup/memory/memory.swappiness" 637 out := cli.DockerCmd(c, "run", "--memory-swappiness", "0", "--name", "test", "busybox", "cat", file).Combined() 638 assert.Equal(c, strings.TrimSpace(out), "0") 639 640 out = inspectField(c, "test", "HostConfig.MemorySwappiness") 641 assert.Equal(c, out, "0") 642 } 643 644 func (s *DockerCLIRunSuite) TestRunWithSwappinessInvalid(c *testing.T) { 645 testRequires(c, memorySwappinessSupport) 646 out, _, err := dockerCmdWithError("run", "--memory-swappiness", "101", "busybox", "true") 647 assert.ErrorContains(c, err, "") 648 expected := "Valid memory swappiness range is 0-100" 649 assert.Assert(c, strings.Contains(out, expected), "Expected output to contain %q, not %q", out, expected) 650 out, _, err = dockerCmdWithError("run", "--memory-swappiness", "-10", "busybox", "true") 651 assert.ErrorContains(c, err, "") 652 assert.Assert(c, strings.Contains(out, expected), "Expected output to contain %q, not %q", out, expected) 653 } 654 655 func (s *DockerCLIRunSuite) TestRunWithMemoryReservation(c *testing.T) { 656 testRequires(c, testEnv.IsLocalDaemon, memoryReservationSupport) 657 658 const file = "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes" 659 out := cli.DockerCmd(c, "run", "--memory-reservation", "200M", "--name", "test", "busybox", "cat", file).Combined() 660 assert.Equal(c, strings.TrimSpace(out), "209715200") 661 662 out = inspectField(c, "test", "HostConfig.MemoryReservation") 663 assert.Equal(c, out, "209715200") 664 } 665 666 func (s *DockerCLIRunSuite) TestRunWithMemoryReservationInvalid(c *testing.T) { 667 testRequires(c, memoryLimitSupport) 668 testRequires(c, testEnv.IsLocalDaemon, memoryReservationSupport) 669 out, _, err := dockerCmdWithError("run", "-m", "500M", "--memory-reservation", "800M", "busybox", "true") 670 assert.ErrorContains(c, err, "") 671 expected := "Minimum memory limit can not be less than memory reservation limit" 672 assert.Assert(c, strings.Contains(strings.TrimSpace(out), expected), "run container should fail with invalid memory reservation") 673 out, _, err = dockerCmdWithError("run", "--memory-reservation", "1k", "busybox", "true") 674 assert.ErrorContains(c, err, "") 675 expected = "Minimum memory reservation allowed is 6MB" 676 assert.Assert(c, strings.Contains(strings.TrimSpace(out), expected), "run container should fail with invalid memory reservation") 677 } 678 679 func (s *DockerCLIRunSuite) TestStopContainerSignal(c *testing.T) { 680 containerID := cli.DockerCmd(c, "run", "--stop-signal", "SIGUSR1", "-d", "busybox", "/bin/sh", "-c", `trap 'echo "exit trapped"; exit 0' USR1; while true; do sleep 1; done`).Stdout() 681 containerID = strings.TrimSpace(containerID) 682 cli.WaitRun(c, containerID) 683 684 cli.DockerCmd(c, "stop", containerID) 685 out := cli.DockerCmd(c, "logs", containerID).Combined() 686 687 assert.Assert(c, strings.Contains(out, "exit trapped"), "Expected `exit trapped` in the log") 688 } 689 690 func (s *DockerCLIRunSuite) TestRunSwapLessThanMemoryLimit(c *testing.T) { 691 testRequires(c, memoryLimitSupport) 692 testRequires(c, swapMemorySupport) 693 out, _, err := dockerCmdWithError("run", "-m", "16m", "--memory-swap", "15m", "busybox", "echo", "test") 694 expected := "Minimum memoryswap limit should be larger than memory limit" 695 assert.ErrorContains(c, err, "") 696 697 assert.Assert(c, strings.Contains(out, expected)) 698 } 699 700 func (s *DockerCLIRunSuite) TestRunInvalidCpusetCpusFlagValue(c *testing.T) { 701 testRequires(c, cgroupCpuset, testEnv.IsLocalDaemon) 702 703 sysInfo := sysinfo.New() 704 cpus, err := parsers.ParseUintList(sysInfo.Cpus) 705 assert.NilError(c, err) 706 var invalid int 707 for i := 0; i <= len(cpus)+1; i++ { 708 if !cpus[i] { 709 invalid = i 710 break 711 } 712 } 713 out, _, err := dockerCmdWithError("run", "--cpuset-cpus", strconv.Itoa(invalid), "busybox", "true") 714 assert.ErrorContains(c, err, "") 715 expected := fmt.Sprintf("Error response from daemon: Requested CPUs are not available - requested %s, available: %s", strconv.Itoa(invalid), sysInfo.Cpus) 716 assert.Assert(c, strings.Contains(out, expected)) 717 } 718 719 func (s *DockerCLIRunSuite) TestRunInvalidCpusetMemsFlagValue(c *testing.T) { 720 testRequires(c, cgroupCpuset) 721 722 sysInfo := sysinfo.New() 723 mems, err := parsers.ParseUintList(sysInfo.Mems) 724 assert.NilError(c, err) 725 var invalid int 726 for i := 0; i <= len(mems)+1; i++ { 727 if !mems[i] { 728 invalid = i 729 break 730 } 731 } 732 out, _, err := dockerCmdWithError("run", "--cpuset-mems", strconv.Itoa(invalid), "busybox", "true") 733 assert.ErrorContains(c, err, "") 734 expected := fmt.Sprintf("Error response from daemon: Requested memory nodes are not available - requested %s, available: %s", strconv.Itoa(invalid), sysInfo.Mems) 735 assert.Assert(c, strings.Contains(out, expected)) 736 } 737 738 func (s *DockerCLIRunSuite) TestRunInvalidCPUShares(c *testing.T) { 739 testRequires(c, cpuShare, DaemonIsLinux) 740 out, _, err := dockerCmdWithError("run", "--cpu-shares", "1", "busybox", "echo", "test") 741 assert.ErrorContains(c, err, "", out) 742 expected := "minimum allowed cpu-shares is 2" 743 assert.Assert(c, strings.Contains(out, expected)) 744 745 out, _, err = dockerCmdWithError("run", "--cpu-shares", "-1", "busybox", "echo", "test") 746 assert.ErrorContains(c, err, "", out) 747 expected = "shares: invalid argument" 748 assert.Assert(c, strings.Contains(out, expected)) 749 750 out, _, err = dockerCmdWithError("run", "--cpu-shares", "99999999", "busybox", "echo", "test") 751 assert.ErrorContains(c, err, "", out) 752 expected = "maximum allowed cpu-shares is" 753 assert.Assert(c, strings.Contains(out, expected)) 754 } 755 756 func (s *DockerCLIRunSuite) TestRunWithDefaultShmSize(c *testing.T) { 757 testRequires(c, DaemonIsLinux) 758 759 const name = "shm-default" 760 out := cli.DockerCmd(c, "run", "--name", name, "busybox", "mount").Combined() 761 shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) 762 if !shmRegex.MatchString(out) { 763 c.Fatalf("Expected shm of 64MB in mount command, got %v", out) 764 } 765 shmSize := inspectField(c, name, "HostConfig.ShmSize") 766 assert.Equal(c, shmSize, "67108864") 767 } 768 769 func (s *DockerCLIRunSuite) TestRunWithShmSize(c *testing.T) { 770 testRequires(c, DaemonIsLinux) 771 772 const name = "shm" 773 out := cli.DockerCmd(c, "run", "--name", name, "--shm-size=1G", "busybox", "mount").Combined() 774 shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`) 775 if !shmRegex.MatchString(out) { 776 c.Fatalf("Expected shm of 1GB in mount command, got %v", out) 777 } 778 shmSize := inspectField(c, name, "HostConfig.ShmSize") 779 assert.Equal(c, shmSize, "1073741824") 780 } 781 782 func (s *DockerCLIRunSuite) TestRunTmpfsMountsEnsureOrdered(c *testing.T) { 783 tmpFile, err := os.CreateTemp("", "test") 784 assert.NilError(c, err) 785 defer tmpFile.Close() 786 out := cli.DockerCmd(c, "run", "--tmpfs", "/run", "-v", tmpFile.Name()+":/run/test", "busybox", "ls", "/run").Combined() 787 assert.Assert(c, strings.Contains(out, "test")) 788 } 789 790 func (s *DockerCLIRunSuite) TestRunTmpfsMounts(c *testing.T) { 791 // TODO Windows (Post TP5): This test cannot run on a Windows daemon as 792 // Windows does not support tmpfs mounts. 793 testRequires(c, DaemonIsLinux) 794 if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "busybox", "touch", "/run/somefile"); err != nil { 795 c.Fatalf("/run directory not mounted on tmpfs %q %s", err, out) 796 } 797 if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run:noexec", "busybox", "touch", "/run/somefile"); err != nil { 798 c.Fatalf("/run directory not mounted on tmpfs %q %s", err, out) 799 } 800 if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run:noexec,nosuid,rw,size=5k,mode=700", "busybox", "touch", "/run/somefile"); err != nil { 801 c.Fatalf("/run failed to mount on tmpfs with valid options %q %s", err, out) 802 } 803 if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run:foobar", "busybox", "touch", "/run/somefile"); err == nil { 804 c.Fatalf("/run mounted on tmpfs when it should have vailed within invalid mount option") 805 } 806 if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "-v", "/run:/run", "busybox", "touch", "/run/somefile"); err == nil { 807 c.Fatalf("Should have generated an error saying Duplicate mount points") 808 } 809 } 810 811 func (s *DockerCLIRunSuite) TestRunTmpfsMountsOverrideImageVolumes(c *testing.T) { 812 const name = "img-with-volumes" 813 buildImageSuccessfully(c, name, build.WithDockerfile(` 814 FROM busybox 815 VOLUME /run 816 RUN touch /run/stuff 817 `)) 818 out := cli.DockerCmd(c, "run", "--tmpfs", "/run", name, "ls", "/run").Combined() 819 assert.Assert(c, !strings.Contains(out, "stuff")) 820 } 821 822 // Test case for #22420 823 func (s *DockerCLIRunSuite) TestRunTmpfsMountsWithOptions(c *testing.T) { 824 testRequires(c, DaemonIsLinux) 825 826 expectedOptions := []string{"rw", "nosuid", "nodev", "noexec", "relatime"} 827 out := cli.DockerCmd(c, "run", "--tmpfs", "/tmp", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'").Combined() 828 for _, option := range expectedOptions { 829 assert.Assert(c, strings.Contains(out, option)) 830 } 831 assert.Assert(c, !strings.Contains(out, "size=")) 832 expectedOptions = []string{"rw", "nosuid", "nodev", "noexec", "relatime"} 833 out = cli.DockerCmd(c, "run", "--tmpfs", "/tmp:rw", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'").Combined() 834 for _, option := range expectedOptions { 835 assert.Assert(c, strings.Contains(out, option)) 836 } 837 assert.Assert(c, !strings.Contains(out, "size=")) 838 expectedOptions = []string{"rw", "nosuid", "nodev", "relatime", "size=8192k"} 839 out = cli.DockerCmd(c, "run", "--tmpfs", "/tmp:rw,exec,size=8192k", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'").Combined() 840 for _, option := range expectedOptions { 841 assert.Assert(c, strings.Contains(out, option)) 842 } 843 844 expectedOptions = []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k"} 845 out = cli.DockerCmd(c, "run", "--tmpfs", "/tmp:rw,size=8192k,exec,size=4096k,noexec", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'").Combined() 846 for _, option := range expectedOptions { 847 assert.Assert(c, strings.Contains(out, option)) 848 } 849 850 // We use debian:bookworm-slim as there is no findmnt in busybox. Also the output will be in the format of 851 // TARGET PROPAGATION 852 // /tmp shared 853 // so we only capture `shared` here. 854 expectedOptions = []string{"shared"} 855 out = cli.DockerCmd(c, "run", "--tmpfs", "/tmp:shared", "debian:bookworm-slim", "findmnt", "-o", "TARGET,PROPAGATION", "/tmp").Combined() 856 for _, option := range expectedOptions { 857 assert.Assert(c, strings.Contains(out, option)) 858 } 859 } 860 861 func (s *DockerCLIRunSuite) TestRunSysctls(c *testing.T) { 862 testRequires(c, DaemonIsLinux) 863 var err error 864 865 out := cli.DockerCmd(c, "run", "--sysctl", "net.ipv4.ip_forward=1", "--name", "test", "busybox", "cat", "/proc/sys/net/ipv4/ip_forward").Combined() 866 assert.Equal(c, strings.TrimSpace(out), "1") 867 868 out = inspectFieldJSON(c, "test", "HostConfig.Sysctls") 869 870 sysctls := make(map[string]string) 871 err = json.Unmarshal([]byte(out), &sysctls) 872 assert.NilError(c, err) 873 assert.Equal(c, sysctls["net.ipv4.ip_forward"], "1") 874 875 out = cli.DockerCmd(c, "run", "--sysctl", "net.ipv4.ip_forward=0", "--name", "test1", "busybox", "cat", "/proc/sys/net/ipv4/ip_forward").Combined() 876 assert.Equal(c, strings.TrimSpace(out), "0") 877 878 out = inspectFieldJSON(c, "test1", "HostConfig.Sysctls") 879 880 err = json.Unmarshal([]byte(out), &sysctls) 881 assert.NilError(c, err) 882 assert.Equal(c, sysctls["net.ipv4.ip_forward"], "0") 883 884 icmd.RunCommand(dockerBinary, "run", "--sysctl", "kernel.foobar=1", "--name", "test2", 885 "busybox", "cat", "/proc/sys/kernel/foobar").Assert(c, icmd.Expected{ 886 ExitCode: 125, 887 Err: "invalid argument", 888 }) 889 } 890 891 // TestRunSeccompProfileDenyUnshare checks that 'docker run --security-opt seccomp=/tmp/profile.json debian:bookworm-slim unshare' exits with operation not permitted. 892 func (s *DockerCLIRunSuite) TestRunSeccompProfileDenyUnshare(c *testing.T) { 893 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, Apparmor) 894 const jsonData = `{ 895 "defaultAction": "SCMP_ACT_ALLOW", 896 "syscalls": [ 897 { 898 "name": "unshare", 899 "action": "SCMP_ACT_ERRNO" 900 } 901 ] 902 }` 903 tmpFile, err := os.CreateTemp("", "profile.json") 904 if err != nil { 905 c.Fatal(err) 906 } 907 defer tmpFile.Close() 908 909 if _, err := tmpFile.Write([]byte(jsonData)); err != nil { 910 c.Fatal(err) 911 } 912 icmd.RunCommand(dockerBinary, "run", "--security-opt", "apparmor=unconfined", 913 "--security-opt", "seccomp="+tmpFile.Name(), 914 "debian:bookworm-slim", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc").Assert(c, icmd.Expected{ 915 ExitCode: 1, 916 Err: "Operation not permitted", 917 }) 918 } 919 920 // TestRunSeccompProfileDenyChmod checks that 'docker run --security-opt seccomp=/tmp/profile.json busybox chmod 400 /etc/hostname' exits with operation not permitted. 921 func (s *DockerCLIRunSuite) TestRunSeccompProfileDenyChmod(c *testing.T) { 922 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled) 923 const jsonData = `{ 924 "defaultAction": "SCMP_ACT_ALLOW", 925 "syscalls": [ 926 { 927 "name": "chmod", 928 "action": "SCMP_ACT_ERRNO" 929 }, 930 { 931 "name":"fchmod", 932 "action": "SCMP_ACT_ERRNO" 933 }, 934 { 935 "name": "fchmodat", 936 "action":"SCMP_ACT_ERRNO" 937 } 938 ] 939 }` 940 tmpFile, err := os.CreateTemp("", "profile.json") 941 assert.NilError(c, err) 942 defer tmpFile.Close() 943 944 if _, err := tmpFile.Write([]byte(jsonData)); err != nil { 945 c.Fatal(err) 946 } 947 icmd.RunCommand(dockerBinary, "run", "--security-opt", "seccomp="+tmpFile.Name(), 948 "busybox", "chmod", "400", "/etc/hostname").Assert(c, icmd.Expected{ 949 ExitCode: 1, 950 Err: "Operation not permitted", 951 }) 952 } 953 954 // TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:bookworm-slim unshare --map-root-user --user sh -c whoami' with a specific profile to 955 // deny unshare of a userns exits with operation not permitted. 956 func (s *DockerCLIRunSuite) TestRunSeccompProfileDenyUnshareUserns(c *testing.T) { 957 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, Apparmor) 958 // from sched.h 959 jsonData := fmt.Sprintf(`{ 960 "defaultAction": "SCMP_ACT_ALLOW", 961 "syscalls": [ 962 { 963 "name": "unshare", 964 "action": "SCMP_ACT_ERRNO", 965 "args": [ 966 { 967 "index": 0, 968 "value": %d, 969 "op": "SCMP_CMP_EQ" 970 } 971 ] 972 } 973 ] 974 }`, uint64(0x10000000)) 975 tmpFile, err := os.CreateTemp("", "profile.json") 976 if err != nil { 977 c.Fatal(err) 978 } 979 defer tmpFile.Close() 980 981 if _, err := tmpFile.Write([]byte(jsonData)); err != nil { 982 c.Fatal(err) 983 } 984 icmd.RunCommand(dockerBinary, "run", 985 "--security-opt", "apparmor=unconfined", "--security-opt", "seccomp="+tmpFile.Name(), 986 "debian:bookworm-slim", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami").Assert(c, icmd.Expected{ 987 ExitCode: 1, 988 Err: "Operation not permitted", 989 }) 990 } 991 992 // TestRunSeccompProfileDenyCloneUserns checks that 'docker run syscall-test' 993 // with a the default seccomp profile exits with operation not permitted. 994 func (s *DockerCLIRunSuite) TestRunSeccompProfileDenyCloneUserns(c *testing.T) { 995 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled) 996 ensureSyscallTest(testutil.GetContext(c), c) 997 998 icmd.RunCommand(dockerBinary, "run", "syscall-test", "userns-test", "id").Assert(c, icmd.Expected{ 999 ExitCode: 1, 1000 Err: "clone failed: Operation not permitted", 1001 }) 1002 } 1003 1004 // TestRunSeccompUnconfinedCloneUserns checks that 1005 // 'docker run --security-opt seccomp=unconfined syscall-test' allows creating a userns. 1006 func (s *DockerCLIRunSuite) TestRunSeccompUnconfinedCloneUserns(c *testing.T) { 1007 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, UserNamespaceInKernel, NotUserNamespace, unprivilegedUsernsClone) 1008 ensureSyscallTest(testutil.GetContext(c), c) 1009 1010 // make sure running w privileged is ok 1011 icmd.RunCommand(dockerBinary, "run", "--security-opt", "seccomp=unconfined", 1012 "syscall-test", "userns-test", "id").Assert(c, icmd.Expected{ 1013 Out: "nobody", 1014 }) 1015 } 1016 1017 // TestRunSeccompAllowPrivCloneUserns checks that 'docker run --privileged syscall-test' 1018 // allows creating a userns. 1019 func (s *DockerCLIRunSuite) TestRunSeccompAllowPrivCloneUserns(c *testing.T) { 1020 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, UserNamespaceInKernel, NotUserNamespace) 1021 ensureSyscallTest(testutil.GetContext(c), c) 1022 1023 // make sure running w privileged is ok 1024 icmd.RunCommand(dockerBinary, "run", "--privileged", "syscall-test", "userns-test", "id").Assert(c, icmd.Expected{ 1025 Out: "nobody", 1026 }) 1027 } 1028 1029 // TestRunSeccompProfileAllow32Bit checks that 32 bit code can run on x86_64 1030 // with the default seccomp profile. 1031 func (s *DockerCLIRunSuite) TestRunSeccompProfileAllow32Bit(c *testing.T) { 1032 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, IsAmd64) 1033 ensureSyscallTest(testutil.GetContext(c), c) 1034 1035 icmd.RunCommand(dockerBinary, "run", "syscall-test", "exit32-test").Assert(c, icmd.Success) 1036 } 1037 1038 // TestRunSeccompAllowSetrlimit checks that 'docker run debian:bookworm-slim ulimit -v 1048510' succeeds. 1039 func (s *DockerCLIRunSuite) TestRunSeccompAllowSetrlimit(c *testing.T) { 1040 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled) 1041 1042 // ulimit uses setrlimit, so we want to make sure we don't break it 1043 icmd.RunCommand(dockerBinary, "run", "debian:bookworm-slim", "bash", "-c", "ulimit -v 1048510").Assert(c, icmd.Success) 1044 } 1045 1046 func (s *DockerCLIRunSuite) TestRunSeccompDefaultProfileAcct(c *testing.T) { 1047 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotUserNamespace) 1048 ensureSyscallTest(testutil.GetContext(c), c) 1049 1050 out, _, err := dockerCmdWithError("run", "syscall-test", "acct-test") 1051 if err == nil || !strings.Contains(out, "Operation not permitted") { 1052 c.Fatalf("test 0: expected Operation not permitted, got: %s", out) 1053 } 1054 1055 out, _, err = dockerCmdWithError("run", "--cap-add", "sys_admin", "syscall-test", "acct-test") 1056 if err == nil || !strings.Contains(out, "Operation not permitted") { 1057 c.Fatalf("test 1: expected Operation not permitted, got: %s", out) 1058 } 1059 1060 out, _, err = dockerCmdWithError("run", "--cap-add", "sys_pacct", "syscall-test", "acct-test") 1061 if err == nil || !strings.Contains(out, "No such file or directory") { 1062 c.Fatalf("test 2: expected No such file or directory, got: %s", out) 1063 } 1064 1065 out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "acct-test") 1066 if err == nil || !strings.Contains(out, "No such file or directory") { 1067 c.Fatalf("test 3: expected No such file or directory, got: %s", out) 1068 } 1069 1070 out, _, err = dockerCmdWithError("run", "--cap-drop", "ALL", "--cap-add", "sys_pacct", "syscall-test", "acct-test") 1071 if err == nil || !strings.Contains(out, "No such file or directory") { 1072 c.Fatalf("test 4: expected No such file or directory, got: %s", out) 1073 } 1074 } 1075 1076 func (s *DockerCLIRunSuite) TestRunSeccompDefaultProfileNS(c *testing.T) { 1077 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotUserNamespace) 1078 ensureSyscallTest(testutil.GetContext(c), c) 1079 1080 out, _, err := dockerCmdWithError("run", "syscall-test", "ns-test", "echo", "hello0") 1081 if err == nil || !strings.Contains(out, "Operation not permitted") { 1082 c.Fatalf("test 0: expected Operation not permitted, got: %s", out) 1083 } 1084 1085 out, _, err = dockerCmdWithError("run", "--cap-add", "sys_admin", "syscall-test", "ns-test", "echo", "hello1") 1086 if err != nil || !strings.Contains(out, "hello1") { 1087 c.Fatalf("test 1: expected hello1, got: %s, %v", out, err) 1088 } 1089 1090 out, _, err = dockerCmdWithError("run", "--cap-drop", "all", "--cap-add", "sys_admin", "syscall-test", "ns-test", "echo", "hello2") 1091 if err != nil || !strings.Contains(out, "hello2") { 1092 c.Fatalf("test 2: expected hello2, got: %s, %v", out, err) 1093 } 1094 1095 out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "ns-test", "echo", "hello3") 1096 if err != nil || !strings.Contains(out, "hello3") { 1097 c.Fatalf("test 3: expected hello3, got: %s, %v", out, err) 1098 } 1099 1100 out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp=unconfined", "syscall-test", "acct-test") 1101 if err == nil || !strings.Contains(out, "No such file or directory") { 1102 c.Fatalf("test 4: expected No such file or directory, got: %s", out) 1103 } 1104 1105 out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp=unconfined", "syscall-test", "ns-test", "echo", "hello4") 1106 if err != nil || !strings.Contains(out, "hello4") { 1107 c.Fatalf("test 5: expected hello4, got: %s, %v", out, err) 1108 } 1109 } 1110 1111 // TestRunNoNewPrivSetuid checks that --security-opt='no-new-privileges=true' prevents 1112 // effective uid transitions on executing setuid binaries. 1113 func (s *DockerCLIRunSuite) TestRunNoNewPrivSetuid(c *testing.T) { 1114 testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon) 1115 ensureNNPTest(testutil.GetContext(c), c) 1116 1117 // test that running a setuid binary results in no effective uid transition 1118 icmd.RunCommand(dockerBinary, "run", "--security-opt", "no-new-privileges=true", "--user", "1000", 1119 "nnp-test", "/usr/bin/nnp-test").Assert(c, icmd.Expected{ 1120 Out: "EUID=1000", 1121 }) 1122 } 1123 1124 // TestLegacyRunNoNewPrivSetuid checks that --security-opt=no-new-privileges prevents 1125 // effective uid transitions on executing setuid binaries. 1126 func (s *DockerCLIRunSuite) TestLegacyRunNoNewPrivSetuid(c *testing.T) { 1127 testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon) 1128 ensureNNPTest(testutil.GetContext(c), c) 1129 1130 // test that running a setuid binary results in no effective uid transition 1131 icmd.RunCommand(dockerBinary, "run", "--security-opt", "no-new-privileges", "--user", "1000", 1132 "nnp-test", "/usr/bin/nnp-test").Assert(c, icmd.Expected{ 1133 Out: "EUID=1000", 1134 }) 1135 } 1136 1137 func (s *DockerCLIRunSuite) TestUserNoEffectiveCapabilitiesChown(c *testing.T) { 1138 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 1139 ensureSyscallTest(testutil.GetContext(c), c) 1140 1141 // test that a root user has default capability CAP_CHOWN 1142 cli.DockerCmd(c, "run", "busybox", "chown", "100", "/tmp") 1143 // test that non root user does not have default capability CAP_CHOWN 1144 icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "busybox", "chown", "100", "/tmp").Assert(c, icmd.Expected{ 1145 ExitCode: 1, 1146 Err: "Operation not permitted", 1147 }) 1148 // test that root user can drop default capability CAP_CHOWN 1149 icmd.RunCommand(dockerBinary, "run", "--cap-drop", "chown", "busybox", "chown", "100", "/tmp").Assert(c, icmd.Expected{ 1150 ExitCode: 1, 1151 Err: "Operation not permitted", 1152 }) 1153 } 1154 1155 func (s *DockerCLIRunSuite) TestUserNoEffectiveCapabilitiesDacOverride(c *testing.T) { 1156 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 1157 ensureSyscallTest(testutil.GetContext(c), c) 1158 1159 // test that a root user has default capability CAP_DAC_OVERRIDE 1160 cli.DockerCmd(c, "run", "busybox", "sh", "-c", "echo test > /etc/passwd") 1161 // test that non root user does not have default capability CAP_DAC_OVERRIDE 1162 icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "busybox", "sh", "-c", "echo test > /etc/passwd").Assert(c, icmd.Expected{ 1163 ExitCode: 1, 1164 Err: "Permission denied", 1165 }) 1166 } 1167 1168 func (s *DockerCLIRunSuite) TestUserNoEffectiveCapabilitiesFowner(c *testing.T) { 1169 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 1170 ensureSyscallTest(testutil.GetContext(c), c) 1171 1172 // test that a root user has default capability CAP_FOWNER 1173 cli.DockerCmd(c, "run", "busybox", "chmod", "777", "/etc/passwd") 1174 // test that non root user does not have default capability CAP_FOWNER 1175 icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "busybox", "chmod", "777", "/etc/passwd").Assert(c, icmd.Expected{ 1176 ExitCode: 1, 1177 Err: "Operation not permitted", 1178 }) 1179 // TODO test that root user can drop default capability CAP_FOWNER 1180 } 1181 1182 // TODO CAP_KILL 1183 1184 func (s *DockerCLIRunSuite) TestUserNoEffectiveCapabilitiesSetuid(c *testing.T) { 1185 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 1186 ensureSyscallTest(testutil.GetContext(c), c) 1187 1188 // test that a root user has default capability CAP_SETUID 1189 cli.DockerCmd(c, "run", "syscall-test", "setuid-test") 1190 // test that non root user does not have default capability CAP_SETUID 1191 icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "syscall-test", "setuid-test").Assert(c, icmd.Expected{ 1192 ExitCode: 1, 1193 Err: "Operation not permitted", 1194 }) 1195 // test that root user can drop default capability CAP_SETUID 1196 icmd.RunCommand(dockerBinary, "run", "--cap-drop", "setuid", "syscall-test", "setuid-test").Assert(c, icmd.Expected{ 1197 ExitCode: 1, 1198 Err: "Operation not permitted", 1199 }) 1200 } 1201 1202 func (s *DockerCLIRunSuite) TestUserNoEffectiveCapabilitiesSetgid(c *testing.T) { 1203 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 1204 ensureSyscallTest(testutil.GetContext(c), c) 1205 1206 // test that a root user has default capability CAP_SETGID 1207 cli.DockerCmd(c, "run", "syscall-test", "setgid-test") 1208 // test that non root user does not have default capability CAP_SETGID 1209 icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "syscall-test", "setgid-test").Assert(c, icmd.Expected{ 1210 ExitCode: 1, 1211 Err: "Operation not permitted", 1212 }) 1213 // test that root user can drop default capability CAP_SETGID 1214 icmd.RunCommand(dockerBinary, "run", "--cap-drop", "setgid", "syscall-test", "setgid-test").Assert(c, icmd.Expected{ 1215 ExitCode: 1, 1216 Err: "Operation not permitted", 1217 }) 1218 } 1219 1220 // TODO CAP_SETPCAP 1221 1222 // sysctlExists checks if a sysctl exists; runc will error if we add any that do not actually 1223 // exist, so do not add the default ones if running on an old kernel. 1224 func sysctlExists(s string) bool { 1225 f := filepath.Join("/proc", "sys", strings.ReplaceAll(s, ".", "/")) 1226 _, err := os.Stat(f) 1227 return err == nil 1228 } 1229 1230 func (s *DockerCLIRunSuite) TestUserNoEffectiveCapabilitiesNetBindService(c *testing.T) { 1231 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 1232 ensureSyscallTest(testutil.GetContext(c), c) 1233 1234 // test that a root user has default capability CAP_NET_BIND_SERVICE 1235 cli.DockerCmd(c, "run", "syscall-test", "socket-test") 1236 // test that non root user does not have default capability CAP_NET_BIND_SERVICE 1237 // as we allow this via sysctl, also tweak the sysctl back to default 1238 args := []string{"run", "--user", "1000:1000"} 1239 if sysctlExists("net.ipv4.ip_unprivileged_port_start") { 1240 args = append(args, "--sysctl", "net.ipv4.ip_unprivileged_port_start=1024") 1241 } 1242 args = append(args, "syscall-test", "socket-test") 1243 icmd.RunCommand(dockerBinary, args...).Assert(c, icmd.Expected{ 1244 ExitCode: 1, 1245 Err: "Permission denied", 1246 }) 1247 // test that root user can drop default capability CAP_NET_BIND_SERVICE 1248 args = []string{"run", "--cap-drop", "net_bind_service"} 1249 if sysctlExists("net.ipv4.ip_unprivileged_port_start") { 1250 args = append(args, "--sysctl", "net.ipv4.ip_unprivileged_port_start=1024") 1251 } 1252 args = append(args, "syscall-test", "socket-test") 1253 icmd.RunCommand(dockerBinary, args...).Assert(c, icmd.Expected{ 1254 ExitCode: 1, 1255 Err: "Permission denied", 1256 }) 1257 } 1258 1259 func (s *DockerCLIRunSuite) TestUserNoEffectiveCapabilitiesNetRaw(c *testing.T) { 1260 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 1261 ensureSyscallTest(testutil.GetContext(c), c) 1262 1263 // test that a root user has default capability CAP_NET_RAW 1264 cli.DockerCmd(c, "run", "syscall-test", "raw-test") 1265 // test that non root user does not have default capability CAP_NET_RAW 1266 icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "syscall-test", "raw-test").Assert(c, icmd.Expected{ 1267 ExitCode: 1, 1268 Err: "Operation not permitted", 1269 }) 1270 // test that root user can drop default capability CAP_NET_RAW 1271 icmd.RunCommand(dockerBinary, "run", "--cap-drop", "net_raw", "syscall-test", "raw-test").Assert(c, icmd.Expected{ 1272 ExitCode: 1, 1273 Err: "Operation not permitted", 1274 }) 1275 } 1276 1277 func (s *DockerCLIRunSuite) TestUserNoEffectiveCapabilitiesChroot(c *testing.T) { 1278 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon) 1279 ensureSyscallTest(testutil.GetContext(c), c) 1280 1281 // test that a root user has default capability CAP_SYS_CHROOT 1282 cli.DockerCmd(c, "run", "busybox", "chroot", "/", "/bin/true") 1283 // test that non root user does not have default capability CAP_SYS_CHROOT 1284 icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "busybox", "chroot", "/", "/bin/true").Assert(c, icmd.Expected{ 1285 ExitCode: 1, 1286 Err: "Operation not permitted", 1287 }) 1288 // test that root user can drop default capability CAP_SYS_CHROOT 1289 icmd.RunCommand(dockerBinary, "run", "--cap-drop", "sys_chroot", "busybox", "chroot", "/", "/bin/true").Assert(c, icmd.Expected{ 1290 ExitCode: 1, 1291 Err: "Operation not permitted", 1292 }) 1293 } 1294 1295 func (s *DockerCLIRunSuite) TestUserNoEffectiveCapabilitiesMknod(c *testing.T) { 1296 testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon) 1297 ensureSyscallTest(testutil.GetContext(c), c) 1298 1299 // test that a root user has default capability CAP_MKNOD 1300 cli.DockerCmd(c, "run", "busybox", "mknod", "/tmp/node", "b", "1", "2") 1301 // test that non root user does not have default capability CAP_MKNOD 1302 // test that root user can drop default capability CAP_SYS_CHROOT 1303 icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "busybox", "mknod", "/tmp/node", "b", "1", "2").Assert(c, icmd.Expected{ 1304 ExitCode: 1, 1305 Err: "Operation not permitted", 1306 }) 1307 // test that root user can drop default capability CAP_MKNOD 1308 icmd.RunCommand(dockerBinary, "run", "--cap-drop", "mknod", "busybox", "mknod", "/tmp/node", "b", "1", "2").Assert(c, icmd.Expected{ 1309 ExitCode: 1, 1310 Err: "Operation not permitted", 1311 }) 1312 } 1313 1314 // TODO CAP_AUDIT_WRITE 1315 // TODO CAP_SETFCAP 1316 1317 func (s *DockerCLIRunSuite) TestRunApparmorProcDirectory(c *testing.T) { 1318 testRequires(c, testEnv.IsLocalDaemon, Apparmor) 1319 1320 // running w seccomp unconfined tests the apparmor profile 1321 result := icmd.RunCommand(dockerBinary, "run", "--security-opt", "seccomp=unconfined", "busybox", "chmod", "777", "/proc/1/cgroup") 1322 result.Assert(c, icmd.Expected{ExitCode: 1}) 1323 if !(strings.Contains(result.Combined(), "Permission denied") || strings.Contains(result.Combined(), "Operation not permitted")) { 1324 c.Fatalf("expected chmod 777 /proc/1/cgroup to fail, got %s: %v", result.Combined(), result.Error) 1325 } 1326 1327 result = icmd.RunCommand(dockerBinary, "run", "--security-opt", "seccomp=unconfined", "busybox", "chmod", "777", "/proc/1/attr/current") 1328 result.Assert(c, icmd.Expected{ExitCode: 1}) 1329 if !(strings.Contains(result.Combined(), "Permission denied") || strings.Contains(result.Combined(), "Operation not permitted")) { 1330 c.Fatalf("expected chmod 777 /proc/1/attr/current to fail, got %s: %v", result.Combined(), result.Error) 1331 } 1332 } 1333 1334 // make sure the default profile can be successfully parsed (using unshare as it is 1335 // something which we know is blocked in the default profile) 1336 func (s *DockerCLIRunSuite) TestRunSeccompWithDefaultProfile(c *testing.T) { 1337 testRequires(c, testEnv.IsLocalDaemon, seccompEnabled) 1338 1339 out, _, err := dockerCmdWithError("run", "--security-opt", "seccomp=../profiles/seccomp/default.json", "debian:bookworm-slim", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami") 1340 assert.ErrorContains(c, err, "", out) 1341 assert.Equal(c, strings.TrimSpace(out), "unshare: unshare failed: Operation not permitted") 1342 } 1343 1344 // TestRunDeviceSymlink checks run with device that follows symlink (#13840 and #22271) 1345 func (s *DockerCLIRunSuite) TestRunDeviceSymlink(c *testing.T) { 1346 testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon) 1347 if _, err := os.Stat("/dev/zero"); err != nil { 1348 c.Skip("Host does not have /dev/zero") 1349 } 1350 1351 // Create a temporary directory to create symlink 1352 tmpDir, err := os.MkdirTemp("", "docker_device_follow_symlink_tests") 1353 assert.NilError(c, err) 1354 1355 defer os.RemoveAll(tmpDir) 1356 1357 // Create a symbolic link to /dev/zero 1358 symZero := filepath.Join(tmpDir, "zero") 1359 err = os.Symlink("/dev/zero", symZero) 1360 assert.NilError(c, err) 1361 1362 // Create a temporary file "temp" inside tmpDir, write some data to "tmpDir/temp", 1363 // then create a symlink "tmpDir/file" to the temporary file "tmpDir/temp". 1364 tmpFile := filepath.Join(tmpDir, "temp") 1365 err = os.WriteFile(tmpFile, []byte("temp"), 0o666) 1366 assert.NilError(c, err) 1367 symFile := filepath.Join(tmpDir, "file") 1368 err = os.Symlink(tmpFile, symFile) 1369 assert.NilError(c, err) 1370 1371 // Create a symbolic link to /dev/zero, this time with a relative path (#22271) 1372 err = os.Symlink("zero", "/dev/symzero") 1373 if err != nil { 1374 c.Fatal("/dev/symzero creation failed") 1375 } 1376 // We need to remove this symbolic link here as it is created in /dev/, not temporary directory as above 1377 defer os.Remove("/dev/symzero") 1378 1379 // md5sum of 'dd if=/dev/zero bs=4K count=8' is bb7df04e1b0a2570657527a7e108ae23 1380 out := cli.DockerCmd(c, "run", "--device", symZero+":/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum").Combined() 1381 assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "bb7df04e1b0a2570657527a7e108ae23"), "expected output bb7df04e1b0a2570657527a7e108ae23") 1382 // symlink "tmpDir/file" to a file "tmpDir/temp" will result in an error as it is not a device. 1383 out, _, err = dockerCmdWithError("run", "--device", symFile+":/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum") 1384 assert.ErrorContains(c, err, "") 1385 assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "not a device node"), "expected output 'not a device node'") 1386 // md5sum of 'dd if=/dev/zero bs=4K count=8' is bb7df04e1b0a2570657527a7e108ae23 (this time check with relative path backed, see #22271) 1387 out = cli.DockerCmd(c, "run", "--device", "/dev/symzero:/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum").Combined() 1388 assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "bb7df04e1b0a2570657527a7e108ae23"), "expected output bb7df04e1b0a2570657527a7e108ae23") 1389 } 1390 1391 // TestRunPIDsLimit makes sure the pids cgroup is set with --pids-limit 1392 func (s *DockerCLIRunSuite) TestRunPIDsLimit(c *testing.T) { 1393 testRequires(c, testEnv.IsLocalDaemon, pidsLimit) 1394 1395 const file = "/sys/fs/cgroup/pids/pids.max" 1396 out := cli.DockerCmd(c, "run", "--name", "skittles", "--pids-limit", "4", "busybox", "cat", file).Combined() 1397 assert.Equal(c, strings.TrimSpace(out), "4") 1398 1399 out = inspectField(c, "skittles", "HostConfig.PidsLimit") 1400 assert.Equal(c, out, "4", "setting the pids limit failed") 1401 } 1402 1403 func (s *DockerCLIRunSuite) TestRunPrivilegedAllowedDevices(c *testing.T) { 1404 testRequires(c, DaemonIsLinux, NotUserNamespace) 1405 1406 const file = "/sys/fs/cgroup/devices/devices.list" 1407 out := cli.DockerCmd(c, "run", "--privileged", "busybox", "cat", file).Combined() 1408 c.Logf("out: %q", out) 1409 assert.Equal(c, strings.TrimSpace(out), "a *:* rwm") 1410 } 1411 1412 func (s *DockerCLIRunSuite) TestRunUserDeviceAllowed(c *testing.T) { 1413 testRequires(c, DaemonIsLinux) 1414 1415 fi, err := os.Stat("/dev/snd/timer") 1416 if err != nil { 1417 c.Skip("Host does not have /dev/snd/timer") 1418 } 1419 stat, ok := fi.Sys().(*syscall.Stat_t) 1420 if !ok { 1421 c.Skip("Could not stat /dev/snd/timer") 1422 } 1423 1424 const file = "/sys/fs/cgroup/devices/devices.list" 1425 out := cli.DockerCmd(c, "run", "--device", "/dev/snd/timer:w", "busybox", "cat", file).Combined() 1426 assert.Assert(c, strings.Contains(out, fmt.Sprintf("c %d:%d w", stat.Rdev/256, stat.Rdev%256))) 1427 } 1428 1429 func (s *DockerDaemonSuite) TestRunSeccompJSONNewFormat(c *testing.T) { 1430 testRequires(c, seccompEnabled) 1431 ctx := testutil.GetContext(c) 1432 1433 s.d.StartWithBusybox(ctx, c) 1434 1435 const jsonData = `{ 1436 "defaultAction": "SCMP_ACT_ALLOW", 1437 "syscalls": [ 1438 { 1439 "names": ["chmod", "fchmod", "fchmodat"], 1440 "action": "SCMP_ACT_ERRNO" 1441 } 1442 ] 1443 }` 1444 tmpFile, err := os.CreateTemp("", "profile.json") 1445 assert.NilError(c, err) 1446 defer tmpFile.Close() 1447 _, err = tmpFile.Write([]byte(jsonData)) 1448 assert.NilError(c, err) 1449 1450 out, err := s.d.Cmd("run", "--security-opt", "seccomp="+tmpFile.Name(), "busybox", "chmod", "777", ".") 1451 assert.ErrorContains(c, err, "") 1452 assert.Assert(c, strings.Contains(out, "Operation not permitted")) 1453 } 1454 1455 func (s *DockerDaemonSuite) TestRunSeccompJSONNoNameAndNames(c *testing.T) { 1456 testRequires(c, seccompEnabled) 1457 ctx := testutil.GetContext(c) 1458 1459 s.d.StartWithBusybox(ctx, c) 1460 1461 const jsonData = `{ 1462 "defaultAction": "SCMP_ACT_ALLOW", 1463 "syscalls": [ 1464 { 1465 "name": "chmod", 1466 "names": ["fchmod", "fchmodat"], 1467 "action": "SCMP_ACT_ERRNO" 1468 } 1469 ] 1470 }` 1471 tmpFile, err := os.CreateTemp("", "profile.json") 1472 assert.NilError(c, err) 1473 defer tmpFile.Close() 1474 _, err = tmpFile.Write([]byte(jsonData)) 1475 assert.NilError(c, err) 1476 1477 out, err := s.d.Cmd("run", "--security-opt", "seccomp="+tmpFile.Name(), "busybox", "chmod", "777", ".") 1478 assert.ErrorContains(c, err, "") 1479 assert.Assert(c, strings.Contains(out, "use either 'name' or 'names'")) 1480 } 1481 1482 func (s *DockerDaemonSuite) TestRunSeccompJSONNoArchAndArchMap(c *testing.T) { 1483 testRequires(c, seccompEnabled) 1484 ctx := testutil.GetContext(c) 1485 1486 s.d.StartWithBusybox(ctx, c) 1487 1488 const jsonData = `{ 1489 "archMap": [ 1490 { 1491 "architecture": "SCMP_ARCH_X86_64", 1492 "subArchitectures": [ 1493 "SCMP_ARCH_X86", 1494 "SCMP_ARCH_X32" 1495 ] 1496 } 1497 ], 1498 "architectures": [ 1499 "SCMP_ARCH_X32" 1500 ], 1501 "defaultAction": "SCMP_ACT_ALLOW", 1502 "syscalls": [ 1503 { 1504 "names": ["chmod", "fchmod", "fchmodat"], 1505 "action": "SCMP_ACT_ERRNO" 1506 } 1507 ] 1508 }` 1509 tmpFile, err := os.CreateTemp("", "profile.json") 1510 assert.NilError(c, err) 1511 defer tmpFile.Close() 1512 _, err = tmpFile.Write([]byte(jsonData)) 1513 assert.NilError(c, err) 1514 1515 out, err := s.d.Cmd("run", "--security-opt", "seccomp="+tmpFile.Name(), "busybox", "chmod", "777", ".") 1516 assert.ErrorContains(c, err, "") 1517 assert.Assert(c, strings.Contains(out, "use either 'architectures' or 'archMap'")) 1518 } 1519 1520 func (s *DockerDaemonSuite) TestRunWithDaemonDefaultSeccompProfile(c *testing.T) { 1521 testRequires(c, seccompEnabled) 1522 ctx := testutil.GetContext(c) 1523 1524 s.d.StartWithBusybox(ctx, c) 1525 1526 // 1) verify I can run containers with the Docker default shipped profile which allows chmod 1527 _, err := s.d.Cmd("run", "busybox", "chmod", "777", ".") 1528 assert.NilError(c, err) 1529 1530 const jsonData = `{ 1531 "defaultAction": "SCMP_ACT_ALLOW", 1532 "syscalls": [ 1533 { 1534 "name": "chmod", 1535 "action": "SCMP_ACT_ERRNO" 1536 }, 1537 { 1538 "name": "fchmodat", 1539 "action": "SCMP_ACT_ERRNO" 1540 } 1541 ] 1542 }` 1543 tmpFile, err := os.CreateTemp("", "profile.json") 1544 assert.NilError(c, err) 1545 defer tmpFile.Close() 1546 _, err = tmpFile.Write([]byte(jsonData)) 1547 assert.NilError(c, err) 1548 1549 // 2) restart the daemon and add a custom seccomp profile in which we deny chmod 1550 s.d.Restart(c, "--seccomp-profile="+tmpFile.Name()) 1551 1552 out, err := s.d.Cmd("run", "busybox", "chmod", "777", ".") 1553 assert.ErrorContains(c, err, "") 1554 assert.Assert(c, strings.Contains(out, "Operation not permitted")) 1555 } 1556 1557 func (s *DockerCLIRunSuite) TestRunWithNanoCPUs(c *testing.T) { 1558 testRequires(c, cpuCfsQuota, cpuCfsPeriod) 1559 1560 const file1 = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us" 1561 const file2 = "/sys/fs/cgroup/cpu/cpu.cfs_period_us" 1562 out := cli.DockerCmd(c, "run", "--cpus", "0.5", "--name", "test", "busybox", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2)).Combined() 1563 assert.Equal(c, strings.TrimSpace(out), "50000\n100000") 1564 1565 clt, err := client.NewClientWithOpts(client.FromEnv) 1566 assert.NilError(c, err) 1567 inspect, err := clt.ContainerInspect(testutil.GetContext(c), "test") 1568 assert.NilError(c, err) 1569 assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(500000000)) 1570 1571 out = inspectField(c, "test", "HostConfig.CpuQuota") 1572 assert.Equal(c, out, "0", "CPU CFS quota should be 0") 1573 out = inspectField(c, "test", "HostConfig.CpuPeriod") 1574 assert.Equal(c, out, "0", "CPU CFS period should be 0") 1575 1576 out, _, err = dockerCmdWithError("run", "--cpus", "0.5", "--cpu-quota", "50000", "--cpu-period", "100000", "busybox", "sh") 1577 assert.ErrorContains(c, err, "") 1578 assert.Assert(c, strings.Contains(out, "Conflicting options: Nano CPUs and CPU Period cannot both be set")) 1579 }