| // +build experimental |
| |
| package daemon |
| |
| import ( |
| "fmt" |
| "strconv" |
| "strings" |
| |
| "github.com/docker/docker/pkg/idtools" |
| flag "github.com/docker/docker/pkg/mflag" |
| "github.com/opencontainers/runc/libcontainer/user" |
| ) |
| |
| func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) { |
| cmd.StringVar(&config.RemappedRoot, []string{"-userns-remap"}, "", usageFn("User/Group setting for user namespaces")) |
| } |
| |
| const ( |
| defaultIDSpecifier string = "default" |
| defaultRemappedID string = "dockremap" |
| ) |
| |
| // Parse the remapped root (user namespace) option, which can be one of: |
| // username - valid username from /etc/passwd |
| // username:groupname - valid username; valid groupname from /etc/group |
| // uid - 32-bit unsigned int valid Linux UID value |
| // uid:gid - uid value; 32-bit unsigned int Linux GID value |
| // |
| // If no groupname is specified, and a username is specified, an attempt |
| // will be made to lookup a gid for that username as a groupname |
| // |
| // If names are used, they are verified to exist in passwd/group |
| func parseRemappedRoot(usergrp string) (string, string, error) { |
| |
| var ( |
| userID, groupID int |
| username, groupname string |
| ) |
| |
| idparts := strings.Split(usergrp, ":") |
| if len(idparts) > 2 { |
| return "", "", fmt.Errorf("Invalid user/group specification in --userns-remap: %q", usergrp) |
| } |
| |
| if uid, err := strconv.ParseInt(idparts[0], 10, 32); err == nil { |
| // must be a uid; take it as valid |
| userID = int(uid) |
| luser, err := user.LookupUid(userID) |
| if err != nil { |
| return "", "", fmt.Errorf("Uid %d has no entry in /etc/passwd: %v", userID, err) |
| } |
| username = luser.Name |
| if len(idparts) == 1 { |
| // if the uid was numeric and no gid was specified, take the uid as the gid |
| groupID = userID |
| lgrp, err := user.LookupGid(groupID) |
| if err != nil { |
| return "", "", fmt.Errorf("Gid %d has no entry in /etc/group: %v", groupID, err) |
| } |
| groupname = lgrp.Name |
| } |
| } else { |
| lookupName := idparts[0] |
| // special case: if the user specified "default", they want Docker to create or |
| // use (after creation) the "dockremap" user/group for root remapping |
| if lookupName == defaultIDSpecifier { |
| lookupName = defaultRemappedID |
| } |
| luser, err := user.LookupUser(lookupName) |
| if err != nil && idparts[0] != defaultIDSpecifier { |
| // error if the name requested isn't the special "dockremap" ID |
| return "", "", fmt.Errorf("Error during uid lookup for %q: %v", lookupName, err) |
| } else if err != nil { |
| // special case-- if the username == "default", then we have been asked |
| // to create a new entry pair in /etc/{passwd,group} for which the /etc/sub{uid,gid} |
| // ranges will be used for the user and group mappings in user namespaced containers |
| _, _, err := idtools.AddNamespaceRangesUser(defaultRemappedID) |
| if err == nil { |
| return defaultRemappedID, defaultRemappedID, nil |
| } |
| return "", "", fmt.Errorf("Error during %q user creation: %v", defaultRemappedID, err) |
| } |
| userID = luser.Uid |
| username = luser.Name |
| if len(idparts) == 1 { |
| // we only have a string username, and no group specified; look up gid from username as group |
| group, err := user.LookupGroup(lookupName) |
| if err != nil { |
| return "", "", fmt.Errorf("Error during gid lookup for %q: %v", lookupName, err) |
| } |
| groupID = group.Gid |
| groupname = group.Name |
| } |
| } |
| |
| if len(idparts) == 2 { |
| // groupname or gid is separately specified and must be resolved |
| // to a unsigned 32-bit gid |
| if gid, err := strconv.ParseInt(idparts[1], 10, 32); err == nil { |
| // must be a gid, take it as valid |
| groupID = int(gid) |
| lgrp, err := user.LookupGid(groupID) |
| if err != nil { |
| return "", "", fmt.Errorf("Gid %d has no entry in /etc/passwd: %v", groupID, err) |
| } |
| groupname = lgrp.Name |
| } else { |
| // not a number; attempt a lookup |
| group, err := user.LookupGroup(idparts[1]) |
| if err != nil { |
| return "", "", fmt.Errorf("Error during gid lookup for %q: %v", idparts[1], err) |
| } |
| groupID = group.Gid |
| groupname = idparts[1] |
| } |
| } |
| return username, groupname, nil |
| } |