From dc80267b594e41cf7e223821dc1446683f0cae36 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 13 May 2020 13:44:29 +0200 Subject: compat handlers: add X-Registry-Auth header support * Support the `X-Registry-Auth` http-request header. * The content of the header is a base64 encoded JSON payload which can either be a single auth config or a map of auth configs (user+pw or token) with the corresponding registries being the keys. Vanilla Docker, projectatomic Docker and the bindings are transparantly supported. * Add a hidden `--registries-conf` flag. Buildah exposes the same flag, mostly for testing purposes. * Do all credential parsing in the client (i.e., `cmd/podman`) pass the username and password in the backend instead of unparsed credentials. * Add a `pkg/auth` which handles most of the heavy lifting. * Go through the authentication-handling code of most commands, bindings and endpoints. Migrate them to the new code and fix issues as seen. A final evaluation and more tests is still required *after* this change. * The manifest-push endpoint is missing certain parameters and should use the ABI function instead. Adding auth-support isn't really possible without these parts working. * The container commands and endpoints (i.e., create and run) have not been changed yet. The APIs don't yet account for the authfile. * Add authentication tests to `pkg/bindings`. Fixes: #6384 Signed-off-by: Valentin Rothberg --- pkg/api/handlers/libpod/images.go | 58 +++++++++++++++--------------------- pkg/api/handlers/libpod/manifests.go | 4 +++ pkg/api/handlers/libpod/play.go | 20 ++++++++++++- 3 files changed, 47 insertions(+), 35 deletions(-) (limited to 'pkg/api/handlers/libpod') diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 1cbcfb52c..4b277d39c 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -21,6 +21,7 @@ import ( image2 "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/auth" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/infra/abi" "github.com/containers/libpod/pkg/errorhandling" @@ -28,6 +29,7 @@ import ( utils2 "github.com/containers/libpod/utils" "github.com/gorilla/schema" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // Commit @@ -339,7 +341,6 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { Reference string `schema:"reference"` - Credentials string `schema:"credentials"` OverrideOS string `schema:"overrideOS"` OverrideArch string `schema:"overrideArch"` TLSVerify bool `schema:"tlsVerify"` @@ -382,20 +383,16 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { return } - var registryCreds *types.DockerAuthConfig - if len(query.Credentials) != 0 { - creds, err := util.ParseRegistryCreds(query.Credentials) - if err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "error parsing credentials %q", query.Credentials)) - return - } - registryCreds = creds + authConf, authfile, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String())) + return } + defer auth.RemoveAuthfile(authfile) // Setup the registry options dockerRegistryOptions := image.DockerRegistryOptions{ - DockerRegistryCreds: registryCreds, + DockerRegistryCreds: authConf, OSChoice: query.OverrideOS, ArchitectureChoice: query.OverrideArch, } @@ -403,6 +400,13 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) } + sys := runtime.SystemContext() + if sys == nil { + sys = image.GetSystemContext("", authfile, false) + } + dockerRegistryOptions.DockerCertPath = sys.DockerCertPath + sys.DockerAuthConfig = authConf + // Prepare the images we want to pull imagesToPull := []string{} res := []handlers.LibpodImagesPullReport{} @@ -411,8 +415,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { if !query.AllTags { imagesToPull = append(imagesToPull, imageName) } else { - systemContext := image.GetSystemContext("", "", false) - tags, err := docker.GetRepositoryTags(context.Background(), systemContext, imageRef) + tags, err := docker.GetRepositoryTags(context.Background(), sys, imageRef) if err != nil { utils.InternalServerError(w, errors.Wrap(err, "error getting repository tags")) return @@ -422,12 +425,6 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { } } - authfile := "" - if sys := runtime.SystemContext(); sys != nil { - dockerRegistryOptions.DockerCertPath = sys.DockerCertPath - authfile = sys.AuthFilePath - } - // Finally pull the images for _, img := range imagesToPull { newImage, err := runtime.ImageRuntime().New( @@ -456,7 +453,6 @@ func PushImage(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) query := struct { - Credentials string `schema:"credentials"` Destination string `schema:"destination"` TLSVerify bool `schema:"tlsVerify"` }{ @@ -492,26 +488,20 @@ func PushImage(w http.ResponseWriter, r *http.Request) { return } - var registryCreds *types.DockerAuthConfig - if len(query.Credentials) != 0 { - creds, err := util.ParseRegistryCreds(query.Credentials) - if err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, - errors.Wrapf(err, "error parsing credentials %q", query.Credentials)) - return - } - registryCreds = creds + authConf, authfile, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String())) + return } + defer auth.RemoveAuthfile(authfile) + logrus.Errorf("AuthConf: %v", authConf) - // TODO: the X-Registry-Auth header is not checked yet here nor in any other - // endpoint. Pushing does NOT work with authentication at the moment. dockerRegistryOptions := &image.DockerRegistryOptions{ - DockerRegistryCreds: registryCreds, + DockerRegistryCreds: authConf, } - authfile := "" if sys := runtime.SystemContext(); sys != nil { dockerRegistryOptions.DockerCertPath = sys.DockerCertPath - authfile = sys.AuthFilePath + dockerRegistryOptions.RegistriesConfPath = sys.SystemRegistriesConfPath } if _, found := r.URL.Query()["tlsVerify"]; found { dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index 93ca367f7..aef92368b 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -120,6 +120,10 @@ func ManifestRemove(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID}) } func ManifestPush(w http.ResponseWriter, r *http.Request) { + // FIXME: parameters are missing (tlsVerify, format). + // Also, we should use the ABI function to avoid duplicate code. + // Also, support for XRegistryAuth headers are missing. + runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go index 26e02bf4f..1cb5cdb6c 100644 --- a/pkg/api/handlers/libpod/play.go +++ b/pkg/api/handlers/libpod/play.go @@ -9,6 +9,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/auth" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/infra/abi" "github.com/gorilla/schema" @@ -47,9 +48,26 @@ func PlayKube(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error closing temporary file")) return } + authConf, authfile, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", auth.XRegistryAuthHeader, r.URL.String())) + return + } + defer auth.RemoveAuthfile(authfile) + var username, password string + if authConf != nil { + username = authConf.Username + password = authConf.Password + } containerEngine := abi.ContainerEngine{Libpod: runtime} - options := entities.PlayKubeOptions{Network: query.Network, Quiet: true} + options := entities.PlayKubeOptions{ + Authfile: authfile, + Username: username, + Password: password, + Network: query.Network, + Quiet: true, + } if _, found := r.URL.Query()["tlsVerify"]; found { options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) } -- cgit v1.2.3-54-g00ecf