Merge pull request #3260 from TomSweeneyRedHat/dev/tsweeney/buildah1.8.3

Vendor Buildah v1.8.3
This commit is contained in:
OpenShift Merge Robot
2019-06-04 19:52:00 +02:00
committed by GitHub
14 changed files with 238 additions and 177 deletions

View File

@ -211,7 +211,7 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin
} }
func getUser(mountPoint string, userspec string) (specs.User, error) { func getUser(mountPoint string, userspec string) (specs.User, error) {
uid, gid, err := chrootuser.GetUser(mountPoint, userspec) uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec)
u := specs.User{ u := specs.User{
UID: uid, UID: uid,
GID: gid, GID: gid,

View File

@ -93,7 +93,7 @@ k8s.io/api kubernetes-1.10.13-beta.0 https://github.com/kubernetes/api
k8s.io/apimachinery kubernetes-1.10.13-beta.0 https://github.com/kubernetes/apimachinery k8s.io/apimachinery kubernetes-1.10.13-beta.0 https://github.com/kubernetes/apimachinery
k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go
github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7 github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7
github.com/containers/buildah bcc5e51a948c1847fab1c932f1a343696a96cafd github.com/containers/buildah v1.8.3
github.com/varlink/go 0f1d566d194b9d6d48e0d47c5e4d822628919066 github.com/varlink/go 0f1d566d194b9d6d48e0d47c5e4d822628919066
# TODO: Gotty has not been updated since 2012. Can we find replacement? # TODO: Gotty has not been updated since 2012. Can we find replacement?
github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512 github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512

View File

@ -14,6 +14,7 @@ import (
"github.com/containers/buildah/pkg/chrootuser" "github.com/containers/buildah/pkg/chrootuser"
"github.com/containers/buildah/util" "github.com/containers/buildah/util"
"github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/fileutils"
"github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/system"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
@ -89,7 +90,10 @@ func addURL(destination, srcurl string, owner idtools.IDPair, hasher io.Writer)
// filesystem, optionally extracting contents of local files that look like // filesystem, optionally extracting contents of local files that look like
// non-empty archives. // non-empty archives.
func (b *Builder) Add(destination string, extract bool, options AddAndCopyOptions, source ...string) error { func (b *Builder) Add(destination string, extract bool, options AddAndCopyOptions, source ...string) error {
excludes := dockerIgnoreHelper(options.Excludes, options.ContextDir) excludes, err := dockerIgnoreMatcher(options.Excludes, options.ContextDir)
if err != nil {
return err
}
mountPoint, err := b.Mount(b.MountLabel) mountPoint, err := b.Mount(b.MountLabel)
if err != nil { if err != nil {
return err return err
@ -100,7 +104,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
} }
}() }()
// Find out which user (and group) the destination should belong to. // Find out which user (and group) the destination should belong to.
user, err := b.user(mountPoint, options.Chown) user, _, err := b.user(mountPoint, options.Chown)
if err != nil { if err != nil {
return err return err
} }
@ -153,12 +157,12 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
} }
// user returns the user (and group) information which the destination should belong to. // user returns the user (and group) information which the destination should belong to.
func (b *Builder) user(mountPoint string, userspec string) (specs.User, error) { func (b *Builder) user(mountPoint string, userspec string) (specs.User, string, error) {
if userspec == "" { if userspec == "" {
userspec = b.User() userspec = b.User()
} }
uid, gid, err := chrootuser.GetUser(mountPoint, userspec) uid, gid, homeDir, err := chrootuser.GetUser(mountPoint, userspec)
u := specs.User{ u := specs.User{
UID: uid, UID: uid,
GID: gid, GID: gid,
@ -175,45 +179,48 @@ func (b *Builder) user(mountPoint string, userspec string) (specs.User, error) {
} }
} }
return u, err return u, homeDir, err
} }
// dockerIgnore struct keep info from .dockerignore // dockerIgnoreMatcher returns a matcher based on the contents of the .dockerignore file under contextDir
type dockerIgnore struct { func dockerIgnoreMatcher(lines []string, contextDir string) (*fileutils.PatternMatcher, error) {
ExcludePath string // if there's no context dir, there's no .dockerignore file to consult
IsExcluded bool if contextDir == "" {
} return nil, nil
}
// dockerIgnoreHelper returns the lines from .dockerignore file without the comments patterns := []string{".dockerignore"}
// and reverses the order for _, ignoreSpec := range lines {
func dockerIgnoreHelper(lines []string, contextDir string) []dockerIgnore { ignoreSpec = strings.TrimSpace(ignoreSpec)
var excludes []dockerIgnore // ignore comments passed back from .dockerignore
// the last match of a file in the .dockerignmatches determines whether it is included or excluded if ignoreSpec == "" || ignoreSpec[0] == '#' {
// reverse the order
for i := len(lines) - 1; i >= 0; i-- {
exclude := lines[i]
// ignore the comment in .dockerignore
if strings.HasPrefix(exclude, "#") || len(exclude) == 0 {
continue continue
} }
excludeFlag := true // if the spec starts with '!' it means the pattern
if strings.HasPrefix(exclude, "!") { // should be included. make a note so that we can move
exclude = strings.TrimPrefix(exclude, "!") // it to the front of the updated pattern
excludeFlag = false includeFlag := ""
if strings.HasPrefix(ignoreSpec, "!") {
includeFlag = "!"
ignoreSpec = ignoreSpec[1:]
} }
excludes = append(excludes, dockerIgnore{ExcludePath: filepath.Join(contextDir, exclude), IsExcluded: excludeFlag}) if ignoreSpec == "" {
continue
}
patterns = append(patterns, includeFlag+filepath.Join(contextDir, ignoreSpec))
} }
if len(excludes) != 0 { // if there are no patterns, save time by not constructing the object
excludes = append(excludes, dockerIgnore{ExcludePath: filepath.Join(contextDir, ".dockerignore"), IsExcluded: true}) if len(patterns) == 0 {
return nil, nil
} }
return excludes // return a matcher object
matcher, err := fileutils.NewPatternMatcher(patterns)
if err != nil {
return nil, errors.Wrapf(err, "error creating file matcher using patterns %v", patterns)
}
return matcher, nil
} }
func addHelper(excludes []dockerIgnore, extract bool, dest string, destfi os.FileInfo, hostOwner idtools.IDPair, options AddAndCopyOptions, copyFileWithTar, copyWithTar, untarPath func(src, dest string) error, source ...string) error { func addHelper(excludes *fileutils.PatternMatcher, extract bool, dest string, destfi os.FileInfo, hostOwner idtools.IDPair, options AddAndCopyOptions, copyFileWithTar, copyWithTar, untarPath func(src, dest string) error, source ...string) error {
dirsInDockerignore, err := getDirsInDockerignore(options.ContextDir, excludes)
if err != nil {
return errors.Wrapf(err, "error checking directories in .dockerignore")
}
for _, src := range source { for _, src := range source {
if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") { if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
// We assume that source is a file, and we're copying // We assume that source is a file, and we're copying
@ -242,7 +249,7 @@ func addHelper(excludes []dockerIgnore, extract bool, dest string, destfi os.Fil
if len(glob) == 0 { if len(glob) == 0 {
return errors.Wrapf(syscall.ENOENT, "no files found matching %q", src) return errors.Wrapf(syscall.ENOENT, "no files found matching %q", src)
} }
outer:
for _, gsrc := range glob { for _, gsrc := range glob {
esrc, err := filepath.EvalSymlinks(gsrc) esrc, err := filepath.EvalSymlinks(gsrc)
if err != nil { if err != nil {
@ -261,7 +268,7 @@ func addHelper(excludes []dockerIgnore, extract bool, dest string, destfi os.Fil
return errors.Wrapf(err, "error creating directory %q", dest) return errors.Wrapf(err, "error creating directory %q", dest)
} }
logrus.Debugf("copying %q to %q", esrc+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*") logrus.Debugf("copying %q to %q", esrc+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*")
if len(excludes) == 0 { if excludes == nil {
if err = copyWithTar(esrc, dest); err != nil { if err = copyWithTar(esrc, dest); err != nil {
return errors.Wrapf(err, "error copying %q to %q", esrc, dest) return errors.Wrapf(err, "error copying %q to %q", esrc, dest)
} }
@ -271,23 +278,12 @@ func addHelper(excludes []dockerIgnore, extract bool, dest string, destfi os.Fil
if err != nil { if err != nil {
return err return err
} }
for _, exclude := range excludes { skip, err := excludes.Matches(path)
match, err := filepath.Match(filepath.Clean(exclude.ExcludePath), filepath.Clean(path)) if err != nil {
if err != nil { return errors.Wrapf(err, "error checking if %s is an excluded path", path)
return err }
} if skip {
prefix, exist := dirsInDockerignore[exclude.ExcludePath] return nil
hasPrefix := false
if exist {
hasPrefix = filepath.HasPrefix(path, prefix)
}
if !(match || hasPrefix) {
continue
}
if (hasPrefix && exclude.IsExcluded) || (match && exclude.IsExcluded) {
return nil
}
break
} }
// combine the filename with the dest directory // combine the filename with the dest directory
fpath, err := filepath.Rel(esrc, path) fpath, err := filepath.Rel(esrc, path)
@ -297,8 +293,8 @@ func addHelper(excludes []dockerIgnore, extract bool, dest string, destfi os.Fil
mtime := info.ModTime() mtime := info.ModTime()
atime := mtime atime := mtime
times := []syscall.Timespec{ times := []syscall.Timespec{
{Sec: atime.Unix(), Nsec: atime.UnixNano() % 1000000000}, syscall.NsecToTimespec(atime.Unix()),
{Sec: mtime.Unix(), Nsec: mtime.UnixNano() % 1000000000}, syscall.NsecToTimespec(mtime.Unix()),
} }
if info.IsDir() { if info.IsDir() {
return addHelperDirectory(esrc, path, filepath.Join(dest, fpath), info, hostOwner, times) return addHelperDirectory(esrc, path, filepath.Join(dest, fpath), info, hostOwner, times)
@ -320,20 +316,6 @@ func addHelper(excludes []dockerIgnore, extract bool, dest string, destfi os.Fil
continue continue
} }
for _, exclude := range excludes {
match, err := filepath.Match(filepath.Clean(exclude.ExcludePath), esrc)
if err != nil {
return err
}
if !match {
continue
}
if exclude.IsExcluded {
continue outer
}
break
}
if !extract || !archive.IsArchivePath(esrc) { if !extract || !archive.IsArchivePath(esrc) {
// This source is a file, and either it's not an // This source is a file, and either it's not an
// archive, or we don't care whether or not it's an // archive, or we don't care whether or not it's an
@ -349,6 +331,7 @@ func addHelper(excludes []dockerIgnore, extract bool, dest string, destfi os.Fil
} }
continue continue
} }
// We're extracting an archive into the destination directory. // We're extracting an archive into the destination directory.
logrus.Debugf("extracting contents of %q into %q", esrc, dest) logrus.Debugf("extracting contents of %q into %q", esrc, dest)
if err = untarPath(esrc, dest); err != nil { if err = untarPath(esrc, dest); err != nil {
@ -381,7 +364,15 @@ func addHelperSymlink(src, dest string, info os.FileInfo, hostOwner idtools.IDPa
return errors.Wrapf(err, "error reading contents of symbolic link at %q", src) return errors.Wrapf(err, "error reading contents of symbolic link at %q", src)
} }
if err = os.Symlink(linkContents, dest); err != nil { if err = os.Symlink(linkContents, dest); err != nil {
return errors.Wrapf(err, "error creating symbolic link to %q at %q", linkContents, dest) if !os.IsExist(err) {
return errors.Wrapf(err, "error creating symbolic link to %q at %q", linkContents, dest)
}
if err = os.RemoveAll(dest); err != nil {
return errors.Wrapf(err, "error clearing symbolic link target %q", dest)
}
if err = os.Symlink(linkContents, dest); err != nil {
return errors.Wrapf(err, "error creating symbolic link to %q at %q (second try)", linkContents, dest)
}
} }
if err = idtools.SafeLchown(dest, hostOwner.UID, hostOwner.GID); err != nil { if err = idtools.SafeLchown(dest, hostOwner.UID, hostOwner.GID); err != nil {
return errors.Wrapf(err, "error setting owner of symbolic link %q to %d:%d", dest, hostOwner.UID, hostOwner.GID) return errors.Wrapf(err, "error setting owner of symbolic link %q to %d:%d", dest, hostOwner.UID, hostOwner.GID)
@ -392,35 +383,3 @@ func addHelperSymlink(src, dest string, info os.FileInfo, hostOwner idtools.IDPa
logrus.Debugf("Symlink(%s, %s)", linkContents, dest) logrus.Debugf("Symlink(%s, %s)", linkContents, dest)
return nil return nil
} }
func getDirsInDockerignore(srcAbsPath string, excludes []dockerIgnore) (map[string]string, error) {
visitedDir := make(map[string]string)
if len(excludes) == 0 {
return visitedDir, nil
}
err := filepath.Walk(srcAbsPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
for _, exclude := range excludes {
match, err := filepath.Match(filepath.Clean(exclude.ExcludePath), filepath.Clean(path))
if err != nil {
return err
}
if !match {
continue
}
if _, exist := visitedDir[exclude.ExcludePath]; exist {
continue
}
visitedDir[exclude.ExcludePath] = path
}
}
return nil
})
if err != nil {
return visitedDir, err
}
return visitedDir, nil
}

View File

@ -26,7 +26,7 @@ const (
Package = "buildah" Package = "buildah"
// Version for the Package. Bump version in contrib/rpm/buildah.spec // Version for the Package. Bump version in contrib/rpm/buildah.spec
// too. // too.
Version = "1.9.0-dev" Version = "1.8.3"
// The value we use to identify what type of information, currently a // The value we use to identify what type of information, currently a
// serialized Builder structure, we are using as per-container state. // serialized Builder structure, we are using as per-container state.
// This should only be changed when we make incompatible changes to // This should only be changed when we make incompatible changes to

View File

@ -84,9 +84,18 @@ type runUsingChrootExecSubprocOptions struct {
// RunUsingChroot runs a chrooted process, using some of the settings from the // RunUsingChroot runs a chrooted process, using some of the settings from the
// passed-in spec, and using the specified bundlePath to hold temporary files, // passed-in spec, and using the specified bundlePath to hold temporary files,
// directories, and mountpoints. // directories, and mountpoints.
func RunUsingChroot(spec *specs.Spec, bundlePath string, stdin io.Reader, stdout, stderr io.Writer) (err error) { func RunUsingChroot(spec *specs.Spec, bundlePath, homeDir string, stdin io.Reader, stdout, stderr io.Writer) (err error) {
var confwg sync.WaitGroup var confwg sync.WaitGroup
var homeFound bool
for _, env := range spec.Process.Env {
if strings.HasPrefix(env, "HOME=") {
homeFound = true
break
}
}
if !homeFound {
spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("HOME=%s", homeDir))
}
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()

View File

@ -3,7 +3,6 @@ package buildah
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"os"
"runtime" "runtime"
"strings" "strings"
"time" "time"
@ -269,21 +268,11 @@ func (b *Builder) Env() []string {
// built using an image built from this container. // built using an image built from this container.
func (b *Builder) SetEnv(k string, v string) { func (b *Builder) SetEnv(k string, v string) {
reset := func(s *[]string) { reset := func(s *[]string) {
getenv := func(name string) string {
for i := range *s {
val := strings.SplitN((*s)[i], "=", 2)
if len(val) == 2 && val[0] == name {
return val[1]
}
}
return name
}
n := []string{} n := []string{}
for i := range *s { for i := range *s {
if !strings.HasPrefix((*s)[i], k+"=") { if !strings.HasPrefix((*s)[i], k+"=") {
n = append(n, (*s)[i]) n = append(n, (*s)[i])
} }
v = os.Expand(v, getenv)
} }
n = append(n, k+"="+v) n = append(n, k+"="+v)
*s = n *s = n

View File

@ -487,6 +487,7 @@ func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) err
// Check the file and see if part of it is a symlink. // Check the file and see if part of it is a symlink.
// Convert it to the target if so. To be ultrasafe // Convert it to the target if so. To be ultrasafe
// do the same for the mountpoint. // do the same for the mountpoint.
hadFinalPathSeparator := len(copy.Dest) > 0 && copy.Dest[len(copy.Dest)-1] == os.PathSeparator
secureMountPoint, err := securejoin.SecureJoin("", s.mountPoint) secureMountPoint, err := securejoin.SecureJoin("", s.mountPoint)
finalPath, err := securejoin.SecureJoin(secureMountPoint, copy.Dest) finalPath, err := securejoin.SecureJoin(secureMountPoint, copy.Dest)
if err != nil { if err != nil {
@ -496,6 +497,11 @@ func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) err
return errors.Wrapf(err, "error resolving copy destination %s", copy.Dest) return errors.Wrapf(err, "error resolving copy destination %s", copy.Dest)
} }
copy.Dest = strings.TrimPrefix(finalPath, secureMountPoint) copy.Dest = strings.TrimPrefix(finalPath, secureMountPoint)
if len(copy.Dest) == 0 || copy.Dest[len(copy.Dest)-1] != os.PathSeparator {
if hadFinalPathSeparator {
copy.Dest += string(os.PathSeparator)
}
}
if copy.Download { if copy.Download {
logrus.Debugf("ADD %#v, %#v", excludes, copy) logrus.Debugf("ADD %#v, %#v", excludes, copy)
@ -507,29 +513,32 @@ func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) err
} }
sources := []string{} sources := []string{}
for _, src := range copy.Src { for _, src := range copy.Src {
contextDir := s.executor.contextDir
copyExcludes := excludes
if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") { if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {
sources = append(sources, src) sources = append(sources, src)
} else if len(copy.From) > 0 { } else if len(copy.From) > 0 {
if other, ok := s.executor.stages[copy.From]; ok && other.index < s.index { if other, ok := s.executor.stages[copy.From]; ok && other.index < s.index {
sources = append(sources, filepath.Join(other.mountPoint, src)) sources = append(sources, filepath.Join(other.mountPoint, src))
contextDir = other.mountPoint
} else if builder, ok := s.executor.containerMap[copy.From]; ok { } else if builder, ok := s.executor.containerMap[copy.From]; ok {
sources = append(sources, filepath.Join(builder.MountPoint, src)) sources = append(sources, filepath.Join(builder.MountPoint, src))
contextDir = builder.MountPoint
} else { } else {
return errors.Errorf("the stage %q has not been built", copy.From) return errors.Errorf("the stage %q has not been built", copy.From)
} }
} else { } else {
sources = append(sources, filepath.Join(s.executor.contextDir, src)) sources = append(sources, filepath.Join(s.executor.contextDir, src))
copyExcludes = append(s.executor.excludes, excludes...)
}
options := buildah.AddAndCopyOptions{
Chown: copy.Chown,
ContextDir: contextDir,
Excludes: copyExcludes,
}
if err := s.builder.Add(copy.Dest, copy.Download, options, sources...); err != nil {
return err
} }
}
options := buildah.AddAndCopyOptions{
Chown: copy.Chown,
ContextDir: s.executor.contextDir,
Excludes: s.executor.excludes,
}
if err := s.builder.Add(copy.Dest, copy.Download, options, sources...); err != nil {
return err
} }
} }
return nil return nil
@ -590,7 +599,11 @@ func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error {
args := run.Args args := run.Args
if run.Shell { if run.Shell {
args = append([]string{"/bin/sh", "-c"}, args...) if len(config.Shell) > 0 && s.builder.Format == buildah.Dockerv2ImageManifest {
args = append(config.Shell, args...)
} else {
args = append([]string{"/bin/sh", "-c"}, args...)
}
} }
if err := s.volumeCacheSave(); err != nil { if err := s.volumeCacheSave(); err != nil {
return err return err

View File

@ -17,7 +17,7 @@ import (
) )
func cloneToDirectory(url, dir string) error { func cloneToDirectory(url, dir string) error {
if !strings.HasPrefix(url, "git://") { if !strings.HasPrefix(url, "git://") && !strings.HasSuffix(url, ".git") {
url = "git://" + url url = "git://" + url
} }
logrus.Debugf("cloning %q to %q", url, dir) logrus.Debugf("cloning %q to %q", url, dir)
@ -72,7 +72,7 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err
if err != nil { if err != nil {
return "", "", errors.Wrapf(err, "error creating temporary directory for %q", url) return "", "", errors.Wrapf(err, "error creating temporary directory for %q", url)
} }
if strings.HasPrefix(url, "git://") { if strings.HasPrefix(url, "git://") || strings.HasSuffix(url, ".git") {
err = cloneToDirectory(url, name) err = cloneToDirectory(url, name)
if err != nil { if err != nil {
if err2 := os.Remove(name); err2 != nil { if err2 := os.Remove(name); err2 != nil {

View File

@ -18,7 +18,7 @@ var (
// it will use the /etc/passwd and /etc/group files inside of the rootdir // it will use the /etc/passwd and /etc/group files inside of the rootdir
// to return this information. // to return this information.
// userspec format [user | user:group | uid | uid:gid | user:gid | uid:group ] // userspec format [user | user:group | uid | uid:gid | user:gid | uid:group ]
func GetUser(rootdir, userspec string) (uint32, uint32, error) { func GetUser(rootdir, userspec string) (uint32, uint32, string, error) {
var gid64 uint64 var gid64 uint64
var gerr error = user.UnknownGroupError("error looking up group") var gerr error = user.UnknownGroupError("error looking up group")
@ -26,7 +26,7 @@ func GetUser(rootdir, userspec string) (uint32, uint32, error) {
userspec = spec[0] userspec = spec[0]
groupspec := "" groupspec := ""
if userspec == "" { if userspec == "" {
return 0, 0, nil return 0, 0, "/", nil
} }
if len(spec) > 1 { if len(spec) > 1 {
groupspec = spec[1] groupspec = spec[1]
@ -65,15 +65,21 @@ func GetUser(rootdir, userspec string) (uint32, uint32, error) {
} }
} }
if uerr == nil && gerr == nil { homedir, err := lookupHomedirInContainer(rootdir, uid64)
return uint32(uid64), uint32(gid64), nil if err != nil {
homedir = "/"
} }
err := errors.Wrapf(uerr, "error determining run uid") if uerr == nil && gerr == nil {
return uint32(uid64), uint32(gid64), homedir, nil
}
err = errors.Wrapf(uerr, "error determining run uid")
if uerr == nil { if uerr == nil {
err = errors.Wrapf(gerr, "error determining run gid") err = errors.Wrapf(gerr, "error determining run gid")
} }
return 0, 0, err
return 0, 0, homedir, err
} }
// GetGroup returns the gid by looking it up in the /etc/group file // GetGroup returns the gid by looking it up in the /etc/group file

View File

@ -25,3 +25,7 @@ func lookupAdditionalGroupsForUIDInContainer(rootdir string, userid uint64) (gid
func lookupUIDInContainer(rootdir string, uid uint64) (string, uint64, error) { func lookupUIDInContainer(rootdir string, uid uint64) (string, uint64, error) {
return "", 0, errors.New("UID lookup not supported") return "", 0, errors.New("UID lookup not supported")
} }
func lookupHomedirInContainer(rootdir string, uid uint64) (string, error) {
return "", errors.New("Home directory lookup not supported")
}

View File

@ -84,6 +84,7 @@ type lookupPasswdEntry struct {
name string name string
uid uint64 uid uint64
gid uint64 gid uint64
home string
} }
type lookupGroupEntry struct { type lookupGroupEntry struct {
name string name string
@ -135,6 +136,7 @@ func parseNextPasswd(rc *bufio.Reader) *lookupPasswdEntry {
name: fields[0], name: fields[0],
uid: uid, uid: uid,
gid: gid, gid: gid,
home: fields[5],
} }
} }
@ -291,3 +293,29 @@ func lookupUIDInContainer(rootdir string, uid uint64) (string, uint64, error) {
return "", 0, user.UnknownUserError(fmt.Sprintf("error looking up uid %q", uid)) return "", 0, user.UnknownUserError(fmt.Sprintf("error looking up uid %q", uid))
} }
func lookupHomedirInContainer(rootdir string, uid uint64) (string, error) {
cmd, f, err := openChrootedFile(rootdir, "/etc/passwd")
if err != nil {
return "", 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.home, nil
}
return "", user.UnknownUserError(fmt.Sprintf("error looking up uid %q for homedir", uid))
}

View File

@ -117,7 +117,12 @@ func getMounts(filePath string) []string {
} }
var mounts []string var mounts []string
for scanner.Scan() { for scanner.Scan() {
mounts = append(mounts, scanner.Text()) if strings.HasPrefix(strings.TrimSpace(scanner.Text()), "/") {
mounts = append(mounts, scanner.Text())
} else {
logrus.Debugf("skipping unrecognized mount in %v: %q",
filePath, scanner.Text())
}
} }
return mounts return mounts
} }
@ -190,58 +195,79 @@ func addSecretsFromMountsFile(filePath, mountLabel, containerWorkingDir, mountPr
var mounts []rspec.Mount var mounts []rspec.Mount
defaultMountsPaths := getMounts(filePath) defaultMountsPaths := getMounts(filePath)
for _, path := range defaultMountsPaths { for _, path := range defaultMountsPaths {
hostDir, ctrDir, err := getMountsMap(path) hostDirOrFile, ctrDirOrFile, err := getMountsMap(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// skip if the hostDir path doesn't exist // skip if the hostDirOrFile path doesn't exist
if _, err = os.Stat(hostDir); err != nil { fileInfo, err := os.Stat(hostDirOrFile)
if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
logrus.Warnf("Path %q from %q doesn't exist, skipping", hostDir, filePath) logrus.Warnf("Path %q from %q doesn't exist, skipping", hostDirOrFile, filePath)
continue continue
} }
return nil, errors.Wrapf(err, "failed to stat %q", hostDir) return nil, errors.Wrapf(err, "failed to stat %q", hostDirOrFile)
} }
ctrDirOnHost := filepath.Join(containerWorkingDir, ctrDir) ctrDirOrFileOnHost := filepath.Join(containerWorkingDir, ctrDirOrFile)
// In the event of a restart, don't want to copy secrets over again as they already would exist in ctrDirOnHost // In the event of a restart, don't want to copy secrets over again as they already would exist in ctrDirOrFileOnHost
_, err = os.Stat(ctrDirOnHost) _, err = os.Stat(ctrDirOrFileOnHost)
if os.IsNotExist(err) { if os.IsNotExist(err) {
if err = os.MkdirAll(ctrDirOnHost, 0755); err != nil {
return nil, errors.Wrapf(err, "making container directory %q failed", ctrDirOnHost) hostDirOrFile, err = resolveSymbolicLink(hostDirOrFile)
}
hostDir, err = resolveSymbolicLink(hostDir)
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := getHostSecretData(hostDir) switch mode := fileInfo.Mode(); {
if err != nil { case mode.IsDir():
return nil, errors.Wrapf(err, "getting host secret data failed") if err = os.MkdirAll(ctrDirOrFileOnHost, 0755); err != nil {
} return nil, errors.Wrapf(err, "making container directory %q failed", ctrDirOrFileOnHost)
for _, s := range data {
if err := s.saveTo(ctrDirOnHost); err != nil {
return nil, errors.Wrapf(err, "error saving data to container filesystem on host %q", ctrDirOnHost)
} }
data, err := getHostSecretData(hostDirOrFile)
if err != nil {
return nil, errors.Wrapf(err, "getting host secret data failed")
}
for _, s := range data {
if err := s.saveTo(ctrDirOrFileOnHost); err != nil {
return nil, errors.Wrapf(err, "error saving data to container filesystem on host %q", ctrDirOrFileOnHost)
}
}
case mode.IsRegular():
data, err := readFile("", hostDirOrFile)
if err != nil {
return nil, errors.Wrapf(err, "error reading file %q", hostDirOrFile)
}
for _, s := range data {
if err := os.MkdirAll(filepath.Dir(ctrDirOrFileOnHost), 0700); err != nil {
return nil, err
}
if err := ioutil.WriteFile(ctrDirOrFileOnHost, s.data, 0700); err != nil {
return nil, errors.Wrapf(err, "error saving data to container filesystem on host %q", ctrDirOrFileOnHost)
}
}
default:
return nil, errors.Errorf("unsupported file type for: %q", hostDirOrFile)
} }
err = label.Relabel(ctrDirOnHost, mountLabel, false) err = label.Relabel(ctrDirOrFileOnHost, mountLabel, false)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "error applying correct labels") return nil, errors.Wrap(err, "error applying correct labels")
} }
if uid != 0 || gid != 0 { if uid != 0 || gid != 0 {
if err := rchown(ctrDirOnHost, uid, gid); err != nil { if err := rchown(ctrDirOrFileOnHost, uid, gid); err != nil {
return nil, err return nil, err
} }
} }
} else if err != nil { } else if err != nil {
return nil, errors.Wrapf(err, "error getting status of %q", ctrDirOnHost) return nil, errors.Wrapf(err, "error getting status of %q", ctrDirOrFileOnHost)
} }
m := rspec.Mount{ m := rspec.Mount{
Source: filepath.Join(mountPrefix, ctrDir), Source: filepath.Join(mountPrefix, ctrDirOrFile),
Destination: ctrDir, Destination: ctrDirOrFile,
Type: "bind", Type: "bind",
Options: []string{"bind", "rprivate"}, Options: []string{"bind", "rprivate"},
} }

View File

@ -64,6 +64,7 @@ func (c *Cmd) Start() error {
if os.Geteuid() != 0 { if os.Geteuid() != 0 {
c.Env = append(c.Env, "_CONTAINERS_USERNS_CONFIGURED=done") c.Env = append(c.Env, "_CONTAINERS_USERNS_CONFIGURED=done")
c.Env = append(c.Env, fmt.Sprintf("_CONTAINERS_ROOTLESS_UID=%d", os.Geteuid())) c.Env = append(c.Env, fmt.Sprintf("_CONTAINERS_ROOTLESS_UID=%d", os.Geteuid()))
c.Env = append(c.Env, fmt.Sprintf("_CONTAINERS_ROOTLESS_GID=%d", os.Getegid()))
} }
// Create the pipe for reading the child's PID. // Create the pipe for reading the child's PID.
@ -183,6 +184,7 @@ func (c *Cmd) Start() error {
for _, m := range c.GidMappings { for _, m := range c.GidMappings {
fmt.Fprintf(g, "%d %d %d\n", m.ContainerID, m.HostID, m.Size) fmt.Fprintf(g, "%d %d %d\n", m.ContainerID, m.HostID, m.Size)
} }
gidmapSet := false
// Set the GID map. // Set the GID map.
if c.UseNewgidmap { if c.UseNewgidmap {
cmd := exec.Command("newgidmap", append([]string{pidString}, strings.Fields(strings.Replace(g.String(), "\n", " ", -1))...)...) cmd := exec.Command("newgidmap", append([]string{pidString}, strings.Fields(strings.Replace(g.String(), "\n", " ", -1))...)...)
@ -190,11 +192,16 @@ func (c *Cmd) Start() error {
cmd.Stdout = g cmd.Stdout = g
cmd.Stderr = g cmd.Stderr = g
err := cmd.Run() err := cmd.Run()
if err != nil { if err == nil {
gidmapSet = true
} else {
fmt.Fprintf(continueWrite, "error running newgidmap: %v: %s", err, g.String()) fmt.Fprintf(continueWrite, "error running newgidmap: %v: %s", err, g.String())
return errors.Wrapf(err, "error running newgidmap: %s", g.String()) fmt.Fprintf(continueWrite, "falling back to single mapping\n")
g.Reset()
g.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Getegid())))
} }
} else { }
if !gidmapSet {
gidmap, err := os.OpenFile(fmt.Sprintf("/proc/%s/gid_map", pidString), os.O_TRUNC|os.O_WRONLY, 0) gidmap, err := os.OpenFile(fmt.Sprintf("/proc/%s/gid_map", pidString), os.O_TRUNC|os.O_WRONLY, 0)
if err != nil { if err != nil {
fmt.Fprintf(continueWrite, "error opening /proc/%s/gid_map: %v", pidString, err) fmt.Fprintf(continueWrite, "error opening /proc/%s/gid_map: %v", pidString, err)
@ -214,6 +221,7 @@ func (c *Cmd) Start() error {
for _, m := range c.UidMappings { for _, m := range c.UidMappings {
fmt.Fprintf(u, "%d %d %d\n", m.ContainerID, m.HostID, m.Size) fmt.Fprintf(u, "%d %d %d\n", m.ContainerID, m.HostID, m.Size)
} }
uidmapSet := false
// Set the GID map. // Set the GID map.
if c.UseNewuidmap { if c.UseNewuidmap {
cmd := exec.Command("newuidmap", append([]string{pidString}, strings.Fields(strings.Replace(u.String(), "\n", " ", -1))...)...) cmd := exec.Command("newuidmap", append([]string{pidString}, strings.Fields(strings.Replace(u.String(), "\n", " ", -1))...)...)
@ -221,11 +229,16 @@ func (c *Cmd) Start() error {
cmd.Stdout = u cmd.Stdout = u
cmd.Stderr = u cmd.Stderr = u
err := cmd.Run() err := cmd.Run()
if err != nil { if err == nil {
uidmapSet = true
} else {
fmt.Fprintf(continueWrite, "error running newuidmap: %v: %s", err, u.String()) fmt.Fprintf(continueWrite, "error running newuidmap: %v: %s", err, u.String())
return errors.Wrapf(err, "error running newuidmap: %s", u.String()) fmt.Fprintf(continueWrite, "falling back to single mapping\n")
u.Reset()
u.Write([]byte(fmt.Sprintf("0 %d 1\n", os.Geteuid())))
} }
} else { }
if !uidmapSet {
uidmap, err := os.OpenFile(fmt.Sprintf("/proc/%s/uid_map", pidString), os.O_TRUNC|os.O_WRONLY, 0) uidmap, err := os.OpenFile(fmt.Sprintf("/proc/%s/uid_map", pidString), os.O_TRUNC|os.O_WRONLY, 0)
if err != nil { if err != nil {
fmt.Fprintf(continueWrite, "error opening /proc/%s/uid_map: %v", pidString, err) fmt.Fprintf(continueWrite, "error opening /proc/%s/uid_map: %v", pidString, err)
@ -354,7 +367,9 @@ func MaybeReexecUsingUserNamespace(evenForRoot bool) {
// range in /etc/subuid and /etc/subgid file is a starting host // range in /etc/subuid and /etc/subgid file is a starting host
// ID and a range size. // ID and a range size.
uidmap, gidmap, err = GetSubIDMappings(me.Username, me.Username) uidmap, gidmap, err = GetSubIDMappings(me.Username, me.Username)
bailOnError(err, "error reading allowed ID mappings") if err != nil {
logrus.Warnf("error reading allowed ID mappings: %v", err)
}
if len(uidmap) == 0 { if len(uidmap) == 0 {
logrus.Warnf("Found no UID ranges set aside for user %q in /etc/subuid.", me.Username) logrus.Warnf("Found no UID ranges set aside for user %q in /etc/subuid.", me.Username)
} }

View File

@ -131,7 +131,8 @@ func (b *Builder) Run(command []string, options RunOptions) error {
return err return err
} }
if err := b.configureUIDGID(g, mountPoint, options); err != nil { homeDir, err := b.configureUIDGID(g, mountPoint, options)
if err != nil {
return err return err
} }
@ -210,7 +211,7 @@ func (b *Builder) Run(command []string, options RunOptions) error {
} }
err = b.runUsingRuntimeSubproc(isolation, options, configureNetwork, configureNetworks, moreCreateArgs, spec, mountPoint, path, Package+"-"+filepath.Base(path)) err = b.runUsingRuntimeSubproc(isolation, options, configureNetwork, configureNetworks, moreCreateArgs, spec, mountPoint, path, Package+"-"+filepath.Base(path))
case IsolationChroot: case IsolationChroot:
err = chroot.RunUsingChroot(spec, path, options.Stdin, options.Stdout, options.Stderr) err = chroot.RunUsingChroot(spec, path, homeDir, options.Stdin, options.Stdout, options.Stderr)
case IsolationOCIRootless: case IsolationOCIRootless:
moreCreateArgs := []string{"--no-new-keyring"} moreCreateArgs := []string{"--no-new-keyring"}
if options.NoPivot { if options.NoPivot {
@ -1454,7 +1455,18 @@ func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, i
} }
if configureNetwork { if configureNetwork {
for name, val := range util.DefaultNetworkSysctl { for name, val := range util.DefaultNetworkSysctl {
g.AddLinuxSysctl(name, val) // Check that the sysctl we are adding is actually supported
// by the kernel
p := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
_, err := os.Stat(p)
if err != nil && !os.IsNotExist(err) {
return false, nil, false, errors.Wrapf(err, "cannot stat %s", p)
}
if err == nil {
g.AddLinuxSysctl(name, val)
} else {
logrus.Warnf("ignoring sysctl %s since %s doesn't exist", name, p)
}
} }
} }
return configureNetwork, configureNetworks, configureUTS, nil return configureNetwork, configureNetworks, configureUTS, nil
@ -1775,14 +1787,14 @@ func getDNSIP(dnsServers []string) (dns []net.IP, err error) {
return dns, nil return dns, nil
} }
func (b *Builder) configureUIDGID(g *generate.Generator, mountPoint string, options RunOptions) error { func (b *Builder) configureUIDGID(g *generate.Generator, mountPoint string, options RunOptions) (string, error) {
// Set the user UID/GID/supplemental group list/capabilities lists. // Set the user UID/GID/supplemental group list/capabilities lists.
user, err := b.user(mountPoint, options.User) user, homeDir, err := b.user(mountPoint, options.User)
if err != nil { if err != nil {
return err return "", err
} }
if err := setupCapabilities(g, b.AddCapabilities, b.DropCapabilities, options.AddCapabilities, options.DropCapabilities); err != nil { if err := setupCapabilities(g, b.AddCapabilities, b.DropCapabilities, options.AddCapabilities, options.DropCapabilities); err != nil {
return err return "", err
} }
g.SetProcessUID(user.UID) g.SetProcessUID(user.UID)
g.SetProcessGID(user.GID) g.SetProcessGID(user.GID)
@ -1797,7 +1809,7 @@ func (b *Builder) configureUIDGID(g *generate.Generator, mountPoint string, opti
g.Config.Process.Capabilities.Bounding = bounding g.Config.Process.Capabilities.Bounding = bounding
} }
return nil return homeDir, nil
} }
func (b *Builder) configureEnvironment(g *generate.Generator, options RunOptions) { func (b *Builder) configureEnvironment(g *generate.Generator, options RunOptions) {