build_userns_linux_test.go 3.9 KB

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