2019-09-19 13:15:37 +02:00
|
|
|
// Copyright Project Harbor Authors
|
|
|
|
//
|
|
|
|
// 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 (
|
2019-10-15 07:45:53 +02:00
|
|
|
"encoding/base64"
|
2019-09-24 09:17:40 +02:00
|
|
|
"fmt"
|
2019-10-12 10:29:38 +02:00
|
|
|
"time"
|
2019-09-24 09:17:40 +02:00
|
|
|
|
2019-10-24 11:11:33 +02:00
|
|
|
tk "github.com/docker/distribution/registry/auth/token"
|
2019-10-12 10:29:38 +02:00
|
|
|
cj "github.com/goharbor/harbor/src/common/job"
|
2019-09-24 09:17:40 +02:00
|
|
|
jm "github.com/goharbor/harbor/src/common/job/models"
|
2019-10-12 10:29:38 +02:00
|
|
|
"github.com/goharbor/harbor/src/common/rbac"
|
2019-10-22 12:47:18 +02:00
|
|
|
"github.com/goharbor/harbor/src/common/utils/log"
|
2019-10-12 10:29:38 +02:00
|
|
|
"github.com/goharbor/harbor/src/core/config"
|
2019-10-24 11:11:33 +02:00
|
|
|
"github.com/goharbor/harbor/src/core/service/token"
|
2019-09-19 13:15:37 +02:00
|
|
|
"github.com/goharbor/harbor/src/jobservice/job"
|
2019-10-12 10:29:38 +02:00
|
|
|
"github.com/goharbor/harbor/src/jobservice/logger"
|
|
|
|
"github.com/goharbor/harbor/src/pkg/robot"
|
|
|
|
"github.com/goharbor/harbor/src/pkg/robot/model"
|
2019-09-24 09:17:40 +02:00
|
|
|
sca "github.com/goharbor/harbor/src/pkg/scan"
|
2019-11-04 10:34:42 +01:00
|
|
|
"github.com/goharbor/harbor/src/pkg/scan/all"
|
2019-09-24 09:17:40 +02:00
|
|
|
sc "github.com/goharbor/harbor/src/pkg/scan/api/scanner"
|
2019-09-19 13:15:37 +02:00
|
|
|
"github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
|
|
|
"github.com/goharbor/harbor/src/pkg/scan/dao/scanner"
|
2019-10-31 05:08:16 +01:00
|
|
|
"github.com/goharbor/harbor/src/pkg/scan/errs"
|
2019-09-24 09:17:40 +02:00
|
|
|
"github.com/goharbor/harbor/src/pkg/scan/report"
|
2019-09-19 13:15:37 +02:00
|
|
|
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
2019-10-12 10:29:38 +02:00
|
|
|
"github.com/google/uuid"
|
2019-09-24 09:17:40 +02:00
|
|
|
"github.com/pkg/errors"
|
2019-09-19 13:15:37 +02:00
|
|
|
)
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
// DefaultController is a default singleton scan API controller.
|
|
|
|
var DefaultController = NewController()
|
|
|
|
|
2019-10-12 10:29:38 +02:00
|
|
|
const (
|
|
|
|
configRegistryEndpoint = "registryEndpoint"
|
|
|
|
configCoreInternalAddr = "coreInternalAddr"
|
2019-10-24 11:11:33 +02:00
|
|
|
authorizationType = "harbor.scanner-adapter/registry-authorization-type"
|
|
|
|
authorizationBearer = "Bearer"
|
|
|
|
authorizationBasic = "Basic"
|
2019-10-12 10:29:38 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// uuidGenerator is a func template which is for generating UUID.
|
|
|
|
type uuidGenerator func() (string, error)
|
|
|
|
|
|
|
|
// configGetter is a func template which is used to wrap the config management
|
|
|
|
// utility methods.
|
|
|
|
type configGetter func(cfg string) (string, error)
|
|
|
|
|
2019-10-15 07:45:53 +02:00
|
|
|
// jcGetter is a func template which is used to get the job service client.
|
|
|
|
type jcGetter func() cj.Client
|
|
|
|
|
2019-09-19 13:15:37 +02:00
|
|
|
// basicController is default implementation of api.Controller interface
|
|
|
|
type basicController struct {
|
2019-09-24 09:17:40 +02:00
|
|
|
// Manage the scan report records
|
|
|
|
manager report.Manager
|
|
|
|
// Scanner controller
|
|
|
|
sc sc.Controller
|
2019-10-12 10:29:38 +02:00
|
|
|
// Robot account controller
|
|
|
|
rc robot.Controller
|
|
|
|
// Job service client
|
2019-10-15 07:45:53 +02:00
|
|
|
jc jcGetter
|
2019-10-12 10:29:38 +02:00
|
|
|
// UUID generator
|
|
|
|
uuid uuidGenerator
|
|
|
|
// Configuration getter func
|
|
|
|
config configGetter
|
2019-09-19 13:15:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewController news a scan API controller
|
|
|
|
func NewController() Controller {
|
2019-09-24 09:17:40 +02:00
|
|
|
return &basicController{
|
|
|
|
// New report manager
|
|
|
|
manager: report.NewManager(),
|
2019-10-12 10:29:38 +02:00
|
|
|
// Refer to the default scanner controller
|
2019-09-24 09:17:40 +02:00
|
|
|
sc: sc.DefaultController,
|
2019-10-12 10:29:38 +02:00
|
|
|
// Refer to the default robot account controller
|
|
|
|
rc: robot.RobotCtr,
|
|
|
|
// Refer to the default job service client
|
2019-10-15 07:45:53 +02:00
|
|
|
jc: func() cj.Client {
|
|
|
|
return cj.GlobalClient
|
|
|
|
},
|
2019-10-12 10:29:38 +02:00
|
|
|
// Generate UUID with uuid lib
|
|
|
|
uuid: func() (string, error) {
|
|
|
|
aUUID, err := uuid.NewUUID()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return aUUID.String(), nil
|
|
|
|
},
|
|
|
|
// Get the required configuration options
|
|
|
|
config: func(cfg string) (string, error) {
|
|
|
|
switch cfg {
|
|
|
|
case configRegistryEndpoint:
|
|
|
|
return config.ExtEndpoint()
|
|
|
|
case configCoreInternalAddr:
|
|
|
|
return config.InternalCoreURL(), nil
|
|
|
|
default:
|
|
|
|
return "", errors.Errorf("configuration option %s not defined", cfg)
|
|
|
|
}
|
|
|
|
},
|
2019-09-24 09:17:40 +02:00
|
|
|
}
|
2019-09-19 13:15:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Scan ...
|
2019-11-04 10:34:42 +01:00
|
|
|
func (bc *basicController) Scan(artifact *v1.Artifact, options ...Option) error {
|
2019-09-24 09:17:40 +02:00
|
|
|
if artifact == nil {
|
|
|
|
return errors.New("nil artifact to scan")
|
|
|
|
}
|
|
|
|
|
2019-11-04 10:34:42 +01:00
|
|
|
// Parse options
|
|
|
|
ops, err := parseOptions(options...)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "scan controller: scan")
|
|
|
|
}
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
r, err := bc.sc.GetRegistrationByProject(artifact.NamespaceID)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "scan controller: scan")
|
|
|
|
}
|
|
|
|
|
2019-10-31 04:33:43 +01:00
|
|
|
// In case it does not exist
|
|
|
|
if r == nil {
|
2019-10-31 05:08:16 +01:00
|
|
|
return errs.WithCode(errs.PreconditionFailed, errs.Errorf("no available scanner for project: %d", artifact.NamespaceID))
|
2019-10-31 04:33:43 +01:00
|
|
|
}
|
|
|
|
|
2019-10-23 10:02:18 +02:00
|
|
|
// Check if it is disabled
|
|
|
|
if r.Disabled {
|
2019-10-31 04:33:43 +01:00
|
|
|
return errs.WithCode(errs.PreconditionFailed, errs.Errorf("scanner %s is disabled", r.Name))
|
2019-10-23 10:02:18 +02:00
|
|
|
}
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
// Check the health of the registration by ping.
|
|
|
|
// The metadata of the scanner adapter is also returned.
|
|
|
|
meta, err := bc.sc.Ping(r)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "scan controller: scan")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate a UUID as track ID which groups the report records generated
|
|
|
|
// by the specified registration for the digest with given mime type.
|
2019-10-12 10:29:38 +02:00
|
|
|
trackID, err := bc.uuid()
|
2019-09-24 09:17:40 +02:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "scan controller: scan")
|
|
|
|
}
|
|
|
|
|
|
|
|
producesMimes := make([]string, 0)
|
|
|
|
matched := false
|
2019-10-31 04:33:43 +01:00
|
|
|
statusConflict := false
|
2019-09-24 09:17:40 +02:00
|
|
|
for _, ca := range meta.Capabilities {
|
|
|
|
for _, cm := range ca.ConsumesMimeTypes {
|
|
|
|
if cm == artifact.MimeType {
|
|
|
|
matched = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if matched {
|
|
|
|
for _, pm := range ca.ProducesMimeTypes {
|
|
|
|
// Create report placeholder first
|
|
|
|
reportPlaceholder := &scan.Report{
|
|
|
|
Digest: artifact.Digest,
|
|
|
|
RegistrationUUID: r.UUID,
|
|
|
|
Status: job.PendingStatus.String(),
|
|
|
|
StatusCode: job.PendingStatus.Code(),
|
|
|
|
TrackID: trackID,
|
|
|
|
MimeType: pm,
|
|
|
|
}
|
2019-11-04 10:34:42 +01:00
|
|
|
// Set requester if it is specified
|
|
|
|
if len(ops.Requester) > 0 {
|
|
|
|
reportPlaceholder.Requester = ops.Requester
|
|
|
|
} else {
|
|
|
|
// Use the trackID as the requester
|
|
|
|
reportPlaceholder.Requester = trackID
|
|
|
|
}
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
_, e := bc.manager.Create(reportPlaceholder)
|
|
|
|
if e != nil {
|
2019-10-31 04:33:43 +01:00
|
|
|
// Check if it is a status conflict error with common error format.
|
|
|
|
// Common error returned if and only if status conflicts.
|
|
|
|
if !statusConflict {
|
|
|
|
statusConflict = errs.AsError(e, errs.Conflict)
|
|
|
|
}
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
// Recorded by error wrap and logged at the same time.
|
|
|
|
if err == nil {
|
|
|
|
err = e
|
|
|
|
} else {
|
|
|
|
err = errors.Wrap(e, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Error(errors.Wrap(e, "scan controller: scan"))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
producesMimes = append(producesMimes, pm)
|
|
|
|
}
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scanner does not support scanning the given artifact.
|
|
|
|
if !matched {
|
|
|
|
return errors.Errorf("the configured scanner %s does not support scanning artifact with mime type %s", r.Name, artifact.MimeType)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If all the record are created failed.
|
|
|
|
if len(producesMimes) == 0 {
|
|
|
|
// Return the last error
|
2019-10-31 04:33:43 +01:00
|
|
|
if statusConflict {
|
|
|
|
return errs.WithCode(errs.Conflict, errs.Wrap(err, "scan controller: scan"))
|
|
|
|
}
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
return errors.Wrap(err, "scan controller: scan")
|
|
|
|
}
|
|
|
|
|
2019-10-24 11:11:33 +02:00
|
|
|
// Get authorization type
|
|
|
|
auth := authorizationBasic
|
|
|
|
if v, ok := meta.Properties[authorizationType]; ok {
|
|
|
|
auth = v
|
|
|
|
}
|
|
|
|
|
|
|
|
jobID, err := bc.launchScanJob(trackID, artifact, r, producesMimes, auth)
|
2019-09-24 09:17:40 +02:00
|
|
|
if err != nil {
|
|
|
|
// Update the status to the concrete error
|
|
|
|
// Change status code to normal error code
|
|
|
|
if e := bc.manager.UpdateStatus(trackID, err.Error(), 0); e != nil {
|
|
|
|
err = errors.Wrap(e, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.Wrap(err, "scan controller: scan")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert the generated job ID now
|
|
|
|
// It will not block the whole process. If any errors happened, just logged.
|
|
|
|
if err := bc.manager.UpdateScanJobID(trackID, jobID); err != nil {
|
|
|
|
logger.Error(errors.Wrap(err, "scan controller: scan"))
|
|
|
|
}
|
|
|
|
|
2019-09-19 13:15:37 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetReport ...
|
2019-09-24 09:17:40 +02:00
|
|
|
func (bc *basicController) GetReport(artifact *v1.Artifact, mimeTypes []string) ([]*scan.Report, error) {
|
|
|
|
if artifact == nil {
|
|
|
|
return nil, errors.New("no way to get report for nil artifact")
|
|
|
|
}
|
|
|
|
|
|
|
|
mimes := make([]string, 0)
|
|
|
|
mimes = append(mimes, mimeTypes...)
|
|
|
|
if len(mimes) == 0 {
|
|
|
|
// Retrieve native as default
|
|
|
|
mimes = append(mimes, v1.MimeTypeNativeReport)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get current scanner settings
|
|
|
|
r, err := bc.sc.GetRegistrationByProject(artifact.NamespaceID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "scan controller: get report")
|
|
|
|
}
|
|
|
|
|
|
|
|
if r == nil {
|
2019-10-31 04:33:43 +01:00
|
|
|
return nil, errs.WithCode(errs.PreconditionFailed, errs.Errorf("no scanner registration configured for project: %d", artifact.NamespaceID))
|
2019-09-24 09:17:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return bc.manager.GetBy(artifact.Digest, r.UUID, mimes)
|
2019-09-19 13:15:37 +02:00
|
|
|
}
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
// GetSummary ...
|
2019-10-15 07:45:53 +02:00
|
|
|
func (bc *basicController) GetSummary(artifact *v1.Artifact, mimeTypes []string, options ...report.Option) (map[string]interface{}, error) {
|
2019-09-24 09:17:40 +02:00
|
|
|
if artifact == nil {
|
|
|
|
return nil, errors.New("no way to get report summaries for nil artifact")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get reports first
|
|
|
|
rps, err := bc.GetReport(artifact, mimeTypes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
summaries := make(map[string]interface{}, len(rps))
|
|
|
|
for _, rp := range rps {
|
2019-10-15 07:45:53 +02:00
|
|
|
sum, err := report.GenerateSummary(rp, options...)
|
2019-09-24 09:17:40 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
summaries[rp.MimeType] = sum
|
|
|
|
}
|
|
|
|
|
|
|
|
return summaries, nil
|
2019-09-19 13:15:37 +02:00
|
|
|
}
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
// GetScanLog ...
|
|
|
|
func (bc *basicController) GetScanLog(uuid string) ([]byte, error) {
|
|
|
|
if len(uuid) == 0 {
|
|
|
|
return nil, errors.New("empty uuid to get scan log")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get by uuid
|
|
|
|
sr, err := bc.manager.Get(uuid)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "scan controller: get scan log")
|
|
|
|
}
|
|
|
|
|
|
|
|
if sr == nil {
|
|
|
|
// Not found
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not job error
|
|
|
|
if sr.StatusCode == job.ErrorStatus.Code() {
|
|
|
|
jst := job.Status(sr.Status)
|
|
|
|
if jst.Code() == -1 {
|
|
|
|
return []byte(sr.Status), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Job log
|
2019-10-15 07:45:53 +02:00
|
|
|
return bc.jc().GetJobLog(sr.JobID)
|
2019-09-19 13:15:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// HandleJobHooks ...
|
2019-09-24 09:17:40 +02:00
|
|
|
func (bc *basicController) HandleJobHooks(trackID string, change *job.StatusChange) error {
|
|
|
|
if len(trackID) == 0 {
|
|
|
|
return errors.New("empty track ID")
|
|
|
|
}
|
|
|
|
|
|
|
|
if change == nil {
|
|
|
|
return errors.New("nil change object")
|
|
|
|
}
|
|
|
|
|
2019-10-21 14:07:00 +02:00
|
|
|
// Clear robot account
|
2019-10-23 10:02:18 +02:00
|
|
|
// Only when the job is successfully done!
|
|
|
|
if change.Status == job.SuccessStatus.String() {
|
2019-10-21 14:07:00 +02:00
|
|
|
if v, ok := change.Metadata.Parameters[sca.JobParameterRobotID]; ok {
|
|
|
|
if rid, y := v.(float64); y {
|
|
|
|
if err := robot.RobotCtr.DeleteRobotAccount(int64(rid)); err != nil {
|
|
|
|
// Should not block the main flow, just logged
|
|
|
|
log.Error(errors.Wrap(err, "scan controller: handle job hook"))
|
|
|
|
} else {
|
2019-10-22 12:47:18 +02:00
|
|
|
log.Debugf("Robot account with id %d for the scan %s is removed", int64(rid), trackID)
|
2019-10-21 14:07:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
// Check in data
|
|
|
|
if len(change.CheckIn) > 0 {
|
|
|
|
checkInReport := &sca.CheckInReport{}
|
|
|
|
if err := checkInReport.FromJSON(change.CheckIn); err != nil {
|
|
|
|
return errors.Wrap(err, "scan controller: handle job hook")
|
|
|
|
}
|
|
|
|
|
|
|
|
rpl, err := bc.manager.GetBy(
|
|
|
|
checkInReport.Digest,
|
|
|
|
checkInReport.RegistrationUUID,
|
|
|
|
[]string{checkInReport.MimeType})
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "scan controller: handle job hook")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(rpl) == 0 {
|
|
|
|
return errors.New("no report found to update data")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := bc.manager.UpdateReportData(
|
|
|
|
rpl[0].UUID,
|
|
|
|
checkInReport.RawReport,
|
|
|
|
change.Metadata.Revision); err != nil {
|
|
|
|
return errors.Wrap(err, "scan controller: handle job hook")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return bc.manager.UpdateStatus(trackID, change.Status, change.Metadata.Revision)
|
|
|
|
}
|
|
|
|
|
2019-10-21 14:07:00 +02:00
|
|
|
// DeleteReports ...
|
|
|
|
func (bc *basicController) DeleteReports(digests ...string) error {
|
2019-11-04 10:34:42 +01:00
|
|
|
if err := bc.manager.DeleteByDigests(digests...); err != nil {
|
|
|
|
return errors.Wrap(err, "scan controller: delete reports")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetStats ...
|
|
|
|
func (bc *basicController) GetStats(requester string) (*all.Stats, error) {
|
|
|
|
sts, err := bc.manager.GetStats(requester)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "scan controller: delete reports")
|
|
|
|
}
|
|
|
|
|
|
|
|
return sts, nil
|
2019-10-21 14:07:00 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 11:11:33 +02:00
|
|
|
// makeBasicAuthorization creates authorization from a robot account based on the arguments for scanning.
|
|
|
|
func (bc *basicController) makeBasicAuthorization(pid int64, repository string, ttl int64) (string, int64, error) {
|
2019-10-12 10:29:38 +02:00
|
|
|
// Use uuid as name to avoid duplicated entries.
|
|
|
|
UUID, err := bc.uuid()
|
|
|
|
if err != nil {
|
2019-10-21 14:07:00 +02:00
|
|
|
return "", -1, errors.Wrap(err, "scan controller: make robot account")
|
2019-10-12 10:29:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
expireAt := time.Now().UTC().Add(time.Duration(ttl) * time.Second).Unix()
|
|
|
|
|
|
|
|
logger.Warningf("repository %s and expire time %d are not supported by robot controller", repository, expireAt)
|
|
|
|
|
2019-10-15 07:45:53 +02:00
|
|
|
resource := rbac.NewProjectNamespace(pid).Resource(rbac.ResourceRepository)
|
2019-10-12 10:29:38 +02:00
|
|
|
access := []*rbac.Policy{{
|
2019-10-15 07:45:53 +02:00
|
|
|
Resource: resource,
|
2019-10-26 19:25:36 +02:00
|
|
|
Action: rbac.ActionScannerPull,
|
2019-10-12 10:29:38 +02:00
|
|
|
}}
|
|
|
|
|
2019-10-17 06:00:51 +02:00
|
|
|
robotReq := &model.RobotCreate{
|
2019-10-15 07:45:53 +02:00
|
|
|
Name: UUID,
|
2019-10-12 10:29:38 +02:00
|
|
|
Description: "for scan",
|
|
|
|
ProjectID: pid,
|
|
|
|
Access: access,
|
|
|
|
}
|
|
|
|
|
2019-10-17 06:00:51 +02:00
|
|
|
rb, err := bc.rc.CreateRobotAccount(robotReq)
|
2019-10-12 10:29:38 +02:00
|
|
|
if err != nil {
|
2019-10-21 14:07:00 +02:00
|
|
|
return "", -1, errors.Wrap(err, "scan controller: make robot account")
|
2019-10-12 10:29:38 +02:00
|
|
|
}
|
|
|
|
|
2019-10-15 07:45:53 +02:00
|
|
|
basic := fmt.Sprintf("%s:%s", rb.Name, rb.Token)
|
|
|
|
encoded := base64.StdEncoding.EncodeToString([]byte(basic))
|
|
|
|
|
2019-10-21 14:07:00 +02:00
|
|
|
return fmt.Sprintf("Basic %s", encoded), rb.ID, nil
|
2019-10-12 10:29:38 +02:00
|
|
|
}
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
// launchScanJob launches a job to run scan
|
2019-10-24 11:11:33 +02:00
|
|
|
func (bc *basicController) launchScanJob(trackID string, artifact *v1.Artifact, registration *scanner.Registration, mimes []string, auth string) (jobID string, err error) {
|
|
|
|
var ck string
|
|
|
|
if registration.UseInternalAddr {
|
|
|
|
ck = configCoreInternalAddr
|
|
|
|
} else {
|
|
|
|
ck = configRegistryEndpoint
|
|
|
|
}
|
|
|
|
|
|
|
|
registryAddr, err := bc.config(ck)
|
2019-09-24 09:17:40 +02:00
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "scan controller: launch scan job")
|
|
|
|
}
|
|
|
|
|
2019-10-24 11:11:33 +02:00
|
|
|
var (
|
|
|
|
authorization string
|
|
|
|
rID int64 = -1
|
|
|
|
)
|
|
|
|
|
|
|
|
if auth == authorizationBearer {
|
|
|
|
authorization, err = makeBearerAuthorization(artifact.Repository, fmt.Sprintf("%s:%s", registration.Name, registration.UUID))
|
|
|
|
} else {
|
|
|
|
// Make authorization from a robot account with 30 minutes
|
|
|
|
authorization, rID, err = bc.makeBasicAuthorization(artifact.NamespaceID, artifact.Repository, 1800)
|
|
|
|
}
|
|
|
|
|
2019-09-24 09:17:40 +02:00
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "scan controller: launch scan job")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set job parameters
|
|
|
|
scanReq := &v1.ScanRequest{
|
|
|
|
Registry: &v1.Registry{
|
2019-10-24 11:11:33 +02:00
|
|
|
URL: registryAddr,
|
2019-10-17 06:00:51 +02:00
|
|
|
Authorization: authorization,
|
2019-09-24 09:17:40 +02:00
|
|
|
},
|
|
|
|
Artifact: artifact,
|
|
|
|
}
|
|
|
|
|
|
|
|
rJSON, err := registration.ToJSON()
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "scan controller: launch scan job")
|
|
|
|
}
|
|
|
|
|
|
|
|
sJSON, err := scanReq.ToJSON()
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "launch scan job")
|
|
|
|
}
|
|
|
|
|
|
|
|
params := make(map[string]interface{})
|
|
|
|
params[sca.JobParamRegistration] = rJSON
|
|
|
|
params[sca.JobParameterRequest] = sJSON
|
|
|
|
params[sca.JobParameterMimes] = mimes
|
2019-10-24 11:11:33 +02:00
|
|
|
if rID > 0 {
|
|
|
|
params[sca.JobParameterRobotID] = rID
|
|
|
|
}
|
2019-09-24 09:17:40 +02:00
|
|
|
|
|
|
|
// Launch job
|
2019-10-12 10:29:38 +02:00
|
|
|
callbackURL, err := bc.config(configCoreInternalAddr)
|
2019-09-24 09:17:40 +02:00
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "launch scan job")
|
|
|
|
}
|
|
|
|
hookURL := fmt.Sprintf("%s/service/notifications/jobs/scan/%s", callbackURL, trackID)
|
|
|
|
|
|
|
|
j := &jm.JobData{
|
|
|
|
Name: job.ImageScanJob,
|
|
|
|
Metadata: &jm.JobMetadata{
|
|
|
|
JobKind: job.KindGeneric,
|
|
|
|
},
|
|
|
|
Parameters: params,
|
|
|
|
StatusHook: hookURL,
|
|
|
|
}
|
|
|
|
|
2019-10-15 07:45:53 +02:00
|
|
|
return bc.jc().SubmitJob(j)
|
2019-09-19 13:15:37 +02:00
|
|
|
}
|
2019-10-24 11:11:33 +02:00
|
|
|
|
|
|
|
// makeBearerAuthorization make a authorization with bearer token
|
|
|
|
func makeBearerAuthorization(repository string, username string) (string, error) {
|
|
|
|
access := []*tk.ResourceActions{
|
|
|
|
{
|
|
|
|
Type: "repository",
|
|
|
|
Name: repository,
|
2019-10-26 19:25:36 +02:00
|
|
|
Actions: []string{rbac.ActionPull.String(), rbac.ActionScannerPull.String()},
|
2019-10-24 11:11:33 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
accessToken, err := token.MakeToken(username, token.Registry, access)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "make bearer authorization")
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Sprintf("Bearer %s", accessToken.Token), nil
|
|
|
|
}
|
2019-11-04 10:34:42 +01:00
|
|
|
|
|
|
|
func parseOptions(options ...Option) (*Options, error) {
|
|
|
|
ops := &Options{}
|
|
|
|
for _, op := range options {
|
|
|
|
if err := op(ops); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ops, nil
|
|
|
|
}
|