diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_internal_linux.go | 41 | ||||
-rw-r--r-- | libpod/container_log_linux.go | 2 | ||||
-rw-r--r-- | libpod/image/image.go | 72 | ||||
-rw-r--r-- | libpod/image/prune.go | 50 | ||||
-rw-r--r-- | libpod/pod.go | 18 | ||||
-rw-r--r-- | libpod/util_linux.go | 12 | ||||
-rw-r--r-- | libpod/util_linux_test.go | 39 |
7 files changed, 193 insertions, 41 deletions
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index dde7cafb1..514cdaee1 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "io/ioutil" + "math" "net" "os" "os/user" @@ -35,6 +36,7 @@ import ( "github.com/containers/podman/v2/pkg/util" "github.com/containers/podman/v2/utils" "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idtools" securejoin "github.com/cyphar/filepath-securejoin" runcuser "github.com/opencontainers/runc/libcontainer/user" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -415,9 +417,44 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } // Look up and add groups the user belongs to, if a group wasn't directly specified - if !rootless.IsRootless() && !strings.Contains(c.config.User, ":") { + if !strings.Contains(c.config.User, ":") { + // the gidMappings that are present inside the container user namespace + var gidMappings []idtools.IDMap + + switch { + case len(c.config.IDMappings.GIDMap) > 0: + gidMappings = c.config.IDMappings.GIDMap + case rootless.IsRootless(): + // Check whether the current user namespace has enough gids available. + availableGids, err := rootless.GetAvailableGids() + if err != nil { + return nil, errors.Wrapf(err, "cannot read number of available GIDs") + } + gidMappings = []idtools.IDMap{{ + ContainerID: 0, + HostID: 0, + Size: int(availableGids), + }} + default: + gidMappings = []idtools.IDMap{{ + ContainerID: 0, + HostID: 0, + Size: math.MaxInt32, + }} + } for _, gid := range execUser.Sgids { - g.AddProcessAdditionalGid(uint32(gid)) + isGidAvailable := false + for _, m := range gidMappings { + if gid >= m.ContainerID && gid < m.ContainerID+m.Size { + isGidAvailable = true + break + } + } + if isGidAvailable { + g.AddProcessAdditionalGid(uint32(gid)) + } else { + logrus.Warnf("additional gid=%d is not present in the user namespace, skip setting it", gid) + } } } diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go index 73c2df76e..d895171cf 100644 --- a/libpod/container_log_linux.go +++ b/libpod/container_log_linux.go @@ -33,7 +33,7 @@ const ( func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error { var config journal.JournalReaderConfig if options.Tail < 0 { - config.NumFromTail = math.MaxUint64 + config.NumFromTail = 0 } else { config.NumFromTail = uint64(options.Tail) } diff --git a/libpod/image/image.go b/libpod/image/image.go index 5dfb33afb..f5bf47694 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -410,48 +410,92 @@ func (ir *Runtime) getLocalImage(inputName string) (string, *storage.Image, erro if inputName == "" { return "", nil, errors.Errorf("input name is blank") } + // Check if the input name has a transport and if so strip it dest, err := alltransports.ParseImageName(inputName) if err == nil && dest.DockerReference() != nil { inputName = dest.DockerReference().String() } - img, err := ir.getImage(stripSha256(inputName)) + // Early check for fully-qualified images and (short) IDs. + img, err := ir.store.Image(stripSha256(inputName)) if err == nil { - return inputName, img, err + return inputName, img, nil } - // container-storage wasn't able to find it in its current form - // check if the input name has a tag, and if not, run it through - // again + // Note that it's crucial to first decompose the image and check if + // it's a fully-qualified one or a "short name". The latter requires + // some normalization with search registries and the + // "localhost/prefix". decomposedImage, err := decompose(inputName) if err != nil { + // We may have a storage reference. We can't parse it to a + // reference before. Otherwise, we'd normalize "alpine" to + // "docker.io/library/alpine:latest" which would break the + // order in which we should query local images below. + if ref, err := is.Transport.ParseStoreReference(ir.store, inputName); err == nil { + img, err = is.Transport.GetStoreImage(ir.store, ref) + if err == nil { + return inputName, img, nil + } + } return "", nil, err } - // The image has a registry name in it and we made sure we looked for it locally - // with a tag. It cannot be local. + // The specified image is fully qualified, so it doesn't exist in the + // storage. if decomposedImage.hasRegistry { + // However ... we may still need to normalize to docker.io: + // `docker.io/foo` -> `docker.io/library/foo` + if ref, err := is.Transport.ParseStoreReference(ir.store, inputName); err == nil { + img, err = is.Transport.GetStoreImage(ir.store, ref) + if err == nil { + return inputName, img, nil + } + } return "", nil, errors.Wrapf(ErrNoSuchImage, imageError) } - // if the image is saved with the repository localhost, searching with localhost prepended is necessary - // We don't need to strip the sha because we have already determined it is not an ID - ref, err := decomposedImage.referenceWithRegistry(DefaultLocalRegistry) + + // "Short-name image", so let's try out certain prefixes: + // 1) DefaultLocalRegistry (i.e., "localhost/) + // 2) Unqualified-search registries from registries.conf + unqualifiedSearchRegistries, err := registries.GetRegistries() if err != nil { return "", nil, err } - img, err = ir.getImage(ref.String()) + + for _, candidate := range append([]string{DefaultLocalRegistry}, unqualifiedSearchRegistries...) { + ref, err := decomposedImage.referenceWithRegistry(candidate) + if err != nil { + return "", nil, err + } + img, err := ir.store.Image(ref.String()) + if err == nil { + return ref.String(), img, nil + } + } + + // Backwards compat: normalize to docker.io as some users may very well + // rely on that. + ref, err := is.Transport.ParseStoreReference(ir.store, inputName) if err == nil { - return inputName, img, err + img, err = is.Transport.GetStoreImage(ir.store, ref) + if err == nil { + return inputName, img, nil + } } - // grab all the local images + // Last resort: look at the repotags of all images and try to find a + // match. images, err := ir.GetImages() if err != nil { return "", nil, err } - // check the repotags of all images for a match + decomposedImage, err = decompose(inputName) + if err != nil { + return "", nil, err + } repoImage, err := findImageInRepotags(decomposedImage, images) if err == nil { return inputName, repoImage, nil diff --git a/libpod/image/prune.go b/libpod/image/prune.go index fcc65fb03..b38265a7e 100644 --- a/libpod/image/prune.go +++ b/libpod/image/prune.go @@ -125,29 +125,39 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ( filterFuncs = append(filterFuncs, generatedFunc) } - pruneImages, err := ir.GetPruneImages(ctx, all, filterFuncs) - if err != nil { - return nil, errors.Wrap(err, "unable to get images to prune") - } - prunedCids := make([]string, 0, len(pruneImages)) - for _, p := range pruneImages { - repotags, err := p.RepoTags() + pruned := []string{} + prev := 0 + for { + toPrune, err := ir.GetPruneImages(ctx, all, filterFuncs) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to get images to prune") } - if err := p.Remove(ctx, true); err != nil { - if errors.Cause(err) == storage.ErrImageUsedByContainer { - logrus.Warnf("Failed to prune image %s as it is in use: %v.\nA container associated with containers/storage i.e. Buildah, CRI-O, etc., maybe associated with this image.\nUsing the rmi command with the --force option will remove the container and image, but may cause failures for other dependent systems.", p.ID(), err) - continue - } - return nil, errors.Wrap(err, "failed to prune image") + numImages := len(toPrune) + if numImages == 0 || numImages == prev { + // If there's nothing left to do, return. + break } - defer p.newImageEvent(events.Prune) - nameOrID := p.ID() - if len(repotags) > 0 { - nameOrID = repotags[0] + prev = numImages + for _, img := range toPrune { + repotags, err := img.RepoTags() + if err != nil { + return nil, err + } + if err := img.Remove(ctx, false); err != nil { + if errors.Cause(err) == storage.ErrImageUsedByContainer { + logrus.Warnf("Failed to prune image %s as it is in use: %v.\nA container associated with containers/storage (e.g., Buildah, CRI-O, etc.) maybe associated with this image.\nUsing the rmi command with the --force option will remove the container and image, but may cause failures for other dependent systems.", img.ID(), err) + continue + } + return nil, errors.Wrap(err, "failed to prune image") + } + defer img.newImageEvent(events.Prune) + nameOrID := img.ID() + if len(repotags) > 0 { + nameOrID = repotags[0] + } + pruned = append(pruned, nameOrID) } - prunedCids = append(prunedCids, nameOrID) + } - return prunedCids, nil + return pruned, nil } diff --git a/libpod/pod.go b/libpod/pod.go index a5a0532be..c8f62ca18 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -327,3 +327,21 @@ func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerSta } return newContainerStats, nil } + +// ProcessLabel returns the SELinux label associated with the pod +func (p *Pod) ProcessLabel() (string, error) { + if !p.HasInfraContainer() { + return "", nil + } + + id, err := p.InfraContainerID() + if err != nil { + return "", err + } + + ctr, err := p.runtime.state.Container(id) + if err != nil { + return "", err + } + return ctr.ProcessLabel(), nil +} diff --git a/libpod/util_linux.go b/libpod/util_linux.go index 03c3ab061..5184ed393 100644 --- a/libpod/util_linux.go +++ b/libpod/util_linux.go @@ -90,19 +90,23 @@ func assembleSystemdCgroupName(baseSlice, newSlice string) (string, error) { return final, nil } +var lvpRelabel = label.Relabel +var lvpInitLabels = label.InitLabels +var lvpReleaseLabel = label.ReleaseLabel + // LabelVolumePath takes a mount path for a volume and gives it an // selinux label of either shared or not func LabelVolumePath(path string) error { - _, mountLabel, err := label.InitLabels([]string{}) + _, mountLabel, err := lvpInitLabels([]string{}) if err != nil { return errors.Wrapf(err, "error getting default mountlabels") } - if err := label.ReleaseLabel(mountLabel); err != nil { + if err := lvpReleaseLabel(mountLabel); err != nil { return errors.Wrapf(err, "error releasing label %q", mountLabel) } - if err := label.Relabel(path, mountLabel, true); err != nil { - if err != syscall.ENOTSUP { + if err := lvpRelabel(path, mountLabel, true); err != nil { + if err == syscall.ENOTSUP { logrus.Debugf("Labeling not supported on %q", path) } else { return errors.Wrapf(err, "error setting selinux label for %s to %q as shared", path, mountLabel) diff --git a/libpod/util_linux_test.go b/libpod/util_linux_test.go new file mode 100644 index 000000000..5fcb04beb --- /dev/null +++ b/libpod/util_linux_test.go @@ -0,0 +1,39 @@ +package libpod + +import ( + "syscall" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLabelVolumePath(t *testing.T) { + // Set up mocked SELinux functions for testing. + oldRelabel := lvpRelabel + oldInitLabels := lvpInitLabels + oldReleaseLabel := lvpReleaseLabel + defer func() { + lvpRelabel = oldRelabel + lvpInitLabels = oldInitLabels + lvpReleaseLabel = oldReleaseLabel + }() + + // Relabel returns ENOTSUP unconditionally. + lvpRelabel = func(path string, fileLabel string, shared bool) error { + return syscall.ENOTSUP + } + + // InitLabels and ReleaseLabel both return dummy values and nil errors. + lvpInitLabels = func(options []string) (string, string, error) { + pLabel := "system_u:system_r:container_t:s0:c1,c2" + mLabel := "system_u:object_r:container_file_t:s0:c1,c2" + return pLabel, mLabel, nil + } + lvpReleaseLabel = func(label string) error { + return nil + } + + // LabelVolumePath should not return an error if the operation is unsupported. + err := LabelVolumePath("/foo/bar") + assert.NoError(t, err) +} |