Description
Container creation with containerd 2.2.0 fails with path escapes from parent errors for images that contain /etc/passwd and /etc/group as absolute symlinks.
Previous versions of containerd, like 2.1.5 (and 1.7.28), are able to create containers from such images without errors.
Using relative symlinks instead of absolute ones for both files does not produce the error.
Apparently containerd tries to open /etc/passwd and /etc/group for user and group name resolution1 but misinterprets the absolute target path of those symlinks as being rooted in the host's file system rather than the container's file system.
Steps to reproduce the issue
Try to run ctr containers create docker.io/nixos/nix:2.32.2 nixos-2-32-2 on Ubuntu 24.04 with containerd v2.2.0 installed.
In that container image /etc/passwd is a symlink into /nix/store/.
root@debug-containerd-220:~# uname -a
Linux debug-containerd-220 6.8.0-45-generic #45-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 30 12:02:04 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
root@debug-containerd-220:~# grep -E '^(NAME|VERSION)=' /etc/os-release
NAME="Ubuntu"
VERSION="24.04.1 LTS (Noble Numbat)"
root@debug-containerd-220:~# apt install containerd.io='2.2.0-*'
root@debug-containerd-220:~# containerd --version
containerd containerd.io v2.2.0 1c4457e00facac03ce1d75f7b6777a7a851e5c41
root@debug-containerd-220:~# ctr image pull docker.io/nixos/nix:2.32.2
.......
application/vnd.oci.image.index.v1+json sha256:04abdb9c74e0bd20913ca84e4704419af31e49e901cd57253ed8f9762def28fd
Completed pull from OCI Registry (docker.io/nixos/nix:2.32.2) elapsed: 24.6s total: 97.3 M (4.0 MiB/s)
root@debug-containerd-220:~# ctr containers create docker.io/nixos/nix:2.32.2 nixos-2-32-2
ctr: mount callback failed on /tmp/containerd-mount263707551: openat etc/passwd: path escapes from parent
To narrow down the issue I've built 3 container images:
- one that matches the original
- one that has the absolute symlink at
/etc/passwd replaced with a relative one
- one that has the absolute symlink at
/etc/passwd as well as /etc/group replaced with relative ones
I observed that container creation succeeds only with the third image.
root@debug-containerd-220:~# mkdir ./nix-2-32-2-containerfiles
root@debug-containerd-220:~# cat <<"EOF" > nix-2-32-2-containerfiles/original
FROM docker.io/nixos/nix:2.32.2
EOF
root@debug-containerd-220:~# cat <<"EOF" > nix-2-32-2-containerfiles/rel-passwd-symlink
FROM docker.io/nixos/nix:2.32.2
RUN ln --symbolic --force "$(realpath --relative-to=/etc /etc/passwd)" /etc/passwd
EOF
root@debug-containerd-220:~# cat <<"EOF" > nix-2-32-2-containerfiles/rel-passwd-group-symlink
FROM docker.io/nixos/nix:2.32.2
RUN \
ln --symbolic --force "$(realpath --relative-to=/etc /etc/passwd)" /etc/passwd; \
ln --symbolic --force "$(realpath --relative-to=/etc /etc/group)" /etc/group
EOF
root@debug-containerd-220:~#
root@debug-containerd-220:~# mkdir ./empty-dir
root@debug-containerd-220:~# for containerfile in nix-2-32-2-containerfiles/*; do podman build --tag localhost/nix-2-32-2:$(basename $containerfile) --file $containerfile ./empty-dir; podman image save localhost/nix-2-32-2:$(basename $containerfile) | ctr image import /dev/stdin; done
.......
Successfully tagged localhost/nix-2-32-2:original
Successfully tagged docker.io/nixos/nix:2.32.2
f9b3c7811e275e67142fd4cd66a4ae1bd90ae3dd7d50b5e839b1a000690800a1
localhost/nix 2 32 2:original saved
application/vnd.docker.distribution.manifest.v2+json sha256:e233d4e348d00873f4136271143e988790564d3f3eac13ac05ec8fda418755f7
.......
Successfully tagged localhost/nix-2-32-2:rel-passwd-group-symlink
947f41d91c5c423efb85c089312579f3551a5defb9f7df3f1878fef85ffc77f9
localhost/nix 2 32 2:rel passwd group sy saved
application/vnd.docker.distribution.manifest.v2+json sha256:423394ddd5fb52ce13c9ee71f5326a3c9d25db0e32a1c506efe41515d4bbdc02
.......
Successfully tagged localhost/nix-2-32-2:rel-passwd-symlink
8f6201eaf0ce9e89fc7aed5e9c4442349a7a46c36cf363f35c2f51f331ade829
localhost/nix 2 32 2:rel passwd symlink saved
application/vnd.docker.distribution.manifest.v2+json sha256:51a0a6acdcfb2d30b1db1d73804f5dd19d928f1269a9431a857929fc8ccf5c21
.......
root@debug-containerd-220:~# ctr container create localhost/nix-2-32-2:original nix-2-32-2-original
ctr: mount callback failed on /tmp/containerd-mount3757137550: openat etc/passwd: path escapes from parent
root@debug-containerd-220:~# echo $?
1
root@debug-containerd-220:~# ctr container create localhost/nix-2-32-2:rel-passwd-symlink nix-2-32-2-rel-passwd-symlink
ctr: mount callback failed on /tmp/containerd-mount2612271920: openat etc/group: path escapes from parent
root@debug-containerd-220:~# echo $?
1
root@debug-containerd-220:~# ctr container create localhost/nix-2-32-2:rel-passwd-group-symlink nix-2-32-2-rel-passwd-group-symlink
root@debug-containerd-220:~# echo $?
0
root@debug-containerd-220:~#
I've also tested all 3 images against containerd 2.1.5 which yielded no errors.
root@debug-containerd-220:~# apt remove --purge containerd.io && apt install containerd.io='2.1.5-*'
.......
root@debug-containerd-220:~# ctr container delete nix-2-32-2-original nix-2-32-2-rel-passwd-group-symlink nix-2-32-2-rel-passwd-symlink
root@debug-containerd-220:~# ctr container create localhost/nix-2-32-2:original nix-2-32-2-original
root@debug-containerd-220:~# echo $?
0
root@debug-containerd-220:~# ctr container create localhost/nix-2-32-2:rel-passwd-symlink nix-2-32-2-rel-passwd-symlink
root@debug-containerd-220:~# echo $?
0
root@debug-containerd-220:~# ctr container create localhost/nix-2-32-2:rel-passwd-group-symlink nix-2-32-2-rel-passwd-group-symlink
root@debug-containerd-220:~# echo $?
0
Describe the results you received and expected
Trying to create a container from the docker.io/nixos/nix:2.32.2 image with containerd 2.2.0, fails with ctr: mount callback failed on /tmp/containerd-mount3757137550: openat etc/passwd: path escapes from parent.
Previous versions of containerd, like 2.1.5 (and 1.7.28), are able to create a container from said image without errors.
Modifying the image so that /etc/passwd and /etc/group are relative instead of absolute symlinks, makes containerd 2.2.0 succeed with creating a container.
I expect containerd 2.2.0 to handle absolute symlinks for both files like in previous versions.
What version of containerd are you using?
root@debug-containerd-220:~# containerd --version
containerd containerd.io v2.2.0 1c4457e00facac03ce1d75f7b6777a7a851e5c41
Any other relevant information
containerd 2.2.0 is built with go 1.24.32 whereas containerd 1.7.28 is built with go 1.23.03.
go 1.24 introduces a check4 that may produce the "path escapes from parent" error5 that I've experienced.
Show configuration if it is related to CRI plugin.
root@debug-containerd-220:~# cat /etc/containerd/config.toml
# Copyright 2018-2022 Docker Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
disabled_plugins = ["cri"]
#root = "/var/lib/containerd"
#state = "/run/containerd"
#subreaper = true
#oom_score = 0
#[grpc]
# address = "/run/containerd/containerd.sock"
# uid = 0
# gid = 0
#[debug]
# address = "/run/containerd/debug.sock"
# uid = 0
# gid = 0
# level = "info"
Description
Container creation with containerd 2.2.0 fails with
path escapes from parenterrors for images that contain/etc/passwdand/etc/groupas absolute symlinks.Previous versions of containerd, like 2.1.5 (and 1.7.28), are able to create containers from such images without errors.
Using relative symlinks instead of absolute ones for both files does not produce the error.
Apparently containerd tries to open
/etc/passwdand/etc/groupfor user and group name resolution1 but misinterprets the absolute target path of those symlinks as being rooted in the host's file system rather than the container's file system.Steps to reproduce the issue
Try to run
ctr containers create docker.io/nixos/nix:2.32.2 nixos-2-32-2on Ubuntu 24.04 with containerd v2.2.0 installed.In that container image
/etc/passwdis a symlink into/nix/store/.To narrow down the issue I've built 3 container images:
/etc/passwdreplaced with a relative one/etc/passwdas well as/etc/groupreplaced with relative onesI observed that container creation succeeds only with the third image.
I've also tested all 3 images against containerd 2.1.5 which yielded no errors.
Describe the results you received and expected
Trying to create a container from the
docker.io/nixos/nix:2.32.2image with containerd 2.2.0, fails withctr: mount callback failed on /tmp/containerd-mount3757137550: openat etc/passwd: path escapes from parent.Previous versions of containerd, like 2.1.5 (and 1.7.28), are able to create a container from said image without errors.
Modifying the image so that
/etc/passwdand/etc/groupare relative instead of absolute symlinks, makes containerd 2.2.0 succeed with creating a container.I expect containerd 2.2.0 to handle absolute symlinks for both files like in previous versions.
What version of containerd are you using?
Any other relevant information
containerd 2.2.0 is built with go 1.24.32 whereas containerd 1.7.28 is built with go 1.23.03.
go 1.24 introduces a check4 that may produce the "path escapes from parent" error5 that I've experienced.
Show configuration if it is related to CRI plugin.
Footnotes
https://github.com/containerd/containerd/blob/v2.2.0/vendor/github.com/moby/sys/user/user.go#L274 ↩
https://github.com/containerd/containerd/blob/v2.2.0/go.mod#L3 ↩
https://github.com/containerd/containerd/blob/v2.1.5/go.mod#L3 ↩
https://github.com/golang/go/commit/43d90c6a14e7b3fd1b3b8085b8071a09231c4b62#diff-47957b402486ac2a9d4182ae5fa01371df1eb7abab86e1c543b5d1af3fc4deccR15 ↩
https://github.com/golang/go/commit/43d90c6a14e7b3fd1b3b8085b8071a09231c4b62#diff-9104369aad12ebcc262dc47f26321742fe4b10d31e30bc53832e869f4a7b3bceR401 ↩