package oci import ( "context" "errors" "os" "strconv" "strings" "github.com/containerd/containerd/containers" containerdoci "github.com/containerd/containerd/oci" "github.com/containerd/continuity/fs" "github.com/opencontainers/runc/libcontainer/user" "github.com/opencontainers/runtime-spec/specs-go" ) func GetUser(ctx context.Context, root, username string) (uint32, uint32, []uint32, error) { // fast path from uid/gid if uid, gid, err := ParseUIDGID(username); err == nil { return uid, gid, nil, nil } passwdFile, err := openUserFile(root, "/etc/passwd") if err == nil { defer passwdFile.Close() } groupFile, err := openUserFile(root, "/etc/group") if err == nil { defer groupFile.Close() } execUser, err := user.GetExecUser(username, nil, passwdFile, groupFile) if err != nil { return 0, 0, nil, err } var sgids []uint32 for _, g := range execUser.Sgids { sgids = append(sgids, uint32(g)) } return uint32(execUser.Uid), uint32(execUser.Gid), sgids, nil } // ParseUIDGID takes the fast path to parse UID and GID if and only if they are both provided func ParseUIDGID(str string) (uid uint32, gid uint32, err error) { if str == "" { return 0, 0, nil } parts := strings.SplitN(str, ":", 2) if len(parts) == 1 { return 0, 0, errors.New("groups ID is not provided") } if uid, err = parseUID(parts[0]); err != nil { return 0, 0, err } if gid, err = parseUID(parts[1]); err != nil { return 0, 0, err } return } func openUserFile(root, p string) (*os.File, error) { p, err := fs.RootPath(root, p) if err != nil { return nil, err } return os.Open(p) } func parseUID(str string) (uint32, error) { if str == "root" { return 0, nil } uid, err := strconv.ParseUint(str, 10, 32) if err != nil { return 0, err } return uint32(uid), nil } // WithUIDGID allows the UID and GID for the Process to be set // FIXME: This is a temporeray fix for the missing supplementary GIDs from containerd // once the PR in containerd is merged we should remove this function. func WithUIDGID(uid, gid uint32, sgids []uint32) containerdoci.SpecOpts { return func(_ context.Context, _ containerdoci.Client, _ *containers.Container, s *containerdoci.Spec) error { setProcess(s) s.Process.User.UID = uid s.Process.User.GID = gid s.Process.User.AdditionalGids = sgids return nil } } // setProcess sets Process to empty if unset // FIXME: Same on this one. Need to be removed after containerd fix merged func setProcess(s *containerdoci.Spec) { if s.Process == nil { s.Process = &specs.Process{} } }