diff --git a/constraint/config/crds/templates.gatekeeper.sh_constrainttemplates.yaml b/constraint/config/crds/templates.gatekeeper.sh_constrainttemplates.yaml index f54950ae9..e1c48bb8a 100644 --- a/constraint/config/crds/templates.gatekeeper.sh_constrainttemplates.yaml +++ b/constraint/config/crds/templates.gatekeeper.sh_constrainttemplates.yaml @@ -75,6 +75,9 @@ spec: be specified in one place (either here or in the "rego" field) items: properties: + enforceVAP: + description: The flag to use VAP for enforcement. + type: boolean engine: description: 'The engine used to evaluate the code. Example: "Rego". Required.' @@ -205,6 +208,9 @@ spec: be specified in one place (either here or in the "rego" field) items: properties: + enforceVAP: + description: The flag to use VAP for enforcement. + type: boolean engine: description: 'The engine used to evaluate the code. Example: "Rego". Required.' @@ -335,6 +341,9 @@ spec: be specified in one place (either here or in the "rego" field) items: properties: + enforceVAP: + description: The flag to use VAP for enforcement. + type: boolean engine: description: 'The engine used to evaluate the code. Example: "Rego". Required.' diff --git a/constraint/deploy/crds.yaml b/constraint/deploy/crds.yaml index 83551848f..cbc9dda20 100644 --- a/constraint/deploy/crds.yaml +++ b/constraint/deploy/crds.yaml @@ -17,7 +17,8 @@ spec: - name: v1 schema: openAPIV3Schema: - description: ConstraintTemplate is the Schema for the constrainttemplates API + description: ConstraintTemplate is the Schema for the constrainttemplates + API properties: apiVersion: description: |- @@ -74,8 +75,12 @@ spec: be specified in one place (either here or in the "rego" field) items: properties: + enforceVAP: + description: The flag to use VAP for enforcement. + type: boolean engine: - description: 'The engine used to evaluate the code. Example: "Rego". Required.' + description: 'The engine used to evaluate the code. Example: + "Rego". Required.' type: string source: description: The source code for the template. Required. @@ -110,7 +115,8 @@ spec: properties: errors: items: - description: CreateCRDError represents a single error caught during parsing, compiling, etc. + description: CreateCRDError represents a single error caught + during parsing, compiling, etc. properties: code: type: string @@ -124,7 +130,8 @@ spec: type: object type: array id: - description: a unique identifier for the pod that wrote the status + description: a unique identifier for the pod that wrote the + status type: string observedGeneration: format: int64 @@ -143,7 +150,8 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: ConstraintTemplate is the Schema for the constrainttemplates API + description: ConstraintTemplate is the Schema for the constrainttemplates + API properties: apiVersion: description: |- @@ -200,8 +208,12 @@ spec: be specified in one place (either here or in the "rego" field) items: properties: + enforceVAP: + description: The flag to use VAP for enforcement. + type: boolean engine: - description: 'The engine used to evaluate the code. Example: "Rego". Required.' + description: 'The engine used to evaluate the code. Example: + "Rego". Required.' type: string source: description: The source code for the template. Required. @@ -236,7 +248,8 @@ spec: properties: errors: items: - description: CreateCRDError represents a single error caught during parsing, compiling, etc. + description: CreateCRDError represents a single error caught + during parsing, compiling, etc. properties: code: type: string @@ -250,7 +263,8 @@ spec: type: object type: array id: - description: a unique identifier for the pod that wrote the status + description: a unique identifier for the pod that wrote the + status type: string observedGeneration: format: int64 @@ -269,7 +283,8 @@ spec: - name: v1beta1 schema: openAPIV3Schema: - description: ConstraintTemplate is the Schema for the constrainttemplates API + description: ConstraintTemplate is the Schema for the constrainttemplates + API properties: apiVersion: description: |- @@ -326,8 +341,12 @@ spec: be specified in one place (either here or in the "rego" field) items: properties: + enforceVAP: + description: The flag to use VAP for enforcement. + type: boolean engine: - description: 'The engine used to evaluate the code. Example: "Rego". Required.' + description: 'The engine used to evaluate the code. Example: + "Rego". Required.' type: string source: description: The source code for the template. Required. @@ -362,7 +381,8 @@ spec: properties: errors: items: - description: CreateCRDError represents a single error caught during parsing, compiling, etc. + description: CreateCRDError represents a single error caught + during parsing, compiling, etc. properties: code: type: string @@ -376,7 +396,8 @@ spec: type: object type: array id: - description: a unique identifier for the pod that wrote the status + description: a unique identifier for the pod that wrote the + status type: string observedGeneration: format: int64 @@ -409,7 +430,8 @@ spec: scope: Cluster versions: - deprecated: true - deprecationWarning: externaldata.gatekeeper.sh/v1alpha1 is deprecated. Use externaldata.gatekeeper.sh/v1beta1 instead. + deprecationWarning: externaldata.gatekeeper.sh/v1alpha1 is deprecated. Use externaldata.gatekeeper.sh/v1beta1 + instead. name: v1alpha1 schema: openAPIV3Schema: @@ -444,7 +466,8 @@ spec: description: Timeout is the timeout when querying the provider. type: integer url: - description: URL is the url for the provider. URL is prefixed with https://. + description: URL is the url for the provider. URL is prefixed with + https://. type: string type: object type: object @@ -484,7 +507,8 @@ spec: description: Timeout is the timeout when querying the provider. type: integer url: - description: URL is the url for the provider. URL is prefixed with https://. + description: URL is the url for the provider. URL is prefixed with + https://. type: string type: object type: object diff --git a/constraint/pkg/apis/externaldata/unversioned/zz_generated.deepcopy.go b/constraint/pkg/apis/externaldata/unversioned/zz_generated.deepcopy.go index f85252d71..6276727ab 100644 --- a/constraint/pkg/apis/externaldata/unversioned/zz_generated.deepcopy.go +++ b/constraint/pkg/apis/externaldata/unversioned/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* diff --git a/constraint/pkg/apis/externaldata/v1alpha1/zz_generated.deepcopy.go b/constraint/pkg/apis/externaldata/v1alpha1/zz_generated.deepcopy.go index b1eb19a64..e21de9381 100644 --- a/constraint/pkg/apis/externaldata/v1alpha1/zz_generated.deepcopy.go +++ b/constraint/pkg/apis/externaldata/v1alpha1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* diff --git a/constraint/pkg/apis/externaldata/v1beta1/zz_generated.deepcopy.go b/constraint/pkg/apis/externaldata/v1beta1/zz_generated.deepcopy.go index 13c57de01..917a08cb5 100644 --- a/constraint/pkg/apis/externaldata/v1beta1/zz_generated.deepcopy.go +++ b/constraint/pkg/apis/externaldata/v1beta1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* diff --git a/constraint/pkg/apis/templates/v1/constrainttemplate_types.go b/constraint/pkg/apis/templates/v1/constrainttemplate_types.go index ca272adbf..f1fdfe8af 100644 --- a/constraint/pkg/apis/templates/v1/constrainttemplate_types.go +++ b/constraint/pkg/apis/templates/v1/constrainttemplate_types.go @@ -72,6 +72,10 @@ type Code struct { // +kubebuilder:validation:Required Engine string `json:"engine"` + // +kubebuilder:validation:Required + // The flag to use VAP for enforcement. + EnforceVAP *bool `json:"enforceVAP,omitempty"` + // +kubebuilder:validation:Required // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields diff --git a/constraint/pkg/apis/templates/v1/zz_generated.conversion.go b/constraint/pkg/apis/templates/v1/zz_generated.conversion.go index 453cd658c..cf51edb2f 100644 --- a/constraint/pkg/apis/templates/v1/zz_generated.conversion.go +++ b/constraint/pkg/apis/templates/v1/zz_generated.conversion.go @@ -250,6 +250,7 @@ func Convert_templates_CRDSpec_To_v1_CRDSpec(in *templates.CRDSpec, out *CRDSpec func autoConvert_v1_Code_To_templates_Code(in *Code, out *templates.Code, s conversion.Scope) error { out.Engine = in.Engine + out.EnforceVAP = (*bool)(unsafe.Pointer(in.EnforceVAP)) out.Source = (*templates.Anything)(unsafe.Pointer(in.Source)) return nil } @@ -261,6 +262,7 @@ func Convert_v1_Code_To_templates_Code(in *Code, out *templates.Code, s conversi func autoConvert_templates_Code_To_v1_Code(in *templates.Code, out *Code, s conversion.Scope) error { out.Engine = in.Engine + out.EnforceVAP = (*bool)(unsafe.Pointer(in.EnforceVAP)) out.Source = (*templates.Anything)(unsafe.Pointer(in.Source)) return nil } diff --git a/constraint/pkg/apis/templates/v1/zz_generated.deepcopy.go b/constraint/pkg/apis/templates/v1/zz_generated.deepcopy.go index 797b58ef4..64338b0f0 100644 --- a/constraint/pkg/apis/templates/v1/zz_generated.deepcopy.go +++ b/constraint/pkg/apis/templates/v1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* @@ -84,6 +83,11 @@ func (in *CRDSpec) DeepCopy() *CRDSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Code) DeepCopyInto(out *Code) { *out = *in + if in.EnforceVAP != nil { + in, out := &in.EnforceVAP, &out.EnforceVAP + *out = new(bool) + **out = **in + } if in.Source != nil { in, out := &in.Source, &out.Source *out = (*in).DeepCopy() diff --git a/constraint/pkg/apis/templates/v1alpha1/constrainttemplate_types.go b/constraint/pkg/apis/templates/v1alpha1/constrainttemplate_types.go index 71f337e26..7cc3596e5 100644 --- a/constraint/pkg/apis/templates/v1alpha1/constrainttemplate_types.go +++ b/constraint/pkg/apis/templates/v1alpha1/constrainttemplate_types.go @@ -71,6 +71,10 @@ type Code struct { // +kubebuilder:validation:Required Engine string `json:"engine"` + // +kubebuilder:validation:Required + // The flag to use VAP for enforcement. + EnforceVAP *bool `json:"enforceVAP,omitempty"` + // +kubebuilder:validation:Required // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields diff --git a/constraint/pkg/apis/templates/v1alpha1/zz_generated.conversion.go b/constraint/pkg/apis/templates/v1alpha1/zz_generated.conversion.go index 38fe6f4cd..ce5c45699 100644 --- a/constraint/pkg/apis/templates/v1alpha1/zz_generated.conversion.go +++ b/constraint/pkg/apis/templates/v1alpha1/zz_generated.conversion.go @@ -250,6 +250,7 @@ func Convert_templates_CRDSpec_To_v1alpha1_CRDSpec(in *templates.CRDSpec, out *C func autoConvert_v1alpha1_Code_To_templates_Code(in *Code, out *templates.Code, s conversion.Scope) error { out.Engine = in.Engine + out.EnforceVAP = (*bool)(unsafe.Pointer(in.EnforceVAP)) out.Source = (*templates.Anything)(unsafe.Pointer(in.Source)) return nil } @@ -261,6 +262,7 @@ func Convert_v1alpha1_Code_To_templates_Code(in *Code, out *templates.Code, s co func autoConvert_templates_Code_To_v1alpha1_Code(in *templates.Code, out *Code, s conversion.Scope) error { out.Engine = in.Engine + out.EnforceVAP = (*bool)(unsafe.Pointer(in.EnforceVAP)) out.Source = (*templates.Anything)(unsafe.Pointer(in.Source)) return nil } diff --git a/constraint/pkg/apis/templates/v1alpha1/zz_generated.deepcopy.go b/constraint/pkg/apis/templates/v1alpha1/zz_generated.deepcopy.go index 6720c01f7..7b3db9007 100644 --- a/constraint/pkg/apis/templates/v1alpha1/zz_generated.deepcopy.go +++ b/constraint/pkg/apis/templates/v1alpha1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* @@ -84,6 +83,11 @@ func (in *CRDSpec) DeepCopy() *CRDSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Code) DeepCopyInto(out *Code) { *out = *in + if in.EnforceVAP != nil { + in, out := &in.EnforceVAP, &out.EnforceVAP + *out = new(bool) + **out = **in + } if in.Source != nil { in, out := &in.Source, &out.Source *out = (*in).DeepCopy() diff --git a/constraint/pkg/apis/templates/v1beta1/constrainttemplate_types.go b/constraint/pkg/apis/templates/v1beta1/constrainttemplate_types.go index 8eda89973..3ec2ea5ca 100644 --- a/constraint/pkg/apis/templates/v1beta1/constrainttemplate_types.go +++ b/constraint/pkg/apis/templates/v1beta1/constrainttemplate_types.go @@ -71,6 +71,10 @@ type Code struct { // +kubebuilder:validation:Required Engine string `json:"engine"` + // +kubebuilder:validation:Required + // The flag to use VAP for enforcement. + EnforceVAP *bool `json:"enforceVAP,omitempty"` + // +kubebuilder:validation:Required // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields diff --git a/constraint/pkg/apis/templates/v1beta1/zz_generated.conversion.go b/constraint/pkg/apis/templates/v1beta1/zz_generated.conversion.go index b99250527..3b598bfb5 100644 --- a/constraint/pkg/apis/templates/v1beta1/zz_generated.conversion.go +++ b/constraint/pkg/apis/templates/v1beta1/zz_generated.conversion.go @@ -250,6 +250,7 @@ func Convert_templates_CRDSpec_To_v1beta1_CRDSpec(in *templates.CRDSpec, out *CR func autoConvert_v1beta1_Code_To_templates_Code(in *Code, out *templates.Code, s conversion.Scope) error { out.Engine = in.Engine + out.EnforceVAP = (*bool)(unsafe.Pointer(in.EnforceVAP)) out.Source = (*templates.Anything)(unsafe.Pointer(in.Source)) return nil } @@ -261,6 +262,7 @@ func Convert_v1beta1_Code_To_templates_Code(in *Code, out *templates.Code, s con func autoConvert_templates_Code_To_v1beta1_Code(in *templates.Code, out *Code, s conversion.Scope) error { out.Engine = in.Engine + out.EnforceVAP = (*bool)(unsafe.Pointer(in.EnforceVAP)) out.Source = (*templates.Anything)(unsafe.Pointer(in.Source)) return nil } diff --git a/constraint/pkg/apis/templates/v1beta1/zz_generated.deepcopy.go b/constraint/pkg/apis/templates/v1beta1/zz_generated.deepcopy.go index 27a564e25..d98d3c105 100644 --- a/constraint/pkg/apis/templates/v1beta1/zz_generated.deepcopy.go +++ b/constraint/pkg/apis/templates/v1beta1/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* @@ -84,6 +83,11 @@ func (in *CRDSpec) DeepCopy() *CRDSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Code) DeepCopyInto(out *Code) { *out = *in + if in.EnforceVAP != nil { + in, out := &in.EnforceVAP, &out.EnforceVAP + *out = new(bool) + **out = **in + } if in.Source != nil { in, out := &in.Source, &out.Source *out = (*in).DeepCopy() diff --git a/constraint/pkg/client/drivers/k8scel/args.go b/constraint/pkg/client/drivers/k8scel/args.go index 9774c0a66..0750fe78b 100644 --- a/constraint/pkg/client/drivers/k8scel/args.go +++ b/constraint/pkg/client/drivers/k8scel/args.go @@ -1,13 +1,5 @@ package k8scel -type vapDefault string - -const ( - VAPGenerationLabel = "gatekeeper.sh/use-vap" - VAPDefaultYes = vapDefault("yes") - VAPDefaultNo = vapDefault("no") -) - type Arg func(*Driver) error // GatherStats starts collecting various stats around the @@ -21,16 +13,15 @@ func GatherStats() Arg { } // VAPGenerationDefault sets the expected default -// value of the `gatekeeper.sh/use-vap` label. +// value for generateVAP field. // If no value is provided, VAP generation // is presumed to be disabled and the engine will // validate ALL policies. Otherwise, the engine // will only validate policies not expected to be // enforced via VAP. -func VAPGenerationDefault(d vapDefault) Arg { +func VAPGenerationDefault(d bool) Arg { return func(driver *Driver) error { - driver.generateVAPDefault = &d - + driver.generateVAPDefault = d return nil } } diff --git a/constraint/pkg/client/drivers/k8scel/driver.go b/constraint/pkg/client/drivers/k8scel/driver.go index 8957ab034..8ba0f226f 100644 --- a/constraint/pkg/client/drivers/k8scel/driver.go +++ b/constraint/pkg/client/drivers/k8scel/driver.go @@ -17,9 +17,7 @@ import ( "github.com/open-policy-agent/frameworks/constraint/pkg/types" "github.com/open-policy-agent/opa/storage" admissionv1 "k8s.io/api/admission/v1" - apimeta "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/admission/plugin/cel" "k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy" "k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions" @@ -54,7 +52,7 @@ var _ drivers.Driver = &Driver{} type Driver struct { mux sync.RWMutex validators map[string]*validatorWrapper - generateVAPDefault *vapDefault + generateVAPDefault bool gatherStats bool } @@ -193,11 +191,9 @@ func (d *Driver) Query(ctx context.Context, target string, constraints []*unstru return nil, fmt.Errorf("unknown constraint template validator: %s", constraint.GetKind()) } - assumeVAPEnforcementNotDisabled := assumeVAPEnforcementWithDefault(constraint, VAPDefaultYes) - - // if we assume VAP enforcement for a given constraint/template combo, Gatekeeper + // if we assume VAP enforcement for a given template, Gatekeeper // should not be evaluating that constraint/template in an admission context. - if isAdmission && assumeVAPEnforcementNotDisabled && wrappedValidator.assumeVAPEnforcement { + if isAdmission && wrappedValidator.assumeVAPEnforcement { continue } @@ -263,36 +259,16 @@ func (d *Driver) GetDescriptionForStat(statName string) (string, error) { } } -func (d *Driver) assumeVAPEnforcement(obj runtime.Object) bool { - if d.generateVAPDefault == nil { - return false - } - - return assumeVAPEnforcementWithDefault(obj, *d.generateVAPDefault) -} - -func assumeVAPEnforcementWithDefault(obj runtime.Object, vapDef vapDefault) bool { - meta, err := apimeta.Accessor(obj) - if err != nil { - return false - } - labels := meta.GetLabels() - if labels == nil { - labels = map[string]string{} - } - shouldGen, ok := labels[VAPGenerationLabel] - if !ok { - shouldGen = string(vapDef) - } - switch vapDefault(shouldGen) { - case VAPDefaultYes: - return true - case VAPDefaultNo: - return false - // on unrecognized value, use the default - default: - return vapDef == VAPDefaultYes +func (d *Driver) assumeVAPEnforcement(ct *templates.ConstraintTemplate) bool { + for _, v := range ct.Spec.Targets[0].Code { + if v.Engine == d.Name() { + if v.EnforceVAP == nil { + return d.generateVAPDefault + } + return *v.EnforceVAP + } } + return d.generateVAPDefault } type ARGetter interface { diff --git a/constraint/pkg/client/drivers/k8scel/driver_test.go b/constraint/pkg/client/drivers/k8scel/driver_test.go index d16a1f302..79da6317e 100644 --- a/constraint/pkg/client/drivers/k8scel/driver_test.go +++ b/constraint/pkg/client/drivers/k8scel/driver_test.go @@ -16,7 +16,7 @@ import ( "sigs.k8s.io/yaml" ) -func makeTemplateWithSource(source *schema.Source, vapGenerationVal *string) *templates.ConstraintTemplate { +func makeTemplateWithSource(source *schema.Source, vapGenerationVal *bool) *templates.ConstraintTemplate { template := &templates.ConstraintTemplate{ ObjectMeta: metav1.ObjectMeta{ Name: "testkind", @@ -27,7 +27,8 @@ func makeTemplateWithSource(source *schema.Source, vapGenerationVal *string) *te Target: "admission.k8s.io", Code: []templates.Code{ { - Engine: schema.Name, + Engine: schema.Name, + GenerateVAP: vapGenerationVal, Source: &templates.Anything{ Value: source.MustToUnstructured(), }, @@ -37,15 +38,10 @@ func makeTemplateWithSource(source *schema.Source, vapGenerationVal *string) *te }, }, } - if vapGenerationVal != nil { - template.SetLabels(map[string]string{ - VAPGenerationLabel: *vapGenerationVal, - }) - } return template } -func makeTemplate(vapGenerationVal *string) *templates.ConstraintTemplate { +func makeTemplate(vapGenerationVal *bool) *templates.ConstraintTemplate { return makeTemplateWithSource(&schema.Source{ Validations: []schema.Validation{ { @@ -56,7 +52,7 @@ func makeTemplate(vapGenerationVal *string) *templates.ConstraintTemplate { }, vapGenerationVal) } -func makeConstraint(vapGenerationVal *string) *unstructured.Unstructured { +func makeConstraint() *unstructured.Unstructured { constraint := &unstructured.Unstructured{ Object: map[string]interface{}{}, } @@ -64,11 +60,6 @@ func makeConstraint(vapGenerationVal *string) *unstructured.Unstructured { if err := unstructured.SetNestedField(constraint.Object, "someValue", "spec", "parameters", "testParam"); err != nil { panic(err) } - if vapGenerationVal != nil { - constraint.SetLabels(map[string]string{ - VAPGenerationLabel: *vapGenerationVal, - }) - } return constraint } @@ -116,7 +107,7 @@ func TestValidation(t *testing.T) { name string template *templates.ConstraintTemplate constraint *unstructured.Unstructured - vapDefault *vapDefault + vapDefault bool isAdmissionRequest bool expectedViolations bool expectedErr bool @@ -131,7 +122,7 @@ func TestValidation(t *testing.T) { }, }, }, nil), - constraint: makeConstraint(nil), + constraint: makeConstraint(), expectedViolations: false, }, { @@ -144,7 +135,7 @@ func TestValidation(t *testing.T) { }, }, }, nil), - constraint: makeConstraint(nil), + constraint: makeConstraint(), expectedViolations: true, }, { @@ -163,7 +154,7 @@ func TestValidation(t *testing.T) { }, }, }, nil), - constraint: makeConstraint(nil), + constraint: makeConstraint(), expectedViolations: false, }, { @@ -182,7 +173,7 @@ func TestValidation(t *testing.T) { }, }, }, nil), - constraint: makeConstraint(nil), + constraint: makeConstraint(), expectedViolations: true, }, { @@ -201,7 +192,7 @@ func TestValidation(t *testing.T) { }, }, }, nil), - constraint: makeConstraint(nil), + constraint: makeConstraint(), expectedViolations: false, }, { @@ -214,7 +205,7 @@ func TestValidation(t *testing.T) { }, }, }, nil), - constraint: makeConstraint(nil), + constraint: makeConstraint(), expectedViolations: false, }, // VAP generation @@ -228,8 +219,8 @@ func TestValidation(t *testing.T) { }, }, }, nil), - constraint: makeConstraint(nil), - vapDefault: ptr.To[vapDefault](VAPDefaultNo), + constraint: makeConstraint(), + vapDefault: false, expectedViolations: true, }, { @@ -242,8 +233,8 @@ func TestValidation(t *testing.T) { }, }, }, nil), - constraint: makeConstraint(nil), - vapDefault: ptr.To[vapDefault](VAPDefaultYes), + constraint: makeConstraint(), + vapDefault: true, expectedViolations: true, }, { @@ -256,9 +247,9 @@ func TestValidation(t *testing.T) { }, }, }, nil), - constraint: makeConstraint(nil), + constraint: makeConstraint(), isAdmissionRequest: true, - vapDefault: ptr.To[vapDefault](VAPDefaultYes), + vapDefault: true, expectedViolations: false, }, { @@ -271,9 +262,9 @@ func TestValidation(t *testing.T) { }, }, }, nil), - constraint: makeConstraint(nil), + constraint: makeConstraint(), isAdmissionRequest: true, - vapDefault: ptr.To[vapDefault](VAPDefaultNo), + vapDefault: false, expectedViolations: true, }, { @@ -285,42 +276,12 @@ func TestValidation(t *testing.T) { Message: "unexpected name", }, }, - }, ptr.To[string](string(VAPDefaultYes))), - constraint: makeConstraint(nil), + }, ptr.To[bool](true)), + constraint: makeConstraint(), isAdmissionRequest: true, - vapDefault: ptr.To[vapDefault](VAPDefaultNo), + vapDefault: false, expectedViolations: false, }, - { - name: "Unsatisfied constraint, default assume no VAP, admission request, constraint override", - template: makeTemplateWithSource(&schema.Source{ - Validations: []schema.Validation{ - { - Expression: `object.metadata.name == "unrecognizable-name"`, - Message: "unexpected name", - }, - }, - }, nil), - constraint: makeConstraint(ptr.To[string](string(VAPDefaultYes))), - isAdmissionRequest: true, - vapDefault: ptr.To[vapDefault](VAPDefaultNo), - expectedViolations: true, - }, - { - name: "Unsatisfied constraint, default assume VAP, admission request, constraint override", - template: makeTemplateWithSource(&schema.Source{ - Validations: []schema.Validation{ - { - Expression: `object.metadata.name == "unrecognizable-name"`, - Message: "unexpected name", - }, - }, - }, nil), - constraint: makeConstraint(ptr.To[string](string(VAPDefaultNo))), - isAdmissionRequest: true, - vapDefault: ptr.To[vapDefault](VAPDefaultYes), - expectedViolations: true, - }, { name: "Unsatisfied constraint, default assume VAP, admission request, constraint template override", template: makeTemplateWithSource(&schema.Source{ @@ -330,33 +291,17 @@ func TestValidation(t *testing.T) { Message: "unexpected name", }, }, - }, ptr.To[string](string(VAPDefaultNo))), - constraint: makeConstraint(nil), - isAdmissionRequest: true, - vapDefault: ptr.To[vapDefault](VAPDefaultYes), - expectedViolations: true, - }, - { - name: "Unsatisfied constraint, VAP disabled (default == nil), all override", - template: makeTemplateWithSource(&schema.Source{ - Validations: []schema.Validation{ - { - Expression: `object.metadata.name == "unrecognizable-name"`, - Message: "unexpected name", - }, - }, - }, ptr.To[string](string(VAPDefaultYes))), - constraint: makeConstraint(ptr.To[string](string(VAPDefaultYes))), + }, ptr.To[bool](false)), + constraint: makeConstraint(), isAdmissionRequest: true, + vapDefault: true, expectedViolations: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { args := []Arg{} - if test.vapDefault != nil { - args = append(args, VAPGenerationDefault(*test.vapDefault)) - } + args = append(args, VAPGenerationDefault(test.vapDefault)) driver, err := New(args...) if err != nil { t.Fatal(err) @@ -379,74 +324,50 @@ func TestAssumeVAPEnforcement(t *testing.T) { tests := []struct { name string template *templates.ConstraintTemplate - vapDefault *vapDefault + vapDefault bool expected bool }{ - { - name: "Enabled, default not set => no consideration of VAP enforcement", - template: makeTemplate(ptr.To[string](string(VAPDefaultYes))), - expected: false, - }, { name: "No stance, default enabled", template: makeTemplate(nil), - vapDefault: ptr.To[vapDefault](VAPDefaultYes), + vapDefault: true, expected: true, }, { name: "No stance, default disabled", template: makeTemplate(nil), - vapDefault: ptr.To[vapDefault](VAPDefaultNo), + vapDefault: false, expected: false, }, { name: "Enabled, default 'no'", - template: makeTemplate(ptr.To[string](string(VAPDefaultYes))), - vapDefault: ptr.To[vapDefault](VAPDefaultNo), + template: makeTemplate(ptr.To[bool](true)), + vapDefault: false, expected: true, }, { name: "Enabled, default 'yes'", - template: makeTemplate(ptr.To[string](string(VAPDefaultYes))), - vapDefault: ptr.To[vapDefault](VAPDefaultYes), + template: makeTemplate(ptr.To[bool](true)), + vapDefault: true, expected: true, }, { name: "Disabled, default 'yes'", - template: makeTemplate(ptr.To[string](string(VAPDefaultNo))), - vapDefault: ptr.To[vapDefault](VAPDefaultYes), + template: makeTemplate(ptr.To[bool](false)), + vapDefault: true, expected: false, }, { name: "Disabled, default 'no'", - template: makeTemplate(ptr.To[string](string(VAPDefaultNo))), - vapDefault: ptr.To[vapDefault](VAPDefaultNo), - expected: false, - }, - { - name: "Nonsense value, default not set => nonsense ignored", - template: makeTemplate(ptr.To[string]("catshaveclaws")), - expected: false, - }, - { - name: "Nonsense value, default set", - template: makeTemplate(ptr.To[string]("catshaveclaws")), - vapDefault: ptr.To[vapDefault](VAPDefaultNo), + template: makeTemplate(ptr.To[bool](false)), + vapDefault: false, expected: false, }, - { - name: "Nonsense value, default set to yes", - template: makeTemplate(ptr.To[string]("catshaveclaws")), - vapDefault: ptr.To[vapDefault](VAPDefaultYes), - expected: true, - }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { args := []Arg{} - if test.vapDefault != nil { - args = append(args, VAPGenerationDefault(*test.vapDefault)) - } + args = append(args, VAPGenerationDefault(test.vapDefault)) driver, err := New(args...) if err != nil { t.Fatal(err) diff --git a/constraint/pkg/core/templates/constrainttemplate_types.go b/constraint/pkg/core/templates/constrainttemplate_types.go index c50da2d6a..1f93ba418 100644 --- a/constraint/pkg/core/templates/constrainttemplate_types.go +++ b/constraint/pkg/core/templates/constrainttemplate_types.go @@ -68,6 +68,10 @@ type Code struct { // The engine used to evaluate the code. Example: "Rego". Required. Engine string `json:"engine,omitempty"` + // +kubebuilder:validation:Required + // The flag to use VAP for enforcement. + EnforceVAP *bool `json:"enforceVAP,omitempty"` + // +kubebuilder:validation:Required // +kubebuilder:validation:Schemaless // +kubebuilder:pruning:PreserveUnknownFields diff --git a/constraint/pkg/core/templates/zz_generated.deepcopy.go b/constraint/pkg/core/templates/zz_generated.deepcopy.go index 3e0d52bde..275da012e 100644 --- a/constraint/pkg/core/templates/zz_generated.deepcopy.go +++ b/constraint/pkg/core/templates/zz_generated.deepcopy.go @@ -1,5 +1,4 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* @@ -84,6 +83,11 @@ func (in *CRDSpec) DeepCopy() *CRDSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Code) DeepCopyInto(out *Code) { *out = *in + if in.EnforceVAP != nil { + in, out := &in.EnforceVAP, &out.EnforceVAP + *out = new(bool) + **out = **in + } if in.Source != nil { in, out := &in.Source, &out.Source *out = (*in).DeepCopy() diff --git a/constraint/pkg/schema/yaml_constant.go b/constraint/pkg/schema/yaml_constant.go index b67a7deb4..b6dde6c7e 100644 --- a/constraint/pkg/schema/yaml_constant.go +++ b/constraint/pkg/schema/yaml_constant.go @@ -7,8 +7,7 @@ const constraintTemplateCRDYaml = `apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.11.3 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.14.0 name: constrainttemplates.templates.gatekeeper.sh spec: group: templates.gatekeeper.sh @@ -27,14 +26,19 @@ spec: API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -71,11 +75,14 @@ spec: items: properties: code: - description: The source code options for the constraint template. - "Rego" can only be specified in one place (either here or - in the "rego" field) + description: |- + The source code options for the constraint template. "Rego" can only + be specified in one place (either here or in the "rego" field) items: properties: + enforceVAP: + description: The flag to use VAP for enforcement. + type: boolean engine: description: 'The engine used to evaluate the code. Example: "Rego". Required.' @@ -107,8 +114,9 @@ spec: properties: byPod: items: - description: ByPodStatus defines the observed state of ConstraintTemplate - as seen by an individual controller + description: |- + ByPodStatus defines the observed state of ConstraintTemplate as seen by + an individual controller properties: errors: items: @@ -151,14 +159,19 @@ spec: API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -195,11 +208,14 @@ spec: items: properties: code: - description: The source code options for the constraint template. - "Rego" can only be specified in one place (either here or - in the "rego" field) + description: |- + The source code options for the constraint template. "Rego" can only + be specified in one place (either here or in the "rego" field) items: properties: + enforceVAP: + description: The flag to use VAP for enforcement. + type: boolean engine: description: 'The engine used to evaluate the code. Example: "Rego". Required.' @@ -231,8 +247,9 @@ spec: properties: byPod: items: - description: ByPodStatus defines the observed state of ConstraintTemplate - as seen by an individual controller + description: |- + ByPodStatus defines the observed state of ConstraintTemplate as seen by + an individual controller properties: errors: items: @@ -275,14 +292,19 @@ spec: API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object @@ -319,11 +341,14 @@ spec: items: properties: code: - description: The source code options for the constraint template. - "Rego" can only be specified in one place (either here or - in the "rego" field) + description: |- + The source code options for the constraint template. "Rego" can only + be specified in one place (either here or in the "rego" field) items: properties: + enforceVAP: + description: The flag to use VAP for enforcement. + type: boolean engine: description: 'The engine used to evaluate the code. Example: "Rego". Required.' @@ -355,8 +380,9 @@ spec: properties: byPod: items: - description: ByPodStatus defines the observed state of ConstraintTemplate - as seen by an individual controller + description: |- + ByPodStatus defines the observed state of ConstraintTemplate as seen by + an individual controller properties: errors: items: