Add secondary group impersonation w/ !cgo support

This commit is contained in:
Thomas Stromberg 2020-03-28 22:01:20 -07:00
parent 3426a24929
commit a6504769e3
5 changed files with 148 additions and 35 deletions

View File

@ -0,0 +1,12 @@
FROM ubuntu:latest
RUN groupadd -g 20000 bar
RUN groupadd -g 10000 foo
RUN useradd -c "Foo user" -u 10000 -g 10000 -G bar -m foo
RUN id foo
USER foo
RUN id

View File

@ -21,7 +21,6 @@ import (
"os"
"os/exec"
"os/user"
"strconv"
"strings"
"syscall"
@ -80,9 +79,9 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
// If specified, run the command as a specific user
if userStr != "" {
cmd.SysProcAttr.Credential, err = impersonate(userStr)
cmd.SysProcAttr.Credential, err = util.SyscallCredentials(userStr)
if err != nil {
return errors.Wrap(err, "impersonate")
return errors.Wrap(err, "credentials")
}
}
@ -93,6 +92,7 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
cmd.Env = env
logrus.Infof("Running: %s", cmd.Args)
if err := cmd.Start(); err != nil {
return errors.Wrap(err, "starting command")
}
@ -113,38 +113,6 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui
return nil
}
func impersonate(userStr string) (*syscall.Credential, error) {
uid, gid, err := util.GetUIDAndGIDFromString(userStr, true)
if err != nil {
return nil, errors.Wrap(err, "get uid/gid")
}
u, err := util.Lookup(userStr)
if err != nil {
return nil, errors.Wrap(err, "lookup")
}
logrus.Infof("user: %+v", u)
// Handle the case of secondary groups
groups := []uint32{41}
gidStr, err := u.GroupIds()
logrus.Infof("groupstr: %s", gidStr)
for _, g := range gidStr {
i, err := strconv.ParseUint(g, 10, 32)
if err != nil {
return nil, errors.Wrap(err, "parseuint")
}
groups = append(groups, uint32(i))
}
return &syscall.Credential{
Uid: uid,
Gid: gid,
Groups: groups,
}, nil
}
// addDefaultHOME adds the default value for HOME if it isn't already set
func addDefaultHOME(u string, envs []string) ([]string, error) {
for _, env := range envs {

13
pkg/util/groupids_cgo.go Normal file
View File

@ -0,0 +1,13 @@
// +build linux darwin
// +build cgo
package util
import (
"os/user"
)
// groupIDs returns all of the group ID's a user is a member of
func groupIDs(u *user.User) ([]string, error) {
return u.GroupIds()
}

View File

@ -0,0 +1,76 @@
// +build linux darwin
// +build !cgo
package util
import (
"bufio"
"bytes"
"io"
"os"
"os/user"
"strconv"
"strings"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var groupFile = "/etc/group"
type group struct {
id string // group ID
name string // group name
members []string // secondary group ids
}
// groupIDs returns all of the group ID's a user is a member of
func groupIDs(u *user.User) ([]string, error) {
logrus.Infof("performing slow lookup of group ids for %s", u.Username)
f, err := os.Open(groupFile)
if err != nil {
return nil, errors.Wrap(err, "open")
}
defer f.Close()
gids := []string{u.Gid}
for _, g := range localGroups(f) {
for _, m := range g.members {
if m == u.Username {
gids = append(gids, g.id)
}
}
}
return gids, nil
}
// localGroups parses a reader in /etc/group form, returning parsed group data
// based on src/os/user/lookup_unix.go - but extended to include secondary groups
func localGroups(r io.Reader) []*group {
var groups []*group
bs := bufio.NewScanner(r)
for bs.Scan() {
line := bs.Bytes()
// There's no spec for /etc/passwd or /etc/group, but we try to follow
// the same rules as the glibc parser, which allows comments and blank
// space at the beginning of a line.
line = bytes.TrimSpace(line)
if len(line) == 0 || line[0] == '#' {
continue
}
// wheel:*:0:root
parts := strings.SplitN(string(line), ":", 4)
if _, err := strconv.Atoi(parts[2]); err != nil {
continue
}
groups = append(groups, &group{name: parts[0], id: parts[2], members: strings.Split(parts[3], ",")})
}
return groups
}

View File

@ -0,0 +1,44 @@
package util
import (
"strconv"
"syscall"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
func SyscallCredentials(userStr string) (*syscall.Credential, error) {
uid, gid, err := GetUIDAndGIDFromString(userStr, true)
if err != nil {
return nil, errors.Wrap(err, "get uid/gid")
}
u, err := Lookup(userStr)
if err != nil {
return nil, errors.Wrap(err, "lookup")
}
logrus.Infof("util.Lookup returned: %+v", u)
var groups []uint32
gidStr, err := groupIDs(u)
if err != nil {
return nil, errors.Wrap(err, "group ids for user")
}
for _, g := range gidStr {
i, err := strconv.ParseUint(g, 10, 32)
if err != nil {
return nil, errors.Wrap(err, "parseuint")
}
groups = append(groups, uint32(i))
}
return &syscall.Credential{
Uid: uid,
Gid: gid,
Groups: groups,
}, nil
}