2022-04-25 20:20:40 +08:00
package engine
import (
2022-12-09 14:45:11 +01:00
"context"
2024-07-25 20:36:19 +03:00
"strings"
2022-04-25 20:20:40 +08:00
2023-02-09 16:15:51 +01:00
"github.com/go-logr/logr"
2022-05-17 13:12:43 +02:00
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2022-04-25 20:20:40 +08:00
"github.com/kyverno/kyverno/pkg/autogen"
2023-01-30 12:41:09 +01:00
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
2023-02-07 05:30:15 +01:00
"github.com/kyverno/kyverno/pkg/engine/internal"
2023-03-27 10:09:46 +02:00
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
2022-04-25 20:20:40 +08:00
"github.com/kyverno/kyverno/pkg/engine/variables"
2023-11-13 17:43:25 +02:00
"k8s.io/client-go/tools/cache"
2022-04-25 20:20:40 +08:00
)
// ApplyBackgroundChecks checks for validity of generate and mutateExisting rules on the resource
// 1. validate variables to be substitute in the general ruleInfo (match,exclude,condition)
2022-08-24 15:08:24 +02:00
// - the caller has to check the ruleResponse to determine whether the path exist
//
2022-04-25 20:20:40 +08:00
// 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
2023-02-06 13:49:04 +01:00
func ( e * engine ) applyBackgroundChecks (
2023-02-09 16:15:51 +01:00
logger logr . Logger ,
2023-01-31 16:28:48 +01:00
policyContext engineapi . PolicyContext ,
2023-03-30 13:59:32 +02:00
) engineapi . PolicyResponse {
2024-05-30 07:29:24 +08:00
return e . filterRules ( policyContext , logger )
2022-04-25 20:20:40 +08:00
}
2023-02-06 13:49:04 +01:00
func ( e * engine ) filterRules (
2023-01-31 16:28:48 +01:00
policyContext engineapi . PolicyContext ,
2023-02-09 16:15:51 +01:00
logger logr . Logger ,
2023-03-30 13:59:32 +02:00
) engineapi . PolicyResponse {
2023-01-31 16:28:48 +01:00
policy := policyContext . Policy ( )
2023-03-30 13:59:32 +02:00
resp := engineapi . NewPolicyResponse ( )
2023-01-31 16:28:48 +01:00
applyRules := policy . GetSpec ( ) . GetApplyRules ( )
2024-04-04 10:09:30 +02:00
for _ , rule := range autogen . ComputeRules ( policy , "" ) {
2023-02-09 16:15:51 +01:00
logger := internal . LoggerWithRule ( logger , rule )
if ruleResp := e . filterRule ( rule , logger , policyContext ) ; ruleResp != nil {
2023-03-30 13:59:32 +02:00
resp . Rules = append ( resp . Rules , * ruleResp )
2023-04-05 12:35:38 +02:00
if applyRules == kyvernov1 . ApplyOne && ruleResp . Status ( ) != engineapi . RuleStatusSkip {
2022-07-29 00:02:26 -07:00
break
}
2022-04-25 20:20:40 +08:00
}
}
2023-03-23 13:58:52 +01:00
return resp
2022-04-25 20:20:40 +08:00
}
2023-02-06 13:49:04 +01:00
func ( e * engine ) filterRule (
2023-01-31 15:30:40 +01:00
rule kyvernov1 . Rule ,
2023-02-09 16:15:51 +01:00
logger logr . Logger ,
2023-01-31 16:28:48 +01:00
policyContext engineapi . PolicyContext ,
2023-01-31 15:30:40 +01:00
) * engineapi . RuleResponse {
2023-12-22 21:07:17 +08:00
if ! rule . HasGenerate ( ) && ! rule . HasMutateExisting ( ) {
2022-04-25 20:20:40 +08:00
return nil
}
2023-01-30 12:41:09 +01:00
ruleType := engineapi . Mutation
2022-04-25 20:20:40 +08:00
if rule . HasGenerate ( ) {
2023-01-30 12:41:09 +01:00
ruleType = engineapi . Generation
2022-04-25 20:20:40 +08:00
}
2023-11-13 17:43:25 +02:00
// get policy exceptions that matches both policy and rule name
exceptions , err := e . GetPolicyExceptions ( policyContext . Policy ( ) , rule . Name )
if err != nil {
logger . Error ( err , "failed to get exceptions" )
return nil
}
2024-07-25 20:36:19 +03:00
// check if there are policy exceptions that match the incoming resource
matchedExceptions := engineutils . MatchesException ( exceptions , policyContext , logger )
if len ( matchedExceptions ) > 0 {
var keys [ ] string
for i , exception := range matchedExceptions {
key , err := cache . MetaNamespaceKeyFunc ( & matchedExceptions [ i ] )
if err != nil {
logger . Error ( err , "failed to compute policy exception key" , "namespace" , exception . GetNamespace ( ) , "name" , exception . GetName ( ) )
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( rule . Name , ruleType , "failed to compute exception key" , err , rule . ReportProperties )
2024-07-25 20:36:19 +03:00
}
keys = append ( keys , key )
2023-11-13 17:43:25 +02:00
}
2024-07-25 20:36:19 +03:00
logger . V ( 3 ) . Info ( "policy rule is skipped due to policy exceptions" , "exceptions" , keys )
2024-09-03 23:06:07 +05:30
return engineapi . RuleSkip ( rule . Name , ruleType , "rule is skipped due to policy exception " + strings . Join ( keys , ", " ) , rule . ReportProperties ) . WithExceptions ( matchedExceptions )
2023-02-07 17:51:25 +01:00
}
2023-01-31 16:28:48 +01:00
newResource := policyContext . NewResource ( )
oldResource := policyContext . OldResource ( )
admissionInfo := policyContext . AdmissionInfo ( )
namespaceLabels := policyContext . NamespaceLabels ( )
2023-03-10 21:55:08 +08:00
policy := policyContext . Policy ( )
2023-03-22 11:18:11 +01:00
gvk , subresource := policyContext . ResourceKind ( )
2023-03-29 06:22:21 +02:00
if err := engineutils . MatchesResourceDescription ( newResource , rule , admissionInfo , namespaceLabels , policy . GetNamespace ( ) , gvk , subresource , policyContext . Operation ( ) ) ; err != nil {
2023-01-30 12:41:09 +01:00
if ruleType == engineapi . Generation {
2022-04-25 20:20:40 +08:00
// if the oldResource matched, return "false" to delete GR for it
2023-03-29 06:22:21 +02:00
if err = engineutils . MatchesResourceDescription ( oldResource , rule , admissionInfo , namespaceLabels , policy . GetNamespace ( ) , gvk , subresource , policyContext . Operation ( ) ) ; err == nil {
2024-09-03 23:06:07 +05:30
return engineapi . RuleFail ( rule . Name , ruleType , "" , rule . ReportProperties )
2022-04-25 20:20:40 +08:00
}
}
2022-05-25 19:56:22 +05:30
logger . V ( 4 ) . Info ( "rule not matched" , "reason" , err . Error ( ) )
2022-04-25 20:20:40 +08:00
return nil
}
2023-01-31 16:28:48 +01:00
policyContext . JSONContext ( ) . Checkpoint ( )
defer policyContext . JSONContext ( ) . Restore ( )
2022-04-25 20:20:40 +08:00
2023-04-05 15:51:07 +02:00
contextLoader := e . ContextLoader ( policyContext . Policy ( ) , rule )
if err := contextLoader ( context . TODO ( ) , rule . Context , policyContext . JSONContext ( ) ) ; err != nil {
2022-04-25 20:20:40 +08:00
logger . V ( 4 ) . Info ( "cannot add external data to the context" , "reason" , err . Error ( ) )
return nil
}
// operate on the copy of the conditions, as we perform variable substitution
2023-05-20 14:06:54 -07:00
copyConditions , err := engineutils . TransformConditions ( rule . GetAnyAllConditions ( ) )
2022-04-25 20:20:40 +08:00
if err != nil {
logger . V ( 4 ) . Info ( "cannot copy AnyAllConditions" , "reason" , err . Error ( ) )
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( rule . Name , ruleType , "failed to convert AnyAllConditions" , err , rule . ReportProperties )
2022-04-25 20:20:40 +08:00
}
// evaluate pre-conditions
2023-06-15 16:32:19 +01:00
pass , msg , err := variables . EvaluateConditions ( logger , policyContext . JSONContext ( ) , copyConditions )
if err != nil {
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( rule . Name , ruleType , "failed to evaluate conditions" , err , rule . ReportProperties )
2022-04-25 20:20:40 +08:00
}
2023-06-15 16:32:19 +01:00
if pass {
2024-09-03 23:06:07 +05:30
return engineapi . RulePass ( rule . Name , ruleType , "" , rule . ReportProperties )
2023-06-15 16:32:19 +01:00
}
if policyContext . OldResource ( ) . Object != nil {
if err = policyContext . JSONContext ( ) . AddResource ( policyContext . OldResource ( ) . Object ) ; err != nil {
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( rule . Name , ruleType , "failed to update JSON context for old resource" , err , rule . ReportProperties )
2023-06-15 16:32:19 +01:00
}
if val , msg , err := variables . EvaluateConditions ( logger , policyContext . JSONContext ( ) , copyConditions ) ; err != nil {
2024-09-03 23:06:07 +05:30
return engineapi . RuleError ( rule . Name , ruleType , "failed to evaluate conditions for old resource" , err , rule . ReportProperties )
2023-06-15 16:32:19 +01:00
} else {
if val {
2024-09-03 23:06:07 +05:30
return engineapi . RuleFail ( rule . Name , ruleType , msg , rule . ReportProperties )
2023-06-15 16:32:19 +01:00
}
}
}
logger . V ( 4 ) . Info ( "skip rule as preconditions are not met" , "rule" , rule . Name , "message" , msg )
2024-09-03 23:06:07 +05:30
return engineapi . RuleSkip ( rule . Name , ruleType , "" , rule . ReportProperties )
2022-04-25 20:20:40 +08:00
}