From 5aead1509c681de533b8966e781e15327fe35ab6 Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Wed, 23 Sep 2020 08:19:05 -0700 Subject: Add X-Registry-Config support * Refactor auth pkg to support X-Registry-Config * Refactor build endpoint to support X-Registry-Config. Supports: * --creds * --authfile * Added X-Reference-Id Header to http.Request to support log event correlation * Log headers from http.Request Signed-off-by: Jhon Honce --- pkg/api/handlers/compat/images.go | 12 ++++++------ pkg/api/handlers/compat/images_build.go | 26 ++++++++++++++------------ pkg/api/handlers/compat/images_push.go | 4 ++-- pkg/api/handlers/libpod/images.go | 4 ++-- pkg/api/handlers/libpod/images_pull.go | 4 ++-- pkg/api/handlers/libpod/play.go | 4 ++-- pkg/api/server/handler_api.go | 23 +++++++++++++++++++---- 7 files changed, 47 insertions(+), 30 deletions(-) (limited to 'pkg/api') diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 0900d1793..940b57343 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -93,7 +93,7 @@ func PruneImages(w http.ResponseWriter, r *http.Request) { }) } - //FIXME/TODO to do this exactly correct, pruneimages needs to return idrs and space-reclaimed, then we are golden + // FIXME/TODO to do this exactly correct, pruneimages needs to return idrs and space-reclaimed, then we are golden ipr := types.ImagesPruneReport{ ImagesDeleted: idr, SpaceReclaimed: 1, // TODO we cannot supply this right now @@ -113,7 +113,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { Changes string `schema:"changes"` Comment string `schema:"comment"` Container string `schema:"container"` - //fromSrc string # fromSrc is currently unused + // fromSrc string # fromSrc is currently unused Pause bool `schema:"pause"` Repo string `schema:"repo"` Tag string `schema:"tag"` @@ -224,7 +224,7 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { Status string `json:"status"` Progress string `json:"progress"` ProgressDetail map[string]string `json:"progressDetail"` - Id string `json:"id"` //nolint + Id string `json:"id"` // nolint }{ Status: iid, ProgressDetail: map[string]string{}, @@ -257,9 +257,9 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { fromImage = fmt.Sprintf("%s:%s", fromImage, query.Tag) } - authConf, authfile, err := auth.GetCredentials(r) + authConf, authfile, key, 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())) + utils.Error(w, "Failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", key, r.URL.String())) return } defer auth.RemoveAuthfile(authfile) @@ -299,7 +299,7 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { Error string `json:"error"` Progress string `json:"progress"` ProgressDetail map[string]string `json:"progressDetail"` - Id string `json:"id"` //nolint + Id string `json:"id"` // nolint }{ Status: fmt.Sprintf("pulling image (%s) from %s", img.Tag, strings.Join(img.Names(), ", ")), ProgressDetail: map[string]string{}, diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index fbaf8d10a..0ce70975d 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -2,7 +2,6 @@ package compat import ( "context" - "encoding/base64" "encoding/json" "fmt" "io" @@ -11,13 +10,13 @@ import ( "os" "path/filepath" "strconv" - "strings" "github.com/containers/buildah" "github.com/containers/buildah/imagebuildah" + "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/handlers/utils" + "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/channel" "github.com/containers/storage/pkg/archive" "github.com/gorilla/schema" @@ -26,15 +25,6 @@ import ( ) func BuildImage(w http.ResponseWriter, r *http.Request) { - authConfigs := map[string]handlers.AuthConfig{} - if hdr, found := r.Header["X-Registry-Config"]; found && len(hdr) > 0 { - authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(hdr[0])) - if json.NewDecoder(authConfigsJSON).Decode(&authConfigs) != nil { - utils.BadRequest(w, "X-Registry-Config", hdr[0], json.NewDecoder(authConfigsJSON).Decode(&authConfigs)) - return - } - } - if hdr, found := r.Header["Content-Type"]; found && len(hdr) > 0 { contentType := hdr[0] switch contentType { @@ -151,6 +141,14 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } } + creds, authfile, key, err := auth.GetCredentials(r) + if err != nil { + // Credential value(s) not returned as their value is not human readable + utils.BadRequest(w, key.String(), "n/a", err) + return + } + defer auth.RemoveAuthfile(authfile) + // Channels all mux'ed in select{} below to follow API build protocol stdout := channel.NewWriter(make(chan []byte, 1)) defer stdout.Close() @@ -179,6 +177,10 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { Err: auxout, ReportWriter: reporter, OutputFormat: buildah.Dockerv2ImageManifest, + SystemContext: &types.SystemContext{ + AuthFilePath: authfile, + DockerAuthConfig: creds, + }, CommonBuildOpts: &buildah.CommonBuildOptions{ CPUPeriod: query.CpuPeriod, CPUQuota: query.CpuQuota, diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index e69a2212a..dd706a156 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -49,9 +49,9 @@ func PushImage(w http.ResponseWriter, r *http.Request) { return } - authConf, authfile, err := auth.GetCredentials(r) + authConf, authfile, key, 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())) + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", key, r.URL.String())) return } defer auth.RemoveAuthfile(authfile) diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index bc1bdc287..3ebf6e4c6 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -438,9 +438,9 @@ func PushImage(w http.ResponseWriter, r *http.Request) { return } - authConf, authfile, err := auth.GetCredentials(r) + authConf, authfile, key, 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())) + utils.Error(w, "Failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", key, r.URL.String())) return } defer auth.RemoveAuthfile(authfile) diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go index ad8d1f38e..791ef7a48 100644 --- a/pkg/api/handlers/libpod/images_pull.go +++ b/pkg/api/handlers/libpod/images_pull.go @@ -74,9 +74,9 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { return } - authConf, authfile, err := auth.GetCredentials(r) + authConf, authfile, key, 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())) + utils.Error(w, "Failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", key, r.URL.String())) return } defer auth.RemoveAuthfile(authfile) diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go index 59f78da8c..2296e170a 100644 --- a/pkg/api/handlers/libpod/play.go +++ b/pkg/api/handlers/libpod/play.go @@ -48,9 +48,9 @@ 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) + authConf, authfile, key, 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())) + utils.Error(w, "Failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse %q header for %s", key, r.URL.String())) return } defer auth.RemoveAuthfile(authfile) diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go index 920811c51..28f5a0b42 100644 --- a/pkg/api/server/handler_api.go +++ b/pkg/api/server/handler_api.go @@ -7,7 +7,9 @@ import ( "runtime" "github.com/containers/podman/v2/pkg/api/handlers/utils" - log "github.com/sirupsen/logrus" + "github.com/containers/podman/v2/pkg/auth" + "github.com/google/uuid" + "github.com/sirupsen/logrus" ) // APIHandler is a wrapper to enhance HandlerFunc's and remove redundant code @@ -19,7 +21,7 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { if err != nil { buf := make([]byte, 1<<20) n := runtime.Stack(buf, true) - log.Warnf("Recovering from API handler panic: %v, %s", err, buf[:n]) + logrus.Warnf("Recovering from API handler panic: %v, %s", err, buf[:n]) // Try to inform client things went south... won't work if handler already started writing response body utils.InternalServerError(w, fmt.Errorf("%v", err)) } @@ -27,10 +29,23 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { // Wrapper to hide some boiler plate fn := func(w http.ResponseWriter, r *http.Request) { - log.Debugf("APIHandler -- Method: %s URL: %s", r.Method, r.URL.String()) + rid := uuid.New().String() + if logrus.IsLevelEnabled(logrus.DebugLevel) { + logrus.Debugf("APIHandler(%s) -- Method: %s URL: %s", rid, r.Method, r.URL.String()) + for k, v := range r.Header { + switch auth.HeaderAuthName(k) { + case auth.XRegistryConfigHeader, auth.XRegistryAuthHeader: + logrus.Debugf("APIHandler(%s) -- Header: %s: ", rid, k) + default: + logrus.Debugf("APIHandler(%s) -- Header: %s: %v", rid, k, v) + } + } + } + // Set in case handler wishes to correlate logging events + r.Header.Set("X-Reference-Id", rid) if err := r.ParseForm(); err != nil { - log.Infof("Failed Request: unable to parse form: %q", err) + logrus.Infof("Failed Request: unable to parse form: %q (%s)", err, rid) } // TODO: Use r.ConnContext when ported to go 1.13 -- cgit v1.2.3-54-g00ecf