diff --git a/make/migrations/postgresql/0015_1.10.0_schema.up.sql b/make/migrations/postgresql/0015_1.10.0_schema.up.sql index d4cc01997..0d1a148f2 100644 --- a/make/migrations/postgresql/0015_1.10.0_schema.up.sql +++ b/make/migrations/postgresql/0015_1.10.0_schema.up.sql @@ -10,6 +10,8 @@ CREATE TABLE scanner_registration access_cred VARCHAR(512) NULL, disabled BOOLEAN NOT NULL DEFAULT FALSE, is_default BOOLEAN NOT NULL DEFAULT FALSE, + use_internal_addr BOOLEAN NOT NULL DEFAULT FALSE, + immutable BOOLEAN NOT NULL DEFAULT FALSE, skip_cert_verify BOOLEAN NOT NULL DEFAULT FALSE, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP diff --git a/src/core/api/scanners.go b/src/core/api/scanners.go index 53321f64a..a7eb417cd 100644 --- a/src/core/api/scanners.go +++ b/src/core/api/scanners.go @@ -164,6 +164,12 @@ func (sa *ScannerAPI) Update() { return } + // Immutable registration is not allowed + if r.Immutable { + sa.SendForbiddenError(errors.Errorf("registration %s is not allowed to update as it is immutable: scanner API: update", r.Name)) + return + } + // full dose updated rr := &scanner.Registration{} if err := sa.DecodeJSONReq(rr); err != nil { @@ -207,17 +213,21 @@ func (sa *ScannerAPI) Update() { // Delete the scanner func (sa *ScannerAPI) Delete() { - uid := sa.GetStringFromPath(":uuid") - - deleted, err := sa.c.DeleteRegistration(uid) - if err != nil { - sa.SendInternalServerError(errors.Wrap(err, "scanner API: delete")) + r := sa.get() + if r == nil { + // meet error return } - if deleted == nil { - // Not found - sa.SendNotFoundError(errors.Errorf("scanner registration: %s", uid)) + // Immutable registration is not allowed + if r.Immutable { + sa.SendForbiddenError(errors.Errorf("registration %s is not allowed to delete as it is immutable: scanner API: update", r.Name)) + return + } + + deleted, err := sa.c.DeleteRegistration(r.UUID) + if err != nil { + sa.SendInternalServerError(errors.Wrap(err, "scanner API: delete")) return } diff --git a/src/core/api/scanners_test.go b/src/core/api/scanners_test.go index 598ae7459..231403c2a 100644 --- a/src/core/api/scanners_test.go +++ b/src/core/api/scanners_test.go @@ -227,6 +227,7 @@ func (suite *ScannerAPITestSuite) TestScannerAPIDelete() { URL: "https://a.b.c", } + suite.mockC.On("GetRegistration", "uuid").Return(r, nil) suite.mockC.On("DeleteRegistration", "uuid").Return(r, nil) deleted := &scanner.Registration{} diff --git a/src/pkg/scan/api/scan/base_controller.go b/src/pkg/scan/api/scan/base_controller.go index ecc906d94..96271521f 100644 --- a/src/pkg/scan/api/scan/base_controller.go +++ b/src/pkg/scan/api/scan/base_controller.go @@ -19,11 +19,13 @@ import ( "fmt" "time" + tk "github.com/docker/distribution/registry/auth/token" cj "github.com/goharbor/harbor/src/common/job" jm "github.com/goharbor/harbor/src/common/job/models" "github.com/goharbor/harbor/src/common/rbac" "github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/core/config" + "github.com/goharbor/harbor/src/core/service/token" "github.com/goharbor/harbor/src/jobservice/job" "github.com/goharbor/harbor/src/jobservice/logger" "github.com/goharbor/harbor/src/pkg/robot" @@ -44,6 +46,9 @@ var DefaultController = NewController() const ( configRegistryEndpoint = "registryEndpoint" configCoreInternalAddr = "coreInternalAddr" + authorizationType = "harbor.scanner-adapter/registry-authorization-type" + authorizationBearer = "Bearer" + authorizationBasic = "Basic" ) // uuidGenerator is a func template which is for generating UUID. @@ -190,7 +195,13 @@ func (bc *basicController) Scan(artifact *v1.Artifact) error { return errors.Wrap(err, "scan controller: scan") } - jobID, err := bc.launchScanJob(trackID, artifact, r, producesMimes) + // Get authorization type + auth := authorizationBasic + if v, ok := meta.Properties[authorizationType]; ok { + auth = v + } + + jobID, err := bc.launchScanJob(trackID, artifact, r, producesMimes, auth) if err != nil { // Update the status to the concrete error // Change status code to normal error code @@ -352,8 +363,8 @@ func (bc *basicController) DeleteReports(digests ...string) error { return bc.manager.DeleteByDigests(digests...) } -// makeAuthorization creates authorization from a robot account based on the arguments for scanning. -func (bc *basicController) makeAuthorization(pid int64, repository string, ttl int64) (string, int64, error) { +// 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) { // Use uuid as name to avoid duplicated entries. UUID, err := bc.uuid() if err != nil { @@ -389,14 +400,31 @@ func (bc *basicController) makeAuthorization(pid int64, repository string, ttl i } // launchScanJob launches a job to run scan -func (bc *basicController) launchScanJob(trackID string, artifact *v1.Artifact, registration *scanner.Registration, mimes []string) (jobID string, err error) { - externalURL, err := bc.config(configRegistryEndpoint) +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) if err != nil { return "", errors.Wrap(err, "scan controller: launch scan job") } - // Make authorization from a robot account with 30 minutes - authorization, rID, err := bc.makeAuthorization(artifact.NamespaceID, artifact.Repository, 1800) + 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) + } + if err != nil { return "", errors.Wrap(err, "scan controller: launch scan job") } @@ -404,7 +432,7 @@ func (bc *basicController) launchScanJob(trackID string, artifact *v1.Artifact, // Set job parameters scanReq := &v1.ScanRequest{ Registry: &v1.Registry{ - URL: externalURL, + URL: registryAddr, Authorization: authorization, }, Artifact: artifact, @@ -424,7 +452,9 @@ func (bc *basicController) launchScanJob(trackID string, artifact *v1.Artifact, params[sca.JobParamRegistration] = rJSON params[sca.JobParameterRequest] = sJSON params[sca.JobParameterMimes] = mimes - params[sca.JobParameterRobotID] = rID + if rID > 0 { + params[sca.JobParameterRobotID] = rID + } // Launch job callbackURL, err := bc.config(configCoreInternalAddr) @@ -444,3 +474,21 @@ func (bc *basicController) launchScanJob(trackID string, artifact *v1.Artifact, return bc.jc().SubmitJob(j) } + +// makeBearerAuthorization make a authorization with bearer token +func makeBearerAuthorization(repository string, username string) (string, error) { + access := []*tk.ResourceActions{ + { + Type: "repository", + Name: repository, + Actions: []string{"pull"}, + }, + } + + 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 +} diff --git a/src/pkg/scan/api/scanner/base_controller.go b/src/pkg/scan/api/scanner/base_controller.go index 59e688067..d3b0f6570 100644 --- a/src/pkg/scan/api/scanner/base_controller.go +++ b/src/pkg/scan/api/scanner/base_controller.go @@ -118,7 +118,11 @@ func (bc *basicController) UpdateRegistration(registration *scanner.Registration // SetDefaultRegistration ... func (bc *basicController) DeleteRegistration(registrationUUID string) (*scanner.Registration, error) { registration, err := bc.manager.Get(registrationUUID) - if registration == nil && err == nil { + if err != nil { + return nil, errors.Wrap(err, "api controller: delete registration") + } + + if registration == nil { // Not found return nil, nil } diff --git a/src/pkg/scan/dao/scanner/model.go b/src/pkg/scan/dao/scanner/model.go index c730e9017..61ca4e696 100644 --- a/src/pkg/scan/dao/scanner/model.go +++ b/src/pkg/scan/dao/scanner/model.go @@ -47,6 +47,12 @@ type Registration struct { // Http connection settings SkipCertVerify bool `orm:"column(skip_cert_verify);default(false)" json:"skip_certVerify"` + // Indicate whether use internal registry addr for the scanner to pull content + UseInternalAddr bool `orm:"column(use_internal_addr);default(false)" json:"use_internal_addr"` + + // Indicate if the registration is immutable which is not allowed to remove + Immutable bool `orm:"column(immutable);default(false)" json:"-"` + // Timestamps CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"` UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"`