Skip to content

Commit ee4179e

Browse files
pauloappbrk8s-infra-cherrypick-robot
authored andcommitted
fix(oci): apply absolute symlink resolution to /etc/group
This is a follow-up to PR #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 #12683 Signed-off-by: Paulo Oliveira <[email protected]>
1 parent 6675140 commit ee4179e

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

pkg/oci/spec_opts.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -963,7 +963,7 @@ func WithAppendAdditionalGroups(groups ...string) SpecOpts {
963963
defer ensureAdditionalGids(s)
964964

965965
var ugroups []user.Group
966-
f, groupErr := root.Open("etc/group")
966+
f, groupErr := openUserFile(root, "etc/group")
967967
if groupErr == nil {
968968
defer f.Close()
969969
ugroups, groupErr = user.ParseGroup(f)
@@ -1191,7 +1191,7 @@ func GIDFromFS(root fs.FS, filter func(user.Group) bool) (gid uint32, err error)
11911191
}
11921192

11931193
func getSupplementalGroupsFromFS(root fs.FS, filter func(user.Group) bool) ([]uint32, error) {
1194-
f, err := root.Open("etc/group")
1194+
f, err := openUserFile(root, "etc/group")
11951195
if err != nil {
11961196
return []uint32{}, err
11971197
}

pkg/oci/spec_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/containerd/containerd/v2/pkg/namespaces"
3030
"github.com/containerd/containerd/v2/pkg/testutil"
3131
"github.com/containerd/continuity/fs/fstest"
32+
"github.com/moby/sys/user"
3233
"github.com/opencontainers/runtime-spec/specs-go"
3334
)
3435

@@ -374,6 +375,49 @@ func TestOpenUserFile_AbsoluteSymlink(t *testing.T) {
374375
}
375376
}
376377

378+
func TestGroupLookup_AbsoluteSymlink(t *testing.T) {
379+
if runtime.GOOS == "windows" {
380+
t.Skip("absolute symlink handling is only supported on non-Windows platforms")
381+
}
382+
383+
expectedContent := []byte("dummygroup:x:1001:paulo\n")
384+
385+
root := t.TempDir()
386+
if err := fstest.Apply(
387+
fstest.CreateDir("/etc", 0o755),
388+
fstest.CreateDir("/nix/store/abcd", 0o755),
389+
fstest.CreateFile("/nix/store/abcd/group", expectedContent, 0o644),
390+
fstest.Symlink("/nix/store/abcd/group", "/etc/group"),
391+
).Apply(root); err != nil {
392+
t.Fatal(err)
393+
}
394+
395+
rootFS := os.DirFS(root)
396+
if _, ok := rootFS.(readLinker); !ok {
397+
rootFS = readLinkFS{root: root, fs: rootFS}
398+
}
399+
400+
gid, err := GIDFromFS(rootFS, func(g user.Group) bool {
401+
return g.Name == "dummygroup"
402+
})
403+
if err != nil {
404+
t.Fatalf("GIDFromFS failed on absolute symlink: %v", err)
405+
}
406+
if gid != 1001 {
407+
t.Errorf("expected GID 1001, got %d", gid)
408+
}
409+
410+
gids, err := getSupplementalGroupsFromFS(rootFS, func(g user.Group) bool {
411+
return g.Name == "dummygroup"
412+
})
413+
if err != nil {
414+
t.Fatalf("getSupplementalGroupsFromFS failed on absolute symlink: %v", err)
415+
}
416+
if len(gids) != 1 || gids[0] != 1001 {
417+
t.Errorf("expected supplemental GIDs [1001], got %v", gids)
418+
}
419+
}
420+
377421
// Helpers for testing ReadLink support
378422
type readLinkFS struct {
379423
root string

0 commit comments

Comments
 (0)