mirror of
https://github.com/kyverno/kyverno.git
synced 2025-04-09 10:42:22 +00:00
only allow one type of rule defined in a single rule
This commit is contained in:
parent
9992ab0f63
commit
76ad9406b1
3 changed files with 108 additions and 38 deletions
|
@ -3,10 +3,23 @@ package v1alpha1
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (r *Rule) HasMutate() bool {
|
||||
return !reflect.DeepEqual(r.Mutation, Mutation{})
|
||||
}
|
||||
|
||||
func (r *Rule) HasValidate() bool {
|
||||
return !reflect.DeepEqual(r.Validation, Validation{})
|
||||
}
|
||||
|
||||
func (r *Rule) HasGenerate() bool {
|
||||
return !reflect.DeepEqual(r.Generation, Generation{})
|
||||
}
|
||||
|
||||
// Validate checks if rule is not empty and all substructures are valid
|
||||
func (r *Rule) Validate() error {
|
||||
// check matches Resoource Description of match resource
|
||||
|
|
|
@ -47,7 +47,7 @@ func (in *ClusterPolicy) DeepCopyInto(out *ClusterPolicy) {
|
|||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,7 @@ func (in *Policy) DeepCopyInto(out *Policy) {
|
|||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -240,6 +240,11 @@ func (in *Policy) DeepCopy() *Policy {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PolicyStatus) DeepCopyInto(out *PolicyStatus) {
|
||||
*out = *in
|
||||
if in.Rules != nil {
|
||||
in, out := &in.Rules, &out.Rules
|
||||
*out = make([]RuleStats, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -380,6 +385,22 @@ func (in *Rule) DeepCopy() *Rule {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RuleStats) DeepCopyInto(out *RuleStats) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleStats.
|
||||
func (in *RuleStats) DeepCopy() *RuleStats {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RuleStats)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Spec) DeepCopyInto(out *Spec) {
|
||||
*out = *in
|
||||
|
|
|
@ -2,6 +2,7 @@ package webhooks
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
|
@ -28,13 +29,20 @@ func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionReques
|
|||
Message: fmt.Sprintf("Failed to unmarshal policy admission request err %v", err),
|
||||
}}
|
||||
}
|
||||
// check for uniqueness of rule names while CREATE/DELET
|
||||
admissionResp = ws.validateUniqueRuleName(policy)
|
||||
|
||||
if err := ws.validatePolicy(policy); err != nil {
|
||||
admissionResp = &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: err.Error(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// helper function to evaluate if policy has validtion or mutation rules defined
|
||||
hasMutateOrValidate := func() bool {
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if !reflect.DeepEqual(rule.Mutation, kyverno.Mutation{}) || !reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
|
||||
if rule.HasMutate() || rule.HasValidate() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -52,64 +60,92 @@ func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionReques
|
|||
return admissionResp
|
||||
}
|
||||
|
||||
func (ws *WebhookServer) validatePolicy(policy *kyverno.ClusterPolicy) *v1beta1.AdmissionResponse {
|
||||
admissionResp := ws.validateUniqueRuleName(policy)
|
||||
if !admissionResp.Allowed {
|
||||
return admissionResp
|
||||
func (ws *WebhookServer) validatePolicy(policy *kyverno.ClusterPolicy) error {
|
||||
// validate only one type of rule defined per rule
|
||||
if err := validateRuleType(policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ws.validateOverlayPattern(policy)
|
||||
// validate resource description block
|
||||
|
||||
// validate ^() can only be used on array
|
||||
|
||||
// check for uniqueness of rule names while CREATE/DELET
|
||||
if err := validateUniqueRuleName(policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateOverlayPattern(policy); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ws *WebhookServer) validateOverlayPattern(policy *kyverno.ClusterPolicy) *v1beta1.AdmissionResponse {
|
||||
// validateRuleType checks only one type of rule is defined per rule
|
||||
func validateRuleType(policy *kyverno.ClusterPolicy) error {
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
|
||||
continue
|
||||
mutate := rule.HasMutate()
|
||||
validate := rule.HasValidate()
|
||||
generate := rule.HasGenerate()
|
||||
|
||||
if !mutate && !validate && !generate {
|
||||
return fmt.Errorf("No rule defined in '%s'", rule.Name)
|
||||
}
|
||||
|
||||
if rule.Validation.Pattern == nil && len(rule.Validation.AnyPattern) == 0 {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: fmt.Sprintf("Invalid policy, neither pattern nor anyPattern found in validate rule %s", rule.Name),
|
||||
},
|
||||
}
|
||||
if (mutate && !validate && !generate) ||
|
||||
(!mutate && validate && !generate) ||
|
||||
(!mutate && !validate && generate) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if rule.Validation.Pattern != nil && len(rule.Validation.AnyPattern) != 0 {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: fmt.Sprintf("Invalid policy, either pattern or anyPattern is allowed in validate rule %s", rule.Name),
|
||||
},
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("Multiple types of rule defined in rule '%s', only one type of rule is allowed per rule", rule.Name)
|
||||
}
|
||||
|
||||
return &v1beta1.AdmissionResponse{Allowed: true}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify if the Rule names are unique within a policy
|
||||
func (ws *WebhookServer) validateUniqueRuleName(policy *kyverno.ClusterPolicy) *v1beta1.AdmissionResponse {
|
||||
func validateUniqueRuleName(policy *kyverno.ClusterPolicy) error {
|
||||
var ruleNames []string
|
||||
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if utils.ContainsString(ruleNames, rule.Name) {
|
||||
msg := fmt.Sprintf(`The policy "%s" is invalid: duplicate rule name: "%s"`, policy.Name, rule.Name)
|
||||
glog.Errorln(msg)
|
||||
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
return errors.New(msg)
|
||||
}
|
||||
ruleNames = append(ruleNames, rule.Name)
|
||||
}
|
||||
|
||||
glog.V(4).Infof("Policy validation passed")
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateOverlayPattern checks one of pattern/anyPattern must exist
|
||||
func validateOverlayPattern(policy *kyverno.ClusterPolicy) error {
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
|
||||
continue
|
||||
}
|
||||
|
||||
if rule.Validation.Pattern == nil && len(rule.Validation.AnyPattern) == 0 {
|
||||
return errors.New(fmt.Sprintf("Invalid policy, neither pattern nor anyPattern found in validate rule %s", rule.Name))
|
||||
}
|
||||
|
||||
if rule.Validation.Pattern != nil && len(rule.Validation.AnyPattern) != 0 {
|
||||
return errors.New(fmt.Sprintf("Invalid policy, either pattern or anyPattern is allowed in validate rule %s", rule.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func failResponseWithMsg(msg string) *v1beta1.AdmissionResponse {
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue