add namespaceParamRef to v1alpha1 and internal

add required field to validation test

test multiple combinartions of paramRefs in binding tests

add validation test cases for new ParamRef fields
This commit is contained in:
Alexander Zielenski 2023-07-10 12:59:49 -07:00
parent 51bfe417b8
commit c8dbf4712c
12 changed files with 637 additions and 165 deletions

View File

@ -98,5 +98,14 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
obj.MatchPolicy = &m obj.MatchPolicy = &m
} }
}, },
func(obj *admissionregistration.ParamRef, c fuzz.Continue) {
c.FuzzNoCustom(obj) // fuzz self without calling this function again
// Populate required field
if obj.ParameterNotFoundAction == nil {
v := admissionregistration.DenyAction
obj.ParameterNotFoundAction = &v
}
},
} }
} }

View File

@ -76,6 +76,18 @@ const (
AllScopes ScopeType = "*" AllScopes ScopeType = "*"
) )
// ParameterNotFoundActionType specifies a failure policy that defines how a binding
// is evaluated when the param referred by its perNamespaceParamRef is not found.
type ParameterNotFoundActionType string
const (
// Allow means all requests will be admitted if no param resources
// could be found.
AllowAction ParameterNotFoundActionType = "Allow"
// Deny means all requests will be denied if no param resources are found.
DenyAction ParameterNotFoundActionType = "Deny"
)
// FailurePolicyType specifies the type of failure policy // FailurePolicyType specifies the type of failure policy
type FailurePolicyType string type FailurePolicyType string
@ -401,6 +413,15 @@ type AuditAnnotation struct {
// ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. // ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources.
// ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters. // ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.
//
// For a given admission request, each binding will cause its policy to be
// evaluated N times, where N is 1 for policies/bindings that don't use
// params, otherwise N is the number of parameters selected by the binding.
//
// The CEL expressions of a policy must have a computed CEL cost below the maximum
// CEL budget. Each evaluation of the policy is given an independent CEL cost budget.
// Adding/removing policies, bindings, or params can not affect whether a
// given (policy, binding, param) combination is within its own CEL budget.
type ValidatingAdmissionPolicyBinding struct { type ValidatingAdmissionPolicyBinding struct {
metav1.TypeMeta metav1.TypeMeta
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata. // Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
@ -430,9 +451,10 @@ type ValidatingAdmissionPolicyBindingSpec struct {
// Required. // Required.
PolicyName string PolicyName string
// ParamRef specifies the parameter resource used to configure the admission control policy. // paramRef specifies the parameter resource used to configure the admission control policy.
// It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. // It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy.
// If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied. // If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.
// If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param.
// +optional // +optional
ParamRef *ParamRef ParamRef *ParamRef
@ -486,14 +508,63 @@ type ValidatingAdmissionPolicyBindingSpec struct {
ValidationActions []ValidationAction ValidationActions []ValidationAction
} }
// ParamRef references a parameter resource // ParamRef describes how to locate the params to be used as input to
// expressions of rules applied by a policy binding.
// +structType=atomic
type ParamRef struct { type ParamRef struct {
// Name of the resource being referenced. // name is the name of the resource being referenced.
//
// One of `name` or `selector` must be set, but `name` and `selector` are
// mutually exclusive properties. If one is set, the other must be unset.
//
// A single parameter used for all admission requests can be configured
// by setting the `name` field, leaving `selector` blank, and setting namespace
// if `paramKind` is namespace-scoped.
//
// +optional
Name string Name string
// Namespace of the referenced resource.
// Should be empty for the cluster-scoped resources // namespace is the namespace of the referenced resource. Allows limiting
// the search for params to a specific namespace. Applies to both `name` and
// `selector` fields.
//
// A per-namespace parameter may be used by specifying a namespace-scoped
// `paramKind` in the policy and leaving this field empty.
//
// - If `paramKind` is cluster-scoped, this field MUST be unset. Setting this
// field results in a configuration error.
//
// - If `paramKind` is namespace-scoped, the namespace of the object being
// evaluated for admission will be used when this field is left unset. Take
// care that if this is left empty the binding must not match any cluster-scoped
// resources, which will result in an error.
//
// +optional // +optional
Namespace string Namespace string
// selector can be used to match multiple param objects based on their labels.
// Supply selector: {} to match all resources of the ParamKind.
//
// If multiple params are found, they are all evaluated with the policy expressions
// and the results are ANDed together.
//
// One of `name` or `selector` must be set, but `name` and `selector` are
// mutually exclusive properties. If one is set, the other must be unset.
//
// +optional
Selector *metav1.LabelSelector
// parameterNotFoundAction controls the behavior of the binding when the resource
// exists, and name or selector is valid, but there are no parameters
// matched by the binding. If the value is set to `Allow`, then no
// matched parameters will be treated as successful validation by the binding.
// If set to `Deny`, then no matched parameters will be subject to the
// `failurePolicy` of the policy.
//
// Allowed values are `Allow` or `Deny`
//
// Required
ParameterNotFoundAction *ParameterNotFoundActionType
} }
// MatchResources decides whether to run the admission control policy on an object based // MatchResources decides whether to run the admission control policy on an object based

View File

@ -49,3 +49,11 @@ func SetDefaults_MatchResources(obj *admissionregistrationv1alpha1.MatchResource
obj.ObjectSelector = &selector obj.ObjectSelector = &selector
} }
} }
// SetDefaults_ParamRef sets defaults for ParamRef
func SetDefaults_ParamRef(obj *admissionregistrationv1alpha1.ParamRef) {
if obj.ParameterNotFoundAction == nil {
v := admissionregistrationv1alpha1.DenyAction
obj.ParameterNotFoundAction = &v
}
}

View File

@ -116,3 +116,58 @@ func TestDefaultAdmissionPolicy(t *testing.T) {
}) })
} }
} }
func TestDefaultAdmissionPolicyBinding(t *testing.T) {
denyAction := v1alpha1.DenyAction
equivalent := v1alpha1.Equivalent
tests := []struct {
name string
original runtime.Object
expected runtime.Object
}{
{
name: "ValidatingAdmissionPolicyBinding.ParamRef.ParameterNotFoundAction",
original: &v1alpha1.ValidatingAdmissionPolicyBinding{
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
ParamRef: &v1alpha1.ParamRef{},
},
},
expected: &v1alpha1.ValidatingAdmissionPolicyBinding{
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
ParamRef: &v1alpha1.ParamRef{
ParameterNotFoundAction: &denyAction,
},
},
},
},
{
name: "ValidatingAdmissionPolicyBinding.MatchResources",
original: &v1alpha1.ValidatingAdmissionPolicyBinding{
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
MatchResources: &v1alpha1.MatchResources{},
},
},
expected: &v1alpha1.ValidatingAdmissionPolicyBinding{
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
MatchResources: &v1alpha1.MatchResources{
NamespaceSelector: &metav1.LabelSelector{},
ObjectSelector: &metav1.LabelSelector{},
MatchPolicy: &equivalent,
},
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
original := test.original
expected := test.expected
legacyscheme.Scheme.Default(original)
if !apiequality.Semantic.DeepEqual(original, expected) {
t.Error(cmp.Diff(expected, original))
}
})
}
}

View File

@ -1151,9 +1151,38 @@ func validateParamRef(pr *admissionregistration.ParamRef, fldPath *field.Path) f
if pr == nil { if pr == nil {
return allErrors return allErrors
} }
for _, msg := range path.ValidatePathSegmentName(pr.Name, false) {
allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), pr.Name, msg)) if len(pr.Name) > 0 {
for _, msg := range path.ValidatePathSegmentName(pr.Name, false) {
allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), pr.Name, msg))
}
if pr.Selector != nil {
allErrors = append(allErrors, field.Forbidden(fldPath.Child("name"), `name and selector are mutually exclusive`))
}
} }
if pr.Selector != nil {
labelSelectorValidationOpts := metav1validation.LabelSelectorValidationOptions{}
allErrors = append(allErrors, metav1validation.ValidateLabelSelector(pr.Selector, labelSelectorValidationOpts, fldPath.Child("selector"))...)
if len(pr.Name) > 0 {
allErrors = append(allErrors, field.Forbidden(fldPath.Child("selector"), `name and selector are mutually exclusive`))
}
}
if len(pr.Name) == 0 && pr.Selector == nil {
allErrors = append(allErrors, field.Required(fldPath, `one of name or selector must be specified`))
}
if pr.ParameterNotFoundAction == nil || len(*pr.ParameterNotFoundAction) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("parameterNotFoundAction"), ""))
} else {
if *pr.ParameterNotFoundAction != admissionregistration.DenyAction && *pr.ParameterNotFoundAction != admissionregistration.AllowAction {
allErrors = append(allErrors, field.NotSupported(fldPath.Child("parameterNotFoundAction"), pr.ParameterNotFoundAction, []string{string(admissionregistration.DenyAction), string(admissionregistration.AllowAction)}))
}
}
return allErrors return allErrors
} }

View File

@ -32,6 +32,8 @@ import (
"k8s.io/kubernetes/pkg/apis/admissionregistration" "k8s.io/kubernetes/pkg/apis/admissionregistration"
) )
func ptr[T any](v T) *T { return &v }
func strPtr(s string) *string { return &s } func strPtr(s string) *string { return &s }
func int32Ptr(i int32) *int32 { return &i } func int32Ptr(i int32) *int32 { return &i }
@ -3517,7 +3519,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
MatchPolicy: func() *admissionregistration.MatchPolicyType { MatchPolicy: func() *admissionregistration.MatchPolicyType {
@ -3537,7 +3540,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
ResourceRules: []admissionregistration.NamedRuleWithOperations{{ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
@ -3591,7 +3595,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, MatchResources: &admissionregistration.MatchResources{ }, MatchResources: &admissionregistration.MatchResources{
ResourceRules: []admissionregistration.NamedRuleWithOperations{{ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
RuleWithOperations: admissionregistration.RuleWithOperations{ RuleWithOperations: admissionregistration.RuleWithOperations{
@ -3616,7 +3621,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, MatchResources: &admissionregistration.MatchResources{ }, MatchResources: &admissionregistration.MatchResources{
ResourceRules: []admissionregistration.NamedRuleWithOperations{{ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
RuleWithOperations: admissionregistration.RuleWithOperations{ RuleWithOperations: admissionregistration.RuleWithOperations{
@ -3641,7 +3647,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
@ -3668,7 +3675,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
@ -3704,7 +3712,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
@ -3731,7 +3740,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
@ -3758,7 +3768,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
@ -3794,7 +3805,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
@ -3821,7 +3833,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
@ -3848,7 +3861,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny, admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny, admissionregistration.Deny},
}, },
@ -3863,12 +3877,80 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.ValidationAction("illegal")}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.ValidationAction("illegal")},
}, },
}, },
expectedError: `Unsupported value: "illegal": supported values: "Audit", "Deny", "Warn"`, expectedError: `Unsupported value: "illegal": supported values: "Audit", "Deny", "Warn"`,
}, {
name: "paramRef selector must not be set when name is set",
config: &admissionregistration.ValidatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com",
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com",
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"label": "value",
},
},
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
},
},
},
expectedError: `spec.paramRef.name: Forbidden: name and selector are mutually exclusive, spec.paramRef.selector: Forbidden: name and selector are mutually exclusive`,
}, {
name: "paramRef parameterNotFoundAction must be set",
config: &admissionregistration.ValidatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com",
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com",
},
},
},
expectedError: "spec.paramRef.parameterNotFoundAction: Required value",
}, {
name: "paramRef parameterNotFoundAction must be an valid value",
config: &admissionregistration.ValidatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com",
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.ParameterNotFoundActionType("invalid")),
},
},
},
expectedError: `spec.paramRef.parameterNotFoundAction: Unsupported value: "invalid": supported values: "Deny", "Allow"`,
}, {
name: "paramRef one of name or selector",
config: &admissionregistration.ValidatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com",
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
ParamRef: &admissionregistration.ParamRef{
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
},
},
},
expectedError: `one of name or selector must be specified`,
}} }}
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
@ -3903,7 +3985,8 @@ func TestValidateValidatingAdmissionPolicyBindingUpdate(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
@ -3937,7 +4020,8 @@ func TestValidateValidatingAdmissionPolicyBindingUpdate(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{
@ -3973,7 +4057,8 @@ func TestValidateValidatingAdmissionPolicyBindingUpdate(t *testing.T) {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "xyzlimit-scale.example.com", PolicyName: "xyzlimit-scale.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "xyzlimit-scale-setting.example.com", Name: "xyzlimit-scale-setting.example.com",
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{ MatchResources: &admissionregistration.MatchResources{

View File

@ -1673,13 +1673,18 @@ func printValidatingAdmissionPolicyBinding(obj *admissionregistration.Validating
Object: runtime.RawExtension{Object: obj}, Object: runtime.RawExtension{Object: obj},
} }
paramName := "<unset>" paramName := "<unset>"
if obj.Spec.ParamRef != nil { if pr := obj.Spec.ParamRef; pr != nil {
if obj.Spec.ParamRef.Namespace != "" { if len(pr.Name) > 0 {
paramName = obj.Spec.ParamRef.Namespace + "/" + obj.Spec.ParamRef.Name if pr.Namespace != "" {
} else { paramName = pr.Namespace + "/" + pr.Name
paramName = obj.Spec.ParamRef.Name } else {
// Can't tell from here if param is cluster-scoped, so all
// params without names get * namespace
paramName = "*/" + pr.Name
}
} else if pr.Selector != nil {
paramName = pr.Selector.String()
} }
} }
row.Cells = append(row.Cells, obj.Name, obj.Spec.PolicyName, paramName, translateTimestampSince(obj.CreationTimestamp)) row.Cells = append(row.Cells, obj.Name, obj.Spec.PolicyName, paramName, translateTimestampSince(obj.CreationTimestamp))
return []metav1.TableRow{row}, nil return []metav1.TableRow{row}, nil

View File

@ -85,15 +85,21 @@ func (v *validatingAdmissionPolicyBindingStrategy) authorize(ctx context.Context
} }
} }
paramRef := binding.Spec.ParamRef var attrs authorizer.AttributesRecord
// require that the user can read (verb "get") the referred resource. paramRef := binding.Spec.ParamRef
attrs := authorizer.AttributesRecord{ verb := "get"
if len(paramRef.Name) == 0 {
verb = "list"
}
attrs = authorizer.AttributesRecord{
User: user, User: user,
Verb: "get", Verb: verb,
ResourceRequest: true, ResourceRequest: true,
Name: paramRef.Name, Name: paramRef.Name,
Namespace: paramRef.Namespace, Namespace: paramRef.Namespace, // if empty, no namespace indicates get across all namespaces
APIGroup: apiGroup, APIGroup: apiGroup,
APIVersion: apiVersion, APIVersion: apiVersion,
Resource: resource, Resource: resource,
@ -104,7 +110,8 @@ func (v *validatingAdmissionPolicyBindingStrategy) authorize(ctx context.Context
return err return err
} }
if d != authorizer.DecisionAllow { if d != authorizer.DecisionAllow {
return fmt.Errorf(`user %v does not have "get" permission on the object referenced by paramRef`, user) return fmt.Errorf(`user %v does not have "%v" permission on the object referenced by paramRef`, verb, user)
} }
return nil return nil
} }

View File

@ -103,19 +103,46 @@ func TestAuthorization(t *testing.T) {
strategy := NewStrategy(tc.auth, tc.policyGetter, tc.resourceResolver) strategy := NewStrategy(tc.auth, tc.policyGetter, tc.resourceResolver)
t.Run("create", func(t *testing.T) { t.Run("create", func(t *testing.T) {
ctx := request.WithUser(context.Background(), tc.userInfo) ctx := request.WithUser(context.Background(), tc.userInfo)
errs := strategy.Validate(ctx, validPolicyBinding()) for _, obj := range validPolicyBindings() {
if len(errs) > 0 != tc.expectErr { errs := strategy.Validate(ctx, obj)
t.Errorf("expected error: %v but got error: %v", tc.expectErr, errs) if len(errs) > 0 != tc.expectErr {
t.Errorf("expected error: %v but got error: %v", tc.expectErr, errs)
}
} }
}) })
t.Run("update", func(t *testing.T) { t.Run("update", func(t *testing.T) {
ctx := request.WithUser(context.Background(), tc.userInfo) ctx := request.WithUser(context.Background(), tc.userInfo)
obj := validPolicyBinding() for _, obj := range validPolicyBindings() {
objWithChangedParamRef := obj.DeepCopy() objWithChangedParamRef := obj.DeepCopy()
objWithChangedParamRef.Spec.ParamRef.Name = "changed" if pr := objWithChangedParamRef.Spec.ParamRef; pr != nil {
errs := strategy.ValidateUpdate(ctx, obj, objWithChangedParamRef) if len(pr.Name) > 0 {
if len(errs) > 0 != tc.expectErr { pr.Name = "changed"
t.Errorf("expected error: %v but got error: %v", tc.expectErr, errs) }
if pr.Selector != nil {
pr.Selector = &metav1.LabelSelector{
MatchLabels: map[string]string{
"changed": "value",
},
}
}
if len(pr.Namespace) > 0 {
pr.Namespace = "othernamespace"
}
if pr.ParameterNotFoundAction == nil || *pr.ParameterNotFoundAction == admissionregistration.AllowAction {
v := admissionregistration.DenyAction
pr.ParameterNotFoundAction = &v
} else {
v := admissionregistration.AllowAction
pr.ParameterNotFoundAction = &v
}
}
errs := strategy.ValidateUpdate(ctx, obj, objWithChangedParamRef)
if len(errs) > 0 != tc.expectErr {
t.Errorf("expected error: %v but got error: %v", tc.expectErr, errs)
}
} }
}) })
}) })

View File

@ -37,121 +37,175 @@ import (
) )
func TestCreate(t *testing.T) { func TestCreate(t *testing.T) {
storage, server := newInsecureStorage(t) for _, configuration := range validPolicyBindings() {
defer server.Terminate(t) t.Run(configuration.Name, func(t *testing.T) {
defer storage.Store.DestroyFunc() storage, server := newInsecureStorage(t)
test := genericregistrytest.New(t, storage.Store).ClusterScope() defer server.Terminate(t)
configuration := validPolicyBinding() defer storage.Store.DestroyFunc()
test.TestCreate( test := genericregistrytest.New(t, storage.Store).ClusterScope()
// valid
configuration, test.TestCreate(
// invalid // valid
newPolicyBinding(""), configuration,
) // invalid
newPolicyBinding(""),
)
})
}
} }
func TestUpdate(t *testing.T) { func TestUpdate(t *testing.T) {
storage, server := newInsecureStorage(t) for _, b := range validPolicyBindings() {
defer server.Terminate(t) storage, server := newInsecureStorage(t)
defer storage.Store.DestroyFunc() defer server.Terminate(t)
test := genericregistrytest.New(t, storage.Store).ClusterScope() defer storage.Store.DestroyFunc()
t.Run(b.Name, func(t *testing.T) {
test.TestUpdate( test := genericregistrytest.New(t, storage.Store).ClusterScope()
// valid test.TestUpdate(
validPolicyBinding(), // valid
// updateFunc b,
func(obj runtime.Object) runtime.Object { // updateFunc
object := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding) func(obj runtime.Object) runtime.Object {
object.Labels = map[string]string{"c": "d"} object := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding)
return object object.Labels = map[string]string{"c": "d"}
}, return object
// invalid updateFunc },
func(obj runtime.Object) runtime.Object { // invalid updateFunc
object := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding) func(obj runtime.Object) runtime.Object {
object.Name = "" object := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding)
return object object.Name = ""
}, return object
) },
)
})
}
} }
func TestGet(t *testing.T) { func TestGet(t *testing.T) {
storage, server := newInsecureStorage(t) for _, b := range validPolicyBindings() {
defer server.Terminate(t) t.Run(b.Name, func(t *testing.T) {
defer storage.Store.DestroyFunc() storage, server := newInsecureStorage(t)
test := genericregistrytest.New(t, storage.Store).ClusterScope() defer server.Terminate(t)
test.TestGet(validPolicyBinding()) defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestGet(b)
})
}
} }
func TestList(t *testing.T) { func TestList(t *testing.T) {
storage, server := newInsecureStorage(t) for _, b := range validPolicyBindings() {
defer server.Terminate(t) t.Run(b.Name, func(t *testing.T) {
defer storage.Store.DestroyFunc() storage, server := newInsecureStorage(t)
test := genericregistrytest.New(t, storage.Store).ClusterScope() defer server.Terminate(t)
test.TestList(validPolicyBinding()) defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestList(b)
})
}
} }
func TestDelete(t *testing.T) { func TestDelete(t *testing.T) {
storage, server := newInsecureStorage(t) for _, b := range validPolicyBindings() {
defer server.Terminate(t) t.Run(b.Name, func(t *testing.T) {
defer storage.Store.DestroyFunc() storage, server := newInsecureStorage(t)
test := genericregistrytest.New(t, storage.Store).ClusterScope() defer server.Terminate(t)
test.TestDelete(validPolicyBinding()) defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
test.TestDelete(b)
})
}
} }
func TestWatch(t *testing.T) { func TestWatch(t *testing.T) {
storage, server := newInsecureStorage(t) for _, b := range validPolicyBindings() {
defer server.Terminate(t) t.Run(b.Name, func(t *testing.T) {
defer storage.Store.DestroyFunc() storage, server := newInsecureStorage(t)
test := genericregistrytest.New(t, storage.Store).ClusterScope() defer server.Terminate(t)
test.TestWatch( defer storage.Store.DestroyFunc()
validPolicyBinding(), test := genericregistrytest.New(t, storage.Store).ClusterScope()
[]labels.Set{}, test.TestWatch(
[]labels.Set{ b,
{"hoo": "bar"}, []labels.Set{},
}, []labels.Set{
[]fields.Set{ {"hoo": "bar"},
{"metadata.name": "foo"}, },
}, []fields.Set{
[]fields.Set{ {"metadata.name": b.Name},
{"metadata.name": "nomatch"}, },
}, []fields.Set{
) {"metadata.name": "nomatch"},
},
)
})
}
} }
func validPolicyBinding() *admissionregistration.ValidatingAdmissionPolicyBinding { func validPolicyBindings() []*admissionregistration.ValidatingAdmissionPolicyBinding {
return &admissionregistration.ValidatingAdmissionPolicyBinding{ denyAction := admissionregistration.DenyAction
ObjectMeta: metav1.ObjectMeta{ return []*admissionregistration.ValidatingAdmissionPolicyBinding{
Name: "foo", {
}, ObjectMeta: metav1.ObjectMeta{
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Name: "foo",
PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Name: "param-test",
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
MatchResources: &admissionregistration.MatchResources{ PolicyName: "replicalimit-policy.example.com",
MatchPolicy: func() *admissionregistration.MatchPolicyType { ParamRef: &admissionregistration.ParamRef{
r := admissionregistration.MatchPolicyType("Exact") Name: "replica-limit-test.example.com",
return &r ParameterNotFoundAction: &denyAction,
}(),
ObjectSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
}, },
NamespaceSelector: &metav1.LabelSelector{ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchLabels: map[string]string{"a": "b"}, },
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-clusterwide",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Name: "replica-limit-test.example.com",
Namespace: "default",
ParameterNotFoundAction: &denyAction,
}, },
ResourceRules: []admissionregistration.NamedRuleWithOperations{ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
{ },
RuleWithOperations: admissionregistration.RuleWithOperations{ },
Operations: []admissionregistration.OperationType{"CREATE"}, {
Rule: admissionregistration.Rule{ ObjectMeta: metav1.ObjectMeta{
APIGroups: []string{"a"}, Name: "foo-selector",
APIVersions: []string{"a"}, },
Resources: []string{"a"}, Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
}, PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"label": "value",
}, },
}, },
ParameterNotFoundAction: &denyAction,
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-selector-clusterwide",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Namespace: "mynamespace",
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"label": "value",
},
},
ParameterNotFoundAction: &denyAction,
},
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
}, },
}, },
} }
@ -166,7 +220,8 @@ func newPolicyBinding(name string) *admissionregistration.ValidatingAdmissionPol
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com", PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{ ParamRef: &admissionregistration.ParamRef{
Name: "param-test", Name: "param-test",
Namespace: "default",
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny}, ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
MatchResources: &admissionregistration.MatchResources{}, MatchResources: &admissionregistration.MatchResources{},

View File

@ -35,32 +35,87 @@ func TestPolicyBindingStrategy(t *testing.T) {
t.Errorf("PolicyBinding should not allow create on update") t.Errorf("PolicyBinding should not allow create on update")
} }
configuration := validPolicyBinding() for _, configuration := range validPolicyBindings() {
strategy.PrepareForCreate(ctx, configuration) strategy.PrepareForCreate(ctx, configuration)
errs := strategy.Validate(ctx, configuration) errs := strategy.Validate(ctx, configuration)
if len(errs) != 0 { if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs) t.Errorf("Unexpected error validating %v", errs)
} }
invalidConfiguration := &admissionregistration.ValidatingAdmissionPolicyBinding{ invalidConfiguration := &admissionregistration.ValidatingAdmissionPolicyBinding{
ObjectMeta: metav1.ObjectMeta{Name: ""}, ObjectMeta: metav1.ObjectMeta{Name: ""},
} }
strategy.PrepareForUpdate(ctx, invalidConfiguration, configuration) strategy.PrepareForUpdate(ctx, invalidConfiguration, configuration)
errs = strategy.ValidateUpdate(ctx, invalidConfiguration, configuration) errs = strategy.ValidateUpdate(ctx, invalidConfiguration, configuration)
if len(errs) == 0 { if len(errs) == 0 {
t.Errorf("Expected a validation error") t.Errorf("Expected a validation error")
}
} }
} }
func validPolicyBinding() *admissionregistration.ValidatingAdmissionPolicyBinding {
return &admissionregistration.ValidatingAdmissionPolicyBinding{ func validPolicyBindings() []*admissionregistration.ValidatingAdmissionPolicyBinding {
ObjectMeta: metav1.ObjectMeta{ denyAction := admissionregistration.DenyAction
Name: "foo", return []*admissionregistration.ValidatingAdmissionPolicyBinding{
}, {
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{ ObjectMeta: metav1.ObjectMeta{
PolicyName: "replicalimit-policy.example.com", Name: "foo",
ParamRef: &admissionregistration.ParamRef{ },
Name: "replica-limit-test.example.com", Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Name: "replica-limit-test.example.com",
ParameterNotFoundAction: &denyAction,
},
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-clusterwide",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Name: "replica-limit-test.example.com",
Namespace: "default",
ParameterNotFoundAction: &denyAction,
},
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-selector",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"label": "value",
},
},
ParameterNotFoundAction: &denyAction,
},
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-selector-clusterwide",
},
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
PolicyName: "replicalimit-policy.example.com",
ParamRef: &admissionregistration.ParamRef{
Namespace: "mynamespace",
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"label": "value",
},
},
ParameterNotFoundAction: &denyAction,
},
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
}, },
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
}, },
} }
} }

View File

@ -39,6 +39,18 @@ const (
AllScopes ScopeType = v1.AllScopes AllScopes ScopeType = v1.AllScopes
) )
// ParameterNotFoundActionType specifies a failure policy that defines how a binding
// is evaluated when the param referred by its perNamespaceParamRef is not found.
// +enum
type ParameterNotFoundActionType string
const (
// Ignore means that an error finding params for a binding is ignored
AllowAction ParameterNotFoundActionType = "Allow"
// Fail means that an error finding params for a binding is ignored
DenyAction ParameterNotFoundActionType = "Deny"
)
// FailurePolicyType specifies a failure policy that defines how unrecognized errors from the admission endpoint are handled. // FailurePolicyType specifies a failure policy that defines how unrecognized errors from the admission endpoint are handled.
// +enum // +enum
type FailurePolicyType string type FailurePolicyType string
@ -363,6 +375,15 @@ type AuditAnnotation struct {
// ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. // ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources.
// ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters. // ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.
//
// For a given admission request, each binding will cause its policy to be
// evaluated N times, where N is 1 for policies/bindings that don't use
// params, otherwise N is the number of parameters selected by the binding.
//
// The CEL expressions of a policy must have a computed CEL cost below the maximum
// CEL budget. Each evaluation of the policy is given an independent CEL cost budget.
// Adding/removing policies, bindings, or params can not affect whether a
// given (policy, binding, param) combination is within its own CEL budget.
type ValidatingAdmissionPolicyBinding struct { type ValidatingAdmissionPolicyBinding struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata. // Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
@ -393,9 +414,10 @@ type ValidatingAdmissionPolicyBindingSpec struct {
// Required. // Required.
PolicyName string `json:"policyName,omitempty" protobuf:"bytes,1,rep,name=policyName"` PolicyName string `json:"policyName,omitempty" protobuf:"bytes,1,rep,name=policyName"`
// ParamRef specifies the parameter resource used to configure the admission control policy. // paramRef specifies the parameter resource used to configure the admission control policy.
// It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. // It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy.
// If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied. // If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.
// If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param.
// +optional // +optional
ParamRef *ParamRef `json:"paramRef,omitempty" protobuf:"bytes,2,rep,name=paramRef"` ParamRef *ParamRef `json:"paramRef,omitempty" protobuf:"bytes,2,rep,name=paramRef"`
@ -450,15 +472,59 @@ type ValidatingAdmissionPolicyBindingSpec struct {
ValidationActions []ValidationAction `json:"validationActions,omitempty" protobuf:"bytes,4,rep,name=validationActions"` ValidationActions []ValidationAction `json:"validationActions,omitempty" protobuf:"bytes,4,rep,name=validationActions"`
} }
// ParamRef references a parameter resource // ParamRef describes how to locate the params to be used as input to
// expressions of rules applied by a policy binding.
// +structType=atomic // +structType=atomic
type ParamRef struct { type ParamRef struct {
// Name of the resource being referenced. // `name` is the name of the resource being referenced.
//
// `name` and `selector` are mutually exclusive properties. If one is set,
// the other must be unset.
//
// +optional
Name string `json:"name,omitempty" protobuf:"bytes,1,rep,name=name"` Name string `json:"name,omitempty" protobuf:"bytes,1,rep,name=name"`
// Namespace of the referenced resource.
// Should be empty for the cluster-scoped resources // namespace is the namespace of the referenced resource. Allows limiting
// the search for params to a specific namespace. Applies to both `name` and
// `selector` fields.
//
// A per-namespace parameter may be used by specifying a namespace-scoped
// `paramKind` in the policy and leaving this field empty.
//
// - If `paramKind` is cluster-scoped, this field MUST be unset. Setting this
// field results in a configuration error.
//
// - If `paramKind` is namespace-scoped, the namespace of the object being
// evaluated for admission will be used when this field is left unset. Take
// care that if this is left empty the binding must not match any cluster-scoped
// resources, which will result in an error.
//
// +optional // +optional
Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,rep,name=namespace"` Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,rep,name=namespace"`
// selector can be used to match multiple param objects based on their labels.
// Supply selector: {} to match all resources of the ParamKind.
//
// If multiple params are found, they are all evaluated with the policy expressions
// and the results are ANDed together.
//
// One of `name` or `selector` must be set, but `name` and `selector` are
// mutually exclusive properties. If one is set, the other must be unset.
//
// +optional
Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,3,rep,name=selector"`
// `parameterNotFoundAction` controls the behavior of the binding when the resource
// exists, and name or selector is valid, but there are no parameters
// matched by the binding. If the value is set to `Allow`, then no
// matched parameters will be treated as successful validation by the binding.
// If set to `Deny`, then no matched parameters will be subject to the
// `failurePolicy` of the policy.
//
// Allowed values are `Allow` or `Deny`
// Default to `Deny`
// +optional
ParameterNotFoundAction *ParameterNotFoundActionType `json:"parameterNotFoundAction,omitempty" protobuf:"bytes,4,rep,name=parameterNotFoundAction"`
} }
// MatchResources decides whether to run the admission control policy on an object based // MatchResources decides whether to run the admission control policy on an object based