Skip to content

[Go 1.24] v2.2.0 fails to create containers from images having /etc/{passwd,group} symlinked to an absolute path #12683

@brunosc-cah

Description

@brunosc-cah

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:

  1. one that matches the original
  2. one that has the absolute symlink at /etc/passwd replaced with a relative one
  3. 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"

Footnotes

  1. https://github.com/containerd/containerd/blob/v2.2.0/vendor/github.com/moby/sys/user/user.go#L274

  2. https://github.com/containerd/containerd/blob/v2.2.0/go.mod#L3

  3. https://github.com/containerd/containerd/blob/v2.1.5/go.mod#L3

  4. https://github.com/golang/go/commit/43d90c6a14e7b3fd1b3b8085b8071a09231c4b62#diff-47957b402486ac2a9d4182ae5fa01371df1eb7abab86e1c543b5d1af3fc4deccR15

  5. https://github.com/golang/go/commit/43d90c6a14e7b3fd1b3b8085b8071a09231c4b62#diff-9104369aad12ebcc262dc47f26321742fe4b10d31e30bc53832e869f4a7b3bceR401

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions