docker_cli_cp_test.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "os/exec"
  7. "path/filepath"
  8. "testing"
  9. )
  10. const (
  11. cpTestPathParent = "/some"
  12. cpTestPath = "/some/path"
  13. cpTestName = "test"
  14. cpFullPath = "/some/path/test"
  15. cpContainerContents = "holla, i am the container"
  16. cpHostContents = "hello, i am the host"
  17. )
  18. // Test for #5656
  19. // Check that garbage paths don't escape the container's rootfs
  20. func TestCpGarbagePath(t *testing.T) {
  21. out, exitCode, err := cmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
  22. if err != nil || exitCode != 0 {
  23. t.Fatal("failed to create a container", out, err)
  24. }
  25. cleanedContainerID := stripTrailingCharacters(out)
  26. defer deleteContainer(cleanedContainerID)
  27. out, _, err = cmd(t, "wait", cleanedContainerID)
  28. if err != nil || stripTrailingCharacters(out) != "0" {
  29. t.Fatal("failed to set up container", out, err)
  30. }
  31. if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
  32. t.Fatal(err)
  33. }
  34. hostFile, err := os.Create(cpFullPath)
  35. if err != nil {
  36. t.Fatal(err)
  37. }
  38. defer hostFile.Close()
  39. defer os.RemoveAll(cpTestPathParent)
  40. fmt.Fprintf(hostFile, "%s", cpHostContents)
  41. tmpdir, err := ioutil.TempDir("", "docker-integration")
  42. if err != nil {
  43. t.Fatal(err)
  44. }
  45. tmpname := filepath.Join(tmpdir, cpTestName)
  46. defer os.RemoveAll(tmpdir)
  47. path := filepath.Join("../../../../../../../../../../../../", cpFullPath)
  48. _, _, err = cmd(t, "cp", cleanedContainerID+":"+path, tmpdir)
  49. if err != nil {
  50. t.Fatalf("couldn't copy from garbage path: %s:%s %s", cleanedContainerID, path, err)
  51. }
  52. file, _ := os.Open(tmpname)
  53. defer file.Close()
  54. test, err := ioutil.ReadAll(file)
  55. if err != nil {
  56. t.Fatal(err)
  57. }
  58. if string(test) == cpHostContents {
  59. t.Errorf("output matched host file -- garbage path can escape container rootfs")
  60. }
  61. if string(test) != cpContainerContents {
  62. t.Errorf("output doesn't match the input for garbage path")
  63. }
  64. logDone("cp - garbage paths relative to container's rootfs")
  65. }
  66. // Check that relative paths are relative to the container's rootfs
  67. func TestCpRelativePath(t *testing.T) {
  68. out, exitCode, err := cmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
  69. if err != nil || exitCode != 0 {
  70. t.Fatal("failed to create a container", out, err)
  71. }
  72. cleanedContainerID := stripTrailingCharacters(out)
  73. defer deleteContainer(cleanedContainerID)
  74. out, _, err = cmd(t, "wait", cleanedContainerID)
  75. if err != nil || stripTrailingCharacters(out) != "0" {
  76. t.Fatal("failed to set up container", out, err)
  77. }
  78. if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
  79. t.Fatal(err)
  80. }
  81. hostFile, err := os.Create(cpFullPath)
  82. if err != nil {
  83. t.Fatal(err)
  84. }
  85. defer hostFile.Close()
  86. defer os.RemoveAll(cpTestPathParent)
  87. fmt.Fprintf(hostFile, "%s", cpHostContents)
  88. tmpdir, err := ioutil.TempDir("", "docker-integration")
  89. if err != nil {
  90. t.Fatal(err)
  91. }
  92. tmpname := filepath.Join(tmpdir, cpTestName)
  93. defer os.RemoveAll(tmpdir)
  94. path, _ := filepath.Rel("/", cpFullPath)
  95. _, _, err = cmd(t, "cp", cleanedContainerID+":"+path, tmpdir)
  96. if err != nil {
  97. t.Fatalf("couldn't copy from relative path: %s:%s %s", cleanedContainerID, path, err)
  98. }
  99. file, _ := os.Open(tmpname)
  100. defer file.Close()
  101. test, err := ioutil.ReadAll(file)
  102. if err != nil {
  103. t.Fatal(err)
  104. }
  105. if string(test) == cpHostContents {
  106. t.Errorf("output matched host file -- relative path can escape container rootfs")
  107. }
  108. if string(test) != cpContainerContents {
  109. t.Errorf("output doesn't match the input for relative path")
  110. }
  111. logDone("cp - relative paths relative to container's rootfs")
  112. }
  113. // Check that absolute paths are relative to the container's rootfs
  114. func TestCpAbsolutePath(t *testing.T) {
  115. out, exitCode, err := cmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
  116. if err != nil || exitCode != 0 {
  117. t.Fatal("failed to create a container", out, err)
  118. }
  119. cleanedContainerID := stripTrailingCharacters(out)
  120. defer deleteContainer(cleanedContainerID)
  121. out, _, err = cmd(t, "wait", cleanedContainerID)
  122. if err != nil || stripTrailingCharacters(out) != "0" {
  123. t.Fatal("failed to set up container", out, err)
  124. }
  125. if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
  126. t.Fatal(err)
  127. }
  128. hostFile, err := os.Create(cpFullPath)
  129. if err != nil {
  130. t.Fatal(err)
  131. }
  132. defer hostFile.Close()
  133. defer os.RemoveAll(cpTestPathParent)
  134. fmt.Fprintf(hostFile, "%s", cpHostContents)
  135. tmpdir, err := ioutil.TempDir("", "docker-integration")
  136. if err != nil {
  137. t.Fatal(err)
  138. }
  139. tmpname := filepath.Join(tmpdir, cpTestName)
  140. defer os.RemoveAll(tmpdir)
  141. path := cpFullPath
  142. _, _, err = cmd(t, "cp", cleanedContainerID+":"+path, tmpdir)
  143. if err != nil {
  144. t.Fatalf("couldn't copy from absolute path: %s:%s %s", cleanedContainerID, path, err)
  145. }
  146. file, _ := os.Open(tmpname)
  147. defer file.Close()
  148. test, err := ioutil.ReadAll(file)
  149. if err != nil {
  150. t.Fatal(err)
  151. }
  152. if string(test) == cpHostContents {
  153. t.Errorf("output matched host file -- absolute path can escape container rootfs")
  154. }
  155. if string(test) != cpContainerContents {
  156. t.Errorf("output doesn't match the input for absolute path")
  157. }
  158. logDone("cp - absolute paths relative to container's rootfs")
  159. }
  160. // Test for #5619
  161. // Check that absolute symlinks are still relative to the container's rootfs
  162. func TestCpAbsoluteSymlink(t *testing.T) {
  163. out, exitCode, err := cmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpFullPath+" container_path")
  164. if err != nil || exitCode != 0 {
  165. t.Fatal("failed to create a container", out, err)
  166. }
  167. cleanedContainerID := stripTrailingCharacters(out)
  168. defer deleteContainer(cleanedContainerID)
  169. out, _, err = cmd(t, "wait", cleanedContainerID)
  170. if err != nil || stripTrailingCharacters(out) != "0" {
  171. t.Fatal("failed to set up container", out, err)
  172. }
  173. if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
  174. t.Fatal(err)
  175. }
  176. hostFile, err := os.Create(cpFullPath)
  177. if err != nil {
  178. t.Fatal(err)
  179. }
  180. defer hostFile.Close()
  181. defer os.RemoveAll(cpTestPathParent)
  182. fmt.Fprintf(hostFile, "%s", cpHostContents)
  183. tmpdir, err := ioutil.TempDir("", "docker-integration")
  184. if err != nil {
  185. t.Fatal(err)
  186. }
  187. tmpname := filepath.Join(tmpdir, cpTestName)
  188. defer os.RemoveAll(tmpdir)
  189. path := filepath.Join("/", "container_path")
  190. _, _, err = cmd(t, "cp", cleanedContainerID+":"+path, tmpdir)
  191. if err != nil {
  192. t.Fatalf("couldn't copy from absolute path: %s:%s %s", cleanedContainerID, path, err)
  193. }
  194. file, _ := os.Open(tmpname)
  195. defer file.Close()
  196. test, err := ioutil.ReadAll(file)
  197. if err != nil {
  198. t.Fatal(err)
  199. }
  200. if string(test) == cpHostContents {
  201. t.Errorf("output matched host file -- absolute symlink can escape container rootfs")
  202. }
  203. if string(test) != cpContainerContents {
  204. t.Errorf("output doesn't match the input for absolute symlink")
  205. }
  206. logDone("cp - absolute symlink relative to container's rootfs")
  207. }
  208. // Test for #5619
  209. // Check that symlinks which are part of the resource path are still relative to the container's rootfs
  210. func TestCpSymlinkComponent(t *testing.T) {
  211. out, exitCode, err := cmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpTestPath+" container_path")
  212. if err != nil || exitCode != 0 {
  213. t.Fatal("failed to create a container", out, err)
  214. }
  215. cleanedContainerID := stripTrailingCharacters(out)
  216. defer deleteContainer(cleanedContainerID)
  217. out, _, err = cmd(t, "wait", cleanedContainerID)
  218. if err != nil || stripTrailingCharacters(out) != "0" {
  219. t.Fatal("failed to set up container", out, err)
  220. }
  221. if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
  222. t.Fatal(err)
  223. }
  224. hostFile, err := os.Create(cpFullPath)
  225. if err != nil {
  226. t.Fatal(err)
  227. }
  228. defer hostFile.Close()
  229. defer os.RemoveAll(cpTestPathParent)
  230. fmt.Fprintf(hostFile, "%s", cpHostContents)
  231. tmpdir, err := ioutil.TempDir("", "docker-integration")
  232. if err != nil {
  233. t.Fatal(err)
  234. }
  235. tmpname := filepath.Join(tmpdir, cpTestName)
  236. defer os.RemoveAll(tmpdir)
  237. path := filepath.Join("/", "container_path", cpTestName)
  238. _, _, err = cmd(t, "cp", cleanedContainerID+":"+path, tmpdir)
  239. if err != nil {
  240. t.Fatalf("couldn't copy from symlink path component: %s:%s %s", cleanedContainerID, path, err)
  241. }
  242. file, _ := os.Open(tmpname)
  243. defer file.Close()
  244. test, err := ioutil.ReadAll(file)
  245. if err != nil {
  246. t.Fatal(err)
  247. }
  248. if string(test) == cpHostContents {
  249. t.Errorf("output matched host file -- symlink path component can escape container rootfs")
  250. }
  251. if string(test) != cpContainerContents {
  252. t.Errorf("output doesn't match the input for symlink path component")
  253. }
  254. logDone("cp - symlink path components relative to container's rootfs")
  255. }
  256. // Check that cp with unprivileged user doesn't return any error
  257. func TestCpUnprivilegedUser(t *testing.T) {
  258. out, exitCode, err := cmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "touch "+cpTestName)
  259. if err != nil || exitCode != 0 {
  260. t.Fatal("failed to create a container", out, err)
  261. }
  262. cleanedContainerID := stripTrailingCharacters(out)
  263. defer deleteContainer(cleanedContainerID)
  264. out, _, err = cmd(t, "wait", cleanedContainerID)
  265. if err != nil || stripTrailingCharacters(out) != "0" {
  266. t.Fatal("failed to set up container", out, err)
  267. }
  268. tmpdir, err := ioutil.TempDir("", "docker-integration")
  269. if err != nil {
  270. t.Fatal(err)
  271. }
  272. defer os.RemoveAll(tmpdir)
  273. if err = os.Chmod(tmpdir, 0777); err != nil {
  274. t.Fatal(err)
  275. }
  276. path := cpTestName
  277. _, _, err = runCommandWithOutput(exec.Command("su", "unprivilegeduser", "-c", dockerBinary+" cp "+cleanedContainerID+":"+path+" "+tmpdir))
  278. if err != nil {
  279. t.Fatalf("couldn't copy with unprivileged user: %s:%s %s", cleanedContainerID, path, err)
  280. }
  281. logDone("cp - unprivileged user")
  282. }