diff --git a/internal/oci/spec/spec.go b/internal/oci/spec/spec.go index db57d1c..091fa9b 100644 --- a/internal/oci/spec/spec.go +++ b/internal/oci/spec/spec.go @@ -377,7 +377,8 @@ type Groups struct { } // ParsePasswdFiles parses the passwd and group files at the supplied paths. If -// either path does not exist it returns empty Passwd data. +// the passwd file does not exist it returns empty Passwd data. If the group +// file does not exist it parses passwd data without supplementary groups. func ParsePasswdFiles(passwd, group string) (Passwd, error) { p, err := os.Open(passwd) //nolint:gosec // We intentionally take a variable here. if errors.Is(err, os.ErrNotExist) { @@ -390,7 +391,7 @@ func ParsePasswdFiles(passwd, group string) (Passwd, error) { g, err := os.Open(group) //nolint:gosec // We intentionally take a variable here. if errors.Is(err, os.ErrNotExist) { - return Passwd{}, nil + return ParsePasswd(p, strings.NewReader("")) } if err != nil { return Passwd{}, errors.Wrap(err, errOpenGroupFile) diff --git a/internal/oci/spec/spec_test.go b/internal/oci/spec/spec_test.go index c207a26..64c37d6 100644 --- a/internal/oci/spec/spec_test.go +++ b/internal/oci/spec/spec_test.go @@ -306,6 +306,12 @@ func TestWithHostNetwork(t *testing.T) { } func TestWithImageConfig(t *testing.T) { + tmp := t.TempDir() + passwd := filepath.Join(tmp, "passwd") + if err := os.WriteFile(passwd, []byte("negz:x:1000:100::/home/negz:/bin/sh\n"), 0600); err != nil { + t.Fatal(err) + } + type args struct { cfg *ociv1.ConfigFile passwd string @@ -383,6 +389,31 @@ func TestWithImageConfig(t *testing.T) { }, }, }, + "UserWithNoGroupFile": { + reason: "We should resolve image config users when the rootfs has passwd data but no group file.", + s: &runtime.Spec{}, + args: args{ + cfg: &ociv1.ConfigFile{ + Config: ociv1.Config{ + Entrypoint: []string{"/bin/sh"}, + User: "negz", + }, + }, + passwd: passwd, + group: filepath.Join(tmp, "group"), + }, + want: want{ + s: &runtime.Spec{ + Process: &runtime.Process{ + Args: []string{"/bin/sh"}, + User: runtime.User{ + UID: 1000, + GID: 100, + }, + }, + }, + }, + }, } for name, tc := range cases { @@ -578,13 +609,24 @@ users:x:100:primary,doesnotexist }, }, "NoGroupFile": { - reason: "We should not return an error if the group file doesn't exist.", + reason: "We should parse passwd data without supplementary groups if the group file doesn't exist.", args: args{ passwd: filepath.Join(tmp, "passwd"), group: filepath.Join(tmp, "nonexist"), }, want: want{ - p: Passwd{}, + p: Passwd{ + UID: map[Username]UID{ + "root": 0, + "negz": 1000, + "primary": 1001, + }, + Groups: map[UID]Groups{ + 0: {PrimaryGID: 0}, + 1000: {PrimaryGID: 100}, + 1001: {PrimaryGID: 100}, + }, + }, }, }, "Success": {