rootless: don't create a namespace unless for containers-storage

This change fixes skopeo usage in restricted environment such as
bubblewrap where it doesn't need extra capabilities or user namespace
to perform its action.

Close #649
Signed-off-by: Tristan Cacqueray <tdecacqu@redhat.com>
This commit is contained in:
Tristan Cacqueray 2019-05-16 01:22:56 +00:00
parent 9fef0eb3f3
commit b46d16f48c
7 changed files with 47 additions and 18 deletions

View File

@ -50,7 +50,6 @@ func copyCmd(global *globalOptions) cli.Command {
`, strings.Join(transports.ListNames(), ", ")), `, strings.Join(transports.ListNames(), ", ")),
ArgsUsage: "SOURCE-IMAGE DESTINATION-IMAGE", ArgsUsage: "SOURCE-IMAGE DESTINATION-IMAGE",
Action: commandAction(opts.run), Action: commandAction(opts.run),
Before: needsRexec,
// FIXME: Do we need to namespace the GPG aspect? // FIXME: Do we need to namespace the GPG aspect?
Flags: append(append(append([]cli.Flag{ Flags: append(append(append([]cli.Flag{
cli.StringSliceFlag{ cli.StringSliceFlag{
@ -86,6 +85,11 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error {
if len(args) != 2 { if len(args) != 2 {
return errorShouldDisplayUsage{errors.New("Exactly two arguments expected")} return errorShouldDisplayUsage{errors.New("Exactly two arguments expected")}
} }
imageNames := args
if err := reexecIfNecessaryForImages(imageNames...); err != nil {
return err
}
policyContext, err := opts.global.getPolicyContext() policyContext, err := opts.global.getPolicyContext()
if err != nil { if err != nil {
@ -93,13 +97,13 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error {
} }
defer policyContext.Destroy() defer policyContext.Destroy()
srcRef, err := alltransports.ParseImageName(args[0]) srcRef, err := alltransports.ParseImageName(imageNames[0])
if err != nil { if err != nil {
return fmt.Errorf("Invalid source name %s: %v", args[0], err) return fmt.Errorf("Invalid source name %s: %v", imageNames[0], err)
} }
destRef, err := alltransports.ParseImageName(args[1]) destRef, err := alltransports.ParseImageName(imageNames[1])
if err != nil { if err != nil {
return fmt.Errorf("Invalid destination name %s: %v", args[1], err) return fmt.Errorf("Invalid destination name %s: %v", imageNames[1], err)
} }
sourceCtx, err := opts.srcImage.newSystemContext() sourceCtx, err := opts.srcImage.newSystemContext()

View File

@ -24,7 +24,6 @@ func deleteCmd(global *globalOptions) cli.Command {
image: imageOpts, image: imageOpts,
} }
return cli.Command{ return cli.Command{
Before: needsRexec,
Name: "delete", Name: "delete",
Usage: "Delete image IMAGE-NAME", Usage: "Delete image IMAGE-NAME",
Description: fmt.Sprintf(` Description: fmt.Sprintf(`
@ -45,10 +44,15 @@ func (opts *deleteOptions) run(args []string, stdout io.Writer) error {
if len(args) != 1 { if len(args) != 1 {
return errors.New("Usage: delete imageReference") return errors.New("Usage: delete imageReference")
} }
imageName := args[0]
ref, err := alltransports.ParseImageName(args[0]) if err := reexecIfNecessaryForImages(imageName); err != nil {
return err
}
ref, err := alltransports.ParseImageName(imageName)
if err != nil { if err != nil {
return fmt.Errorf("Invalid source name %s: %v", args[0], err) return fmt.Errorf("Invalid source name %s: %v", imageName, err)
} }
sys, err := opts.image.newSystemContext() sys, err := opts.image.newSystemContext()

View File

@ -68,7 +68,6 @@ func inspectCmd(global *globalOptions) cli.Command {
Destination: &opts.config, Destination: &opts.config,
}, },
}, sharedFlags...), imageFlags...), }, sharedFlags...), imageFlags...),
Before: needsRexec,
Action: commandAction(opts.run), Action: commandAction(opts.run),
} }
} }
@ -80,7 +79,13 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
if len(args) != 1 { if len(args) != 1 {
return errors.New("Exactly one argument expected") return errors.New("Exactly one argument expected")
} }
img, err := parseImage(ctx, opts.image, args[0]) imageName := args[0]
if err := reexecIfNecessaryForImages(imageName); err != nil {
return err
}
img, err := parseImage(ctx, opts.image, imageName)
if err != nil { if err != nil {
return err return err
} }

View File

@ -32,7 +32,6 @@ func layersCmd(global *globalOptions) cli.Command {
Name: "layers", Name: "layers",
Usage: "Get layers of IMAGE-NAME", Usage: "Get layers of IMAGE-NAME",
ArgsUsage: "IMAGE-NAME [LAYER...]", ArgsUsage: "IMAGE-NAME [LAYER...]",
Before: needsRexec,
Hidden: true, Hidden: true,
Action: commandAction(opts.run), Action: commandAction(opts.run),
Flags: append(sharedFlags, imageFlags...), Flags: append(sharedFlags, imageFlags...),
@ -44,6 +43,11 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
if len(args) == 0 { if len(args) == 0 {
return errors.New("Usage: layers imageReference [layer...]") return errors.New("Usage: layers imageReference [layer...]")
} }
imageName := args[0]
if err := reexecIfNecessaryForImages(imageName); err != nil {
return err
}
ctx, cancel := opts.global.commandTimeoutContext() ctx, cancel := opts.global.commandTimeoutContext()
defer cancel() defer cancel()
@ -53,7 +57,7 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
return err return err
} }
cache := blobinfocache.DefaultCache(sys) cache := blobinfocache.DefaultCache(sys)
rawSource, err := parseImageSource(ctx, opts.image, args[0]) rawSource, err := parseImageSource(ctx, opts.image, imageName)
if err != nil { if err != nil {
return err return err
} }

View File

@ -5,3 +5,7 @@ package main
func maybeReexec() error { func maybeReexec() error {
return nil return nil
} }
func reexecIfNecessaryForImages(inputImageNames ...string) error {
return nil
}

View File

@ -2,6 +2,8 @@ package main
import ( import (
"github.com/containers/buildah/pkg/unshare" "github.com/containers/buildah/pkg/unshare"
"github.com/containers/image/storage"
"github.com/containers/image/transports/alltransports"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/syndtr/gocapability/capability" "github.com/syndtr/gocapability/capability"
) )
@ -32,3 +34,13 @@ func maybeReexec() error {
} }
return nil return nil
} }
func reexecIfNecessaryForImages(imageNames ...string) error {
// Check if container-storage are used before doing unshare
for _, imageName := range imageNames {
if alltransports.TransportFromImageName(imageName).Name() == storage.Transport.Name() {
return maybeReexec()
}
}
return nil
}

View File

@ -16,10 +16,6 @@ type errorShouldDisplayUsage struct {
error error
} }
func needsRexec(c *cli.Context) error {
return maybeReexec()
}
// commandAction intermediates between the cli.ActionFunc interface and the real handler, // commandAction intermediates between the cli.ActionFunc interface and the real handler,
// primarily to ensure that cli.Context is not available to the handler, which in turn // primarily to ensure that cli.Context is not available to the handler, which in turn
// makes sure that the cli.String() etc. flag access functions are not used, // makes sure that the cli.String() etc. flag access functions are not used,