Skip to content

Commit

Permalink
PoC: ImageSignature endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
mfojtik committed Jan 17, 2017
1 parent 283a990 commit b410299
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 1 deletion.
34 changes: 33 additions & 1 deletion pkg/cmd/dockerregistry/dockerregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io/ioutil"
"net/http"
"os"
"strings"
"time"

log "github.com/Sirupsen/logrus"
Expand Down Expand Up @@ -75,7 +76,6 @@ func Execute(configFile io.Reader) {

// TODO add https scheme
adminRouter := app.NewRoute().PathPrefix("/admin/").Subrouter()

pruneAccessRecords := func(*http.Request) []auth.Access {
return []auth.Access{
{
Expand All @@ -98,6 +98,38 @@ func Execute(configFile io.Reader) {
pruneAccessRecords,
)

// openshiftRouter groups the OpenShift related registry extensions
openshiftRouter := app.NewRoute().PathPrefix("/openshift/").Subrouter()

signatureRouter := openshiftRouter.NewRoute().PathPrefix("/signature/").Subrouter()
signatureAccessRecords := func(*http.Request) []auth.Access {
return []auth.Access{
{
Resource: auth.Resource{
Type: "signature",
},
Action: "get",
},
}
}

app.RegisterRoute(
// GET /openshift/signature/<reference>
// FIXME: Because the reference.ReferenceRegexp is anchored, we have to strip the
// leading '^' from the regexp in order to match the image reference in a sub-path.
signatureRouter.Path("/{reference:"+strings.TrimPrefix(reference.ReferenceRegexp.String(), "^")+"}").Methods("GET"),
server.SignatureDispatcher,
handlers.NameNotRequired,
signatureAccessRecords,
)

// Advertise features supported by OpenShift
// FIXME: is there a way to advertise this only in /v2/ endpoint?
if app.Config.HTTP.Headers == nil {
app.Config.HTTP.Headers = http.Header{}
}
app.Config.HTTP.Headers.Set("X-Registry-Supports-Signatures", "1")

app.RegisterHealthChecks()
handler := alive("/", app)
// TODO: temporarily keep for backwards compatibility; remove in the future
Expand Down
8 changes: 8 additions & 0 deletions pkg/dockerregistry/server/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,14 @@ func (ac *AccessController) Authorized(ctx context.Context, accessRecords ...reg
}
}

case "signature":
switch access.Action {
case "get":
// For /signature/<reference> we pass the request to OpenShift API using the user
// token so the authorization happen on the OpenShift API side.
continue
}

case "admin":
switch access.Action {
case "prune":
Expand Down
91 changes: 91 additions & 0 deletions pkg/dockerregistry/server/signaturedispatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package server

import (
"encoding/json"
"fmt"
"net/http"

"k8s.io/kubernetes/pkg/api"
kapierrors "k8s.io/kubernetes/pkg/api/errors"

ctxu "github.com/docker/distribution/context"

"github.com/docker/distribution/context"
"github.com/docker/distribution/registry/api/errcode"
"github.com/docker/distribution/registry/handlers"

imageapi "github.com/openshift/origin/pkg/image/api"
imageapiv1 "github.com/openshift/origin/pkg/image/api/v1"

gorillahandlers "github.com/gorilla/handlers"
)

// SignatureDispatcher dispatch the signatures endpoint.
func SignatureDispatcher(ctx *handlers.Context, r *http.Request) http.Handler {
signatureHandler := &signatureHandler{Context: ctx, originalReference: ctxu.GetStringValue(ctx, "vars.reference")}
signatureHandler.Reference, _ = imageapi.ParseDockerImageReference(signatureHandler.originalReference)
return gorillahandlers.MethodHandler{
"GET": http.HandlerFunc(signatureHandler.Get),
}
}

type signatureHandler struct {
Context *handlers.Context
Reference imageapi.DockerImageReference
originalReference string
}

// Get serves the /signatures/<reference> endpoint. It requires the user token for the
// OpenShift authorization to fetch the ImageStreamImage and extract the Docker image
// signature from it which is then returned as versioned array to client.
func (s *signatureHandler) Get(w http.ResponseWriter, req *http.Request) {
context.GetLogger(s.Context).Debugf("(*signatureHandler).Get")
client, ok := UserClientFrom(s.Context)
if !ok {
s.handleError(s.Context, errcode.ErrorCodeUnknown.WithDetail("unable to get origin client"), w)
return
}

if len(s.Reference.Namespace) == 0 || len(s.Reference.Name) == 0 || len(s.Reference.ID) == 0 {
s.handleError(s.Context, errcode.ErrorCodeUnsupported.WithDetail(fmt.Sprintf("invalid image format %v", s.originalReference)), w)
return
}

image, err := client.ImageStreamImages(s.Reference.Namespace).Get(s.Reference.Name, s.Reference.ID)
if err != nil {
if kapierrors.IsNotFound(err) {
w.WriteHeader(http.StatusNotFound)
return
}
if kapierrors.IsUnauthorized(err) {
s.handleError(s.Context, errcode.ErrorCodeUnauthorized.WithDetail(fmt.Sprintf("not authorized to get image %q signature: %v", s.Reference.String(), err)), w)
return
}
s.handleError(s.Context, errcode.ErrorCodeUnknown.WithDetail(fmt.Sprintf("unable to get image %q signature: %v", s.Reference.String(), err)), w)
return
}

versionedSignatures := []imageapiv1.ImageSignature{}
if err := api.Scheme.Convert(&image.Image.Signatures, &versionedSignatures, nil); err != nil {
s.handleError(s.Context, errcode.ErrorCodeUnknown.WithDetail(fmt.Sprintf("failed to convert image signature to versioned object %v", err)), w)
return
}

data, err := json.Marshal(versionedSignatures)
if err != nil {
s.handleError(s.Context, errcode.ErrorCodeUnknown.WithDetail(fmt.Sprintf("failed to serialize image signature %v", err)), w)
return
}

w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write(data)
}

func (s *signatureHandler) handleError(ctx context.Context, err error, w http.ResponseWriter) {
context.GetLogger(s.Context).Errorf("(*signatureHandler).Get: %v", err)
ctx, w = context.WithResponseWriter(ctx, w)
if serveErr := errcode.ServeJSON(w, err); serveErr != nil {
context.GetResponseLogger(ctx).Errorf("error sending error response: %v", serveErr)
return
}
}

0 comments on commit b410299

Please sign in to comment.