api/worker: introduce optional authorization

This allows clients to specify the contents of the HTTP Authorization
header so that Clair can access protected resources.
This commit is contained in:
Jimmy Zelinskie 2016-05-04 15:47:14 -04:00
parent 01efa56929
commit 9b5afc79ca
6 changed files with 24 additions and 12 deletions

View File

@ -59,6 +59,7 @@ Server: clair
The POST route for the Layers resource performs the indexing of a Layer from the provided path and displays the provided Layer with an updated `IndexByVersion` property. The POST route for the Layers resource performs the indexing of a Layer from the provided path and displays the provided Layer with an updated `IndexByVersion` property.
This request blocks for the entire duration of the downloading and indexing of the layer. This request blocks for the entire duration of the downloading and indexing of the layer.
The Authorization field is an optional value whose contents will fill the Authorization HTTP Header when requesting the layer via HTTP.
###### Example Request ###### Example Request
@ -68,7 +69,8 @@ POST http://localhost:6060/v1/layers HTTP/1.1
{ {
"Layer": { "Layer": {
"Name": "523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6", "Name": "523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6",
"Path": "/mnt/layers/523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6/layer.tar", "Path": "https://mystorage.com/layers/523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6/layer.tar",
"Authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.EkN-DOsnsuRjRO6BxXemmJDm3HbxrbRzXglbN2S4sOkopdU4IsDxTI8jO19W_A4K8ZPJijNLis4EZsHeY559a4DFOd50_OqgHGuERTqYZyuhtF39yxJPAjUESwxk2J5k_4zM3O-vtd1Ghyo4IbqKKSy6J9mTniYJPenn5-HIirE",
"ParentName": "140f9bdfeb9784cf8730e9dab5dd12fbd704151cf555ac8cae650451794e5ac2", "ParentName": "140f9bdfeb9784cf8730e9dab5dd12fbd704151cf555ac8cae650451794e5ac2",
"Format": "Docker" "Format": "Docker"
} }
@ -85,7 +87,8 @@ Server: clair
{ {
"Layer": { "Layer": {
"Name": "523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6", "Name": "523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6",
"Path": "/mnt/layers/523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6/layer.tar", "Path": "https://mystorage.com/layers/523ef1d23f222195488575f52a39c729c76a8c5630c9a194139cb246fb212da6/layer.tar",
"Authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.EkN-DOsnsuRjRO6BxXemmJDm3HbxrbRzXglbN2S4sOkopdU4IsDxTI8jO19W_A4K8ZPJijNLis4EZsHeY559a4DFOd50_OqgHGuERTqYZyuhtF39yxJPAjUESwxk2J5k_4zM3O-vtd1Ghyo4IbqKKSy6J9mTniYJPenn5-HIirE",
"ParentName": "140f9bdfeb9784cf8730e9dab5dd12fbd704151cf555ac8cae650451794e5ac2", "ParentName": "140f9bdfeb9784cf8730e9dab5dd12fbd704151cf555ac8cae650451794e5ac2",
"Format": "Docker", "Format": "Docker",
"IndexedByVersion": 1 "IndexedByVersion": 1

View File

@ -37,6 +37,7 @@ type Layer struct {
Name string `json:"Name,omitempty"` Name string `json:"Name,omitempty"`
NamespaceName string `json:"NamespaceName,omitempty"` NamespaceName string `json:"NamespaceName,omitempty"`
Path string `json:"Path,omitempty"` Path string `json:"Path,omitempty"`
Authorization string `json:"Authorization,omitempty"`
ParentName string `json:"ParentName,omitempty"` ParentName string `json:"ParentName,omitempty"`
Format string `json:"Format,omitempty"` Format string `json:"Format,omitempty"`
IndexedByVersion int `json:"IndexedByVersion,omitempty"` IndexedByVersion int `json:"IndexedByVersion,omitempty"`

View File

@ -109,7 +109,7 @@ func postLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx
return postLayerRoute, http.StatusBadRequest return postLayerRoute, http.StatusBadRequest
} }
err = worker.Process(ctx.Store, request.Layer.Name, request.Layer.ParentName, request.Layer.Path, request.Layer.Format) err = worker.Process(ctx.Store, request.Layer.Name, request.Layer.ParentName, request.Layer.Path, request.Layer.Authorization, request.Layer.Format)
if err != nil { if err != nil {
if err == utils.ErrCouldNotExtract || if err == utils.ErrCouldNotExtract ||
err == utils.ErrExtractedFileTooBig || err == utils.ErrExtractedFileTooBig ||
@ -131,6 +131,7 @@ func postLayer(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx
Name: request.Layer.Name, Name: request.Layer.Name,
ParentName: request.Layer.ParentName, ParentName: request.Layer.ParentName,
Path: request.Layer.Path, Path: request.Layer.Path,
Authorization: request.Layer.Authorization,
Format: request.Layer.Format, Format: request.Layer.Format,
IndexedByVersion: worker.Version, IndexedByVersion: worker.Version,
}}) }})

View File

@ -70,10 +70,17 @@ func RegisterDataDetector(name string, f DataDetector) {
} }
// DetectData finds the Data of the layer by using every registered DataDetector // DetectData finds the Data of the layer by using every registered DataDetector
func DetectData(path string, format string, toExtract []string, maxFileSize int64) (data map[string][]byte, err error) { func DetectData(path, authorization, format string, toExtract []string, maxFileSize int64) (data map[string][]byte, err error) {
var layerReader io.ReadCloser var layerReader io.ReadCloser
if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
r, err := http.Get(path) request, err := http.NewRequest("GET", path, nil)
if err != nil {
return nil, ErrCouldNotFindLayer
}
if authorization != "" {
request.Header.Set("Authorization", authorization)
}
r, err := http.DefaultClient.Do(request)
if err != nil { if err != nil {
log.Warningf("could not download layer: %s", err) log.Warningf("could not download layer: %s", err)
return nil, ErrCouldNotFindLayer return nil, ErrCouldNotFindLayer

View File

@ -50,7 +50,7 @@ var (
// then stores everything in the database. // then stores everything in the database.
// TODO(Quentin-M): We could have a goroutine that looks for layers that have been analyzed with an // TODO(Quentin-M): We could have a goroutine that looks for layers that have been analyzed with an
// older engine version and that processes them. // older engine version and that processes them.
func Process(datastore database.Datastore, name, parentName, path, imageFormat string) error { func Process(datastore database.Datastore, name, parentName, path, authorization, imageFormat string) error {
// Verify parameters. // Verify parameters.
if name == "" { if name == "" {
return cerrors.NewBadRequestError("could not process a layer which does not have a name") return cerrors.NewBadRequestError("could not process a layer which does not have a name")
@ -104,7 +104,7 @@ func Process(datastore database.Datastore, name, parentName, path, imageFormat s
} }
// Analyze the content. // Analyze the content.
layer.Namespace, layer.Features, err = detectContent(name, path, imageFormat, layer.Parent) layer.Namespace, layer.Features, err = detectContent(name, path, authorization, imageFormat, layer.Parent)
if err != nil { if err != nil {
return err return err
} }
@ -113,8 +113,8 @@ func Process(datastore database.Datastore, name, parentName, path, imageFormat s
} }
// detectContent downloads a layer's archive and extracts its Namespace and Features. // detectContent downloads a layer's archive and extracts its Namespace and Features.
func detectContent(name, path, imageFormat string, parent *database.Layer) (namespace *database.Namespace, features []database.FeatureVersion, err error) { func detectContent(name, path, authorization, imageFormat string, parent *database.Layer) (namespace *database.Namespace, features []database.FeatureVersion, err error) {
data, err := detectors.DetectData(path, imageFormat, append(detectors.GetRequiredFilesFeatures(), data, err := detectors.DetectData(path, authorization, imageFormat, append(detectors.GetRequiredFilesFeatures(),
detectors.GetRequiredFilesNamespace()...), maxFileSize) detectors.GetRequiredFilesNamespace()...), maxFileSize)
if err != nil { if err != nil {
log.Errorf("layer %s: failed to extract data from %s: %s", name, utils.CleanURL(path), err) log.Errorf("layer %s: failed to extract data from %s: %s", name, utils.CleanURL(path), err)

View File

@ -47,9 +47,9 @@ func TestProcessWithDistUpgrade(t *testing.T) {
// wheezy.tar: FROM debian:wheezy // wheezy.tar: FROM debian:wheezy
// jessie.tar: RUN sed -i "s/precise/trusty/" /etc/apt/sources.list && apt-get update && // jessie.tar: RUN sed -i "s/precise/trusty/" /etc/apt/sources.list && apt-get update &&
// apt-get -y dist-upgrade // apt-get -y dist-upgrade
assert.Nil(t, Process(datastore, "blank", "", path+"blank.tar.gz", "Docker")) assert.Nil(t, Process(datastore, "blank", "", path+"blank.tar.gz", "", "Docker"))
assert.Nil(t, Process(datastore, "wheezy", "blank", path+"wheezy.tar.gz", "Docker")) assert.Nil(t, Process(datastore, "wheezy", "blank", path+"wheezy.tar.gz", "", "Docker"))
assert.Nil(t, Process(datastore, "jessie", "wheezy", path+"jessie.tar.gz", "Docker")) assert.Nil(t, Process(datastore, "jessie", "wheezy", path+"jessie.tar.gz", "", "Docker"))
wheezy, err := datastore.FindLayer("wheezy", true, false) wheezy, err := datastore.FindLayer("wheezy", true, false)
if assert.Nil(t, err) { if assert.Nil(t, err) {