Skip to content

fix(oci): handle absolute symlinks in rootfs user lookup#12732

Merged
samuelkarp merged 2 commits intocontainerd:mainfrom
pauloappbr:fix/12683-go124-symlink-resolution
Jan 14, 2026
Merged

fix(oci): handle absolute symlinks in rootfs user lookup#12732
samuelkarp merged 2 commits intocontainerd:mainfrom
pauloappbr:fix/12683-go124-symlink-resolution

Conversation

@pauloappbr
Copy link
Copy Markdown
Contributor

Analysis

This PR addresses a regression/behavior change introduced with Go 1.24 builds regarding strict path validation in os.DirFS (via os.Root).

In scenarios involving container images based on NixOS (or other distributions using absolute symlinks for configuration files), standard files like /etc/passwd or /etc/group are often symlinks pointing to absolute paths (e.g., /nix/store/...).

In Go 1.24, calling root.Open("etc/passwd") fails with path escapes from parent if 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 openUserFile in pkg/oci/spec_opts.go to wrap the file opening logic for UserFromFS and GIDFromFS.

The logic is as follows:

  1. Attempt to open the file normally.
  2. If Open fails, check if the filesystem supports ReadLink (using a local interface readLinker to maintain compatibility with Go versions prior to 1.23).
  3. If the file is an absolute symlink, re-anchor the path relative to the rootfs (stripping the leading /) 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

  • Ran unit tests go test -v ./pkg/oci/... (All passed).
  • Verified locally with a reproduction test case that mimics the Go 1.24 behavior and the NixOS directory structure.

Fixes: #12683

Comment thread pkg/oci/spec_opts.go Outdated
Comment thread pkg/oci/spec_opts.go
Comment thread pkg/oci/spec_opts.go Outdated
Comment thread pkg/oci/spec_opts.go Outdated
@AkihiroSuda AkihiroSuda added cherry-pick/1.7.x Change to be cherry picked to release/1.7 branch cherry-pick/2.1.x Change to be cherry picked to release/2.1 branch cherry-pick/2.2.x Change to be cherry picked to release/2.2 branch labels Jan 7, 2026
Comment thread pkg/oci/spec_opts.go
Comment thread pkg/oci/spec_test.go Outdated
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]>
@pauloappbr pauloappbr force-pushed the fix/12683-go124-symlink-resolution branch from 236b4ed to 85b5418 Compare January 8, 2026 11:47
@pauloappbr pauloappbr requested a review from AkihiroSuda January 8, 2026 11:51
Comment thread pkg/oci/spec_test.go
}
}

func TestOpenUserFile_AbsoluteSymlink(t *testing.T) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)))
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@pauloappbr pauloappbr requested a review from fuweid January 13, 2026 23:01
@github-project-automation github-project-automation Bot moved this from Needs Triage to Review In Progress in Pull Request Review Jan 14, 2026
@h0nIg
Copy link
Copy Markdown

h0nIg commented Feb 7, 2026

@AkihiroSuda will this get patched in 2.2.x as well?

@h0nIg
Copy link
Copy Markdown

h0nIg commented Mar 11, 2026

/cherrypick release/2.2

@k8s-infra-cherrypick-robot
Copy link
Copy Markdown

@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.

Details

In response to this:

/cherrypick release/2.2

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.

@h0nIg
Copy link
Copy Markdown

h0nIg commented Mar 11, 2026

@samuelkarp can you please help?

@AkihiroSuda
Copy link
Copy Markdown
Member

/cherrypick release/2.2

@AkihiroSuda
Copy link
Copy Markdown
Member

/cherrypick release/2.1
/cherrypick release/1.7

@k8s-infra-cherrypick-robot
Copy link
Copy Markdown

@AkihiroSuda: new pull request created: #13015

Details

In response to this:

/cherrypick release/2.2

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.

@k8s-infra-cherrypick-robot
Copy link
Copy Markdown

@AkihiroSuda: #12732 failed to apply on top of branch "release/1.7":

Applying: fix(oci): handle absolute symlinks in rootfs user lookup
Using index info to reconstruct a base tree...
A	pkg/oci/spec_opts.go
A	pkg/oci/spec_test.go
Falling back to patching base and 3-way merge...
Auto-merging oci/spec_test.go
Auto-merging oci/spec_opts.go
CONFLICT (content): Merge conflict in oci/spec_opts.go
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0001 fix(oci): handle absolute symlinks in rootfs user lookup

Details

In response to this:

/cherrypick release/2.1
/cherrypick release/1.7

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.

@k8s-infra-cherrypick-robot
Copy link
Copy Markdown

@AkihiroSuda: #12732 failed to apply on top of branch "release/2.1":

Applying: fix(oci): handle absolute symlinks in rootfs user lookup
Using index info to reconstruct a base tree...
M	pkg/oci/spec_opts.go
Falling back to patching base and 3-way merge...
Auto-merging pkg/oci/spec_opts.go
CONFLICT (content): Merge conflict in pkg/oci/spec_opts.go
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0001 fix(oci): handle absolute symlinks in rootfs user lookup

Details

In response to this:

/cherrypick release/2.1
/cherrypick release/1.7

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 AkihiroSuda added cherry-picked/2.2.x PR commits are cherry-picked into release/2.2 branch and removed cherry-pick/1.7.x Change to be cherry picked to release/1.7 branch cherry-pick/2.1.x Change to be cherry picked to release/2.1 branch cherry-pick/2.2.x Change to be cherry picked to release/2.2 branch labels Mar 12, 2026
k8s-infra-cherrypick-robot pushed a commit to k8s-infra-cherrypick-robot/containerd that referenced this pull request Mar 12, 2026
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]>
agentydragon added a commit to agentydragon/ducktape that referenced this pull request Mar 31, 2026
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.
agentydragon added a commit to agentydragon/ducktape that referenced this pull request Mar 31, 2026
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
nxsre added a commit to xcph/k3s that referenced this pull request Apr 13, 2026
- 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/client Go client cherry-picked/2.2.x PR commits are cherry-picked into release/2.2 branch go Pull requests that update Go code size/L

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

7 participants