package policycache import ( "strings" "sync" kyverno "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/common" "github.com/kyverno/kyverno/pkg/policy" ) type pMap struct { lock sync.RWMutex // kindDataMap field stores names of ClusterPolicies and Namespaced Policies. // Since both the policy name use same type (i.e. string), Both policies can be differentiated based on // "namespace". namespace policy get stored with policy namespace with policy name" // kindDataMap {"kind": {{"policytype" : {"policyName","nsname/policyName}}},"kind2": {{"policytype" : {"nsname/policyName" }}}} kindDataMap map[string]map[PolicyType][]string // nameCacheMap stores the names of all existing policies in dataMap // Policy names are stored as / nameCacheMap map[PolicyType]map[string]bool } func (m *pMap) add(policy kyverno.PolicyInterface) { m.lock.Lock() defer m.lock.Unlock() spec := policy.GetSpec() enforcePolicy := spec.ValidationFailureAction == kyverno.Enforce for _, k := range spec.ValidationFailureActionOverrides { if k.Action == kyverno.Enforce { enforcePolicy = true break } } mutateMap := m.nameCacheMap[Mutate] validateEnforceMap := m.nameCacheMap[ValidateEnforce] validateAuditMap := m.nameCacheMap[ValidateAudit] generateMap := m.nameCacheMap[Generate] imageVerifyMap := m.nameCacheMap[VerifyImages] var pName = policy.GetName() pSpace := policy.GetNamespace() if pSpace != "" { pName = pSpace + "/" + pName } for _, rule := range autogen.ComputeRules(policy) { if len(rule.MatchResources.Any) > 0 { for _, rmr := range rule.MatchResources.Any { addCacheHelper(rmr, m, rule, mutateMap, pName, enforcePolicy, validateEnforceMap, validateAuditMap, generateMap, imageVerifyMap) } } else if len(rule.MatchResources.All) > 0 { for _, rmr := range rule.MatchResources.All { addCacheHelper(rmr, m, rule, mutateMap, pName, enforcePolicy, validateEnforceMap, validateAuditMap, generateMap, imageVerifyMap) } } else { r := kyverno.ResourceFilter{UserInfo: rule.MatchResources.UserInfo, ResourceDescription: rule.MatchResources.ResourceDescription} addCacheHelper(r, m, rule, mutateMap, pName, enforcePolicy, validateEnforceMap, validateAuditMap, generateMap, imageVerifyMap) } } m.nameCacheMap[Mutate] = mutateMap m.nameCacheMap[ValidateEnforce] = validateEnforceMap m.nameCacheMap[ValidateAudit] = validateAuditMap m.nameCacheMap[Generate] = generateMap m.nameCacheMap[VerifyImages] = imageVerifyMap } func (m *pMap) get(key PolicyType, gvk, namespace string) (names []string) { m.lock.RLock() defer m.lock.RUnlock() _, kind := common.GetKindFromGVK(gvk) for _, policyName := range m.kindDataMap[kind][key] { ns, key, isNamespacedPolicy := policy.ParseNamespacedPolicy(policyName) if !isNamespacedPolicy && namespace == "" { names = append(names, key) } else { if ns == namespace { names = append(names, policyName) } } } return names } func (m *pMap) remove(policy kyverno.PolicyInterface) { m.lock.Lock() defer m.lock.Unlock() var pName = policy.GetName() pSpace := policy.GetNamespace() if pSpace != "" { pName = pSpace + "/" + pName } for _, rule := range autogen.ComputeRules(policy) { if len(rule.MatchResources.Any) > 0 { for _, rmr := range rule.MatchResources.Any { removeCacheHelper(rmr, m, pName) } } else if len(rule.MatchResources.All) > 0 { for _, rmr := range rule.MatchResources.All { removeCacheHelper(rmr, m, pName) } } else { r := kyverno.ResourceFilter{UserInfo: rule.MatchResources.UserInfo, ResourceDescription: rule.MatchResources.ResourceDescription} removeCacheHelper(r, m, pName) } } } func addCacheHelper(rmr kyverno.ResourceFilter, m *pMap, rule kyverno.Rule, mutateMap map[string]bool, pName string, enforcePolicy bool, validateEnforceMap map[string]bool, validateAuditMap map[string]bool, generateMap map[string]bool, imageVerifyMap map[string]bool) { for _, gvk := range rmr.Kinds { _, k := common.GetKindFromGVK(gvk) kind := strings.Title(k) _, ok := m.kindDataMap[kind] if !ok { m.kindDataMap[kind] = make(map[PolicyType][]string) } if rule.HasMutate() { if !mutateMap[kind+"/"+pName] { mutateMap[kind+"/"+pName] = true mutatePolicy := m.kindDataMap[kind][Mutate] m.kindDataMap[kind][Mutate] = append(mutatePolicy, pName) } continue } if rule.HasValidate() { if enforcePolicy { if !validateEnforceMap[kind+"/"+pName] { validateEnforceMap[kind+"/"+pName] = true validatePolicy := m.kindDataMap[kind][ValidateEnforce] m.kindDataMap[kind][ValidateEnforce] = append(validatePolicy, pName) } continue } // ValidateAudit if !validateAuditMap[kind+"/"+pName] { validateAuditMap[kind+"/"+pName] = true validatePolicy := m.kindDataMap[kind][ValidateAudit] m.kindDataMap[kind][ValidateAudit] = append(validatePolicy, pName) } continue } if rule.HasGenerate() { if !generateMap[kind+"/"+pName] { generateMap[kind+"/"+pName] = true generatePolicy := m.kindDataMap[kind][Generate] m.kindDataMap[kind][Generate] = append(generatePolicy, pName) } continue } if rule.HasVerifyImages() { if !imageVerifyMap[kind+"/"+pName] { imageVerifyMap[kind+"/"+pName] = true imageVerifyMapPolicy := m.kindDataMap[kind][VerifyImages] m.kindDataMap[kind][VerifyImages] = append(imageVerifyMapPolicy, pName) } continue } } } func removeCacheHelper(rmr kyverno.ResourceFilter, m *pMap, pName string) { for _, gvk := range rmr.Kinds { _, kind := common.GetKindFromGVK(gvk) dataMap := m.kindDataMap[kind] for policyType, policies := range dataMap { var newPolicies []string for _, p := range policies { if p == pName { continue } newPolicies = append(newPolicies, p) } m.kindDataMap[kind][policyType] = newPolicies } for _, nameCache := range m.nameCacheMap { if ok := nameCache[kind+"/"+pName]; ok { delete(nameCache, kind+"/"+pName) } } } }