diff --git a/containerd.spec b/containerd.spec index d4b3dcdc46a9fc55b68e8e242f906aef996d537a..826872b3d2c18d4536e3af57a92486e67d547683 100644 --- a/containerd.spec +++ b/containerd.spec @@ -2,7 +2,7 @@ %global debug_package %{nil} Version: 1.6.22 Name: containerd -Release: 23 +Release: 24 Summary: An industry-standard container runtime License: ASL 2.0 URL: https://containerd.io @@ -68,6 +68,12 @@ install -D -p -m 0644 %{S:7} %{buildroot}%{_sysconfdir}/containerd/config.toml %exclude %{_bindir}/containerd-stress %changelog +* Tue Sep 23 2025 dongyuzhen - 1.6.22-24 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC:fix userns with Dockerfile VOLUME mounts that need copy + * Mon Sep 22 2025 dongyuzhen - 1.6.22-23 - Type:bugfix - ID:NA diff --git a/git-commit b/git-commit index dba6168f11384a43bd72c67836587277c245c380..79e408b2de6a5d596a2043f020f8ff7616b728d6 100644 --- a/git-commit +++ b/git-commit @@ -1 +1 @@ -d2c431a196e52330a83118f4cc28f8d6fb6d9a22 +7e251398337fb5ea1fba2c22b7a6155e94b25759 diff --git a/patch/0046-containerd-cri-Fix-userns-with-Dockerfile-VOLUME-mounts.patch b/patch/0046-containerd-cri-Fix-userns-with-Dockerfile-VOLUME-mounts.patch new file mode 100644 index 0000000000000000000000000000000000000000..77a967d269e66c49c7d1a68b91c64d797ad656c3 --- /dev/null +++ b/patch/0046-containerd-cri-Fix-userns-with-Dockerfile-VOLUME-mounts.patch @@ -0,0 +1,103 @@ +From aaa965608d565cd2212be215198483f3a7c79bd8 Mon Sep 17 00:00:00 2001 +From: Rodrigo Campos +Date: Tue, 19 Aug 2025 12:36:07 +0200 +Subject: [PATCH] cri: Fix userns with Dockerfile VOLUME mounts that need copy + +If a Dockerfile is using a `VOLUME` directive and the directory exists +in the rootfs, like in this example: + + FROM docker.io/library/alpine:latest + VOLUME [ "/run" ] + +The alpine container image already contains a "/run" directory. This +will force the code in WithVolumes() to copy its content to the new +volume created for the VOLUME directive. This copies the content as well +as the ownership. + +However, as we perform the mounts from the host POV without being inside +a userns, the idmap option will just shift the IDs in ways that will +screw up the ownerships when copied. We should only use the idmap option +when running the container inside a userns, so the ownerships are fine +(the userns will do a shift and the idmap another, to make it all seem +as if there was no UID/GID shift in the first place). + +This PR does just that, remove the idmap option from mounts so we copy +the files without any ID transformations. It's simpler and easier to +reason about if we just don't mount with the idmap option here: all +files are copied just fine without ID transformations and ID +transformation is applied via the idmap option at mount time when +running the pod. + +Also, note that `VOLUME` directives that refer to directories that don't +exist on the rootfs work fine (`VOLUME [ "/rata" ]` for example), as +there is no copy done in that case so the permissions weren't changed. + +Signed-off-by: Rodrigo Campos +--- + mount/temp.go | 27 +++++++++++++++++++++++++++ + pkg/cri/opts/container.go | 5 +++++ + 2 files changed, 32 insertions(+) + +diff --git a/mount/temp.go b/mount/temp.go +index 889d49c1a..407a746cd 100644 +--- a/mount/temp.go ++++ b/mount/temp.go +@@ -20,6 +20,7 @@ import ( + "context" + "fmt" + "os" ++ "strings" + + "github.com/containerd/containerd/log" + ) +@@ -67,6 +68,32 @@ func WithTempMount(ctx context.Context, mounts []Mount, f func(root string) erro + return nil + } + ++// RemoveIDMapOption copies and removes the uidmap/gidmap options on any of the mounts using it. ++func RemoveIDMapOption(mounts []Mount) []Mount { ++ var out []Mount ++ for i, m := range mounts { ++ for j, opt := range m.Options { ++ if strings.HasPrefix(opt, "uidmap") || strings.HasPrefix(opt, "gidmap") { ++ if out == nil { ++ out = copyMounts(mounts) ++ } ++ out[i].Options = append(out[i].Options[:j], out[i].Options[j+1:]...) ++ } ++ } ++ } ++ if out != nil { ++ return out ++ } ++ return mounts ++} ++ ++// copyMounts creates a copy of the original slice to allow for modification and not altering the original ++func copyMounts(in []Mount) []Mount { ++ out := make([]Mount, len(in)) ++ copy(out, in) ++ return out ++} ++ + // WithReadonlyTempMount mounts the provided mounts to a temp dir as readonly, + // and pass the temp dir to f. The mounts are valid during the call to the f. + // Finally we will unmount and remove the temp dir regardless of the result of f. +diff --git a/pkg/cri/opts/container.go b/pkg/cri/opts/container.go +index 5ea1b8739..5ad353f6d 100644 +--- a/pkg/cri/opts/container.go ++++ b/pkg/cri/opts/container.go +@@ -75,6 +75,11 @@ func WithVolumes(volumeMounts map[string]string) containerd.NewContainerOpts { + mounts[0].Options = append(mounts[0].Options, "ro") + } + ++ // Always copy files without UID/GID transformations. ++ // The ID transformations are only needed when running inside a userns, that is not ++ // the case here. ++ mounts = mount.RemoveIDMapOption(mounts) ++ + root, err := os.MkdirTemp("", "ctd-volume") + if err != nil { + return err +-- +2.33.0 diff --git a/series.conf b/series.conf index a86dc0e5fe820fa492cf527238eaebb44e5d4c50..53da919f23f443d0428ecc47f255d4f5467a6135 100644 --- a/series.conf +++ b/series.conf @@ -42,3 +42,4 @@ patch/0042-containerd-delete-task-asynchronously-to-avoid-conta.patch patch/0043-containerd-fix-dead-loop.patch patch/0044-containerd-remove-limitnofile-from-containerd-service.patch patch/0045-containerd-Fix-ctr-snapshot-mount-produce-invalid-mount-command.patch +patch/0046-containerd-cri-Fix-userns-with-Dockerfile-VOLUME-mounts.patch