docker_cli_run_unix_test.go 56 KB

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