Merge pull request #12478 from steven-zou/feat/read_pro_config

feat(p2p):enhance policy enforcer
This commit is contained in:
Steven Zou 2020-07-16 11:40:29 +08:00 committed by GitHub
commit 4d4a04fad4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 96 additions and 17 deletions

View File

@ -28,8 +28,8 @@ import (
"github.com/goharbor/harbor/src/core/config" "github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/core/service/token" "github.com/goharbor/harbor/src/core/service/token"
"github.com/goharbor/harbor/src/jobservice/job" "github.com/goharbor/harbor/src/jobservice/job"
"github.com/goharbor/harbor/src/jobservice/logger"
"github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/q" "github.com/goharbor/harbor/src/lib/q"
"github.com/goharbor/harbor/src/lib/selector" "github.com/goharbor/harbor/src/lib/selector"
"github.com/goharbor/harbor/src/pkg/p2p/preheat" "github.com/goharbor/harbor/src/pkg/p2p/preheat"
@ -56,6 +56,9 @@ const (
resourcePullAction = "pull" resourcePullAction = "pull"
manifestAPIPattern = "%s/v2/%s/manifests/%s" manifestAPIPattern = "%s/v2/%s/manifests/%s"
accessCredHeaderKey = "Authorization" accessCredHeaderKey = "Authorization"
proMetaKeyContentTrust = "enable_content_trust"
proMetaKeyVulnerability = "prevent_vul"
proMetaKeySeverity = "severity"
) )
// Enforcer defines preheat policy enforcement operations. // Enforcer defines preheat policy enforcement operations.
@ -189,12 +192,24 @@ func (de *defaultEnforcer) EnforcePolicy(ctx context.Context, policyID int64) (i
return -1, enforceError(err) return -1, enforceError(err)
} }
// Retrieve the initial candidates // Get the project info
candidates, err := de.getCandidates(ctx, pl) pro, err := de.getProject(ctx, pl.ProjectID)
if err != nil { if err != nil {
return -1, enforceError(err) return -1, enforceError(err)
} }
// Retrieve the initial candidates
candidates, err := de.getCandidates(ctx, pl, pro)
if err != nil {
return -1, enforceError(err)
}
// Override security settings if necessary
ov := overrideSecuritySettings(pl, pro)
for _, ss := range ov {
log.Infof("Policy %s.%s's criteria '%s' is override from value '%s' to '%s'", ss...)
}
// Do filters // Do filters
filtered, err := policy.NewFilter(). filtered, err := policy.NewFilter().
BuildFrom(pl). BuildFrom(pl).
@ -222,7 +237,7 @@ func (de *defaultEnforcer) PreheatArtifact(ctx context.Context, art *artifact.Ar
} }
// Get project info // Get project info
p, err := de.proCtl.Get(ctx, art.ProjectID, project.CVEAllowlist(true)) p, err := de.getProject(ctx, art.ProjectID)
if err != nil { if err != nil {
return nil, enforceErrorExt(err, art) return nil, enforceErrorExt(err, art)
} }
@ -257,20 +272,26 @@ func (de *defaultEnforcer) PreheatArtifact(ctx context.Context, art *artifact.Ar
// Get and check if the provider instance bound with the policy is healthy // Get and check if the provider instance bound with the policy is healthy
inst, err := de.instMgr.Get(ctx, pl.ProviderID) inst, err := de.instMgr.Get(ctx, pl.ProviderID)
if err != nil { if err != nil {
logger.Errorf("Failed to get the preheat provider instance bound with the policy %d:%s with error: %s", pl.ID, pl.Name, err.Error()) log.Errorf("Failed to get the preheat provider instance bound with the policy %d:%s with error: %s", pl.ID, pl.Name, err.Error())
continue continue
} }
// Skip unhealthy instance // Skip unhealthy instance
if err := checkProviderHealthy(inst); err != nil { if err := checkProviderHealthy(inst); err != nil {
logger.Errorf("The preheat provider instance bound with the policy %d:%s is not healthy: %s", pl.ID, pl.Name, err.Error()) log.Errorf("The preheat provider instance bound with the policy %d:%s is not healthy: %s", pl.ID, pl.Name, err.Error())
continue continue
} }
// Override security settings if necessary
ov := overrideSecuritySettings(pl, p)
for _, ss := range ov {
log.Infof("Policy %s.%s's criteria '%s' is override from value '%s' to '%s'", ss...)
}
filtered, err := policy.NewFilter().BuildFrom(pl).Filter(candidates) filtered, err := policy.NewFilter().BuildFrom(pl).Filter(candidates)
if err != nil { if err != nil {
// Log error and continue // Log error and continue
logger.Errorf("Failed to do filter for policy %d:%s with error: %s", pl.ID, pl.Name, err.Error()) log.Errorf("Failed to do filter for policy %d:%s with error: %s", pl.ID, pl.Name, err.Error())
continue continue
} }
@ -281,7 +302,7 @@ func (de *defaultEnforcer) PreheatArtifact(ctx context.Context, art *artifact.Ar
eid, err := de.launchExecutions(ctx, filtered, pl, inst) eid, err := de.launchExecutions(ctx, filtered, pl, inst)
if err != nil { if err != nil {
// Log error and continue // Log error and continue
logger.Errorf("Failed to launch execution for policy %d:%s with error: %s", pl.ID, pl.Name, err.Error()) log.Errorf("Failed to launch execution for policy %d:%s with error: %s", pl.ID, pl.Name, err.Error())
} else { } else {
// Success and then append the execution id to list // Success and then append the execution id to list
ids = append(ids, eid) ids = append(ids, eid)
@ -299,13 +320,7 @@ func (de *defaultEnforcer) PreheatArtifact(ctx context.Context, art *artifact.Ar
} }
// getCandidates get the initial candidates by evaluating the policy // getCandidates get the initial candidates by evaluating the policy
func (de *defaultEnforcer) getCandidates(ctx context.Context, ps *pol.Schema) ([]*selector.Candidate, error) { func (de *defaultEnforcer) getCandidates(ctx context.Context, ps *pol.Schema, p *models.Project) ([]*selector.Candidate, error) {
// Get project info
p, err := de.proCtl.Get(ctx, ps.ProjectID, project.CVEAllowlist(true))
if err != nil {
return nil, err
}
// Get the initial candidates // Get the initial candidates
// Here we have a hidden filter, the artifact type filter. // Here we have a hidden filter, the artifact type filter.
// Only get the image type at this moment. // Only get the image type at this moment.
@ -370,7 +385,7 @@ func (de *defaultEnforcer) launchExecutions(ctx context.Context, candidates []*s
for _, c := range candidates { for _, c := range candidates {
if _, err = de.startTask(ctx, eid, c, insData); err != nil { if _, err = de.startTask(ctx, eid, c, insData); err != nil {
// Just log the error and skip // Just log the error and skip
logger.Errorf("start task error for preheating image: %s/%s:%s@%s", c.Namespace, c.Repository, c.Tags[0], c.Digest) log.Errorf("start task error for preheating image: %s/%s:%s@%s", c.Namespace, c.Repository, c.Tags[0], c.Digest)
continue continue
} }
@ -498,6 +513,12 @@ func (de *defaultEnforcer) toCandidates(ctx context.Context, p *models.Project,
return candidates, nil return candidates, nil
} }
// getProject gets the full metadata of the specified project
func (de *defaultEnforcer) getProject(ctx context.Context, id int64) (*models.Project, error) {
// Get project info with CVE allow list and metadata
return de.proCtl.Get(ctx, id, project.CVEAllowlist(true), project.Metadata(true))
}
// enforceError is a wrap error // enforceError is a wrap error
func enforceError(e error) error { func enforceError(e error) error {
return errors.Wrap(e, "enforce policy error") return errors.Wrap(e, "enforce policy error")
@ -549,3 +570,55 @@ func checkProviderHealthy(inst *provider.Instance) error {
return nil return nil
} }
// Check the project security settings and override the related settings in the policy if necessary.
// NOTES: if the security settings (relevant with signature and vulnerability) are set at the project configuration,
// they will have the highest priority and override the related settings of the preheat policy.
// e.g (use signature as an example, similar case to vulnerability severity part):
// if policy.signature = false and project.config.signature = true; then policy.signature = true
// if policy.signature = true and project.config.signature = true; then policy.signature = true
// if policy.signature = true and project.config.signature = false; then policy.signature = true
// if policy.signature = false and project.config.signature = false; then policy.signature = false
//
// If override happened, then return the override setting list
func overrideSecuritySettings(p *pol.Schema, pro *models.Project) [][]interface{} {
if p == nil || pro == nil {
return nil
}
override := make([][]interface{}, 0)
for _, fl := range p.Filters {
switch fl.Type {
case pol.FilterTypeSignature:
if ct, ok := pro.Metadata[proMetaKeyContentTrust]; ok && ct == "true" {
if sig, ok := fl.Value.(bool); !ok || (ok && !sig) {
// Record this is a override case
r1 := []interface{}{pro.Name, p.Name, fl.Type, fmt.Sprintf("%v", sig), ct}
override = append(override, r1)
}
// Override: must be set align with project configuration setting
fl.Value = true
}
case pol.FilterTypeVulnerability:
if v, ok := pro.Metadata[proMetaKeyVulnerability]; ok && v == "true" {
if se, ok := pro.Metadata[proMetaKeySeverity]; ok && len(se) > 0 {
se = strings.ToTitle(se)
code := vuln.Severity(se).Code()
if sev, ok := fl.Value.(int); !ok || (ok && sev < code) {
// Record this is a override case
r2 := []interface{}{pro.Name, p.Name, fl.Type, fmt.Sprintf("%v:%d", fl.Value, sev), fmt.Sprintf("%s:%d", se, code)}
override = append(override, r2)
// Override: must be set align with project configuration setting
fl.Value = code
}
}
}
default:
}
}
return override
}

View File

@ -115,10 +115,16 @@ func (suite *EnforcerTestSuite) SetupSuite() {
context.TODO(), context.TODO(),
(int64)(1), (int64)(1),
mock.Anything, mock.Anything,
mock.Anything,
).Return(&models.Project{ ).Return(&models.Project{
ProjectID: 1, ProjectID: 1,
Name: "library", Name: "library",
CVEAllowlist: models.CVEAllowlist{}, CVEAllowlist: models.CVEAllowlist{},
Metadata: map[string]string{
proMetaKeyContentTrust: "true",
proMetaKeyVulnerability: "true",
proMetaKeySeverity: "low",
},
}, nil) }, nil)
fakeInstanceMgr := &instance.FakeManager{} fakeInstanceMgr := &instance.FakeManager{}
@ -198,7 +204,7 @@ func mockPolicies() []*po.Schema {
}, },
{ {
Type: po.FilterTypeSignature, Type: po.FilterTypeSignature,
Value: true, Value: false,
}, },
{ {
Type: po.FilterTypeVulnerability, Type: po.FilterTypeVulnerability,