2019-10-18 17:45:24 -07:00
package policy
2019-09-27 16:31:27 -07:00
import (
2020-03-24 23:12:45 +05:30
"encoding/json"
2019-09-27 16:31:27 -07:00
"fmt"
2021-02-04 02:39:42 +05:30
"reflect"
2021-09-06 02:52:51 -07:00
"regexp"
2022-03-16 00:50:33 -04:00
"sort"
2021-02-04 02:39:42 +05:30
"strings"
2022-01-17 04:06:44 +00:00
"github.com/distribution/distribution/reference"
2021-09-06 02:52:51 -07:00
jsonpatch "github.com/evanphx/json-patch/v5"
2021-02-01 12:59:13 -08:00
"github.com/jmespath/go-jmespath"
2021-10-29 18:13:20 +02:00
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
2022-04-14 17:50:18 +05:30
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
2022-04-01 10:34:25 +02:00
"github.com/kyverno/kyverno/pkg/autogen"
2021-10-05 00:30:57 +05:30
dclient "github.com/kyverno/kyverno/pkg/dclient"
2021-02-01 12:59:13 -08:00
"github.com/kyverno/kyverno/pkg/engine"
2022-04-01 10:34:25 +02:00
"github.com/kyverno/kyverno/pkg/engine/context"
2021-03-19 20:07:54 +01:00
"github.com/kyverno/kyverno/pkg/engine/variables"
2020-11-13 16:25:51 -08:00
"github.com/kyverno/kyverno/pkg/openapi"
2020-12-15 15:21:39 -08:00
"github.com/kyverno/kyverno/pkg/utils"
2022-04-01 10:34:25 +02:00
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
2021-10-29 18:13:20 +02:00
"github.com/pkg/errors"
2022-04-06 22:43:07 +02:00
admissionv1 "k8s.io/api/admission/v1"
2021-03-02 10:01:06 +05:30
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
2019-09-27 16:31:27 -07:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2022-03-21 12:51:12 +01:00
"k8s.io/apimachinery/pkg/util/sets"
2022-03-15 09:48:58 +01:00
"k8s.io/apimachinery/pkg/util/validation/field"
2021-09-06 02:52:51 -07:00
"k8s.io/apimachinery/pkg/util/yaml"
2021-10-29 18:13:20 +02:00
"sigs.k8s.io/controller-runtime/pkg/log"
2019-09-27 16:31:27 -07:00
)
2022-05-02 17:39:37 +01:00
var allowedVariables = regexp . MustCompile ( ` request\.|serviceAccountName|serviceAccountNamespace|element|elementIndex|@|images\.|([a-z_0-9]+\()[^ { }] ` )
2021-11-03 11:16:55 -07:00
2022-05-02 17:39:37 +01:00
var allowedVariablesBackground = regexp . MustCompile ( ` request\.|element|elementIndex|@|images\.|([a-z_0-9]+\()[^ { }] ` )
2021-11-03 11:16:55 -07:00
// wildCardAllowedVariables represents regex for the allowed fields in wildcards
var wildCardAllowedVariables = regexp . MustCompile ( ` \ { \ { \s*(request\.|serviceAccountName|serviceAccountNamespace)[^ { }]*\}\} ` )
2021-11-30 18:14:58 +01:00
var errOperationForbidden = errors . New ( "variables are forbidden in the path of a JSONPatch" )
2021-09-06 02:52:51 -07:00
// validateJSONPatchPathForForwardSlash checks for forward slash
func validateJSONPatchPathForForwardSlash ( patch string ) error {
2021-11-30 18:14:58 +01:00
// Replace all variables in PatchesJSON6902, all variable checks should have happened already.
// This prevents further checks from failing unexpectedly.
patch = variables . ReplaceAllVars ( patch , func ( s string ) string { return "kyvernojsonpatchvariable" } )
2021-09-06 02:52:51 -07:00
re , err := regexp . Compile ( "^/" )
if err != nil {
return err
}
jsonPatch , err := yaml . ToJSON ( [ ] byte ( patch ) )
if err != nil {
return err
}
decodedPatch , err := jsonpatch . DecodePatch ( jsonPatch )
if err != nil {
return err
}
for _ , operation := range decodedPatch {
path , err := operation . Path ( )
if err != nil {
return err
}
val := re . MatchString ( path )
if ! val {
return fmt . Errorf ( "%s" , path )
}
}
return nil
}
2021-11-04 23:26:22 -07:00
// Validate checks the policy and rules declarations for required configurations
2022-05-03 07:30:04 +02:00
func Validate ( policy kyverno . PolicyInterface , client dclient . Interface , mock bool , openAPIController * openapi . Controller ) ( * admissionv1 . AdmissionResponse , error ) {
2022-03-23 08:33:15 +01:00
namespaced := policy . IsNamespaced ( )
2022-03-31 08:44:00 +02:00
spec := policy . GetSpec ( )
2022-04-04 22:16:45 +02:00
background := spec . BackgroundProcessingEnabled ( )
2022-05-06 13:46:36 +08:00
onPolicyUpdate := spec . GetMutateExistingOnPolicyUpdate ( )
2021-11-03 11:16:55 -07:00
2022-03-21 09:53:12 +01:00
var errs field . ErrorList
specPath := field . NewPath ( "spec" )
2020-08-24 03:41:03 +05:30
2022-03-21 09:53:12 +01:00
err := ValidateVariables ( policy , background )
if err != nil {
return nil , err
2021-10-22 01:48:22 +05:30
}
2022-05-05 18:34:49 +05:30
if onPolicyUpdate {
err := ValidateOnPolicyUpdate ( policy , onPolicyUpdate )
if err != nil {
return nil , err
}
}
2021-10-22 01:48:22 +05:30
var res [ ] * metav1 . APIResourceList
2022-03-21 12:51:12 +01:00
clusterResources := sets . NewString ( )
2021-11-03 11:16:55 -07:00
if ! mock && namespaced {
2022-03-21 09:53:12 +01:00
// Get all the cluster type kind supported by cluster
2022-05-03 07:30:04 +02:00
res , err := client . Discovery ( ) . DiscoveryCache ( ) . ServerPreferredResources ( )
2021-10-22 01:48:22 +05:30
if err != nil {
2022-03-16 00:50:33 -04:00
return nil , err
2021-10-22 01:48:22 +05:30
}
for _ , resList := range res {
for _ , r := range resList . APIResources {
if ! r . Namespaced {
2022-03-21 12:51:12 +01:00
clusterResources . Insert ( r . Kind )
2021-10-22 01:48:22 +05:30
}
}
}
2022-03-21 12:51:12 +01:00
}
2021-10-22 01:48:22 +05:30
2022-03-23 08:33:15 +01:00
if errs := policy . Validate ( clusterResources ) ; len ( errs ) != 0 {
2022-03-21 12:51:12 +01:00
return nil , errs . ToAggregate ( )
2021-10-22 01:48:22 +05:30
}
2022-03-28 16:01:27 +02:00
rules := autogen . ComputeRules ( policy )
2022-03-15 09:48:58 +01:00
rulesPath := specPath . Child ( "rules" )
2022-03-09 16:28:31 +01:00
for i , rule := range rules {
2022-03-15 09:48:58 +01:00
rulePath := rulesPath . Index ( i )
2021-09-06 02:52:51 -07:00
//check for forward slash
if err := validateJSONPatchPathForForwardSlash ( rule . Mutation . PatchesJSON6902 ) ; err != nil {
2022-03-16 00:50:33 -04:00
return nil , fmt . Errorf ( "path must begin with a forward slash: spec.rules[%d]: %s" , i , err )
2021-09-06 02:52:51 -07:00
}
2020-12-09 15:37:45 -08:00
if jsonPatchOnPod ( rule ) {
2022-04-13 01:11:37 -07:00
msg := "Pods managed by workload controllers should not be directly mutated using policies. " +
"Use the autogen feature or write policies that match Pod controllers."
log . Log . V ( 1 ) . Info ( msg )
2022-04-06 22:43:07 +02:00
return & admissionv1 . AdmissionResponse {
2022-03-16 00:50:33 -04:00
Allowed : true ,
2022-04-13 01:11:37 -07:00
Warnings : [ ] string { msg } ,
2022-03-16 00:50:33 -04:00
} , nil
2020-12-09 15:37:45 -08:00
}
2022-03-16 00:50:33 -04:00
2019-10-21 14:22:31 -07:00
// validate resource description
2022-03-16 17:15:46 +01:00
if path , err := validateResources ( rulePath , rule ) ; err != nil {
2022-03-16 00:50:33 -04:00
return nil , fmt . Errorf ( "path: spec.rules[%d].%s: %v" , i , path , err )
2019-10-21 14:22:31 -07:00
}
2020-10-14 17:39:45 -07:00
2021-10-14 22:44:11 +05:30
err := validateElementInForEach ( rule )
if err != nil {
2022-03-16 00:50:33 -04:00
return nil , err
2021-10-14 22:44:11 +05:30
}
2020-10-14 17:39:45 -07:00
if err := validateRuleContext ( rule ) ; err != nil {
2022-03-16 00:50:33 -04:00
return nil , fmt . Errorf ( "path: spec.rules[%d]: %v" , i , err )
2020-10-14 17:39:45 -07:00
}
2020-11-13 16:25:51 -08:00
// validate Cluster Resources in namespaced policy
// For namespaced policy, ClusterResource type field and values are not allowed in match and exclude
2021-11-03 11:16:55 -07:00
if namespaced {
2022-03-16 00:50:33 -04:00
return nil , checkClusterResourceInMatchAndExclude ( rule , clusterResources , mock , res )
2020-09-01 21:42:05 +05:30
}
2019-09-27 16:31:27 -07:00
2020-03-11 18:14:23 -07:00
// validate rule actions
// - Mutate
// - Validate
// - Generate
2022-03-09 16:28:31 +01:00
if err := validateActions ( i , & rules [ i ] , client , mock ) ; err != nil {
2022-03-16 00:50:33 -04:00
return nil , err
2019-09-27 19:03:55 -07:00
}
2020-02-26 16:08:56 +05:30
2021-07-29 01:29:53 +05:30
// If a rule's match block does not match any kind,
// we should only allow it to have metadata in its overlay
if len ( rule . MatchResources . Any ) > 0 {
for _ , rmr := range rule . MatchResources . Any {
if len ( rmr . Kinds ) == 0 {
2022-03-16 00:50:33 -04:00
return nil , validateMatchKindHelper ( rule )
2021-07-29 01:29:53 +05:30
}
}
} else if len ( rule . MatchResources . All ) > 0 {
for _ , rmr := range rule . MatchResources . All {
if len ( rmr . Kinds ) == 0 {
2022-03-16 00:50:33 -04:00
return nil , validateMatchKindHelper ( rule )
2021-07-29 01:29:53 +05:30
}
}
} else {
if len ( rule . MatchResources . Kinds ) == 0 {
2022-03-16 00:50:33 -04:00
return nil , validateMatchKindHelper ( rule )
2020-02-26 16:08:56 +05:30
}
2021-07-14 23:49:15 +05:30
}
2022-04-04 22:16:45 +02:00
if utils . ContainsString ( rule . MatchResources . Kinds , "*" ) && spec . BackgroundProcessingEnabled ( ) {
2022-03-16 00:50:33 -04:00
return nil , fmt . Errorf ( "wildcard policy not allowed in background mode. Set spec.background=false to disable background mode for this policy rule " )
2021-10-14 12:45:32 +05:30
}
if ( utils . ContainsString ( rule . MatchResources . Kinds , "*" ) && len ( rule . MatchResources . Kinds ) > 1 ) || ( utils . ContainsString ( rule . ExcludeResources . Kinds , "*" ) && len ( rule . ExcludeResources . Kinds ) > 1 ) {
2022-03-16 00:50:33 -04:00
return nil , fmt . Errorf ( "wildard policy can not deal more than one kind" )
2021-10-14 12:45:32 +05:30
}
2021-07-14 23:49:15 +05:30
if utils . ContainsString ( rule . MatchResources . Kinds , "*" ) || utils . ContainsString ( rule . ExcludeResources . Kinds , "*" ) {
2021-10-14 12:45:32 +05:30
if rule . HasGenerate ( ) || rule . HasVerifyImages ( ) || rule . Validation . ForEachValidation != nil {
2022-03-16 00:50:33 -04:00
return nil , fmt . Errorf ( "wildcard policy does not support rule type" )
2021-10-14 12:45:32 +05:30
}
if rule . HasValidate ( ) {
2022-03-06 20:07:51 +01:00
if rule . Validation . GetPattern ( ) != nil || rule . Validation . GetAnyPattern ( ) != nil {
2021-10-14 12:45:32 +05:30
if ! ruleOnlyDealsWithResourceMetaData ( rule ) {
2022-03-16 00:50:33 -04:00
return nil , fmt . Errorf ( "policy can only deal with the metadata field of the resource if" +
2021-10-14 12:45:32 +05:30
" the rule does not match any kind" )
}
}
if rule . Validation . Deny != nil {
2022-03-06 20:07:51 +01:00
kyvernoConditions , _ := utils . ApiextensionsJsonToKyvernoConditions ( rule . Validation . Deny . GetAnyAllConditions ( ) )
2021-10-14 12:45:32 +05:30
switch typedConditions := kyvernoConditions . ( type ) {
case [ ] kyverno . Condition : // backwards compatibility
for _ , condition := range typedConditions {
2022-03-06 20:07:51 +01:00
key := condition . GetKey ( )
if ! strings . Contains ( key . ( string ) , "request.object.metadata." ) && ( ! wildCardAllowedVariables . MatchString ( key . ( string ) ) || strings . Contains ( key . ( string ) , "request.object.spec" ) ) {
2022-03-16 00:50:33 -04:00
return nil , fmt . Errorf ( "policy can only deal with the metadata field of the resource if" +
2021-10-14 12:45:32 +05:30
" the rule does not match any kind" )
}
}
}
}
}
2021-11-04 23:26:22 -07:00
2021-10-14 12:45:32 +05:30
if rule . HasMutate ( ) {
if ! ruleOnlyDealsWithResourceMetaData ( rule ) {
2022-03-16 00:50:33 -04:00
return nil , fmt . Errorf ( "policy can only deal with the metadata field of the resource if" +
2021-10-14 12:45:32 +05:30
" the rule does not match any kind" )
}
}
2021-11-04 23:26:22 -07:00
if rule . HasVerifyImages ( ) {
2022-03-15 09:48:58 +01:00
verifyImagePath := rulePath . Child ( "verifyImages" )
for index , i := range rule . VerifyImages {
errs = append ( errs , i . Validate ( verifyImagePath . Index ( index ) ) ... )
2021-11-04 23:26:22 -07:00
}
}
2022-03-15 09:48:58 +01:00
if len ( errs ) != 0 {
2022-03-16 00:50:33 -04:00
return nil , errs . ToAggregate ( )
2022-03-15 09:48:58 +01:00
}
2020-02-26 16:08:56 +05:30
}
2020-08-29 06:52:22 +05:30
2022-03-16 00:50:33 -04:00
var podOnlyMap = make ( map [ string ] bool ) //Validate that Kind is only Pod
podOnlyMap [ "Pod" ] = true
if reflect . DeepEqual ( common . GetKindsFromRule ( rule ) , podOnlyMap ) && podControllerAutoGenExclusion ( policy ) {
2022-04-13 01:11:37 -07:00
msg := "Policies that match Pods apply to all Pods including those created and managed by controllers " +
"excluded from autogen. Use preconditions to exclude the Pods managed by controllers which are " +
"excluded from autogen. Refer to https://kyverno.io/docs/writing-policies/autogen/ for details."
2022-04-06 22:43:07 +02:00
return & admissionv1 . AdmissionResponse {
2022-03-16 00:50:33 -04:00
Allowed : true ,
2022-04-13 01:11:37 -07:00
Warnings : [ ] string { msg } ,
2022-03-16 00:50:33 -04:00
} , nil
}
2021-10-14 12:45:32 +05:30
//Validate Kind with match resource kinds
2021-10-05 00:30:57 +05:30
match := rule . MatchResources
exclude := rule . ExcludeResources
for _ , value := range match . Any {
2022-03-08 18:29:33 +05:30
if ! utils . ContainsString ( value . ResourceDescription . Kinds , "*" ) {
2022-03-31 08:44:00 +02:00
err := validateKinds ( value . ResourceDescription . Kinds , mock , client , policy )
2022-03-08 18:29:33 +05:30
if err != nil {
2022-03-16 00:50:33 -04:00
return nil , errors . Wrapf ( err , "the kind defined in the any match resource is invalid" )
2022-03-08 18:29:33 +05:30
}
2021-10-05 00:30:57 +05:30
}
}
for _ , value := range match . All {
2022-03-08 18:29:33 +05:30
if ! utils . ContainsString ( value . ResourceDescription . Kinds , "*" ) {
2022-03-31 08:44:00 +02:00
err := validateKinds ( value . ResourceDescription . Kinds , mock , client , policy )
2022-03-08 18:29:33 +05:30
if err != nil {
2022-03-16 00:50:33 -04:00
return nil , errors . Wrapf ( err , "the kind defined in the all match resource is invalid" )
2022-03-08 18:29:33 +05:30
}
2021-08-21 03:58:49 +05:30
}
}
2021-10-05 00:30:57 +05:30
for _ , value := range exclude . Any {
2022-03-08 18:29:33 +05:30
if ! utils . ContainsString ( value . ResourceDescription . Kinds , "*" ) {
2022-03-31 08:44:00 +02:00
err := validateKinds ( value . ResourceDescription . Kinds , mock , client , policy )
2022-03-08 18:29:33 +05:30
if err != nil {
2022-03-16 00:50:33 -04:00
return nil , errors . Wrapf ( err , "the kind defined in the any exclude resource is invalid" )
2022-03-08 18:29:33 +05:30
}
2021-10-05 00:30:57 +05:30
}
}
for _ , value := range exclude . All {
2022-03-08 18:29:33 +05:30
if ! utils . ContainsString ( value . ResourceDescription . Kinds , "*" ) {
2022-03-31 08:44:00 +02:00
err := validateKinds ( value . ResourceDescription . Kinds , mock , client , policy )
2022-03-08 18:29:33 +05:30
if err != nil {
2022-03-16 00:50:33 -04:00
return nil , errors . Wrapf ( err , "the kind defined in the all exclude resource is invalid" )
2022-03-08 18:29:33 +05:30
}
2021-10-05 00:30:57 +05:30
}
}
2021-10-14 12:45:32 +05:30
if ! utils . ContainsString ( rule . MatchResources . Kinds , "*" ) {
2022-03-31 08:44:00 +02:00
err := validateKinds ( rule . MatchResources . Kinds , mock , client , policy )
2021-10-14 12:45:32 +05:30
if err != nil {
2022-03-16 00:50:33 -04:00
return nil , errors . Wrapf ( err , "match resource kind is invalid" )
2021-10-14 12:45:32 +05:30
}
2022-03-31 08:44:00 +02:00
err = validateKinds ( rule . ExcludeResources . Kinds , mock , client , policy )
2021-10-14 12:45:32 +05:30
if err != nil {
2022-03-16 00:50:33 -04:00
return nil , errors . Wrapf ( err , "exclude resource kind is invalid" )
2021-10-14 12:45:32 +05:30
}
2021-10-05 00:30:57 +05:30
}
2021-08-21 03:58:49 +05:30
2020-08-29 06:52:22 +05:30
// Validate string values in labels
if ! isLabelAndAnnotationsString ( rule ) {
2022-03-16 00:50:33 -04:00
return nil , fmt . Errorf ( "labels and annotations supports only string values, \"use double quotes around the non string values\"" )
2020-08-29 06:52:22 +05:30
}
2020-12-30 12:10:41 +05:30
// add label to source mentioned in policy
if ! mock && rule . Generation . Clone . Name != "" {
obj , err := client . GetResource ( "" , rule . Generation . Kind , rule . Generation . Clone . Namespace , rule . Generation . Clone . Name )
if err != nil {
log . Log . Error ( err , fmt . Sprintf ( "source resource %s/%s/%s not found." , rule . Generation . Kind , rule . Generation . Clone . Namespace , rule . Generation . Clone . Name ) )
continue
}
updateSource := true
label := obj . GetLabels ( )
if len ( label ) == 0 {
label = make ( map [ string ] string )
2021-11-03 11:16:55 -07:00
label [ "generate.kyverno.io/clone-policy-name" ] = policy . GetName ( )
2020-12-30 12:10:41 +05:30
} else {
if label [ "generate.kyverno.io/clone-policy-name" ] != "" {
policyNames := label [ "generate.kyverno.io/clone-policy-name" ]
2021-11-03 11:16:55 -07:00
if ! strings . Contains ( policyNames , policy . GetName ( ) ) {
policyNames = policyNames + "," + policy . GetName ( )
2020-12-30 12:10:41 +05:30
label [ "generate.kyverno.io/clone-policy-name" ] = policyNames
} else {
updateSource = false
}
} else {
2021-11-03 11:16:55 -07:00
label [ "generate.kyverno.io/clone-policy-name" ] = policy . GetName ( )
2020-12-30 12:10:41 +05:30
}
}
if updateSource {
log . Log . V ( 4 ) . Info ( "updating existing clone source" )
obj . SetLabels ( label )
_ , err = client . UpdateResource ( obj . GetAPIVersion ( ) , rule . Generation . Kind , rule . Generation . Clone . Namespace , obj , false )
if err != nil {
2021-04-08 12:10:30 -07:00
log . Log . Error ( err , "failed to update source" , "kind" , obj . GetKind ( ) , "name" , obj . GetName ( ) , "namespace" , obj . GetNamespace ( ) )
2020-12-30 12:10:41 +05:30
continue
}
2021-04-08 12:10:30 -07:00
log . Log . V ( 4 ) . Info ( "updated source" , "kind" , obj . GetKind ( ) , "name" , obj . GetName ( ) , "namespace" , obj . GetNamespace ( ) )
2020-12-30 12:10:41 +05:30
}
}
2019-09-27 19:03:55 -07:00
}
2020-01-25 14:53:12 +05:30
2022-03-31 08:44:00 +02:00
if spec . SchemaValidation == nil || * spec . SchemaValidation {
if err := openAPIController . ValidatePolicyMutation ( policy ) ; err != nil {
2022-03-16 00:50:33 -04:00
return nil , err
2021-11-03 11:16:55 -07:00
}
}
2022-03-16 00:50:33 -04:00
return nil , nil
2021-11-03 11:16:55 -07:00
}
2022-03-31 08:44:00 +02:00
func ValidateVariables ( p kyverno . PolicyInterface , backgroundMode bool ) error {
2021-11-03 11:16:55 -07:00
vars := hasVariables ( p )
if len ( vars ) == 0 {
return nil
}
if err := hasInvalidVariables ( p , backgroundMode ) ; err != nil {
return fmt . Errorf ( "policy contains invalid variables: %s" , err . Error ( ) )
}
if backgroundMode {
if err := containsUserVariables ( p , vars ) ; err != nil {
return fmt . Errorf ( "only select variables are allowed in background mode. Set spec.background=false to disable background mode for this policy rule: %s " , err )
}
}
return nil
}
// hasInvalidVariables - checks for unexpected variables in the policy
2022-03-31 08:44:00 +02:00
func hasInvalidVariables ( policy kyverno . PolicyInterface , background bool ) error {
2022-03-28 16:01:27 +02:00
for _ , r := range autogen . ComputeRules ( policy ) {
2021-11-03 11:16:55 -07:00
ruleCopy := r . DeepCopy ( )
if err := ruleForbiddenSectionsHaveVariables ( ruleCopy ) ; err != nil {
2020-04-01 19:06:13 +05:30
return err
}
2021-11-03 11:16:55 -07:00
// skip variable checks on verifyImages.attestations, as variables in attestations are dynamic
for _ , vi := range ruleCopy . VerifyImages {
for _ , a := range vi . Attestations {
a . Conditions = nil
}
}
ctx := buildContext ( ruleCopy , background )
if _ , err := variables . SubstituteAllInRule ( log . Log , ctx , * ruleCopy ) ; ! checkNotFoundErr ( err ) {
return fmt . Errorf ( "variable substitution failed for rule %s: %s" , ruleCopy . Name , err . Error ( ) )
}
2020-03-04 19:16:26 +05:30
}
2019-09-27 19:03:55 -07:00
return nil
}
2022-05-05 18:34:49 +05:30
func ValidateOnPolicyUpdate ( p kyverno . PolicyInterface , onPolicyUpdate bool ) error {
vars := hasVariables ( p )
if len ( vars ) == 0 {
return nil
}
if err := hasInvalidVariables ( p , onPolicyUpdate ) ; err != nil {
return fmt . Errorf ( "policy contains invalid variables: %s" , err . Error ( ) )
}
if err := containsUserVariables ( p , vars ) ; err != nil {
2022-05-06 13:46:36 +08:00
return fmt . Errorf ( "only select variables are allowed in on policy update. Set spec.mutateExistingOnPolicyUpdate=false to disable update policy mode for this policy rule: %s " , err )
2022-05-05 18:34:49 +05:30
}
return nil
}
2021-11-03 11:16:55 -07:00
// for now forbidden sections are match, exclude and
func ruleForbiddenSectionsHaveVariables ( rule * kyverno . Rule ) error {
var err error
err = jsonPatchPathHasVariables ( rule . Mutation . PatchesJSON6902 )
2021-11-30 18:14:58 +01:00
if err != nil && errors . Is ( errOperationForbidden , err ) {
2021-11-03 11:16:55 -07:00
return fmt . Errorf ( "rule \"%s\" should not have variables in patchesJSON6902 path section" , rule . Name )
}
err = objectHasVariables ( rule . ExcludeResources )
if err != nil {
return fmt . Errorf ( "rule \"%s\" should not have variables in exclude section" , rule . Name )
}
err = objectHasVariables ( rule . MatchResources )
if err != nil {
return fmt . Errorf ( "rule \"%s\" should not have variables in match section" , rule . Name )
}
return nil
}
// hasVariables - check for variables in the policy
2022-03-31 08:44:00 +02:00
func hasVariables ( policy kyverno . PolicyInterface ) [ ] [ ] string {
2021-11-03 11:16:55 -07:00
policyRaw , _ := json . Marshal ( policy )
matches := variables . RegexVariables . FindAllStringSubmatch ( string ( policyRaw ) , - 1 )
return matches
}
func jsonPatchPathHasVariables ( patch string ) error {
jsonPatch , err := yaml . ToJSON ( [ ] byte ( patch ) )
if err != nil {
return err
}
decodedPatch , err := jsonpatch . DecodePatch ( jsonPatch )
if err != nil {
return err
}
for _ , operation := range decodedPatch {
path , err := operation . Path ( )
if err != nil {
return err
}
vars := variables . RegexVariables . FindAllString ( path , - 1 )
if len ( vars ) > 0 {
2021-11-30 18:14:58 +01:00
return errOperationForbidden
2021-11-03 11:16:55 -07:00
}
}
return nil
}
func objectHasVariables ( object interface { } ) error {
var err error
objectJSON , err := json . Marshal ( object )
if err != nil {
return err
}
if len ( common . RegexVariables . FindAllStringSubmatch ( string ( objectJSON ) , - 1 ) ) > 0 {
return fmt . Errorf ( "invalid variables" )
}
return nil
}
func buildContext ( rule * kyverno . Rule , background bool ) * context . MockContext {
re := getAllowedVariables ( background )
ctx := context . NewMockContext ( re )
addContextVariables ( rule . Context , ctx )
for _ , fe := range rule . Validation . ForEachValidation {
addContextVariables ( fe . Context , ctx )
}
for _ , fe := range rule . Mutation . ForEachMutation {
addContextVariables ( fe . Context , ctx )
}
return ctx
}
func getAllowedVariables ( background bool ) * regexp . Regexp {
if background {
return allowedVariablesBackground
}
return allowedVariables
}
func addContextVariables ( entries [ ] kyverno . ContextEntry , ctx * context . MockContext ) {
for _ , contextEntry := range entries {
2022-04-25 12:06:07 +01:00
if contextEntry . APICall != nil || contextEntry . ImageRegistry != nil || contextEntry . Variable != nil {
2021-11-03 11:16:55 -07:00
ctx . AddVariable ( contextEntry . Name + "*" )
}
if contextEntry . ConfigMap != nil {
ctx . AddVariable ( contextEntry . Name + ".data.*" )
}
}
}
func checkNotFoundErr ( err error ) bool {
if err != nil {
switch err . ( type ) {
case jmespath . NotFoundError :
return true
case context . InvalidVariableErr :
return false
default :
return false
}
}
return true
}
2021-10-14 22:44:11 +05:30
func validateElementInForEach ( document apiextensions . JSON ) error {
jsonByte , err := json . Marshal ( document )
if err != nil {
return err
}
var jsonInterface interface { }
err = json . Unmarshal ( jsonByte , & jsonInterface )
if err != nil {
return err
}
_ , err = variables . ValidateElementInForEach ( log . Log , jsonInterface )
return err
}
2021-07-29 01:29:53 +05:30
func validateMatchKindHelper ( rule kyverno . Rule ) error {
if ! ruleOnlyDealsWithResourceMetaData ( rule ) {
return fmt . Errorf ( "policy can only deal with the metadata field of the resource if" +
2021-10-14 12:45:32 +05:30
" the rule does not match any kind" )
2021-07-29 01:29:53 +05:30
}
2021-11-03 11:16:55 -07:00
2021-10-12 23:29:20 +02:00
return fmt . Errorf ( "at least one element must be specified in a kind block, the kind attribute is mandatory when working with the resources element" )
2021-07-29 01:29:53 +05:30
}
2020-08-29 06:52:22 +05:30
// isLabelAndAnnotationsString :- Validate if labels and annotations contains only string values
func isLabelAndAnnotationsString ( rule kyverno . Rule ) bool {
// checkMetadata - Verify if the labels and annotations contains string value inside metadata
checkMetadata := func ( patternMap map [ string ] interface { } ) bool {
for k := range patternMap {
if k == "metadata" {
metaKey , ok := patternMap [ k ] . ( map [ string ] interface { } )
if ok {
// range over metadata
for mk := range metaKey {
if mk == "labels" {
labelKey , ok := metaKey [ mk ] . ( map [ string ] interface { } )
if ok {
// range over labels
for _ , val := range labelKey {
if reflect . TypeOf ( val ) . String ( ) != "string" {
return false
}
}
}
} else if mk == "annotations" {
annotationKey , ok := metaKey [ mk ] . ( map [ string ] interface { } )
if ok {
// range over annotations
for _ , val := range annotationKey {
if reflect . TypeOf ( val ) . String ( ) != "string" {
return false
}
}
}
}
}
}
}
}
return true
}
2022-03-06 20:07:51 +01:00
patternMap , ok := rule . Validation . GetPattern ( ) . ( map [ string ] interface { } )
2020-08-29 06:52:22 +05:30
if ok {
return checkMetadata ( patternMap )
2022-03-06 20:07:51 +01:00
} else if rule . Validation . GetAnyPattern ( ) != nil {
2020-11-13 16:25:51 -08:00
anyPatterns , err := rule . Validation . DeserializeAnyPattern ( )
if err != nil {
2020-12-09 15:37:45 -08:00
log . Log . Error ( err , "failed to deserialize anyPattern, expect type array" )
2020-11-13 16:25:51 -08:00
return false
}
2020-08-29 06:52:22 +05:30
for _ , pattern := range anyPatterns {
patternMap , ok := pattern . ( map [ string ] interface { } )
if ok {
ret := checkMetadata ( patternMap )
2022-05-10 11:24:27 +02:00
if ! ret {
2020-08-29 06:52:22 +05:30
return ret
}
}
}
}
return true
}
2020-02-26 16:08:56 +05:30
func ruleOnlyDealsWithResourceMetaData ( rule kyverno . Rule ) bool {
2022-03-06 20:07:51 +01:00
patches , _ := rule . Mutation . GetPatchStrategicMerge ( ) . ( map [ string ] interface { } )
2022-01-04 17:36:33 -08:00
for k := range patches {
2020-02-26 16:08:56 +05:30
if k != "metadata" {
return false
}
}
2022-01-04 17:36:33 -08:00
if rule . Mutation . PatchesJSON6902 != "" {
bytes := [ ] byte ( rule . Mutation . PatchesJSON6902 )
jp , _ := jsonpatch . DecodePatch ( bytes )
for _ , o := range jp {
path , _ := o . Path ( )
if ! strings . HasPrefix ( path , "/metadata" ) {
return false
}
2021-10-14 12:45:32 +05:30
}
}
2022-03-06 20:07:51 +01:00
patternMap , _ := rule . Validation . GetPattern ( ) . ( map [ string ] interface { } )
2020-02-26 16:08:56 +05:30
for k := range patternMap {
if k != "metadata" {
return false
}
}
2020-11-13 16:25:51 -08:00
anyPatterns , err := rule . Validation . DeserializeAnyPattern ( )
if err != nil {
2020-12-09 15:37:45 -08:00
log . Log . Error ( err , "failed to deserialize anyPattern, expect type array" )
2020-11-13 16:25:51 -08:00
return false
}
for _ , pattern := range anyPatterns {
2020-02-26 16:08:56 +05:30
patternMap , _ := pattern . ( map [ string ] interface { } )
for k := range patternMap {
if k != "metadata" {
return false
}
}
}
return true
}
2022-03-16 17:15:46 +01:00
func validateResources ( path * field . Path , rule kyverno . Rule ) ( string , error ) {
2019-12-04 18:50:51 -08:00
// validate userInfo in match and exclude
2022-03-16 17:15:46 +01:00
if errs := rule . ExcludeResources . UserInfo . Validate ( path . Child ( "exclude" ) ) ; len ( errs ) != 0 {
return "exclude" , errs . ToAggregate ( )
2019-12-04 18:50:51 -08:00
}
2021-07-29 01:29:53 +05:30
if ( len ( rule . MatchResources . Any ) > 0 || len ( rule . MatchResources . All ) > 0 ) && ! reflect . DeepEqual ( rule . MatchResources . ResourceDescription , kyverno . ResourceDescription { } ) {
2021-10-12 23:29:20 +02:00
return "match." , fmt . Errorf ( "can't specify any/all together with match resources" )
2021-07-29 01:29:53 +05:30
}
if ( len ( rule . ExcludeResources . Any ) > 0 || len ( rule . ExcludeResources . All ) > 0 ) && ! reflect . DeepEqual ( rule . ExcludeResources . ResourceDescription , kyverno . ResourceDescription { } ) {
2021-10-12 23:29:20 +02:00
return "exclude." , fmt . Errorf ( "can't specify any/all together with exclude resources" )
2019-09-27 16:31:27 -07:00
}
2021-07-29 01:29:53 +05:30
if len ( rule . ExcludeResources . Any ) > 0 && len ( rule . ExcludeResources . All ) > 0 {
2021-10-12 23:29:20 +02:00
return "match." , fmt . Errorf ( "can't specify any and all together" )
2021-07-29 01:29:53 +05:30
}
if len ( rule . MatchResources . Any ) > 0 {
for _ , rmr := range rule . MatchResources . Any {
// matched resources
if path , err := validateMatchedResourceDescription ( rmr . ResourceDescription ) ; err != nil {
return fmt . Sprintf ( "match.resources.%s" , path ) , err
}
}
} else if len ( rule . MatchResources . All ) > 0 {
for _ , rmr := range rule . MatchResources . All {
// matched resources
if path , err := validateMatchedResourceDescription ( rmr . ResourceDescription ) ; err != nil {
return fmt . Sprintf ( "match.resources.%s" , path ) , err
}
}
} else {
// matched resources
if path , err := validateMatchedResourceDescription ( rule . MatchResources . ResourceDescription ) ; err != nil {
return fmt . Sprintf ( "match.resources.%s" , path ) , err
}
}
2021-03-02 10:01:06 +05:30
//validating the values present under validate.preconditions, if they exist
2022-03-06 20:07:51 +01:00
if target := rule . GetAnyAllConditions ( ) ; target != nil {
if path , err := validateConditions ( target , "preconditions" ) ; err != nil {
2021-02-02 02:57:16 +05:30
return fmt . Sprintf ( "validate.%s" , path ) , err
}
}
2021-03-02 10:01:06 +05:30
//validating the values present under validate.conditions, if they exist
2022-03-06 20:07:51 +01:00
if rule . Validation . Deny != nil {
if target := rule . Validation . Deny . GetAnyAllConditions ( ) ; target != nil {
if path , err := validateConditions ( target , "conditions" ) ; err != nil {
return fmt . Sprintf ( "validate.deny.%s" , path ) , err
}
2021-02-02 02:57:16 +05:30
}
}
return "" , nil
}
// validateConditions validates all the 'conditions' or 'preconditions' of a rule depending on the corresponding 'condition.key'.
// As of now, it is validating the 'value' field whether it contains the only allowed set of values or not when 'condition.key' is {{request.operation}}
2021-03-02 10:01:06 +05:30
// this is backwards compatible i.e. conditions can be provided in the old manner as well i.e. without 'any' or 'all'
func validateConditions ( conditions apiextensions . JSON , schemaKey string ) ( string , error ) {
// Conditions can only exist under some specific keys of the policy schema
allowedSchemaKeys := map [ string ] bool {
"preconditions" : true ,
"conditions" : true ,
}
if ! allowedSchemaKeys [ schemaKey ] {
2022-05-10 11:24:27 +02:00
return schemaKey , fmt . Errorf ( "wrong schema key found for validating the conditions. Conditions can only occur under one of ['preconditions', 'conditions'] keys in the policy schema" )
2021-03-02 10:01:06 +05:30
}
// conditions are currently in the form of []interface{}
kyvernoConditions , err := utils . ApiextensionsJsonToKyvernoConditions ( conditions )
if err != nil {
2022-05-10 11:24:27 +02:00
return schemaKey , err
2021-03-02 10:01:06 +05:30
}
switch typedConditions := kyvernoConditions . ( type ) {
case kyverno . AnyAllConditions :
// validating the conditions under 'any', if there are any
if ! reflect . DeepEqual ( typedConditions , kyverno . AnyAllConditions { } ) && typedConditions . AnyConditions != nil {
for i , condition := range typedConditions . AnyConditions {
if path , err := validateConditionValues ( condition ) ; err != nil {
return fmt . Sprintf ( "%s.any[%d].%s" , schemaKey , i , path ) , err
}
}
}
// validating the conditions under 'all', if there are any
if ! reflect . DeepEqual ( typedConditions , kyverno . AnyAllConditions { } ) && typedConditions . AllConditions != nil {
for i , condition := range typedConditions . AllConditions {
if path , err := validateConditionValues ( condition ) ; err != nil {
return fmt . Sprintf ( "%s.all[%d].%s" , schemaKey , i , path ) , err
}
}
}
case [ ] kyverno . Condition : // backwards compatibility
for i , condition := range typedConditions {
if path , err := validateConditionValues ( condition ) ; err != nil {
return fmt . Sprintf ( "%s[%d].%s" , schemaKey , i , path ) , err
}
2021-02-02 02:57:16 +05:30
}
}
return "" , nil
}
// validateConditionValues validates whether all the values under the 'value' field of a 'conditions' field
// are apt with respect to the provided 'condition.key'
func validateConditionValues ( c kyverno . Condition ) ( string , error ) {
2022-03-06 20:07:51 +01:00
k := c . GetKey ( )
v := c . GetValue ( )
if k == nil || v == nil || c . Operator == "" {
2021-11-11 22:27:18 +05:30
return "" , fmt . Errorf ( "entered value of `key`, `value` or `operator` is missing or misspelled" )
}
2022-03-06 20:07:51 +01:00
switch reflect . TypeOf ( k ) . Kind ( ) {
2022-02-03 20:16:58 +05:30
case reflect . String :
value , err := validateValuesKeyRequest ( c )
return value , err
default :
return "" , nil
}
}
func validateValuesKeyRequest ( c kyverno . Condition ) ( string , error ) {
2022-03-06 20:07:51 +01:00
k := c . GetKey ( )
switch strings . ReplaceAll ( k . ( string ) , " " , "" ) {
2021-02-02 02:57:16 +05:30
case "{{request.operation}}" :
return validateConditionValuesKeyRequestOperation ( c )
default :
return "" , nil
}
}
// validateConditionValuesKeyRequestOperation validates whether all the values under the 'value' field of a 'conditions' field
// are one of ["CREATE", "UPDATE", "DELETE", "CONNECT"] when 'condition.key' is {{request.operation}}
func validateConditionValuesKeyRequestOperation ( c kyverno . Condition ) ( string , error ) {
valuesAllowed := map [ string ] bool {
"CREATE" : true ,
"UPDATE" : true ,
"DELETE" : true ,
"CONNECT" : true ,
}
2022-03-06 20:07:51 +01:00
v := c . GetValue ( )
switch reflect . TypeOf ( v ) . Kind ( ) {
2021-02-02 02:57:16 +05:30
case reflect . String :
2022-03-06 20:07:51 +01:00
valueStr := v . ( string )
2021-02-17 02:36:07 +05:30
// allow templatized values like {{ config-map.data.sample-key }}
// because they might be actually pointing to a rightful value in the provided config-map
if len ( valueStr ) >= 4 && valueStr [ : 2 ] == "{{" && valueStr [ len ( valueStr ) - 2 : ] == "}}" {
return "" , nil
}
if ! valuesAllowed [ valueStr ] {
2022-03-06 20:07:51 +01:00
return fmt . Sprintf ( "value: %s" , v . ( string ) ) , fmt . Errorf ( "unknown value '%s' found under the 'value' field. Only the following values are allowed: [CREATE, UPDATE, DELETE, CONNECT]" , v . ( string ) )
2021-02-02 02:57:16 +05:30
}
case reflect . Slice :
2022-03-06 20:07:51 +01:00
values := reflect . ValueOf ( v )
2021-02-02 02:57:16 +05:30
for i := 0 ; i < values . Len ( ) ; i ++ {
value := values . Index ( i ) . Interface ( ) . ( string )
if ! valuesAllowed [ value ] {
return fmt . Sprintf ( "value[%d]" , i ) , fmt . Errorf ( "unknown value '%s' found under the 'value' field. Only the following values are allowed: [CREATE, UPDATE, DELETE, CONNECT]" , value )
}
}
default :
2022-03-06 20:07:51 +01:00
return "value" , fmt . Errorf ( "'value' field found to be of the type %v. The provided value/values are expected to be either in the form of a string or list" , reflect . TypeOf ( v ) . Kind ( ) )
2019-09-27 16:31:27 -07:00
}
2019-10-21 14:22:31 -07:00
return "" , nil
}
2019-09-27 16:31:27 -07:00
2020-11-09 11:26:12 -08:00
func validateRuleContext ( rule kyverno . Rule ) error {
2020-10-14 17:39:45 -07:00
if rule . Context == nil || len ( rule . Context ) == 0 {
return nil
}
for _ , entry := range rule . Context {
2020-11-09 11:26:12 -08:00
if entry . Name == "" {
2020-10-14 17:39:45 -07:00
return fmt . Errorf ( "a name is required for context entries" )
}
2022-04-25 12:06:07 +01:00
for _ , v := range [ ] string { "images" , "request" , "serviceAccountName" , "serviceAccountNamespace" , "element" , "elementIndex" } {
if entry . Name == v || strings . HasPrefix ( entry . Name , v + "." ) {
return fmt . Errorf ( "entry name %s is invalid as it conflicts with a pre-defined variable %s" , entry . Name , v )
}
}
2020-10-19 12:36:55 -07:00
2021-02-01 12:59:13 -08:00
var err error
2022-04-25 12:06:07 +01:00
if entry . ConfigMap != nil && entry . APICall == nil && entry . ImageRegistry == nil && entry . Variable == nil {
2021-02-01 12:59:13 -08:00
err = validateConfigMap ( entry )
2022-04-25 12:06:07 +01:00
} else if entry . ConfigMap == nil && entry . APICall != nil && entry . ImageRegistry == nil && entry . Variable == nil {
2021-02-01 12:59:13 -08:00
err = validateAPICall ( entry )
2022-04-25 12:06:07 +01:00
} else if entry . ConfigMap == nil && entry . APICall == nil && entry . ImageRegistry != nil && entry . Variable == nil {
2022-01-17 04:06:44 +00:00
err = validateImageRegistry ( entry )
2022-04-25 12:06:07 +01:00
} else if entry . ConfigMap == nil && entry . APICall == nil && entry . ImageRegistry == nil && entry . Variable != nil {
err = validateVariable ( entry )
2021-02-01 12:59:13 -08:00
} else {
2022-04-25 12:06:07 +01:00
return fmt . Errorf ( "exactly one of configMap or apiCall or imageRegistry or variable is required for context entries" )
2021-02-01 12:59:13 -08:00
}
2020-10-14 17:39:45 -07:00
2021-02-01 12:59:13 -08:00
if err != nil {
return err
}
}
return nil
}
2022-04-25 12:06:07 +01:00
func validateVariable ( entry kyverno . ContextEntry ) error {
// If JMESPath contains variables, the validation will fail because it's not possible to infer which value
// will be inserted by the variable
// Skip validation if a variable is detected
jmesPath := variables . ReplaceAllVars ( entry . Variable . JMESPath , func ( s string ) string { return "kyvernojmespathvariable" } )
if ! strings . Contains ( jmesPath , "kyvernojmespathvariable" ) && entry . Variable . JMESPath != "" {
if _ , err := jmespath . NewParser ( ) . Parse ( entry . Variable . JMESPath ) ; err != nil {
return fmt . Errorf ( "failed to parse JMESPath %s: %v" , entry . Variable . JMESPath , err )
}
2021-02-01 12:59:13 -08:00
}
2022-04-25 12:06:07 +01:00
if entry . Variable . Value == nil && jmesPath == "" {
return fmt . Errorf ( "a variable must define a value or a jmesPath expression" )
2021-02-01 12:59:13 -08:00
}
2022-04-25 12:06:07 +01:00
if entry . Variable . Default != nil && jmesPath == "" {
return fmt . Errorf ( "a variable must define a default value only when a jmesPath expression is defined" )
2022-01-17 04:06:44 +00:00
}
2022-04-25 12:06:07 +01:00
return nil
}
2022-01-17 04:06:44 +00:00
2022-04-25 12:06:07 +01:00
func validateConfigMap ( entry kyverno . ContextEntry ) error {
2021-02-01 12:59:13 -08:00
if entry . ConfigMap . Name == "" {
return fmt . Errorf ( "a name is required for configMap context entry" )
}
if entry . ConfigMap . Namespace == "" {
return fmt . Errorf ( "a namespace is required for configMap context entry" )
}
return nil
}
func validateAPICall ( entry kyverno . ContextEntry ) error {
2021-03-19 20:07:54 +01:00
// Replace all variables to prevent validation failing on variable keys.
2021-03-23 18:17:47 +01:00
urlPath := variables . ReplaceAllVars ( entry . APICall . URLPath , func ( s string ) string { return "kyvernoapicallvariable" } )
2021-03-19 20:07:54 +01:00
if _ , err := engine . NewAPIPath ( urlPath ) ; err != nil {
2021-02-01 12:59:13 -08:00
return err
}
2021-05-04 18:28:30 +02:00
// If JMESPath contains variables, the validation will fail because it's not possible to infer which value
// will be inserted by the variable
// Skip validation if a variable is detected
jmesPath := variables . ReplaceAllVars ( entry . APICall . JMESPath , func ( s string ) string { return "kyvernojmespathvariable" } )
if ! strings . Contains ( jmesPath , "kyvernojmespathvariable" ) && entry . APICall . JMESPath != "" {
2021-02-01 12:59:13 -08:00
if _ , err := jmespath . NewParser ( ) . Parse ( entry . APICall . JMESPath ) ; err != nil {
return fmt . Errorf ( "failed to parse JMESPath %s: %v" , entry . APICall . JMESPath , err )
2020-10-14 17:39:45 -07:00
}
}
return nil
}
2022-01-17 04:06:44 +00:00
func validateImageRegistry ( entry kyverno . ContextEntry ) error {
if entry . ImageRegistry . Reference == "" {
return fmt . Errorf ( "a ref is required for imageRegistry context entry" )
}
// Replace all variables to prevent validation failing on variable keys.
ref := variables . ReplaceAllVars ( entry . ImageRegistry . Reference , func ( s string ) string { return "kyvernoimageref" } )
// it's no use validating a refernce that contains a variable
if ! strings . Contains ( ref , "kyvernoimageref" ) {
_ , err := reference . Parse ( ref )
if err != nil {
return errors . Wrapf ( err , "bad image: %s" , ref )
}
}
// If JMESPath contains variables, the validation will fail because it's not possible to infer which value
// will be inserted by the variable
// Skip validation if a variable is detected
jmesPath := variables . ReplaceAllVars ( entry . ImageRegistry . JMESPath , func ( s string ) string { return "kyvernojmespathvariable" } )
if ! strings . Contains ( jmesPath , "kyvernojmespathvariable" ) && entry . ImageRegistry . JMESPath != "" {
if _ , err := jmespath . NewParser ( ) . Parse ( entry . ImageRegistry . JMESPath ) ; err != nil {
return fmt . Errorf ( "failed to parse JMESPath %s: %v" , entry . ImageRegistry . JMESPath , err )
}
}
return nil
}
2020-12-09 15:37:45 -08:00
// validateResourceDescription checks if all necessary fields are present and have values. Also checks a Selector.
2019-09-27 16:31:27 -07:00
// field type is checked through openapi
// Returns error if
2019-10-01 15:01:24 -07:00
// - kinds is empty array in matched resource block, i.e. kinds: []
2019-09-27 16:31:27 -07:00
// - selector is invalid
2019-10-21 14:22:31 -07:00
func validateMatchedResourceDescription ( rd kyverno . ResourceDescription ) ( string , error ) {
2019-10-18 17:45:24 -07:00
if reflect . DeepEqual ( rd , kyverno . ResourceDescription { } ) {
2019-10-21 14:22:31 -07:00
return "" , fmt . Errorf ( "match resources not specified" )
2019-09-27 16:31:27 -07:00
}
2019-10-21 14:22:31 -07:00
return "" , nil
2019-10-03 18:19:47 -07:00
}
2020-08-19 21:37:23 +05:30
// checkClusterResourceInMatchAndExclude returns false if namespaced ClusterPolicy contains cluster wide resources in
// Match and Exclude block
2022-03-21 12:51:12 +01:00
func checkClusterResourceInMatchAndExclude ( rule kyverno . Rule , clusterResources sets . String , mock bool , res [ ] * metav1 . APIResourceList ) error {
2021-10-22 01:48:22 +05:30
if ! mock {
// Check for generate policy
// - if resource to be generated is namespaced resource then the namespace field
// should be mentioned
// - if resource to be generated is non namespaced resource then the namespace field
// should not be mentioned
if rule . HasGenerate ( ) {
generateResourceKind := rule . Generation . Kind
2022-01-20 15:18:52 +08:00
generateResourceAPIVersion := rule . Generation . APIVersion
2021-10-22 01:48:22 +05:30
for _ , resList := range res {
for _ , r := range resList . APIResources {
2022-01-20 15:18:52 +08:00
if r . Kind == generateResourceKind && ( len ( generateResourceAPIVersion ) == 0 || r . Version == generateResourceAPIVersion ) {
2021-10-22 01:48:22 +05:30
if r . Namespaced {
if rule . Generation . Namespace == "" {
return fmt . Errorf ( "path: spec.rules[%v]: please mention the namespace to generate a namespaced resource" , rule . Name )
}
} else {
if rule . Generation . Namespace != "" {
return fmt . Errorf ( "path: spec.rules[%v]: do not mention the namespace to generate a non namespaced resource" , rule . Name )
}
}
}
}
2020-08-19 21:37:23 +05:30
}
}
}
return nil
}
2020-12-09 15:37:45 -08:00
// jsonPatchOnPod checks if a rule applies JSON patches to Pod
func jsonPatchOnPod ( rule kyverno . Rule ) bool {
if ! rule . HasMutate ( ) {
return false
}
2020-12-15 15:21:39 -08:00
if utils . ContainsString ( rule . MatchResources . Kinds , "Pod" ) && rule . Mutation . PatchesJSON6902 != "" {
2020-12-09 15:37:45 -08:00
return true
}
return false
}
2021-10-05 00:30:57 +05:30
2022-03-31 08:44:00 +02:00
func podControllerAutoGenExclusion ( policy kyverno . PolicyInterface ) bool {
2022-03-16 00:50:33 -04:00
annotations := policy . GetAnnotations ( )
2022-03-22 21:43:19 +01:00
val , ok := annotations [ kyverno . PodControllersAnnotation ]
2022-04-22 00:10:02 -07:00
if ! ok || val == "none" {
return false
}
2022-03-16 00:50:33 -04:00
reorderVal := strings . Split ( strings . ToLower ( val ) , "," )
sort . Slice ( reorderVal , func ( i , j int ) bool { return reorderVal [ i ] < reorderVal [ j ] } )
2022-05-10 11:24:27 +02:00
if ok && ! reflect . DeepEqual ( reorderVal , [ ] string { "cronjob" , "daemonset" , "deployment" , "job" , "statefulset" } ) {
2022-03-16 00:50:33 -04:00
return true
}
return false
}
2022-02-23 22:27:18 +05:30
// validateKinds verifies if an API resource that matches 'kind' is valid kind
// and found in the cache, returns error if not found
2022-05-03 07:30:04 +02:00
func validateKinds ( kinds [ ] string , mock bool , client dclient . Interface , p kyverno . PolicyInterface ) error {
2021-10-05 00:30:57 +05:30
for _ , kind := range kinds {
2022-04-01 10:34:25 +02:00
gv , k := kubeutils . GetKindFromGVK ( kind )
2022-03-31 08:44:00 +02:00
if k == p . GetKind ( ) {
2021-10-05 00:30:57 +05:30
return fmt . Errorf ( "kind and match resource kind should not be the same" )
}
2022-02-23 22:27:18 +05:30
2022-04-01 10:34:25 +02:00
if ! mock && ! kubeutils . SkipSubResources ( k ) && ! strings . Contains ( kind , "*" ) {
2022-05-03 07:30:04 +02:00
_ , _ , err := client . Discovery ( ) . FindResource ( gv , k )
2022-02-23 22:27:18 +05:30
if err != nil {
2022-05-09 18:50:50 -07:00
return fmt . Errorf ( "unable to convert GVK to GVR for kinds %s, err: %s" , kinds , err )
2022-02-23 22:27:18 +05:30
}
}
2021-10-05 00:30:57 +05:30
}
return nil
}