summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjason <jason@towerofpower.montleon.intra>2022-03-24 16:07:36 -0400
committerJason Montleon <jmontleo@redhat.com>2022-03-26 16:39:11 -0400
commit3cc17393734a54320968b3cc46cad83f17111492 (patch)
treed76452c03777ba3330e89f97675a61e62937a84c
parenta416fd6de40820e266e9e1e312c5f13bbb0b189f (diff)
downloadpodman-3cc17393734a54320968b3cc46cad83f17111492.tar.gz
podman-3cc17393734a54320968b3cc46cad83f17111492.tar.bz2
podman-3cc17393734a54320968b3cc46cad83f17111492.zip
Resolves #13629 Add RegistryAuthHeader to manifest push
Signed-off-by: Jason Montleon <jmontleo@redhat.com>
-rwxr-xr-xcontrib/cirrus/setup_environment.sh2
-rw-r--r--pkg/api/handlers/libpod/manifests.go44
-rw-r--r--pkg/bindings/manifests/manifests.go78
-rw-r--r--pkg/bindings/manifests/types.go45
-rw-r--r--pkg/bindings/manifests/types_add_options.go60
-rw-r--r--pkg/bindings/manifests/types_modify_options.go60
-rw-r--r--pkg/domain/infra/tunnel/manifest.go8
-rw-r--r--test/e2e/manifest_test.go37
8 files changed, 297 insertions, 37 deletions
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index 864c78484..906a898b2 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -36,6 +36,8 @@ do
fi
done
+cp hack/podman-registry /bin
+
# Make sure cni network plugins directory exists
mkdir -p /etc/cni/net.d
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
index 250736579..ad9ad88d1 100644
--- a/pkg/api/handlers/libpod/manifests.go
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -162,13 +162,35 @@ func ManifestAdd(w http.ResponseWriter, r *http.Request) {
// Wrapper to support 3.x with 4.x libpod
query := struct {
entities.ManifestAddOptions
- Images []string
+ Images []string
+ TLSVerify bool `schema:"tlsVerify"`
}{}
if err := json.NewDecoder(r.Body).Decode(&query); err != nil {
utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return
}
+ authconf, authfile, err := auth.GetCredentials(r)
+ if err != nil {
+ utils.Error(w, http.StatusBadRequest, err)
+ return
+ }
+ defer auth.RemoveAuthfile(authfile)
+ var username, password string
+ if authconf != nil {
+ username = authconf.Username
+ password = authconf.Password
+ }
+ query.ManifestAddOptions.Authfile = authfile
+ query.ManifestAddOptions.Username = username
+ query.ManifestAddOptions.Password = password
+ if sys := runtime.SystemContext(); sys != nil {
+ query.ManifestAddOptions.CertDir = sys.DockerCertPath
+ }
+ if _, found := r.URL.Query()["tlsVerify"]; found {
+ query.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
+ }
+
name := utils.GetName(r)
if _, err := runtime.LibimageRuntime().LookupManifestList(name); err != nil {
utils.Error(w, http.StatusNotFound, err)
@@ -271,7 +293,7 @@ func ManifestPushV3(w http.ResponseWriter, r *http.Request) {
utils.Error(w, http.StatusBadRequest, errors.Wrapf(err, "error pushing image %q", query.Destination))
return
}
- utils.WriteResponse(w, http.StatusOK, digest)
+ utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: digest})
}
// ManifestPush push image to registry
@@ -350,6 +372,24 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) {
return
}
+ authconf, authfile, err := auth.GetCredentials(r)
+ if err != nil {
+ utils.Error(w, http.StatusBadRequest, err)
+ return
+ }
+ defer auth.RemoveAuthfile(authfile)
+ var username, password string
+ if authconf != nil {
+ username = authconf.Username
+ password = authconf.Password
+ }
+ body.ManifestAddOptions.Authfile = authfile
+ body.ManifestAddOptions.Username = username
+ body.ManifestAddOptions.Password = password
+ if sys := runtime.SystemContext(); sys != nil {
+ body.ManifestAddOptions.CertDir = sys.DockerCertPath
+ }
+
var report entities.ManifestModifyReport
switch {
case strings.EqualFold("update", body.Operation):
diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go
index 458cb913a..a8f576ecd 100644
--- a/pkg/bindings/manifests/manifests.go
+++ b/pkg/bindings/manifests/manifests.go
@@ -11,7 +11,9 @@ import (
"github.com/blang/semver"
"github.com/containers/image/v5/manifest"
+ imageTypes "github.com/containers/image/v5/types"
"github.com/containers/podman/v4/pkg/api/handlers"
+ "github.com/containers/podman/v4/pkg/auth"
"github.com/containers/podman/v4/pkg/bindings"
"github.com/containers/podman/v4/pkg/bindings/images"
"github.com/containers/podman/v4/version"
@@ -93,15 +95,19 @@ func Add(ctx context.Context, name string, options *AddOptions) (string, error)
if bindings.ServiceVersion(ctx).GTE(semver.MustParse("4.0.0")) {
optionsv4 := ModifyOptions{
- All: options.All,
- Annotations: options.Annotation,
- Arch: options.Arch,
- Features: options.Features,
- Images: options.Images,
- OS: options.OS,
- OSFeatures: nil,
- OSVersion: options.OSVersion,
- Variant: options.Variant,
+ All: options.All,
+ Annotations: options.Annotation,
+ Arch: options.Arch,
+ Features: options.Features,
+ Images: options.Images,
+ OS: options.OS,
+ OSFeatures: nil,
+ OSVersion: options.OSVersion,
+ Variant: options.Variant,
+ Username: options.Username,
+ Password: options.Password,
+ Authfile: options.Authfile,
+ SkipTLSVerify: options.SkipTLSVerify,
}
optionsv4.WithOperation("update")
return Modify(ctx, name, options.Images, &optionsv4)
@@ -118,11 +124,27 @@ func Add(ctx context.Context, name string, options *AddOptions) (string, error)
}
reader := strings.NewReader(opts)
- headers := make(http.Header)
+ header, err := auth.MakeXRegistryAuthHeader(&imageTypes.SystemContext{AuthFilePath: options.GetAuthfile()}, options.GetUsername(), options.GetPassword())
+ if err != nil {
+ return "", err
+ }
+
+ params, err := options.ToParams()
+ if err != nil {
+ return "", 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()))
+ }
+
v := version.APIVersion[version.Libpod][version.MinimalAPI]
- headers.Add("API-Version",
+ header.Add("API-Version",
fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch))
- response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/manifests/%s/add", nil, headers, name)
+
+ response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/manifests/%s/add", params, header, name)
if err != nil {
return "", err
}
@@ -179,6 +201,14 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt
return "", err
}
+ header, err := auth.MakeXRegistryAuthHeader(&imageTypes.SystemContext{AuthFilePath: options.GetAuthfile()}, options.GetUsername(), options.GetPassword())
+ if err != nil {
+ return "", err
+ }
+ v := version.APIVersion[version.Libpod][version.MinimalAPI]
+ header.Add("API-Version",
+ fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch))
+
params, err := options.ToParams()
if err != nil {
return "", err
@@ -192,18 +222,18 @@ func Push(ctx context.Context, name, destination string, options *images.PushOpt
var response *bindings.APIResponse
if bindings.ServiceVersion(ctx).GTE(semver.MustParse("4.0.0")) {
- response, err = conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/registry/%s", params, nil, name, destination)
+ response, err = conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/registry/%s", params, header, name, destination)
} else {
params.Set("image", name)
params.Set("destination", destination)
- response, err = conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/push", params, nil, name)
+ response, err = conn.DoRequest(ctx, nil, http.MethodPost, "/manifests/%s/push", params, header, name)
}
if err != nil {
return "", err
}
defer response.Body.Close()
- return idr.ID, err
+ return idr.ID, response.Process(&idr)
}
// Modify modifies the given manifest list using options and the optional list of images
@@ -223,7 +253,23 @@ func Modify(ctx context.Context, name string, images []string, options *ModifyOp
}
reader := strings.NewReader(opts)
- response, err := conn.DoRequest(ctx, reader, http.MethodPut, "/manifests/%s", nil, nil, name)
+ header, err := auth.MakeXRegistryAuthHeader(&imageTypes.SystemContext{AuthFilePath: options.GetAuthfile()}, options.GetUsername(), options.GetPassword())
+ if err != nil {
+ return "", err
+ }
+
+ params, err := options.ToParams()
+ if err != nil {
+ return "", 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, reader, http.MethodPut, "/manifests/%s", params, header, name)
if err != nil {
return "", err
}
diff --git a/pkg/bindings/manifests/types.go b/pkg/bindings/manifests/types.go
index 5ff28ee30..d0b0b2e71 100644
--- a/pkg/bindings/manifests/types.go
+++ b/pkg/bindings/manifests/types.go
@@ -20,14 +20,18 @@ type ExistsOptions struct {
//go:generate go run ../generator/generator.go AddOptions
// AddOptions are optional options for adding manifest lists
type AddOptions struct {
- All *bool
- Annotation map[string]string
- Arch *string
- Features []string
- Images []string
- OS *string
- OSVersion *string
- Variant *string
+ All *bool
+ Annotation map[string]string
+ Arch *string
+ Features []string
+ Images []string
+ OS *string
+ OSVersion *string
+ Variant *string
+ Authfile *string
+ Password *string
+ Username *string
+ SkipTLSVerify *bool
}
//go:generate go run ../generator/generator.go RemoveOptions
@@ -40,15 +44,18 @@ type RemoveOptions struct {
type ModifyOptions struct {
// Operation values are "update", "remove" and "annotate". This allows the service to
// efficiently perform each update on a manifest list.
- Operation *string
- All *bool // All when true, operate on all images in a manifest list that may be included in Images
- Annotations map[string]string // Annotations to add to manifest list
- Arch *string // Arch overrides the architecture for the image
- Features []string // Feature list for the image
- Images []string // Images is an optional list of images to add/remove to/from manifest list depending on operation
- OS *string // OS overrides the operating system for the image
- OSFeatures []string // OS features for the image
- OSVersion *string // OSVersion overrides the operating system for the image
- Variant *string // Variant overrides the operating system variant for the image
-
+ Operation *string
+ All *bool // All when true, operate on all images in a manifest list that may be included in Images
+ Annotations map[string]string // Annotations to add to manifest list
+ Arch *string // Arch overrides the architecture for the image
+ Features []string // Feature list for the image
+ Images []string // Images is an optional list of images to add/remove to/from manifest list depending on operation
+ OS *string // OS overrides the operating system for the image
+ OSFeatures []string // OS features for the image
+ OSVersion *string // OSVersion overrides the operating system for the image
+ Variant *string // Variant overrides the operating system variant for the image
+ Authfile *string
+ Password *string
+ Username *string
+ SkipTLSVerify *bool
}
diff --git a/pkg/bindings/manifests/types_add_options.go b/pkg/bindings/manifests/types_add_options.go
index 0696a69b6..5ba1cc5fa 100644
--- a/pkg/bindings/manifests/types_add_options.go
+++ b/pkg/bindings/manifests/types_add_options.go
@@ -136,3 +136,63 @@ func (o *AddOptions) GetVariant() string {
}
return *o.Variant
}
+
+// WithAuthfile set field Authfile to given value
+func (o *AddOptions) WithAuthfile(value string) *AddOptions {
+ o.Authfile = &value
+ return o
+}
+
+// GetAuthfile returns value of field Authfile
+func (o *AddOptions) GetAuthfile() string {
+ if o.Authfile == nil {
+ var z string
+ return z
+ }
+ return *o.Authfile
+}
+
+// WithPassword set field Password to given value
+func (o *AddOptions) WithPassword(value string) *AddOptions {
+ o.Password = &value
+ return o
+}
+
+// GetPassword returns value of field Password
+func (o *AddOptions) GetPassword() string {
+ if o.Password == nil {
+ var z string
+ return z
+ }
+ return *o.Password
+}
+
+// WithUsername set field Username to given value
+func (o *AddOptions) WithUsername(value string) *AddOptions {
+ o.Username = &value
+ return o
+}
+
+// GetUsername returns value of field Username
+func (o *AddOptions) GetUsername() string {
+ if o.Username == nil {
+ var z string
+ return z
+ }
+ return *o.Username
+}
+
+// WithSkipTLSVerify set field SkipTLSVerify to given value
+func (o *AddOptions) WithSkipTLSVerify(value bool) *AddOptions {
+ o.SkipTLSVerify = &value
+ return o
+}
+
+// GetSkipTLSVerify returns value of field SkipTLSVerify
+func (o *AddOptions) GetSkipTLSVerify() bool {
+ if o.SkipTLSVerify == nil {
+ var z bool
+ return z
+ }
+ return *o.SkipTLSVerify
+}
diff --git a/pkg/bindings/manifests/types_modify_options.go b/pkg/bindings/manifests/types_modify_options.go
index 6d75c1e5f..9d2ed2613 100644
--- a/pkg/bindings/manifests/types_modify_options.go
+++ b/pkg/bindings/manifests/types_modify_options.go
@@ -166,3 +166,63 @@ func (o *ModifyOptions) GetVariant() string {
}
return *o.Variant
}
+
+// WithAuthfile set field Authfile to given value
+func (o *ModifyOptions) WithAuthfile(value string) *ModifyOptions {
+ o.Authfile = &value
+ return o
+}
+
+// GetAuthfile returns value of field Authfile
+func (o *ModifyOptions) GetAuthfile() string {
+ if o.Authfile == nil {
+ var z string
+ return z
+ }
+ return *o.Authfile
+}
+
+// WithPassword set field Password to given value
+func (o *ModifyOptions) WithPassword(value string) *ModifyOptions {
+ o.Password = &value
+ return o
+}
+
+// GetPassword returns value of field Password
+func (o *ModifyOptions) GetPassword() string {
+ if o.Password == nil {
+ var z string
+ return z
+ }
+ return *o.Password
+}
+
+// WithUsername set field Username to given value
+func (o *ModifyOptions) WithUsername(value string) *ModifyOptions {
+ o.Username = &value
+ return o
+}
+
+// GetUsername returns value of field Username
+func (o *ModifyOptions) GetUsername() string {
+ if o.Username == nil {
+ var z string
+ return z
+ }
+ return *o.Username
+}
+
+// WithSkipTLSVerify set field SkipTLSVerify to given value
+func (o *ModifyOptions) WithSkipTLSVerify(value bool) *ModifyOptions {
+ o.SkipTLSVerify = &value
+ return o
+}
+
+// GetSkipTLSVerify returns value of field SkipTLSVerify
+func (o *ModifyOptions) GetSkipTLSVerify() bool {
+ if o.SkipTLSVerify == nil {
+ var z bool
+ return z
+ }
+ return *o.SkipTLSVerify
+}
diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go
index d2efed8d3..9ac3fdb83 100644
--- a/pkg/domain/infra/tunnel/manifest.go
+++ b/pkg/domain/infra/tunnel/manifest.go
@@ -50,6 +50,7 @@ func (ir *ImageEngine) ManifestInspect(_ context.Context, name string) ([]byte,
func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames []string, opts entities.ManifestAddOptions) (string, error) {
options := new(manifests.AddOptions).WithAll(opts.All).WithArch(opts.Arch).WithVariant(opts.Variant)
options.WithFeatures(opts.Features).WithImages(imageNames).WithOS(opts.OS).WithOSVersion(opts.OSVersion)
+ options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile)
if len(opts.Annotation) != 0 {
annotations := make(map[string]string)
for _, annotationSpec := range opts.Annotation {
@@ -61,6 +62,13 @@ func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames []
}
options.WithAnnotation(annotations)
}
+ if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
+ if s == types.OptionalBoolTrue {
+ options.WithSkipTLSVerify(true)
+ } else {
+ options.WithSkipTLSVerify(false)
+ }
+ }
id, err := manifests.Add(ir.ClientCtx, name, options)
if err != nil {
diff --git a/test/e2e/manifest_test.go b/test/e2e/manifest_test.go
index eaa9cdae6..6e029d3a4 100644
--- a/test/e2e/manifest_test.go
+++ b/test/e2e/manifest_test.go
@@ -5,6 +5,7 @@ import (
"path/filepath"
"strings"
+ podmanRegistry "github.com/containers/podman/v4/hack/podman-registry-go"
. "github.com/containers/podman/v4/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -272,6 +273,42 @@ var _ = Describe("Podman manifest", func() {
))
})
+ It("authenticated push", func() {
+ registry, err := podmanRegistry.Start()
+ Expect(err).To(BeNil())
+
+ session := podmanTest.Podman([]string{"manifest", "create", "foo"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"pull", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"tag", ALPINE, "localhost:" + registry.Port + "/alpine:latest"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--creds=" + registry.User + ":" + registry.Password, "--format=v2s2", "localhost:" + registry.Port + "/alpine:latest"})
+ push.WaitWithDefaultTimeout()
+ Expect(push).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"manifest", "add", "--tls-verify=false", "--creds=" + registry.User + ":" + registry.Password, "foo", "localhost:" + registry.Port + "/alpine:latest"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ push = podmanTest.Podman([]string{"manifest", "push", "--tls-verify=false", "--creds=" + registry.User + ":" + registry.Password, "foo", "localhost:" + registry.Port + "/credstest"})
+ push.WaitWithDefaultTimeout()
+ Expect(push).Should(Exit(0))
+
+ push = podmanTest.Podman([]string{"manifest", "push", "--tls-verify=false", "--creds=podmantest:wrongpasswd", "foo", "localhost:" + registry.Port + "/credstest"})
+ push.WaitWithDefaultTimeout()
+ Expect(push).To(ExitWithError())
+
+ err = registry.Stop()
+ Expect(err).To(BeNil())
+ })
+
It("push --rm", func() {
SkipIfRemote("remote does not support --rm")
session := podmanTest.Podman([]string{"manifest", "create", "foo"})