Merge pull request #41377 from thaJeztah/refactor_idtools

pkg/idtools: refactor to avoid string-splitting
This commit is contained in:
Brian Goff 2020-08-20 11:54:01 -07:00 committed by GitHub
commit 3902057020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 47 deletions

View File

@ -9,7 +9,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings"
"sync" "sync"
"syscall" "syscall"
@ -107,14 +106,14 @@ func accessible(isOwner, isGroup bool, perms os.FileMode) bool {
// LookupUser uses traditional local system files lookup (from libcontainer/user) on a username, // LookupUser uses traditional local system files lookup (from libcontainer/user) on a username,
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs // followed by a call to `getent` for supporting host configured non-files passwd and group dbs
func LookupUser(username string) (user.User, error) { func LookupUser(name string) (user.User, error) {
// first try a local system files lookup using existing capabilities // first try a local system files lookup using existing capabilities
usr, err := user.LookupUser(username) usr, err := user.LookupUser(name)
if err == nil { if err == nil {
return usr, nil return usr, nil
} }
// local files lookup failed; attempt to call `getent` to query configured passwd dbs // local files lookup failed; attempt to call `getent` to query configured passwd dbs
usr, err = getentUser(fmt.Sprintf("%s %s", "passwd", username)) usr, err = getentUser(name)
if err != nil { if err != nil {
return user.User{}, err return user.User{}, err
} }
@ -130,11 +129,11 @@ func LookupUID(uid int) (user.User, error) {
return usr, nil return usr, nil
} }
// local files lookup failed; attempt to call `getent` to query configured passwd dbs // local files lookup failed; attempt to call `getent` to query configured passwd dbs
return getentUser(fmt.Sprintf("%s %d", "passwd", uid)) return getentUser(strconv.Itoa(uid))
} }
func getentUser(args string) (user.User, error) { func getentUser(name string) (user.User, error) {
reader, err := callGetent(args) reader, err := callGetent("passwd", name)
if err != nil { if err != nil {
return user.User{}, err return user.User{}, err
} }
@ -143,21 +142,21 @@ func getentUser(args string) (user.User, error) {
return user.User{}, err return user.User{}, err
} }
if len(users) == 0 { if len(users) == 0 {
return user.User{}, fmt.Errorf("getent failed to find passwd entry for %q", strings.Split(args, " ")[1]) return user.User{}, fmt.Errorf("getent failed to find passwd entry for %q", name)
} }
return users[0], nil return users[0], nil
} }
// LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name, // LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name,
// followed by a call to `getent` for supporting host configured non-files passwd and group dbs // followed by a call to `getent` for supporting host configured non-files passwd and group dbs
func LookupGroup(groupname string) (user.Group, error) { func LookupGroup(name string) (user.Group, error) {
// first try a local system files lookup using existing capabilities // first try a local system files lookup using existing capabilities
group, err := user.LookupGroup(groupname) group, err := user.LookupGroup(name)
if err == nil { if err == nil {
return group, nil return group, nil
} }
// local files lookup failed; attempt to call `getent` to query configured group dbs // local files lookup failed; attempt to call `getent` to query configured group dbs
return getentGroup(fmt.Sprintf("%s %s", "group", groupname)) return getentGroup(name)
} }
// LookupGID uses traditional local system files lookup (from libcontainer/user) on a group ID, // LookupGID uses traditional local system files lookup (from libcontainer/user) on a group ID,
@ -169,11 +168,11 @@ func LookupGID(gid int) (user.Group, error) {
return group, nil return group, nil
} }
// local files lookup failed; attempt to call `getent` to query configured group dbs // local files lookup failed; attempt to call `getent` to query configured group dbs
return getentGroup(fmt.Sprintf("%s %d", "group", gid)) return getentGroup(strconv.Itoa(gid))
} }
func getentGroup(args string) (user.Group, error) { func getentGroup(name string) (user.Group, error) {
reader, err := callGetent(args) reader, err := callGetent("group", name)
if err != nil { if err != nil {
return user.Group{}, err return user.Group{}, err
} }
@ -182,18 +181,18 @@ func getentGroup(args string) (user.Group, error) {
return user.Group{}, err return user.Group{}, err
} }
if len(groups) == 0 { if len(groups) == 0 {
return user.Group{}, fmt.Errorf("getent failed to find groups entry for %q", strings.Split(args, " ")[1]) return user.Group{}, fmt.Errorf("getent failed to find groups entry for %q", name)
} }
return groups[0], nil return groups[0], nil
} }
func callGetent(args string) (io.Reader, error) { func callGetent(database, key string) (io.Reader, error) {
entOnce.Do(func() { getentCmd, _ = resolveBinary("getent") }) entOnce.Do(func() { getentCmd, _ = resolveBinary("getent") })
// if no `getent` command on host, can't do anything else // if no `getent` command on host, can't do anything else
if getentCmd == "" { if getentCmd == "" {
return nil, fmt.Errorf("") return nil, fmt.Errorf("unable to find getent command")
} }
out, err := execCmd(getentCmd, args) out, err := execCmd(getentCmd, database, key)
if err != nil { if err != nil {
exitCode, errC := system.GetExitCode(err) exitCode, errC := system.GetExitCode(err)
if errC != nil { if errC != nil {
@ -203,8 +202,7 @@ func callGetent(args string) (io.Reader, error) {
case 1: case 1:
return nil, fmt.Errorf("getent reported invalid parameters/database unknown") return nil, fmt.Errorf("getent reported invalid parameters/database unknown")
case 2: case 2:
terms := strings.Split(args, " ") return nil, fmt.Errorf("getent unable to find entry %q in %s database", key, database)
return nil, fmt.Errorf("getent unable to find entry %q in %s database", terms[1], terms[0])
case 3: case 3:
return nil, fmt.Errorf("getent database doesn't support enumeration") return nil, fmt.Errorf("getent database doesn't support enumeration")
default: default:
@ -235,19 +233,19 @@ func lazyChown(p string, uid, gid int, stat *system.StatT) error {
// NewIdentityMapping takes a requested username and // NewIdentityMapping takes a requested username and
// using the data from /etc/sub{uid,gid} ranges, creates the // using the data from /etc/sub{uid,gid} ranges, creates the
// proper uid and gid remapping ranges for that user/group pair // proper uid and gid remapping ranges for that user/group pair
func NewIdentityMapping(username string) (*IdentityMapping, error) { func NewIdentityMapping(name string) (*IdentityMapping, error) {
usr, err := LookupUser(username) usr, err := LookupUser(name)
if err != nil { if err != nil {
return nil, fmt.Errorf("Could not get user for username %s: %v", username, err) return nil, fmt.Errorf("Could not get user for username %s: %v", name, err)
} }
uid := strconv.Itoa(usr.Uid) uid := strconv.Itoa(usr.Uid)
subuidRangesWithUserName, err := parseSubuid(username) subuidRangesWithUserName, err := parseSubuid(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
subgidRangesWithUserName, err := parseSubgid(username) subgidRangesWithUserName, err := parseSubgid(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -265,10 +263,10 @@ func NewIdentityMapping(username string) (*IdentityMapping, error) {
subgidRanges := append(subgidRangesWithUserName, subgidRangesWithUID...) subgidRanges := append(subgidRangesWithUserName, subgidRangesWithUID...)
if len(subuidRanges) == 0 { if len(subuidRanges) == 0 {
return nil, errors.Errorf("no subuid ranges found for user %q", username) return nil, errors.Errorf("no subuid ranges found for user %q", name)
} }
if len(subgidRanges) == 0 { if len(subgidRanges) == 0 {
return nil, errors.Errorf("no subgid ranges found for user %q", username) return nil, errors.Errorf("no subgid ranges found for user %q", name)
} }
return &IdentityMapping{ return &IdentityMapping{

View File

@ -17,18 +17,13 @@ import (
var ( var (
once sync.Once once sync.Once
userCommand string userCommand string
cmdTemplates = map[string]string{
"adduser": "--system --shell /bin/false --no-create-home --disabled-login --disabled-password --group %s",
"useradd": "-r -s /bin/false %s",
"usermod": "-%s %d-%d %s",
}
idOutRegexp = regexp.MustCompile(`uid=([0-9]+).*gid=([0-9]+)`) idOutRegexp = regexp.MustCompile(`uid=([0-9]+).*gid=([0-9]+)`)
)
const (
// default length for a UID/GID subordinate range // default length for a UID/GID subordinate range
defaultRangeLen = 65536 defaultRangeLen = 65536
defaultRangeStart = 100000 defaultRangeStart = 100000
userMod = "usermod"
) )
// AddNamespaceRangesUser takes a username and uses the standard system // AddNamespaceRangesUser takes a username and uses the standard system
@ -67,7 +62,7 @@ func AddNamespaceRangesUser(name string) (int, int, error) {
return uid, gid, nil return uid, gid, nil
} }
func addUser(userName string) error { func addUser(name string) error {
once.Do(func() { once.Do(func() {
// set up which commands are used for adding users/groups dependent on distro // set up which commands are used for adding users/groups dependent on distro
if _, err := resolveBinary("adduser"); err == nil { if _, err := resolveBinary("adduser"); err == nil {
@ -76,13 +71,18 @@ func addUser(userName string) error {
userCommand = "useradd" userCommand = "useradd"
} }
}) })
if userCommand == "" { var args []string
return fmt.Errorf("Cannot add user; no useradd/adduser binary found") switch userCommand {
case "adduser":
args = []string{"--system", "--shell", "/bin/false", "--no-create-home", "--disabled-login", "--disabled-password", "--group", name}
case "useradd":
args = []string{"-r", "-s", "/bin/false", name}
default:
return fmt.Errorf("cannot add user; no useradd/adduser binary found")
} }
args := fmt.Sprintf(cmdTemplates[userCommand], userName)
out, err := execCmd(userCommand, args) if out, err := execCmd(userCommand, args...); err != nil {
if err != nil { return fmt.Errorf("failed to add user with error: %v; output: %q", err, string(out))
return fmt.Errorf("Failed to add user with error: %v; output: %q", err, string(out))
} }
return nil return nil
} }
@ -101,7 +101,7 @@ func createSubordinateRanges(name string) error {
if err != nil { if err != nil {
return fmt.Errorf("Can't find available subuid range: %v", err) return fmt.Errorf("Can't find available subuid range: %v", err)
} }
out, err := execCmd(userMod, fmt.Sprintf(cmdTemplates[userMod], "v", startID, startID+defaultRangeLen-1, name)) out, err := execCmd("usermod", "-v", fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1), name)
if err != nil { if err != nil {
return fmt.Errorf("Unable to add subuid range to user: %q; output: %s, err: %v", name, out, err) return fmt.Errorf("Unable to add subuid range to user: %q; output: %s, err: %v", name, out, err)
} }
@ -117,7 +117,7 @@ func createSubordinateRanges(name string) error {
if err != nil { if err != nil {
return fmt.Errorf("Can't find available subgid range: %v", err) return fmt.Errorf("Can't find available subgid range: %v", err)
} }
out, err := execCmd(userMod, fmt.Sprintf(cmdTemplates[userMod], "w", startID, startID+defaultRangeLen-1, name)) out, err := execCmd("usermod", "-w", fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1), name)
if err != nil { if err != nil {
return fmt.Errorf("Unable to add subgid range to user: %q; output: %s, err: %v", name, out, err) return fmt.Errorf("Unable to add subgid range to user: %q; output: %s, err: %v", name, out, err)
} }

View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings"
) )
func resolveBinary(binname string) (string, error) { func resolveBinary(binname string) (string, error) {
@ -26,7 +25,7 @@ func resolveBinary(binname string) (string, error) {
return "", fmt.Errorf("Binary %q does not resolve to a binary of that name in $PATH (%q)", binname, resolvedPath) return "", fmt.Errorf("Binary %q does not resolve to a binary of that name in $PATH (%q)", binname, resolvedPath)
} }
func execCmd(cmd, args string) ([]byte, error) { func execCmd(cmd string, arg ...string) ([]byte, error) {
execCmd := exec.Command(cmd, strings.Split(args, " ")...) execCmd := exec.Command(cmd, arg...)
return execCmd.CombinedOutput() return execCmd.CombinedOutput()
} }