build_userns_linux_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package build // import "github.com/docker/docker/integration/build"
  2. import (
  3. "bufio"
  4. "bytes"
  5. "context"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "strings"
  10. "testing"
  11. "github.com/docker/docker/api/types"
  12. "github.com/docker/docker/integration/internal/container"
  13. "github.com/docker/docker/pkg/stdcopy"
  14. "github.com/docker/docker/testutil/daemon"
  15. "github.com/docker/docker/testutil/fakecontext"
  16. "gotest.tools/v3/assert"
  17. "gotest.tools/v3/skip"
  18. )
  19. // Implements a test for https://github.com/moby/moby/issues/41723
  20. // Images built in a user-namespaced daemon should have capabilities serialised in
  21. // VFS_CAP_REVISION_2 (no user-namespace root uid) format rather than V3 (that includes
  22. // the root uid).
  23. func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) {
  24. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  25. skip.If(t, testEnv.IsRemoteDaemon())
  26. skip.If(t, !testEnv.IsUserNamespaceInKernel())
  27. skip.If(t, testEnv.IsRootless())
  28. const imageTag = "capabilities:1.0"
  29. tmp, err := ioutil.TempDir("", "integration-")
  30. assert.NilError(t, err)
  31. defer os.RemoveAll(tmp)
  32. dUserRemap := daemon.New(t)
  33. dUserRemap.StartWithBusybox(t, "--userns-remap", "default")
  34. dUserRemapRunning := true
  35. defer func() {
  36. if dUserRemapRunning {
  37. dUserRemap.Stop(t)
  38. }
  39. }()
  40. dockerfile := `
  41. FROM debian:bullseye
  42. RUN setcap CAP_NET_BIND_SERVICE=+eip /bin/sleep
  43. `
  44. ctx := context.Background()
  45. source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
  46. defer source.Close()
  47. clientUserRemap := dUserRemap.NewClientT(t)
  48. resp, err := clientUserRemap.ImageBuild(ctx,
  49. source.AsTarReader(t),
  50. types.ImageBuildOptions{
  51. Tags: []string{imageTag},
  52. })
  53. assert.NilError(t, err)
  54. defer resp.Body.Close()
  55. buf := make([]byte, 1024)
  56. for {
  57. n, err := resp.Body.Read(buf)
  58. if err != nil && err != io.EOF {
  59. t.Fatalf("Error reading ImageBuild response: %v", err)
  60. break
  61. }
  62. if n == 0 {
  63. break
  64. }
  65. }
  66. reader, err := clientUserRemap.ImageSave(ctx, []string{imageTag})
  67. assert.NilError(t, err, "failed to download capabilities image")
  68. defer reader.Close()
  69. tar, err := os.Create(tmp + "/image.tar")
  70. assert.NilError(t, err, "failed to create image tar file")
  71. defer tar.Close()
  72. _, err = io.Copy(tar, reader)
  73. assert.NilError(t, err, "failed to write image tar file")
  74. dUserRemap.Stop(t)
  75. dUserRemap.Cleanup(t)
  76. dUserRemapRunning = false
  77. dNoUserRemap := daemon.New(t)
  78. dNoUserRemap.StartWithBusybox(t)
  79. defer dNoUserRemap.Stop(t)
  80. clientNoUserRemap := dNoUserRemap.NewClientT(t)
  81. tarFile, err := os.Open(tmp + "/image.tar")
  82. assert.NilError(t, err, "failed to open image tar file")
  83. tarReader := bufio.NewReader(tarFile)
  84. loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader, false)
  85. assert.NilError(t, err, "failed to load image tar file")
  86. defer loadResp.Body.Close()
  87. for {
  88. n, err := loadResp.Body.Read(buf)
  89. if err != nil && err != io.EOF {
  90. t.Fatalf("Error reading ImageLoad response: %v", err)
  91. break
  92. }
  93. if n == 0 {
  94. break
  95. }
  96. }
  97. cid := container.Run(ctx, t, clientNoUserRemap,
  98. container.WithImage(imageTag),
  99. container.WithCmd("/sbin/getcap", "-n", "/bin/sleep"),
  100. )
  101. logReader, err := clientNoUserRemap.ContainerLogs(ctx, cid, types.ContainerLogsOptions{
  102. ShowStdout: true,
  103. })
  104. assert.NilError(t, err)
  105. actualStdout := new(bytes.Buffer)
  106. actualStderr := ioutil.Discard
  107. _, err = stdcopy.StdCopy(actualStdout, actualStderr, logReader)
  108. assert.NilError(t, err)
  109. if strings.TrimSpace(actualStdout.String()) != "/bin/sleep cap_net_bind_service=eip" {
  110. // Activate when fix is merged: https://github.com/moby/moby/pull/41724
  111. //t.Fatalf("run produced invalid output: %q, expected %q", actualStdout.String(), "/bin/sleep cap_net_bind_service=eip")
  112. // t.Logf("run produced invalid output (expected until #41724 merges): %q, expected %q",
  113. // actualStdout.String(),
  114. // "/bin/sleep cap_net_bind_service=eip")
  115. } else {
  116. // Shouldn't happen until fix is merged: https://github.com/moby/moby/pull/41724
  117. t.Fatalf("run produced valid output (unexpected until #41724 merges): %q, expected %q",
  118. actualStdout.String(),
  119. "/bin/sleep cap_net_bind_service=eip")
  120. }
  121. }