support using internal registry addr to perform scan

- do changes to the sql schema
- add `UseInternalAddr` and `Immutable` properties to scanner registration
- support multiple authentication type
  - basic
  - bearer token

Signed-off-by: Steven Zou <szou@vmware.com>
This commit is contained in:
Steven Zou 2019-10-24 17:11:33 +08:00
parent 956d9db1d5
commit cb59ba3bbc
6 changed files with 89 additions and 18 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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{}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"`