1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

refactor: Rule names validation (#3406)

* refactor: UserInfo validation

Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>

* refactor: Rule type validation

Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>

* refactor: Rule names validation

Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>

Co-authored-by: Vyankatesh Kudtarkar <vyankateshkd@gmail.com>
This commit is contained in:
Charles-Edouard Brétéché 2022-03-17 04:36:21 +01:00 committed by GitHub
parent adcb71f1d6
commit cc212ac766
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 181 additions and 169 deletions

View file

@ -8,118 +8,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Spec contains a list of Rule instances and other policy controls.
type Spec struct {
// Rules is a list of Rule instances. A Policy contains multiple rules and
// each rule can validate, mutate, or generate resources.
Rules []Rule `json:"rules,omitempty" yaml:"rules,omitempty"`
// FailurePolicy defines how unrecognized errors from the admission endpoint are handled.
// Rules within the same policy share the same failure behavior.
// Allowed values are Ignore or Fail. Defaults to Fail.
// +optional
FailurePolicy *FailurePolicyType `json:"failurePolicy,omitempty" yaml:"failurePolicy,omitempty"`
// ValidationFailureAction controls if a validation policy rule failure should disallow
// the admission review request (enforce), or allow (audit) the admission review request
// and report an error in a policy report. Optional. The default value is "audit".
// +optional
// +kubebuilder:validation:Enum=audit;enforce
ValidationFailureAction string `json:"validationFailureAction,omitempty" yaml:"validationFailureAction,omitempty"`
// ValidationFailureActionOverrides is a Cluster Policy attribute that specifies ValidationFailureAction
// namespace-wise. It overrides ValidationFailureAction for the specified namespaces.
// +optional
ValidationFailureActionOverrides []ValidationFailureActionOverride `json:"validationFailureActionOverrides,omitempty" yaml:"validationFailureActionOverrides,omitempty"`
// Background controls if rules are applied to existing resources during a background scan.
// Optional. Default value is "true". The value must be set to "false" if the policy rule
// uses variables that are only available in the admission review request (e.g. user name).
// +optional
Background *bool `json:"background,omitempty" yaml:"background,omitempty"`
// SchemaValidation skips policy validation checks.
// Optional. The default value is set to "true", it must be set to "false" to disable the validation checks.
// +optional
SchemaValidation *bool `json:"schemaValidation,omitempty" yaml:"schemaValidation,omitempty"`
// WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy.
// After the configured time expires, the admission request may fail, or may simply ignore the policy results,
// based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds.
WebhookTimeoutSeconds *int32 `json:"webhookTimeoutSeconds,omitempty" yaml:"webhookTimeoutSeconds,omitempty"`
}
func (s *Spec) GetRules() []Rule {
return s.Rules
}
func (s *Spec) SetRules(rules []Rule) {
s.Rules = rules
}
// HasMutateOrValidateOrGenerate checks for rule types
func (s *Spec) HasMutateOrValidateOrGenerate() bool {
for _, rule := range s.Rules {
if rule.HasMutate() || rule.HasValidate() || rule.HasGenerate() {
return true
}
}
return false
}
// HasMutate checks for mutate rule types
func (s *Spec) HasMutate() bool {
for _, rule := range s.Rules {
if rule.HasMutate() {
return true
}
}
return false
}
// HasValidate checks for validate rule types
func (s *Spec) HasValidate() bool {
for _, rule := range s.Rules {
if rule.HasValidate() {
return true
}
}
return false
}
// HasGenerate checks for generate rule types
func (s *Spec) HasGenerate() bool {
for _, rule := range s.Rules {
if rule.HasGenerate() {
return true
}
}
return false
}
// HasVerifyImages checks for image verification rule types
func (s *Spec) HasVerifyImages() bool {
for _, rule := range s.Rules {
if rule.HasVerifyImages() {
return true
}
}
return false
}
// BackgroundProcessingEnabled checks if background is set to true
func (s *Spec) BackgroundProcessingEnabled() bool {
if s.Background == nil {
return true
}
return *s.Background
}
// FailurePolicyType specifies a failure policy that defines how unrecognized errors from the admission endpoint are handled.
// +kubebuilder:validation:Enum=Ignore;Fail
type FailurePolicyType string

View file

@ -18,7 +18,6 @@ func Test_Validate_RuleType_EmptyRule(t *testing.T) {
assert.Equal(t, errs[0].Field, "dummy")
assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
assert.Equal(t, errs[0].Detail, "No operation defined in the rule 'validate-user-privilege'.(supported operations: mutate,validate,generate,verifyImages)")
}
func Test_Validate_RuleType_MultipleRule(t *testing.T) {

View file

@ -0,0 +1,38 @@
package v1
import (
"testing"
"gotest.tools/assert"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func Test_Validate_UniqueRuleName(t *testing.T) {
subject := Spec{
Rules: []Rule{{
Name: "deny-privileged-disallowpriviligedescalation",
MatchResources: MatchResources{
ResourceDescription: ResourceDescription{
Kinds: []string{
"Pod",
},
},
},
}, {
Name: "deny-privileged-disallowpriviligedescalation",
MatchResources: MatchResources{
ResourceDescription: ResourceDescription{
Kinds: []string{
"Pod",
},
},
},
}},
}
path := field.NewPath("dummy")
errs := subject.Validate(path)
assert.Assert(t, len(errs) == 1)
assert.Equal(t, errs[0].Field, "dummy.rules[1].name")
assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
assert.Equal(t, errs[0].Detail, "Duplicate rule name: 'deny-privileged-disallowpriviligedescalation'")
}

View file

@ -0,0 +1,141 @@
package v1
import (
"fmt"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
)
// Spec contains a list of Rule instances and other policy controls.
type Spec struct {
// Rules is a list of Rule instances. A Policy contains multiple rules and
// each rule can validate, mutate, or generate resources.
Rules []Rule `json:"rules,omitempty" yaml:"rules,omitempty"`
// FailurePolicy defines how unrecognized errors from the admission endpoint are handled.
// Rules within the same policy share the same failure behavior.
// Allowed values are Ignore or Fail. Defaults to Fail.
// +optional
FailurePolicy *FailurePolicyType `json:"failurePolicy,omitempty" yaml:"failurePolicy,omitempty"`
// ValidationFailureAction controls if a validation policy rule failure should disallow
// the admission review request (enforce), or allow (audit) the admission review request
// and report an error in a policy report. Optional. The default value is "audit".
// +optional
// +kubebuilder:validation:Enum=audit;enforce
ValidationFailureAction string `json:"validationFailureAction,omitempty" yaml:"validationFailureAction,omitempty"`
// ValidationFailureActionOverrides is a Cluster Policy attribute that specifies ValidationFailureAction
// namespace-wise. It overrides ValidationFailureAction for the specified namespaces.
// +optional
ValidationFailureActionOverrides []ValidationFailureActionOverride `json:"validationFailureActionOverrides,omitempty" yaml:"validationFailureActionOverrides,omitempty"`
// Background controls if rules are applied to existing resources during a background scan.
// Optional. Default value is "true". The value must be set to "false" if the policy rule
// uses variables that are only available in the admission review request (e.g. user name).
// +optional
Background *bool `json:"background,omitempty" yaml:"background,omitempty"`
// SchemaValidation skips policy validation checks.
// Optional. The default value is set to "true", it must be set to "false" to disable the validation checks.
// +optional
SchemaValidation *bool `json:"schemaValidation,omitempty" yaml:"schemaValidation,omitempty"`
// WebhookTimeoutSeconds specifies the maximum time in seconds allowed to apply this policy.
// After the configured time expires, the admission request may fail, or may simply ignore the policy results,
// based on the failure policy. The default timeout is 10s, the value must be between 1 and 30 seconds.
WebhookTimeoutSeconds *int32 `json:"webhookTimeoutSeconds,omitempty" yaml:"webhookTimeoutSeconds,omitempty"`
}
func (s *Spec) GetRules() []Rule {
return s.Rules
}
func (s *Spec) SetRules(rules []Rule) {
s.Rules = rules
}
// HasMutateOrValidateOrGenerate checks for rule types
func (s *Spec) HasMutateOrValidateOrGenerate() bool {
for _, rule := range s.Rules {
if rule.HasMutate() || rule.HasValidate() || rule.HasGenerate() {
return true
}
}
return false
}
// HasMutate checks for mutate rule types
func (s *Spec) HasMutate() bool {
for _, rule := range s.Rules {
if rule.HasMutate() {
return true
}
}
return false
}
// HasValidate checks for validate rule types
func (s *Spec) HasValidate() bool {
for _, rule := range s.Rules {
if rule.HasValidate() {
return true
}
}
return false
}
// HasGenerate checks for generate rule types
func (s *Spec) HasGenerate() bool {
for _, rule := range s.Rules {
if rule.HasGenerate() {
return true
}
}
return false
}
// HasVerifyImages checks for image verification rule types
func (s *Spec) HasVerifyImages() bool {
for _, rule := range s.Rules {
if rule.HasVerifyImages() {
return true
}
}
return false
}
// BackgroundProcessingEnabled checks if background is set to true
func (s *Spec) BackgroundProcessingEnabled() bool {
if s.Background == nil {
return true
}
return *s.Background
}
// ValidateRuleNames checks if the rule names are unique across a policy
func (s *Spec) ValidateRuleNames(path *field.Path) field.ErrorList {
var errs field.ErrorList
names := sets.NewString()
for i, rule := range s.Rules {
rulePath := path.Index(i)
if names.Has(rule.Name) {
errs = append(errs, field.Invalid(rulePath.Child("name"), rule, fmt.Sprintf(`Duplicate rule name: '%s'`, rule.Name)))
}
names.Insert(rule.Name)
}
return errs
}
// Validate implements programmatic validation
func (s *Spec) Validate(path *field.Path) field.ErrorList {
var errs field.ErrorList
errs = append(errs, s.ValidateRuleNames(path.Child("rules"))...)
return errs
}

View file

@ -95,8 +95,8 @@ func Validate(policy *kyverno.ClusterPolicy, client *dclient.Client, mock bool,
return nil, fmt.Errorf("invalid policy name %s: must be no more than 63 characters", policy.Name)
}
if path, err := validateUniqueRuleName(*policy); err != nil {
return nil, fmt.Errorf("path: spec.%s: %v", path, err)
if errs := policy.Spec.Validate(specPath); len(errs) != 0 {
return nil, errs.ToAggregate()
}
if policy.ObjectMeta.Namespace != "" {
@ -1096,19 +1096,6 @@ func validateConditionValuesKeyRequestOperation(c kyverno.Condition) (string, er
return "", nil
}
// validateUniqueRuleName checks if the rule names are unique across a policy
func validateUniqueRuleName(p kyverno.ClusterPolicy) (string, error) {
var ruleNames []string
for i, rule := range p.Spec.GetRules() {
if utils.ContainsString(ruleNames, rule.Name) {
return fmt.Sprintf("rule[%d]", i), fmt.Errorf(`duplicate rule name: '%s'`, rule.Name)
}
ruleNames = append(ruleNames, rule.Name)
}
return "", nil
}
func validateRuleContext(rule kyverno.Rule) error {
if rule.Context == nil || len(rule.Context) == 0 {
return nil

View file

@ -13,47 +13,6 @@ import (
"gotest.tools/assert"
)
func Test_Validate_UniqueRuleName(t *testing.T) {
rawPolicy := []byte(`
{
"spec": {
"validationFailureAction": "audit",
"rules": [
{
"name": "deny-privileged-disallowpriviligedescalation",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {}
},
{
"name": "deny-privileged-disallowpriviligedescalation",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"validate": {}
}
]
}
}
`)
var policy *kyverno.ClusterPolicy
err := json.Unmarshal(rawPolicy, &policy)
assert.NilError(t, err)
_, err = validateUniqueRuleName(*policy)
assert.Assert(t, err != nil)
}
func Test_Validate_ResourceDescription_Empty(t *testing.T) {
var err error
rawResourcedescirption := []byte(`{}`)