Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --insecure flag to podman manifest inspect for Docker compatibility #15359

Merged
merged 1 commit into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions cmd/podman/manifest/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ package manifest
import (
"fmt"

"github.com/containers/image/v5/types"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/spf13/cobra"
)

var (
inspectCmd = &cobra.Command{
Use: "inspect IMAGE",
tlsVerifyCLI bool
inspectCmd = &cobra.Command{
Use: "inspect [options] IMAGE",
Short: "Display the contents of a manifest list or image index",
Long: "Display the contents of a manifest list or image index.",
RunE: inspect,
Expand All @@ -25,10 +28,24 @@ func init() {
Command: inspectCmd,
Parent: manifestCmd,
})
flags := inspectCmd.Flags()

flags.Bool("verbose", false, "Added for Docker compatibility")
_ = flags.MarkHidden("verbose")
flags.BoolVar(&tlsVerifyCLI, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry")
flags.Bool("insecure", false, "Purely for Docker compatibility")
vrothberg marked this conversation as resolved.
Show resolved Hide resolved
_ = flags.MarkHidden("insecure")
}

func inspect(cmd *cobra.Command, args []string) error {
buf, err := registry.ImageEngine().ManifestInspect(registry.Context(), args[0])
opts := entities.ManifestInspectOptions{}
if cmd.Flags().Changed("tls-verify") {
opts.SkipTLSVerify = types.NewOptionalBool(!tlsVerifyCLI)
} else if cmd.Flags().Changed("insecure") {
insecure, _ := cmd.Flags().GetBool("insecure")
opts.SkipTLSVerify = types.NewOptionalBool(insecure)
}
buf, err := registry.ImageEngine().ManifestInspect(registry.Context(), args[0], opts)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions docs/source/markdown/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ podman-manifest-add.1.md
podman-manifest-annotate.1.md
podman-manifest-create.1.md
podman-manifest-push.1.md
podman-manifest-inspect.1.md
podman-pause.1.md
podman-pod-clone.1.md
podman-pod-create.1.md
Expand Down
2 changes: 1 addition & 1 deletion docs/source/markdown/options/tls-verify.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
####> This option file is used in:
####> podman build, container runlabel, create, kube play, login, manifest add, manifest create, manifest push, pull, push, run, search
####> podman build, container runlabel, create, kube play, login, manifest add, manifest create, manifest inspect, manifest push, pull, push, run, search
####> If you edit this file, make sure your changes
####> are applicable to all of those.
#### **--tls-verify**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
podman\-manifest\-inspect - Display a manifest list or image index

## SYNOPSIS
**podman manifest inspect** *listnameorindexname*
**podman manifest inspect** [*options*] *listnameorindexname*

## DESCRIPTION

Displays the manifest list or image index stored using the specified image name.

## RETURN VALUE

A formatted JSON representation of the manifest list or image index.

## OPTIONS

@@option tls-verify

## EXAMPLES

```
Expand Down
18 changes: 17 additions & 1 deletion pkg/api/handlers/libpod/manifests.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,26 @@ func ManifestExists(w http.ResponseWriter, r *http.Request) {

func ManifestInspect(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
name := utils.GetName(r)
// Wrapper to support 3.x with 4.x libpod
query := struct {
TLSVerify bool `schema:"tlsVerify"`
}{}

if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusBadRequest,
fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err))
return
}

imageEngine := abi.ImageEngine{Libpod: runtime}
rawManifest, err := imageEngine.ManifestInspect(r.Context(), name)
opts := entities.ManifestInspectOptions{}
if _, found := r.URL.Query()["tlsVerify"]; found {
opts.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
}

rawManifest, err := imageEngine.ManifestInspect(r.Context(), name, opts)
if err != nil {
utils.Error(w, http.StatusNotFound, err)
return
Expand Down
5 changes: 5 additions & 0 deletions pkg/api/server/register_manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// type: string
// required: true
// description: the name or ID of the manifest list
// - in: query
// name: tlsVerify
// type: boolean
// default: true
// description: Require HTTPS and verify signatures when contacting registries.
// responses:
// 200:
// $ref: "#/responses/manifestInspect"
Expand Down
18 changes: 16 additions & 2 deletions pkg/bindings/manifests/manifests.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,27 @@ func Exists(ctx context.Context, name string, options *ExistsOptions) (bool, err
}

// Inspect returns a manifest list for a given name.
func Inspect(ctx context.Context, name string, _ *InspectOptions) (*manifest.Schema2List, error) {
func Inspect(ctx context.Context, name string, options *InspectOptions) (*manifest.Schema2List, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
if options == nil {
options = new(InspectOptions)
}

params, err := options.ToParams()
if err != nil {
return nil, err
}
// SkipTLSVerify is special. We need to delete the param added by
// ToParams() and change the key and flip the bool
if options.SkipTLSVerify != nil {
params.Del("SkipTLSVerify")
params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify()))
}

response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/manifests/%s/json", nil, nil, name)
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/manifests/%s/json", params, nil, name)
if err != nil {
return nil, err
}
Expand Down
1 change: 1 addition & 0 deletions pkg/bindings/manifests/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package manifests
//
//go:generate go run ../generator/generator.go InspectOptions
type InspectOptions struct {
SkipTLSVerify *bool
}

// CreateOptions are optional options for creating manifests
Expand Down
15 changes: 15 additions & 0 deletions pkg/bindings/manifests/types_inspect_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/domain/entities/engine_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type ImageEngine interface { //nolint:interfacebloat
Untag(ctx context.Context, nameOrID string, tags []string, options ImageUntagOptions) error
ManifestCreate(ctx context.Context, name string, images []string, opts ManifestCreateOptions) (string, error)
ManifestExists(ctx context.Context, name string) (*BoolReport, error)
ManifestInspect(ctx context.Context, name string) ([]byte, error)
ManifestInspect(ctx context.Context, name string, opts ManifestInspectOptions) ([]byte, error)
ManifestAdd(ctx context.Context, listName string, imageNames []string, opts ManifestAddOptions) (string, error)
ManifestAnnotate(ctx context.Context, names, image string, opts ManifestAnnotateOptions) (string, error)
ManifestRemoveDigest(ctx context.Context, names, image string) (string, error)
Expand Down
6 changes: 6 additions & 0 deletions pkg/domain/entities/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ type ManifestCreateOptions struct {
SkipTLSVerify types.OptionalBool `json:"-" schema:"-"`
}

// ManifestInspectOptions provides model for inspecting manifest
type ManifestInspectOptions struct {
// Should TLS registry certificate be verified?
SkipTLSVerify types.OptionalBool `json:"-" schema:"-"`
}

// ManifestAddOptions provides model for adding digests to manifest list
//
// swagger:model
Expand Down
12 changes: 9 additions & 3 deletions pkg/domain/infra/abi/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/containers/image/v5/pkg/shortnames"
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/storage"
"github.com/opencontainers/go-digest"
Expand Down Expand Up @@ -67,7 +68,7 @@ func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entiti
}

// ManifestInspect returns the content of a manifest list or image
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) {
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) ([]byte, error) {
// NOTE: we have to do a bit of a limbo here as `podman manifest
// inspect foo` wants to do a remote-inspect of foo iff "foo" in the
// containers storage is an ordinary image but not a manifest list.
Expand All @@ -77,7 +78,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
if errors.Is(err, storage.ErrImageUnknown) || errors.Is(err, libimage.ErrNotAManifestList) {
// Do a remote inspect if there's no local image or if the
// local image is not a manifest list.
return ir.remoteManifestInspect(ctx, name)
return ir.remoteManifestInspect(ctx, name, opts)
}

return nil, err
Expand All @@ -101,9 +102,14 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte
}

// inspect a remote manifest list.
func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string) ([]byte, error) {
func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) ([]byte, error) {
sys := ir.Libpod.SystemContext()

sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify
if opts.SkipTLSVerify == types.OptionalBoolTrue {
sys.OCIInsecureSkipTLSVerify = true
}

resolved, err := shortnames.Resolve(sys, name)
if err != nil {
return nil, err
Expand Down
13 changes: 11 additions & 2 deletions pkg/domain/infra/tunnel/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,17 @@ func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entiti
}

// ManifestInspect returns contents of manifest list with given name
func (ir *ImageEngine) ManifestInspect(_ context.Context, name string) ([]byte, error) {
list, err := manifests.Inspect(ir.ClientCtx, name, nil)
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) ([]byte, error) {
options := new(manifests.InspectOptions)
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
if s == types.OptionalBoolTrue {
options.WithSkipTLSVerify(true)
} else {
options.WithSkipTLSVerify(false)
}
}

list, err := manifests.Inspect(ir.ClientCtx, name, options)
if err != nil {
return nil, fmt.Errorf("getting content of manifest list or image %s: %w", name, err)
}
Expand Down
14 changes: 0 additions & 14 deletions test/system/010-images.bats
Original file line number Diff line number Diff line change
Expand Up @@ -232,20 +232,6 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z
run_podman rmi ${aaa_name}:${aaa_tag} ${zzz_name}:${zzz_tag}
}

# Regression test for #8931
@test "podman images - bare manifest list" {
# Create an empty manifest list and list images.

run_podman inspect --format '{{.ID}}' $IMAGE
iid=$output

run_podman manifest create test:1.0
run_podman images --format '{{.ID}}' --no-trunc
[[ "$output" == *"sha256:$iid"* ]]

run_podman rmi test:1.0
}

@test "podman images - rmi -af removes all containers and pods" {
pname=$(random_string)
run_podman create --pod new:$pname $IMAGE
Expand Down
20 changes: 20 additions & 0 deletions test/system/012-manifest.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bats

load helpers

# Regression test for #8931
@test "podman images - bare manifest list" {
# Create an empty manifest list and list images.

run_podman inspect --format '{{.ID}}' $IMAGE
iid=$output

run_podman manifest create test:1.0
run_podman manifest inspect --verbose $output
is "$output" ".*\"mediaType\": \"application/vnd.docker.distribution.manifest.list.v2+json\"" "--insecure is a noop want to make sure manifest inspect is successful"
run_podman images --format '{{.ID}}' --no-trunc
is "$output" ".*sha256:$iid" "Original image ID still shown in podman-images output"
run_podman rmi test:1.0
}

# vim: filetype=sh
29 changes: 29 additions & 0 deletions test/system/150-login.bats
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,35 @@ function _test_skopeo_credential_sharing() {
rm -f $authfile
}

@test "podman manifest --tls-verify - basic test" {
run_podman login --tls-verify=false \
--username ${PODMAN_LOGIN_USER} \
--password-stdin \
localhost:${PODMAN_LOGIN_REGISTRY_PORT} <<<"${PODMAN_LOGIN_PASS}"
is "$output" "Login Succeeded!" "output from podman login"

manifest1="localhost:${PODMAN_LOGIN_REGISTRY_PORT}/test:1.0"
run_podman manifest create $manifest1
mid=$output
run_podman manifest push --authfile=$authfile \
--tls-verify=false $mid \
$manifest1
run_podman manifest rm $manifest1
run_podman manifest inspect --insecure $manifest1
is "$output" ".*\"mediaType\": \"application/vnd.docker.distribution.manifest.list.v2+json\"" "Verify --insecure works against an insecure registry"
run_podman 125 manifest inspect --insecure=false $manifest1
is "$output" ".*Error: reading image \"docker://$manifest1\": pinging container registry localhost:${PODMAN_LOGIN_REGISTRY_PORT}:" "Verify --insecure=false fails"
run_podman manifest inspect --tls-verify=false $manifest1
is "$output" ".*\"mediaType\": \"application/vnd.docker.distribution.manifest.list.v2+json\"" "Verify --tls-verify=false works against an insecure registry"
run_podman 125 manifest inspect --tls-verify=true $manifest1
is "$output" ".*Error: reading image \"docker://$manifest1\": pinging container registry localhost:${PODMAN_LOGIN_REGISTRY_PORT}:" "Verify --tls-verify=true fails"

# Now log out
run_podman logout localhost:${PODMAN_LOGIN_REGISTRY_PORT}
is "$output" "Removed login credentials for localhost:${PODMAN_LOGIN_REGISTRY_PORT}" \
"output from podman logout"
}

# END cooperation with skopeo
# END actual tests
###############################################################################
Expand Down