mirror of
https://github.com/goharbor/harbor
synced 2025-04-17 17:38:07 +00:00
147 lines
4.6 KiB
Go
147 lines
4.6 KiB
Go
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package scan
|
|
|
|
import (
|
|
"github.com/docker/distribution"
|
|
"github.com/docker/distribution/manifest/schema2"
|
|
"github.com/vmware/harbor/src/common/models"
|
|
"github.com/vmware/harbor/src/common/utils/clair"
|
|
"github.com/vmware/harbor/src/common/utils/registry/auth"
|
|
"github.com/vmware/harbor/src/jobservice/config"
|
|
"github.com/vmware/harbor/src/jobservice/utils"
|
|
|
|
"fmt"
|
|
"net/http"
|
|
)
|
|
|
|
// Initializer will handle the initialise state pull the manifest, prepare token.
|
|
type Initializer struct {
|
|
Context *JobContext
|
|
}
|
|
|
|
// Enter ...
|
|
func (iz *Initializer) Enter() (string, error) {
|
|
logger := iz.Context.Logger
|
|
logger.Infof("Entered scan initializer")
|
|
regURL, err := config.LocalRegURL()
|
|
if err != nil {
|
|
logger.Errorf("Failed to read regURL, error: %v", err)
|
|
return "", err
|
|
}
|
|
c := &http.Cookie{Name: models.UISecretCookie, Value: config.JobserviceSecret()}
|
|
repoClient, err := utils.NewRepositoryClient(regURL, false, auth.NewCookieCredential(c),
|
|
config.InternalTokenServiceEndpoint(), iz.Context.Repository, "pull")
|
|
if err != nil {
|
|
logger.Errorf("An error occurred while creating repository client: %v", err)
|
|
return "", err
|
|
}
|
|
|
|
_, _, payload, err := repoClient.PullManifest(iz.Context.Digest, []string{schema2.MediaTypeManifest})
|
|
if err != nil {
|
|
logger.Errorf("Error pulling manifest for image %s:%s :%v", iz.Context.Repository, iz.Context.Tag, err)
|
|
return "", err
|
|
}
|
|
manifest, _, err := distribution.UnmarshalManifest(schema2.MediaTypeManifest, payload)
|
|
if err != nil {
|
|
logger.Error("Failed to unMarshal manifest from response")
|
|
return "", err
|
|
}
|
|
|
|
tk, err := utils.GetTokenForRepo(iz.Context.Repository)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
iz.Context.token = tk
|
|
iz.Context.clairClient = clair.NewClient(config.ClairEndpoint(), logger)
|
|
iz.prepareLayers(regURL, manifest.References())
|
|
return StateScanLayer, nil
|
|
}
|
|
|
|
func (iz *Initializer) prepareLayers(registryEndpoint string, descriptors []distribution.Descriptor) {
|
|
// logger := iz.Context.Logger
|
|
tokenHeader := map[string]string{"Authorization": fmt.Sprintf("Bearer %s", iz.Context.token)}
|
|
for _, d := range descriptors {
|
|
if d.MediaType == schema2.MediaTypeConfig {
|
|
continue
|
|
}
|
|
l := models.ClairLayer{
|
|
Name: fmt.Sprintf("%d-%s", iz.Context.JobID, d.Digest),
|
|
Headers: tokenHeader,
|
|
Format: "Docker",
|
|
Path: utils.BuildBlobURL(registryEndpoint, iz.Context.Repository, string(d.Digest)),
|
|
}
|
|
if len(iz.Context.layers) > 0 {
|
|
l.ParentName = iz.Context.layers[len(iz.Context.layers)-1].Name
|
|
}
|
|
iz.Context.layers = append(iz.Context.layers, l)
|
|
}
|
|
}
|
|
|
|
// Exit ...
|
|
func (iz *Initializer) Exit() error {
|
|
return nil
|
|
}
|
|
|
|
//LayerScanHandler will call clair API to trigger scanning.
|
|
type LayerScanHandler struct {
|
|
Context *JobContext
|
|
}
|
|
|
|
// Enter ...
|
|
func (ls *LayerScanHandler) Enter() (string, error) {
|
|
logger := ls.Context.Logger
|
|
currentLayer := ls.Context.layers[ls.Context.current]
|
|
logger.Infof("Entered scan layer handler, current: %d, layer name: %s", ls.Context.current, currentLayer.Name)
|
|
err := ls.Context.clairClient.ScanLayer(currentLayer)
|
|
if err != nil {
|
|
logger.Errorf("Unexpected error: %v", err)
|
|
return "", err
|
|
}
|
|
ls.Context.current++
|
|
if ls.Context.current == len(ls.Context.layers) {
|
|
return StateSummarize, nil
|
|
}
|
|
logger.Infof("After scanning, return with next state: %s", StateScanLayer)
|
|
return StateScanLayer, nil
|
|
}
|
|
|
|
// Exit ...
|
|
func (ls *LayerScanHandler) Exit() error {
|
|
return nil
|
|
}
|
|
|
|
// SummarizeHandler will summarize the vulnerability and feature information of Clair, and store into Harbor's DB.
|
|
type SummarizeHandler struct {
|
|
Context *JobContext
|
|
}
|
|
|
|
// Enter ...
|
|
func (sh *SummarizeHandler) Enter() (string, error) {
|
|
logger := sh.Context.Logger
|
|
logger.Infof("Entered summarize handler")
|
|
layerName := sh.Context.layers[len(sh.Context.layers)-1].Name
|
|
logger.Infof("Top layer's name: %s, will use it to get the vulnerability result of image", layerName)
|
|
if err := clair.UpdateScanOverview(sh.Context.Digest, layerName); err != nil {
|
|
return "", nil
|
|
}
|
|
return models.JobFinished, nil
|
|
}
|
|
|
|
// Exit ...
|
|
func (sh *SummarizeHandler) Exit() error {
|
|
return nil
|
|
}
|