vendor buildah, image, storage, cni

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2019-03-28 10:30:09 +01:00
parent e7a2eecf5f
commit a5443a532b
79 changed files with 1875 additions and 1110 deletions

View File

@@ -0,0 +1,108 @@
package chrootuser
import (
"os/user"
"strconv"
"strings"
"github.com/pkg/errors"
)
var (
// ErrNoSuchUser indicates that the user provided by the caller does not
// exist in /etc/passws
ErrNoSuchUser = errors.New("user does not exist in /etc/passwd")
)
// GetUser will return the uid, gid of the user specified in the userspec
// it will use the /etc/passwd and /etc/group files inside of the rootdir
// to return this information.
// userspec format [user | user:group | uid | uid:gid | user:gid | uid:group ]
func GetUser(rootdir, userspec string) (uint32, uint32, error) {
var gid64 uint64
var gerr error = user.UnknownGroupError("error looking up group")
spec := strings.SplitN(userspec, ":", 2)
userspec = spec[0]
groupspec := ""
if userspec == "" {
return 0, 0, nil
}
if len(spec) > 1 {
groupspec = spec[1]
}
uid64, uerr := strconv.ParseUint(userspec, 10, 32)
if uerr == nil && groupspec == "" {
// We parsed the user name as a number, and there's no group
// component, so try to look up the primary GID of the user who
// has this UID.
var name string
name, gid64, gerr = lookupGroupForUIDInContainer(rootdir, uid64)
if gerr == nil {
userspec = name
} else {
// Leave userspec alone, but swallow the error and just
// use GID 0.
gid64 = 0
gerr = nil
}
}
if uerr != nil {
// The user ID couldn't be parsed as a number, so try to look
// up the user's UID and primary GID.
uid64, gid64, uerr = lookupUserInContainer(rootdir, userspec)
gerr = uerr
}
if groupspec != "" {
// We have a group name or number, so parse it.
gid64, gerr = strconv.ParseUint(groupspec, 10, 32)
if gerr != nil {
// The group couldn't be parsed as a number, so look up
// the group's GID.
gid64, gerr = lookupGroupInContainer(rootdir, groupspec)
}
}
if uerr == nil && gerr == nil {
return uint32(uid64), uint32(gid64), nil
}
err := errors.Wrapf(uerr, "error determining run uid")
if uerr == nil {
err = errors.Wrapf(gerr, "error determining run gid")
}
return 0, 0, err
}
// GetGroup returns the gid by looking it up in the /etc/group file
// groupspec format [ group | gid ]
func GetGroup(rootdir, groupspec string) (uint32, error) {
gid64, gerr := strconv.ParseUint(groupspec, 10, 32)
if gerr != nil {
// The group couldn't be parsed as a number, so look up
// the group's GID.
gid64, gerr = lookupGroupInContainer(rootdir, groupspec)
}
if gerr != nil {
return 0, errors.Wrapf(gerr, "error looking up group for gid %q", groupspec)
}
return uint32(gid64), nil
}
// GetAdditionalGroupsForUser returns a list of gids that userid is associated with
func GetAdditionalGroupsForUser(rootdir string, userid uint64) ([]uint32, error) {
gids, err := lookupAdditionalGroupsForUIDInContainer(rootdir, userid)
if err != nil {
return nil, errors.Wrapf(err, "error looking up supplemental groups for uid %d", userid)
}
return gids, nil
}
// LookupUIDInContainer returns username and gid associated with a UID in a container
// it will use the /etc/passwd files inside of the rootdir
// to return this information.
func LookupUIDInContainer(rootdir string, uid uint64) (user string, gid uint64, err error) {
return lookupUIDInContainer(rootdir, uid)
}

View File

@@ -0,0 +1,27 @@
// +build !linux
package chrootuser
import (
"github.com/pkg/errors"
)
func lookupUserInContainer(rootdir, username string) (uint64, uint64, error) {
return 0, 0, errors.New("user lookup not supported")
}
func lookupGroupInContainer(rootdir, groupname string) (uint64, error) {
return 0, errors.New("group lookup not supported")
}
func lookupGroupForUIDInContainer(rootdir string, userid uint64) (string, uint64, error) {
return "", 0, errors.New("primary group lookup by uid not supported")
}
func lookupAdditionalGroupsForUIDInContainer(rootdir string, userid uint64) (gid []uint32, err error) {
return nil, errors.New("supplemental groups list lookup by uid not supported")
}
func lookupUIDInContainer(rootdir string, uid uint64) (string, uint64, error) {
return "", 0, errors.New("UID lookup not supported")
}

View File

@@ -0,0 +1,293 @@
// +build linux
package chrootuser
import (
"bufio"
"flag"
"fmt"
"io"
"os"
"os/exec"
"os/user"
"strconv"
"strings"
"sync"
"github.com/containers/storage/pkg/reexec"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
const (
openChrootedCommand = "chrootuser-open"
)
func init() {
reexec.Register(openChrootedCommand, openChrootedFileMain)
}
func openChrootedFileMain() {
status := 0
flag.Parse()
if len(flag.Args()) < 1 {
os.Exit(1)
}
// Our first parameter is the directory to chroot into.
if err := unix.Chdir(flag.Arg(0)); err != nil {
fmt.Fprintf(os.Stderr, "chdir(): %v", err)
os.Exit(1)
}
if err := unix.Chroot(flag.Arg(0)); err != nil {
fmt.Fprintf(os.Stderr, "chroot(): %v", err)
os.Exit(1)
}
// Anything else is a file we want to dump out.
for _, filename := range flag.Args()[1:] {
f, err := os.Open(filename)
if err != nil {
fmt.Fprintf(os.Stderr, "open(%q): %v", filename, err)
status = 1
continue
}
_, err = io.Copy(os.Stdout, f)
if err != nil {
fmt.Fprintf(os.Stderr, "read(%q): %v", filename, err)
}
f.Close()
}
os.Exit(status)
}
func openChrootedFile(rootdir, filename string) (*exec.Cmd, io.ReadCloser, error) {
// The child process expects a chroot and one or more filenames that
// will be consulted relative to the chroot directory and concatenated
// to its stdout. Start it up.
cmd := reexec.Command(openChrootedCommand, rootdir, filename)
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, nil, err
}
err = cmd.Start()
if err != nil {
return nil, nil, err
}
// Hand back the child's stdout for reading, and the child to reap.
return cmd, stdout, nil
}
var (
lookupUser, lookupGroup sync.Mutex
)
type lookupPasswdEntry struct {
name string
uid uint64
gid uint64
}
type lookupGroupEntry struct {
name string
gid uint64
user string
}
func readWholeLine(rc *bufio.Reader) ([]byte, error) {
line, isPrefix, err := rc.ReadLine()
if err != nil {
return nil, err
}
for isPrefix {
// We didn't get a whole line. Keep reading chunks until we find an end of line, and discard them.
for isPrefix {
logrus.Debugf("discarding partial line %q", string(line))
_, isPrefix, err = rc.ReadLine()
if err != nil {
return nil, err
}
}
// That last read was the end of a line, so now we try to read the (beginning of?) the next line.
line, isPrefix, err = rc.ReadLine()
if err != nil {
return nil, err
}
}
return line, nil
}
func parseNextPasswd(rc *bufio.Reader) *lookupPasswdEntry {
line, err := readWholeLine(rc)
if err != nil {
return nil
}
fields := strings.Split(string(line), ":")
if len(fields) < 7 {
return nil
}
uid, err := strconv.ParseUint(fields[2], 10, 32)
if err != nil {
return nil
}
gid, err := strconv.ParseUint(fields[3], 10, 32)
if err != nil {
return nil
}
return &lookupPasswdEntry{
name: fields[0],
uid: uid,
gid: gid,
}
}
func parseNextGroup(rc *bufio.Reader) *lookupGroupEntry {
line, err := readWholeLine(rc)
if err != nil {
return nil
}
fields := strings.Split(string(line), ":")
if len(fields) < 4 {
return nil
}
gid, err := strconv.ParseUint(fields[2], 10, 32)
if err != nil {
return nil
}
return &lookupGroupEntry{
name: fields[0],
gid: gid,
user: fields[3],
}
}
func lookupUserInContainer(rootdir, username string) (uid uint64, gid uint64, err error) {
cmd, f, err := openChrootedFile(rootdir, "/etc/passwd")
if err != nil {
return 0, 0, err
}
defer func() {
_ = cmd.Wait()
}()
rc := bufio.NewReader(f)
defer f.Close()
lookupUser.Lock()
defer lookupUser.Unlock()
pwd := parseNextPasswd(rc)
for pwd != nil {
if pwd.name != username {
pwd = parseNextPasswd(rc)
continue
}
return pwd.uid, pwd.gid, nil
}
return 0, 0, user.UnknownUserError(fmt.Sprintf("error looking up user %q", username))
}
func lookupGroupForUIDInContainer(rootdir string, userid uint64) (username string, gid uint64, err error) {
cmd, f, err := openChrootedFile(rootdir, "/etc/passwd")
if err != nil {
return "", 0, err
}
defer func() {
_ = cmd.Wait()
}()
rc := bufio.NewReader(f)
defer f.Close()
lookupUser.Lock()
defer lookupUser.Unlock()
pwd := parseNextPasswd(rc)
for pwd != nil {
if pwd.uid != userid {
pwd = parseNextPasswd(rc)
continue
}
return pwd.name, pwd.gid, nil
}
return "", 0, ErrNoSuchUser
}
func lookupAdditionalGroupsForUIDInContainer(rootdir string, userid uint64) (gid []uint32, err error) {
// Get the username associated with userid
username, _, err := lookupGroupForUIDInContainer(rootdir, userid)
if err != nil {
return nil, err
}
cmd, f, err := openChrootedFile(rootdir, "/etc/group")
if err != nil {
return nil, err
}
defer func() {
_ = cmd.Wait()
}()
rc := bufio.NewReader(f)
defer f.Close()
lookupGroup.Lock()
defer lookupGroup.Unlock()
grp := parseNextGroup(rc)
for grp != nil {
if strings.Contains(grp.user, username) {
gid = append(gid, uint32(grp.gid))
}
grp = parseNextGroup(rc)
}
return gid, nil
}
func lookupGroupInContainer(rootdir, groupname string) (gid uint64, err error) {
cmd, f, err := openChrootedFile(rootdir, "/etc/group")
if err != nil {
return 0, err
}
defer func() {
_ = cmd.Wait()
}()
rc := bufio.NewReader(f)
defer f.Close()
lookupGroup.Lock()
defer lookupGroup.Unlock()
grp := parseNextGroup(rc)
for grp != nil {
if grp.name != groupname {
grp = parseNextGroup(rc)
continue
}
return grp.gid, nil
}
return 0, user.UnknownGroupError(fmt.Sprintf("error looking up group %q", groupname))
}
func lookupUIDInContainer(rootdir string, uid uint64) (string, uint64, error) {
cmd, f, err := openChrootedFile(rootdir, "/etc/passwd")
if err != nil {
return "", 0, err
}
defer func() {
_ = cmd.Wait()
}()
rc := bufio.NewReader(f)
defer f.Close()
lookupUser.Lock()
defer lookupUser.Unlock()
pwd := parseNextPasswd(rc)
for pwd != nil {
if pwd.uid != uid {
pwd = parseNextPasswd(rc)
continue
}
return pwd.name, pwd.gid, nil
}
return "", 0, user.UnknownUserError(fmt.Sprintf("error looking up uid %q", uid))
}