2019-08-13 09:37:02 -07:00
package policy
import (
2020-12-21 11:04:19 -08:00
"errors"
2022-03-22 23:55:35 +05:30
"strings"
2019-08-13 09:37:02 -07:00
"sync"
"time"
2020-03-17 11:05:20 -07:00
"github.com/go-logr/logr"
2021-10-29 18:13:20 +02:00
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
2022-03-28 16:01:27 +02:00
"github.com/kyverno/kyverno/pkg/autogen"
2021-02-04 02:39:42 +05:30
"github.com/kyverno/kyverno/pkg/common"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/response"
2021-05-15 19:15:04 +05:30
"github.com/kyverno/kyverno/pkg/metrics"
2021-07-23 21:46:50 +05:30
policyExecutionDuration "github.com/kyverno/kyverno/pkg/metrics/policyexecutionduration"
policyResults "github.com/kyverno/kyverno/pkg/metrics/policyresults"
2022-04-01 10:34:25 +02:00
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
2019-08-13 09:37:02 -07:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
2022-03-31 08:44:00 +02:00
func ( pc * PolicyController ) processExistingResources ( policy kyverno . PolicyInterface ) {
logger := pc . log . WithValues ( "policy" , policy . GetName ( ) )
2020-12-21 11:04:19 -08:00
logger . V ( 4 ) . Info ( "applying policy to existing resources" )
// Parse through all the resources drops the cache after configured rebuild time
2019-08-13 09:37:02 -07:00
pc . rm . Drop ( )
2020-12-21 11:04:19 -08:00
2022-03-28 16:01:27 +02:00
for _ , rule := range autogen . ComputeRules ( policy ) {
2022-01-05 12:37:44 +05:30
if ! rule . HasValidate ( ) && ! rule . HasVerifyImages ( ) {
2019-08-13 09:37:02 -07:00
continue
}
2022-04-05 17:12:22 +02:00
matchKinds := rule . MatchResources . GetKinds ( )
2021-10-05 00:15:09 -07:00
pc . processExistingKinds ( matchKinds , policy , rule , logger )
2019-08-13 09:37:02 -07:00
}
2020-12-21 11:04:19 -08:00
}
2022-03-31 08:44:00 +02:00
func ( pc * PolicyController ) applyAndReportPerNamespace ( policy kyverno . PolicyInterface , kind string , ns string , rule kyverno . Rule , logger logr . Logger , metricAlreadyRegistered * bool ) {
2020-12-21 11:04:19 -08:00
rMap := pc . getResourcesPerNamespace ( kind , ns , rule , logger )
2022-03-31 08:44:00 +02:00
excludeAutoGenResources ( policy , rMap , logger )
2020-12-21 11:04:19 -08:00
if len ( rMap ) == 0 {
return
}
2020-12-23 15:10:07 -08:00
var engineResponses [ ] * response . EngineResponse
2020-12-21 11:04:19 -08:00
for _ , resource := range rMap {
responses := pc . applyPolicy ( policy , resource , logger )
engineResponses = append ( engineResponses , responses ... )
}
2020-05-17 09:51:18 -07:00
2021-05-15 19:15:04 +05:30
if ! * metricAlreadyRegistered && len ( engineResponses ) > 0 {
for _ , engineResponse := range engineResponses {
2021-07-23 21:46:50 +05:30
// registering the kyverno_policy_results_total metric concurrently
2022-03-30 15:04:30 +02:00
go pc . registerPolicyResultsMetricValidation ( logger , policy , * engineResponse )
2021-07-23 21:46:50 +05:30
// registering the kyverno_policy_execution_duration_seconds metric concurrently
2022-03-30 15:04:30 +02:00
go pc . registerPolicyExecutionDurationMetricValidate ( logger , policy , * engineResponse )
2021-05-15 19:15:04 +05:30
}
* metricAlreadyRegistered = true
}
2021-06-30 00:43:11 +03:00
pc . report ( engineResponses , logger )
2020-12-21 11:04:19 -08:00
}
2022-03-30 15:04:30 +02:00
func ( pc * PolicyController ) registerPolicyResultsMetricValidation ( logger logr . Logger , policy kyverno . PolicyInterface , engineResponse response . EngineResponse ) {
2022-04-06 20:14:13 +02:00
if err := policyResults . ProcessEngineResponse ( pc . promConfig , policy , engineResponse , metrics . BackgroundScan , metrics . ResourceCreated ) ; err != nil {
2022-03-30 15:04:30 +02:00
logger . Error ( err , "error occurred while registering kyverno_policy_results_total metrics for the above policy" , "name" , policy . GetName ( ) )
2021-05-15 19:15:04 +05:30
}
}
2022-03-30 15:04:30 +02:00
func ( pc * PolicyController ) registerPolicyExecutionDurationMetricValidate ( logger logr . Logger , policy kyverno . PolicyInterface , engineResponse response . EngineResponse ) {
2022-04-06 20:14:13 +02:00
if err := policyExecutionDuration . ProcessEngineResponse ( pc . promConfig , policy , engineResponse , metrics . BackgroundScan , "" , metrics . ResourceCreated ) ; err != nil {
2022-03-30 15:04:30 +02:00
logger . Error ( err , "error occurred while registering kyverno_policy_execution_duration_seconds metrics for the above policy" , "name" , policy . GetName ( ) )
2021-05-15 21:34:26 +05:30
}
}
2022-03-31 08:44:00 +02:00
func ( pc * PolicyController ) applyPolicy ( policy kyverno . PolicyInterface , resource unstructured . Unstructured , logger logr . Logger ) ( engineResponses [ ] * response . EngineResponse ) {
2020-12-21 11:04:19 -08:00
// pre-processing, check if the policy and resource version has been processed before
2022-03-31 08:44:00 +02:00
if ! pc . rm . ProcessResource ( policy . GetName ( ) , policy . GetResourceVersion ( ) , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , resource . GetResourceVersion ( ) ) {
logger . V ( 4 ) . Info ( "policy and resource already processed" , "policyResourceVersion" , policy . GetResourceVersion ( ) , "resourceResourceVersion" , resource . GetResourceVersion ( ) , "kind" , resource . GetKind ( ) , "namespace" , resource . GetNamespace ( ) , "name" , resource . GetName ( ) )
2020-12-21 11:04:19 -08:00
}
2021-02-04 02:39:42 +05:30
namespaceLabels := common . GetNamespaceSelectorsFromNamespaceLister ( resource . GetKind ( ) , resource . GetNamespace ( ) , pc . nsLister , logger )
2022-03-31 08:44:00 +02:00
engineResponse := applyPolicy ( policy , resource , logger , pc . configHandler . GetExcludeGroupRole ( ) , pc . client , namespaceLabels )
2020-12-21 11:04:19 -08:00
engineResponses = append ( engineResponses , engineResponse ... )
// post-processing, register the resource as processed
pc . rm . RegisterResource ( policy . GetName ( ) , policy . GetResourceVersion ( ) , resource . GetKind ( ) , resource . GetNamespace ( ) , resource . GetName ( ) , resource . GetResourceVersion ( ) )
return
2020-06-25 09:52:54 -07:00
}
2020-09-01 09:11:20 -07:00
// excludeAutoGenResources filter out the pods / jobs with ownerReference
2022-03-31 08:44:00 +02:00
func excludeAutoGenResources ( policy kyverno . PolicyInterface , resourceMap map [ string ] unstructured . Unstructured , log logr . Logger ) {
2020-06-25 09:52:54 -07:00
for uid , r := range resourceMap {
2022-03-31 08:44:00 +02:00
if engine . ManagedPodResource ( policy , r ) {
2020-09-01 09:11:20 -07:00
log . V ( 4 ) . Info ( "exclude resource" , "namespace" , r . GetNamespace ( ) , "kind" , r . GetKind ( ) , "name" , r . GetName ( ) )
2020-06-25 09:52:54 -07:00
delete ( resourceMap , uid )
}
}
2019-08-13 09:37:02 -07:00
}
2020-01-24 12:05:53 -08:00
//Condition defines condition type
2019-09-03 19:31:42 -07:00
type Condition int
const (
2020-01-24 12:05:53 -08:00
//NotEvaluate to not evaluate condition
2019-09-03 19:31:42 -07:00
NotEvaluate Condition = 0
2020-01-24 12:05:53 -08:00
// Process to evaluate condition
Process Condition = 1
// Skip to ignore/skip the condition
Skip Condition = 2
2019-09-03 19:31:42 -07:00
)
2019-08-14 10:01:47 -07:00
//NewResourceManager returns a new ResourceManager
2019-08-13 09:37:02 -07:00
func NewResourceManager ( rebuildTime int64 ) * ResourceManager {
rm := ResourceManager {
2020-12-21 11:04:19 -08:00
scope : make ( map [ string ] bool ) ,
2019-08-13 09:37:02 -07:00
data : make ( map [ string ] interface { } ) ,
time : time . Now ( ) ,
rebuildTime : rebuildTime ,
}
// set time it was built
return & rm
}
2019-08-14 10:01:47 -07:00
// ResourceManager stores the details on already processed resources for caching
2019-08-13 09:37:02 -07:00
type ResourceManager struct {
// we drop and re-build the cache
// based on the memory consumer of by the map
2020-12-21 11:04:19 -08:00
scope map [ string ] bool
2019-08-13 09:37:02 -07:00
data map [ string ] interface { }
mux sync . RWMutex
time time . Time
rebuildTime int64 // after how many seconds should we rebuild the cache
}
type resourceManager interface {
ProcessResource ( policy , pv , kind , ns , name , rv string ) bool
//TODO removeResource(kind, ns, name string) error
RegisterResource ( policy , pv , kind , ns , name , rv string )
2020-12-21 11:04:19 -08:00
RegisterScope ( kind string , namespaced bool )
GetScope ( kind string ) ( bool , error )
2019-08-13 09:37:02 -07:00
Drop ( )
}
//Drop drop the cache after every rebuild interval mins
func ( rm * ResourceManager ) Drop ( ) {
2019-08-13 10:03:00 -07:00
timeSince := time . Since ( rm . time )
if timeSince > time . Duration ( rm . rebuildTime ) * time . Second {
2019-08-13 09:37:02 -07:00
rm . mux . Lock ( )
defer rm . mux . Unlock ( )
rm . data = map [ string ] interface { } { }
2019-08-13 10:03:00 -07:00
rm . time = time . Now ( )
2019-08-13 09:37:02 -07:00
}
}
var empty struct { }
//RegisterResource stores if the policy is processed on this resource version
func ( rm * ResourceManager ) RegisterResource ( policy , pv , kind , ns , name , rv string ) {
rm . mux . Lock ( )
defer rm . mux . Unlock ( )
// add the resource
key := buildKey ( policy , pv , kind , ns , name , rv )
rm . data [ key ] = empty
}
//ProcessResource returns true if the policy was not applied on the resource
func ( rm * ResourceManager ) ProcessResource ( policy , pv , kind , ns , name , rv string ) bool {
rm . mux . RLock ( )
defer rm . mux . RUnlock ( )
key := buildKey ( policy , pv , kind , ns , name , rv )
_ , ok := rm . data [ key ]
2020-01-27 08:58:53 -08:00
return ! ok
2019-08-13 09:37:02 -07:00
}
2020-12-21 11:04:19 -08:00
// RegisterScope stores the scope of the given kind
func ( rm * ResourceManager ) RegisterScope ( kind string , namespaced bool ) {
rm . mux . Lock ( )
defer rm . mux . Unlock ( )
rm . scope [ kind ] = namespaced
}
// GetScope gets the scope of the given kind
// return error if kind is not registered
func ( rm * ResourceManager ) GetScope ( kind string ) ( bool , error ) {
rm . mux . RLock ( )
defer rm . mux . RUnlock ( )
namespaced , ok := rm . scope [ kind ]
if ! ok {
return false , errors . New ( "NotFound" )
}
return namespaced , nil
}
2019-08-13 09:37:02 -07:00
func buildKey ( policy , pv , kind , ns , name , rv string ) string {
return policy + "/" + pv + "/" + kind + "/" + ns + "/" + name + "/" + rv
}
2021-09-14 00:06:23 +05:30
2022-03-31 08:44:00 +02:00
func ( pc * PolicyController ) processExistingKinds ( kinds [ ] string , policy kyverno . PolicyInterface , rule kyverno . Rule , logger logr . Logger ) {
2021-09-14 00:06:23 +05:30
2022-03-22 23:55:35 +05:30
for _ , kind := range kinds {
logger = logger . WithValues ( "rule" , rule . Name , "kind" , kind )
_ , err := pc . rm . GetScope ( kind )
2021-09-14 00:06:23 +05:30
if err != nil {
2022-04-01 10:34:25 +02:00
gv , k := kubeutils . GetKindFromGVK ( kind )
2022-03-22 23:55:35 +05:30
if ! strings . Contains ( k , "*" ) {
2022-05-03 07:30:04 +02:00
resourceSchema , _ , err := pc . client . Discovery ( ) . FindResource ( gv , k )
2022-03-22 23:55:35 +05:30
if err != nil {
logger . Error ( err , "failed to find resource" , "kind" , k )
continue
}
pc . rm . RegisterScope ( k , resourceSchema . Namespaced )
2021-09-14 00:06:23 +05:30
}
}
// this tracker would help to ensure that even for multiple namespaces, duplicate metric are not generated
metricRegisteredTracker := false
2022-03-31 08:44:00 +02:00
if policy . GetNamespace ( ) != "" {
ns := policy . GetNamespace ( )
2022-03-22 23:55:35 +05:30
pc . applyAndReportPerNamespace ( policy , kind , ns , rule , logger . WithValues ( "kind" , kind ) . WithValues ( "ns" , ns ) , & metricRegisteredTracker )
2021-09-14 00:06:23 +05:30
continue
}
2022-01-18 20:59:35 +08:00
2022-03-22 23:55:35 +05:30
pc . applyAndReportPerNamespace ( policy , kind , "" , rule , logger . WithValues ( "kind" , kind ) , & metricRegisteredTracker )
2021-09-14 00:06:23 +05:30
}
}