From 0d5b53ca85736d58acfca9c3045ee07a2469faab Mon Sep 17 00:00:00 2001 From: Tristan Cacqueray Date: Thu, 16 May 2019 01:22:56 +0000 Subject: [PATCH] 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 Depends-On: https://github.com/containers/image/pull/631 Signed-off-by: Tristan Cacqueray --- cmd/skopeo/copy.go | 5 ++++- cmd/skopeo/delete.go | 9 +++++--- cmd/skopeo/inspect.go | 6 +++++- cmd/skopeo/layers.go | 7 ++++++- cmd/skopeo/unshare.go | 4 ++++ cmd/skopeo/unshare_linux.go | 12 +++++++++++ cmd/skopeo/utils.go | 4 ---- vendor.conf | 2 +- .../containers/image/transports/transports.go | 10 +++++++++ .../image/transports/transports_test.go | 21 +++++++++++++++++++ 10 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 vendor/github.com/containers/image/transports/transports_test.go diff --git a/cmd/skopeo/copy.go b/cmd/skopeo/copy.go index 9e1d093b21..2df1155076 100644 --- a/cmd/skopeo/copy.go +++ b/cmd/skopeo/copy.go @@ -50,7 +50,6 @@ func copyCmd(global *globalOptions) cli.Command { `, strings.Join(transports.ListNames(), ", ")), ArgsUsage: "SOURCE-IMAGE DESTINATION-IMAGE", Action: commandAction(opts.run), - Before: needsRexec, // FIXME: Do we need to namespace the GPG aspect? Flags: append(append(append([]cli.Flag{ cli.StringSliceFlag{ @@ -87,6 +86,10 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error { return errorShouldDisplayUsage{errors.New("Exactly two arguments expected")} } + if err := needReexec(args); err != nil { + return err + } + policyContext, err := opts.global.getPolicyContext() if err != nil { return fmt.Errorf("Error loading trust policy: %v", err) diff --git a/cmd/skopeo/delete.go b/cmd/skopeo/delete.go index 2072f5565c..8e9ddf3ecf 100644 --- a/cmd/skopeo/delete.go +++ b/cmd/skopeo/delete.go @@ -24,9 +24,8 @@ func deleteCmd(global *globalOptions) cli.Command { image: imageOpts, } return cli.Command{ - Before: needsRexec, - Name: "delete", - Usage: "Delete image IMAGE-NAME", + Name: "delete", + Usage: "Delete image IMAGE-NAME", Description: fmt.Sprintf(` Delete an "IMAGE_NAME" from a transport @@ -46,6 +45,10 @@ func (opts *deleteOptions) run(args []string, stdout io.Writer) error { return errors.New("Usage: delete imageReference") } + if err := needReexec(args); err != nil { + return err + } + ref, err := alltransports.ParseImageName(args[0]) if err != nil { return fmt.Errorf("Invalid source name %s: %v", args[0], err) diff --git a/cmd/skopeo/inspect.go b/cmd/skopeo/inspect.go index 9edf8632b1..ff009d7f44 100644 --- a/cmd/skopeo/inspect.go +++ b/cmd/skopeo/inspect.go @@ -68,7 +68,6 @@ func inspectCmd(global *globalOptions) cli.Command { Destination: &opts.config, }, }, sharedFlags...), imageFlags...), - Before: needsRexec, Action: commandAction(opts.run), } } @@ -80,6 +79,11 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error) if len(args) != 1 { return errors.New("Exactly one argument expected") } + + if err := needReexec(args); err != nil { + return err + } + img, err := parseImage(ctx, opts.image, args[0]) if err != nil { return err diff --git a/cmd/skopeo/layers.go b/cmd/skopeo/layers.go index 3a09d8bd3e..6892289389 100644 --- a/cmd/skopeo/layers.go +++ b/cmd/skopeo/layers.go @@ -32,7 +32,6 @@ func layersCmd(global *globalOptions) cli.Command { Name: "layers", Usage: "Get layers of IMAGE-NAME", ArgsUsage: "IMAGE-NAME [LAYER...]", - Before: needsRexec, Hidden: true, Action: commandAction(opts.run), Flags: append(sharedFlags, imageFlags...), @@ -45,6 +44,12 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) { return errors.New("Usage: layers imageReference [layer...]") } + imageNames := make([]string, 1) + imageNames[0] = args[0] + if err := needReexec(imageNames); err != nil { + return err + } + ctx, cancel := opts.global.commandTimeoutContext() defer cancel() diff --git a/cmd/skopeo/unshare.go b/cmd/skopeo/unshare.go index 1e9a391dd9..18a6bde905 100644 --- a/cmd/skopeo/unshare.go +++ b/cmd/skopeo/unshare.go @@ -5,3 +5,7 @@ package main func maybeReexec() error { return nil } + +func needReexec(inputImageNames []string) error { + return nil +} diff --git a/cmd/skopeo/unshare_linux.go b/cmd/skopeo/unshare_linux.go index f9291c0c0c..fb6996104a 100644 --- a/cmd/skopeo/unshare_linux.go +++ b/cmd/skopeo/unshare_linux.go @@ -2,6 +2,8 @@ package main import ( "github.com/containers/buildah/pkg/unshare" + "github.com/containers/image/storage" + "github.com/containers/image/transports" "github.com/pkg/errors" "github.com/syndtr/gocapability/capability" ) @@ -32,3 +34,13 @@ func maybeReexec() error { } return nil } + +func needReexec(imageNames []string) error { + // Check if container-storage are used before doing unshare + for _, imageName := range imageNames { + if transports.TransportFromImageName(imageName).Name() == storage.Transport.Name() { + return maybeReexec() + } + } + return nil +} diff --git a/cmd/skopeo/utils.go b/cmd/skopeo/utils.go index df4c48c60c..320aec8389 100644 --- a/cmd/skopeo/utils.go +++ b/cmd/skopeo/utils.go @@ -16,10 +16,6 @@ type errorShouldDisplayUsage struct { error } -func needsRexec(c *cli.Context) error { - return maybeReexec() -} - // 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 // makes sure that the cli.String() etc. flag access functions are not used, diff --git a/vendor.conf b/vendor.conf index 0a16afa9f6..2b675aa1ea 100644 --- a/vendor.conf +++ b/vendor.conf @@ -2,7 +2,7 @@ github.com/urfave/cli v1.20.0 github.com/kr/pretty v0.1.0 github.com/kr/text v0.1.0 -github.com/containers/image ff926d3c79684793a2135666a2cb738f44ba33dc +github.com/containers/image d6424f9968a50d0effec269b0397180e78c44161 github.com/containers/buildah 810efa340ab43753034e2ed08ec290e4abab7e72 github.com/vbauerster/mpb v3.3.4 github.com/mattn/go-isatty v0.0.4 diff --git a/vendor/github.com/containers/image/transports/transports.go b/vendor/github.com/containers/image/transports/transports.go index 687d0a44e3..f3c8534017 100644 --- a/vendor/github.com/containers/image/transports/transports.go +++ b/vendor/github.com/containers/image/transports/transports.go @@ -3,6 +3,7 @@ package transports import ( "fmt" "sort" + "strings" "sync" "github.com/containers/image/types" @@ -88,3 +89,12 @@ func ListNames() []string { sort.Strings(names) return names } + +// Return the ImageTransport of an image name or nil when unknown +func TransportFromImageName(imageName string) types.ImageTransport { + parts := strings.SplitN(imageName, ":", 2) + if len(parts) == 2 { + return Get(parts[0]) + } + return nil +} diff --git a/vendor/github.com/containers/image/transports/transports_test.go b/vendor/github.com/containers/image/transports/transports_test.go new file mode 100644 index 0000000000..1a076495db --- /dev/null +++ b/vendor/github.com/containers/image/transports/transports_test.go @@ -0,0 +1,21 @@ +package transports + +import ( + "testing" + + "github.com/containers/image/directory" + "github.com/containers/image/storage" + "github.com/containers/image/transports" + "github.com/stretchr/testify/assert" +) + +func TestTransportFromImageName(t *testing.T) { + dirTransport := transports.TransportFromImageName("dir:/tmp/test") + assert.Equal(t, dirTransport.Name(), directory.Transport.Name()) + storageTransport := transports.TransportFromImageName("containers-storage:ref:test") + assert.Equal(t, storageTransport.Name(), storage.Transport.Name()) + badTransport := transports.TransportFromImageName("unknown:ref:test") + assert.Equal(t, badTransport, nil) + badTransport2 := transports.TransportFromImageName("unknown") + assert.Equal(t, badTransport2, nil) +}