mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-21 16:19:21 +01:00
feat(p2p):enhance policy enforcer
Read security settings from the project configurations and override the preheat policy settings if necessary. 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 Signed-off-by: Steven Zou <szou@vmware.com> Signed-off-by: Steven Zou <szou@vmware.com>
This commit is contained in:
parent
c000608d55
commit
fcfde5a588
@ -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
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user