fix(oci): handle absolute symlinks in rootfs user lookup#12732
fix(oci): handle absolute symlinks in rootfs user lookup#12732samuelkarp merged 2 commits intocontainerd:mainfrom
Conversation
142503e to
98d0567
Compare
Go 1.24 introduced stricter checks for os.DirFS (via os.Root), which causes failures when /etc/passwd or /etc/group are absolute symlinks pointing outside the mount root (common in NixOS). This patch introduces a helper that detects absolute symlinks and resolves them relative to the rootfs before opening, preventing the 'path escapes from parent' error. Fixes #12683 Signed-off-by: Paulo Oliveira <[email protected]>
236b4ed to
85b5418
Compare
| } | ||
| } | ||
|
|
||
| func TestOpenUserFile_AbsoluteSymlink(t *testing.T) { |
There was a problem hiding this comment.
I think this test doesn't trigger readlink logic becase they are in the same folder.
Please consider using fstest to build test case. It's easy to read, like this.
func TestOpenUserFile_AbsoluteSymlink(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("absolute symlink handling is only supported on non-Windows platforms")
}
expectedContent := []byte("root:x:0:0:root:/root:/bin/bash" + t.Name())
root := t.TempDir()
if err := fstest.Apply(
fstest.CreateDir("/etc", 0o755),
fstest.CreateDir("/nix/store/abcd", 0o755),
fstest.CreateFile("/nix/store/abcd/passwd", expectedContent, 0o644),
// /etc/passwd -> /nix/store/abcd/passwd (absolute symlink)
fstest.Symlink("/nix/store/abcd/passwd", "/etc/passwd"),
).Apply(root); err != nil {
t.Fatal(err)
}
rootFS := os.DirFS(root)
if _, ok := rootFS.(readLinker); !ok {
t.Logf("os.DirFS does not implement ReadLink; wrapping to use ReadLink")
rootFS = readLinkFS{root: root, fs: rootFS}
}
f, err := openUserFile(rootFS, "etc/passwd")
if err != nil {
t.Fatalf("openUserFile failed on absolute symlink: %v", err)
}
defer f.Close()
content, err := io.ReadAll(f)
if err != nil {
t.Fatal(err)
}
if string(content) != string(expectedContent) {
t.Errorf("expected content %q, got %q", string(expectedContent), string(content))
}
}
type readLinkFS struct {
root string
fs fs.FS
}
func (r readLinkFS) Open(name string) (fs.File, error) {
return r.fs.Open(name)
}
func (r readLinkFS) ReadLink(name string) (string, error) {
return os.Readlink(filepath.Join(r.root, filepath.FromSlash(name)))
}There was a problem hiding this comment.
Thanks for the suggestion! I've updated the test case to use fstest and the readLinkFS wrapper as proposed. It looks much cleaner and robust.
Signed-off-by: Paulo Oliveira <[email protected]>
lookup for /etc/passwd patch containerd/containerd#12732
lookup for /etc/passwd patch containerd/containerd#12732
|
@AkihiroSuda will this get patched in 2.2.x as well? |
lookup for /etc/passwd patch containerd/containerd#12732
|
/cherrypick release/2.2 |
|
@h0nIg: only containerd org members may request cherry picks. If you are already part of the org, make sure to change your membership to public. Otherwise you can still do the cherry-pick manually. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
|
@samuelkarp can you please help? |
|
/cherrypick release/2.2 |
|
/cherrypick release/2.1 |
|
@AkihiroSuda: new pull request created: #13015 DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
|
@AkihiroSuda: #12732 failed to apply on top of branch "release/1.7": DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
|
@AkihiroSuda: #12732 failed to apply on top of branch "release/2.1": DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
This is a follow-up to PR containerd#12732. As noted by @TheColorman, while the previous PR successfully resolved absolute symlinks pointing outside the mount root for /etc/passwd during user lookups, the same logic was missing for group lookups. This caused `openat etc/group: path escapes from parent` errors when /etc/group was also an absolute symlink (e.g., in NixOS environments). This patch updates GIDFromFS, getSupplementalGroupsFromFS, and WithAppendAdditionalGroups to use the openUserFile helper, ensuring absolute symlinks are correctly re-anchored across all OCI user/group resolution paths. Includes unit test for validation. Fixes containerd#12683 Signed-off-by: Paulo Oliveira <[email protected]>
Cherry-pick of containerd/containerd#12732 to release/2.2 fixes Go 1.24 regression in NixOS-based container images. Needed for attic pod on wyrm2.
containerd overlay on wyrm2 fixes the absolute symlink handling (containerd/containerd#12732), unblocking NixOS-image workloads on Proxmox nodes. - Pin attic pod + DB to topology.kubernetes.io/region: proxmox - Switch cache PVC from hetzner-longhorn to local-path - Update CNPG compliance: attic-db is now Proxmox-single
- replace github.com/containerd/containerd/v2 with ./deps/containerd - add patches/containerd-pr-12732.patch and scripts/setup-patched-containerd.sh - ignore deps/containerd (populate via script before build) Upstream: containerd/containerd#12732
Analysis
This PR addresses a regression/behavior change introduced with Go 1.24 builds regarding strict path validation in
os.DirFS(viaos.Root).In scenarios involving container images based on NixOS (or other distributions using absolute symlinks for configuration files), standard files like
/etc/passwdor/etc/groupare often symlinks pointing to absolute paths (e.g.,/nix/store/...).In Go 1.24, calling
root.Open("etc/passwd")fails withpath escapes from parentif the symlink target is absolute, even if that target resolves to a valid path within the container's root filesystem context. This breaks container creation for these images.Solution
I introduced a helper function
openUserFileinpkg/oci/spec_opts.goto wrap the file opening logic forUserFromFSandGIDFromFS.The logic is as follows:
Openfails, check if the filesystem supportsReadLink(using a local interfacereadLinkerto maintain compatibility with Go versions prior to 1.23)./) and attempt to open it again.This approach ensures compatibility with NixOS-style images while respecting the safety constraints of the standard library where possible.
Testing
go test -v ./pkg/oci/...(All passed).Fixes: #12683