1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 10:28:36 +00:00

feat: support vap bindings in reports (#9506)

* feat: support vap bindings in reports

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>

* fix: add binding to the rule response

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>

* add chainsaw test

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>

* fix lint

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>

* fix chainsaw

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>

* add chainsaw tests

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>

* fix chainsaw tests

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>

---------

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
Co-authored-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Mariam Fahmy 2024-01-29 13:49:17 +02:00 committed by GitHub
parent c40dab9ea0
commit 9ed14cb779
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 585 additions and 38 deletions

View file

@ -59,9 +59,11 @@ func createReportControllers(
var ctrls []internal.Controller
var warmups []func(context.Context) error
var vapInformer admissionregistrationv1alpha1informers.ValidatingAdmissionPolicyInformer
var vapBindingInformer admissionregistrationv1alpha1informers.ValidatingAdmissionPolicyBindingInformer
// check if validating admission policies are registered in the API server
if validatingAdmissionPolicyReports {
vapInformer = kubeInformer.Admissionregistration().V1alpha1().ValidatingAdmissionPolicies()
vapBindingInformer = kubeInformer.Admissionregistration().V1alpha1().ValidatingAdmissionPolicyBindings()
}
kyvernoV1 := kyvernoInformer.Kyverno().V1()
@ -120,6 +122,7 @@ func createReportControllers(
kyvernoV1.ClusterPolicies(),
kyvernoV2beta1.PolicyExceptions(),
vapInformer,
vapBindingInformer,
kubeInformer.Core().V1().Namespaces(),
resourceReportController,
backgroundScanInterval,

View file

@ -57,13 +57,14 @@ type controller struct {
engine engineapi.Engine
// listers
polLister kyvernov1listers.PolicyLister
cpolLister kyvernov1listers.ClusterPolicyLister
polexLister kyvernov2beta1listers.PolicyExceptionLister
vapLister admissionregistrationv1alpha1listers.ValidatingAdmissionPolicyLister
bgscanrLister cache.GenericLister
cbgscanrLister cache.GenericLister
nsLister corev1listers.NamespaceLister
polLister kyvernov1listers.PolicyLister
cpolLister kyvernov1listers.ClusterPolicyLister
polexLister kyvernov2beta1listers.PolicyExceptionLister
vapLister admissionregistrationv1alpha1listers.ValidatingAdmissionPolicyLister
vapBindingLister admissionregistrationv1alpha1listers.ValidatingAdmissionPolicyBindingLister
bgscanrLister cache.GenericLister
cbgscanrLister cache.GenericLister
nsLister corev1listers.NamespaceLister
// queue
queue workqueue.RateLimitingInterface
@ -89,6 +90,7 @@ func NewController(
cpolInformer kyvernov1informers.ClusterPolicyInformer,
polexInformer kyvernov2beta1informers.PolicyExceptionInformer,
vapInformer admissionregistrationv1alpha1informers.ValidatingAdmissionPolicyInformer,
vapBindingInformer admissionregistrationv1alpha1informers.ValidatingAdmissionPolicyBindingInformer,
nsInformer corev1informers.NamespaceInformer,
metadataCache resource.MetadataCache,
forceDelay time.Duration,
@ -125,6 +127,12 @@ func NewController(
logger.Error(err, "failed to register event handlers")
}
}
if vapBindingInformer != nil {
c.vapBindingLister = vapBindingInformer.Lister()
if _, err := controllerutils.AddEventHandlersT(vapBindingInformer.Informer(), c.addVAPBinding, c.updateVAPBinding, c.deleteVAPBinding); err != nil {
logger.Error(err, "failed to register event handlers")
}
}
if _, err := controllerutils.AddEventHandlersT(polInformer.Informer(), c.addPolicy, c.updatePolicy, c.deletePolicy); err != nil {
logger.Error(err, "failed to register event handlers")
}
@ -195,6 +203,20 @@ func (c *controller) deleteVAP(obj *admissionregistrationv1alpha1.ValidatingAdmi
c.enqueueResources()
}
func (c *controller) addVAPBinding(obj *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding) {
c.enqueueResources()
}
func (c *controller) updateVAPBinding(old, obj *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding) {
if old.GetResourceVersion() != obj.GetResourceVersion() {
c.enqueueResources()
}
}
func (c *controller) deleteVAPBinding(obj *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding) {
c.enqueueResources()
}
func (c *controller) enqueueResources() {
for _, key := range c.metadataCache.GetAllResourceKeys() {
c.queue.Add(key)
@ -225,7 +247,7 @@ func (c *controller) getMeta(namespace, name string) (metav1.Object, error) {
}
}
func (c *controller) needsReconcile(namespace, name, hash string, exceptions []kyvernov2beta1.PolicyException, policies ...engineapi.GenericPolicy) (bool, bool, error) {
func (c *controller) needsReconcile(namespace, name, hash string, exceptions []kyvernov2beta1.PolicyException, bindings []admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding, policies ...engineapi.GenericPolicy) (bool, bool, error) {
// if the reportMetadata does not exist, we need a full reconcile
reportMetadata, err := c.getMeta(namespace, name)
if err != nil {
@ -260,6 +282,9 @@ func (c *controller) needsReconcile(namespace, name, hash string, exceptions []k
for _, exception := range exceptions {
expected[reportutils.PolicyExceptionLabel(exception)] = exception.GetResourceVersion()
}
for _, binding := range bindings {
expected[reportutils.ValidatingAdmissionPolicyBindingLabel(binding)] = binding.GetResourceVersion()
}
actual := map[string]string{}
for key, value := range reportMetadata.GetLabels() {
if reportutils.IsPolicyLabel(key) {
@ -282,6 +307,7 @@ func (c *controller) reconcileReport(
gvk schema.GroupVersionKind,
resource resource.Resource,
exceptions []kyvernov2beta1.PolicyException,
bindings []admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding,
policies ...engineapi.GenericPolicy,
) error {
// namespace labels to be used by the scanner
@ -314,6 +340,9 @@ func (c *controller) reconcileReport(
for _, exception := range exceptions {
expected[reportutils.PolicyExceptionLabel(exception)] = exception.GetResourceVersion()
}
for _, binding := range bindings {
expected[reportutils.ValidatingAdmissionPolicyBindingLabel(binding)] = binding.GetResourceVersion()
}
actual := map[string]string{}
for key, value := range observed.GetLabels() {
@ -346,11 +375,22 @@ func (c *controller) reconcileReport(
policyNameToLabel[key] = reportutils.PolicyExceptionLabel(exception)
}
for _, binding := range bindings {
key, err := cache.MetaNamespaceKeyFunc(binding)
if err != nil {
return err
}
policyNameToLabel[key] = reportutils.ValidatingAdmissionPolicyBindingLabel(binding)
}
for _, result := range observed.GetResults() {
// if the policy did not change, keep the result
label := policyNameToLabel[result.Policy]
exceptionLabel := policyNameToLabel[result.Properties["exception"]]
if (label != "" && expected[label] == actual[label]) || (exceptionLabel != "" && expected[exceptionLabel] == actual[exceptionLabel]) {
vapBindingLabel := policyNameToLabel[result.Properties["binding"]]
if (label != "" && expected[label] == actual[label]) ||
(exceptionLabel != "" && expected[exceptionLabel] == actual[exceptionLabel]) ||
(vapBindingLabel != "" && expected[vapBindingLabel] == actual[vapBindingLabel]) {
ruleResults = append(ruleResults, result)
}
}
@ -358,15 +398,24 @@ func (c *controller) reconcileReport(
// calculate necessary results
for _, policy := range policies {
reevaluate := false
for _, polex := range exceptions {
if actual[reportutils.PolicyExceptionLabel(polex)] != polex.GetResourceVersion() {
reevaluate = true
break
if policy.GetType() == engineapi.KyvernoPolicyType {
for _, polex := range exceptions {
if actual[reportutils.PolicyExceptionLabel(polex)] != polex.GetResourceVersion() {
reevaluate = true
break
}
}
} else {
for _, binding := range bindings {
if actual[reportutils.ValidatingAdmissionPolicyBindingLabel(binding)] != binding.GetResourceVersion() {
reevaluate = true
break
}
}
}
if full || reevaluate || actual[reportutils.PolicyLabel(policy)] != policy.GetResourceVersion() {
scanner := utils.NewScanner(logger, c.engine, c.config, c.jp, c.client)
for _, result := range scanner.ScanResource(ctx, *target, nsLabels, policy) {
for _, result := range scanner.ScanResource(ctx, *target, nsLabels, bindings, policy) {
if result.Error != nil {
return result.Error
} else if result.EngineResponse != nil {
@ -388,6 +437,9 @@ func (c *controller) reconcileReport(
for _, exception := range exceptions {
reportutils.SetPolicyExceptionLabel(desired, exception)
}
for _, binding := range bindings {
reportutils.SetValidatingAdmissionPolicyBindingLabel(desired, binding)
}
reportutils.SetResourceVersionLabels(desired, target)
reportutils.SetResults(desired, ruleResults...)
if full || !controllerutils.HasAnnotation(desired, annotationLastScanTime) {
@ -472,20 +524,28 @@ func (c *controller) reconcile(ctx context.Context, log logr.Logger, key, namesp
policies = append(policies, engineapi.NewValidatingAdmissionPolicy(pol))
}
}
var vapBindings []admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding
if c.vapBindingLister != nil {
// load validating admission policy bindings
vapBindings, err = utils.FetchValidatingAdmissionPolicyBindings(c.vapBindingLister)
if err != nil {
return err
}
}
// load policy exceptions with background process enabled
exceptions, err := utils.FetchPolicyExceptions(c.polexLister, namespace)
if err != nil {
return err
}
// we have the resource, check if we need to reconcile
if needsReconcile, full, err := c.needsReconcile(namespace, name, resource.Hash, exceptions, policies...); err != nil {
if needsReconcile, full, err := c.needsReconcile(namespace, name, resource.Hash, exceptions, vapBindings, policies...); err != nil {
return err
} else {
defer func() {
c.queue.AddAfter(key, c.forceDelay)
}()
if needsReconcile {
return c.reconcileReport(ctx, namespace, name, full, uid, gvk, resource, exceptions, policies...)
return c.reconcileReport(ctx, namespace, name, full, uid, gvk, resource, exceptions, vapBindings, policies...)
}
}
return nil

View file

@ -13,6 +13,7 @@ import (
"github.com/kyverno/kyverno/pkg/engine/jmespath"
"github.com/kyverno/kyverno/pkg/validatingadmissionpolicy"
"go.uber.org/multierr"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -30,7 +31,7 @@ type ScanResult struct {
}
type Scanner interface {
ScanResource(context.Context, unstructured.Unstructured, map[string]string, ...engineapi.GenericPolicy) map[*engineapi.GenericPolicy]ScanResult
ScanResource(context.Context, unstructured.Unstructured, map[string]string, []v1alpha1.ValidatingAdmissionPolicyBinding, ...engineapi.GenericPolicy) map[*engineapi.GenericPolicy]ScanResult
}
func NewScanner(
@ -49,7 +50,7 @@ func NewScanner(
}
}
func (s *scanner) ScanResource(ctx context.Context, resource unstructured.Unstructured, nsLabels map[string]string, policies ...engineapi.GenericPolicy) map[*engineapi.GenericPolicy]ScanResult {
func (s *scanner) ScanResource(ctx context.Context, resource unstructured.Unstructured, nsLabels map[string]string, bindings []v1alpha1.ValidatingAdmissionPolicyBinding, policies ...engineapi.GenericPolicy) map[*engineapi.GenericPolicy]ScanResult {
results := map[*engineapi.GenericPolicy]ScanResult{}
for i, policy := range policies {
var errors []error
@ -79,6 +80,11 @@ func (s *scanner) ScanResource(ctx context.Context, resource unstructured.Unstru
} else {
pol := policy.AsValidatingAdmissionPolicy()
policyData := validatingadmissionpolicy.NewPolicyData(*pol)
for _, binding := range bindings {
if binding.Spec.PolicyName == pol.Name {
policyData.AddBinding(binding)
}
}
res, err := validatingadmissionpolicy.Validate(policyData, resource, s.client)
if err != nil {
errors = append(errors, err)

View file

@ -136,3 +136,15 @@ func FetchValidatingAdmissionPolicies(vapLister admissionregistrationv1alpha1lis
}
return policies, nil
}
func FetchValidatingAdmissionPolicyBindings(vapBindingLister admissionregistrationv1alpha1listers.ValidatingAdmissionPolicyBindingLister) ([]admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding, error) {
var bindings []admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding
if pols, err := vapBindingLister.List(labels.Everything()); err != nil {
return nil, err
} else {
for _, pol := range pols {
bindings = append(bindings, *pol)
}
}
return bindings, nil
}

View file

@ -5,6 +5,7 @@ import (
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
pssutils "github.com/kyverno/kyverno/pkg/pss/utils"
"k8s.io/api/admissionregistration/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/pod-security-admission/api"
@ -44,6 +45,8 @@ type RuleResponse struct {
podSecurityChecks *PodSecurityChecks
// exception is the exception applied (if any)
exception *kyvernov2beta1.PolicyException
// binding is the validatingadmissionpolicybinding (if any)
binding *v1alpha1.ValidatingAdmissionPolicyBinding
// emitWarning enable passing rule message as warning to api server warning header
emitWarning bool
}
@ -90,6 +93,11 @@ func (r RuleResponse) WithException(exception *kyvernov2beta1.PolicyException) *
return &r
}
func (r RuleResponse) WithBinding(binding *v1alpha1.ValidatingAdmissionPolicyBinding) *RuleResponse {
r.binding = binding
return &r
}
func (r RuleResponse) WithPodSecurityChecks(checks PodSecurityChecks) *RuleResponse {
r.podSecurityChecks = &checks
return &r
@ -125,6 +133,10 @@ func (r *RuleResponse) Exception() *kyvernov2beta1.PolicyException {
return r.exception
}
func (r *RuleResponse) ValidatingAdmissionPolicyBinding() *v1alpha1.ValidatingAdmissionPolicyBinding {
return r.binding
}
func (r *RuleResponse) IsException() bool {
return r.exception != nil
}

View file

@ -13,6 +13,8 @@ import (
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
"k8s.io/api/admissionregistration/v1alpha1"
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -28,18 +30,23 @@ const (
AnnotationResourceNamespace = "audit.kyverno.io/resource.namespace"
AnnotationResourceName = "audit.kyverno.io/resource.name"
// policy labels
LabelDomainClusterPolicy = "cpol.kyverno.io"
LabelDomainPolicy = "pol.kyverno.io"
LabelPrefixClusterPolicy = LabelDomainClusterPolicy + "/"
LabelPrefixPolicy = LabelDomainPolicy + "/"
LabelPrefixPolicyException = "polex.kyverno.io/"
LabelPrefixValidatingAdmissionPolicy = "validatingadmissionpolicy.apiserver.io/"
LabelDomainClusterPolicy = "cpol.kyverno.io"
LabelDomainPolicy = "pol.kyverno.io"
LabelPrefixClusterPolicy = LabelDomainClusterPolicy + "/"
LabelPrefixPolicy = LabelDomainPolicy + "/"
LabelPrefixPolicyException = "polex.kyverno.io/"
LabelPrefixValidatingAdmissionPolicy = "validatingadmissionpolicy.apiserver.io/"
LabelPrefixValidatingAdmissionPolicyBinding = "validatingadmissionpolicybinding.apiserver.io/"
// aggregated admission report label
LabelAggregatedReport = "audit.kyverno.io/report.aggregate"
)
func IsPolicyLabel(label string) bool {
return strings.HasPrefix(label, LabelPrefixPolicy) || strings.HasPrefix(label, LabelPrefixClusterPolicy) || strings.HasPrefix(label, LabelPrefixPolicyException)
return strings.HasPrefix(label, LabelPrefixPolicy) ||
strings.HasPrefix(label, LabelPrefixClusterPolicy) ||
strings.HasPrefix(label, LabelPrefixPolicyException) ||
strings.HasPrefix(label, LabelPrefixValidatingAdmissionPolicy) ||
strings.HasPrefix(label, LabelPrefixValidatingAdmissionPolicyBinding)
}
func PolicyNameFromLabel(namespace, label string) (string, error) {
@ -79,6 +86,10 @@ func PolicyExceptionLabel(exception kyvernov2beta1.PolicyException) string {
return LabelPrefixPolicyException + exception.GetName()
}
func ValidatingAdmissionPolicyBindingLabel(binding admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding) string {
return LabelPrefixValidatingAdmissionPolicyBinding + binding.GetName()
}
func CleanupKyvernoLabels(obj metav1.Object) {
labels := obj.GetLabels()
for key := range labels {
@ -144,6 +155,10 @@ func SetPolicyExceptionLabel(report kyvernov1alpha2.ReportInterface, exception k
controllerutils.SetLabel(report, PolicyExceptionLabel(exception), exception.GetResourceVersion())
}
func SetValidatingAdmissionPolicyBindingLabel(report kyvernov1alpha2.ReportInterface, binding v1alpha1.ValidatingAdmissionPolicyBinding) {
controllerutils.SetLabel(report, ValidatingAdmissionPolicyBindingLabel(binding), binding.GetResourceVersion())
}
func GetResourceUid(report metav1.Object) types.UID {
return types.UID(controllerutils.GetLabel(report, LabelResourceUid))
}

View file

@ -144,6 +144,11 @@ func EngineResponseToReportResults(response engineapi.EngineResponse) []policyre
Seconds: time.Now().Unix(),
},
}
if ruleResult.ValidatingAdmissionPolicyBinding() != nil {
result.Properties = map[string]string{
"binding": ruleResult.ValidatingAdmissionPolicyBinding().Name,
}
}
results = append(results, result)
}
}

View file

@ -126,16 +126,23 @@ func Validate(policyData PolicyData, resource unstructured.Unstructured, client
a = admission.NewAttributesRecord(resource.DeepCopyObject(), nil, resource.GroupVersionKind(), resource.GetNamespace(), resource.GetName(), gvr, "", admission.Create, nil, false, nil)
resPath := fmt.Sprintf("%s/%s/%s", a.GetNamespace(), a.GetKind().Kind, a.GetName())
logger.V(3).Info("validate resource %s against policy %s", resPath, policy.GetName())
return validateResource(policy, resource, a)
return validateResource(policy, nil, resource, a)
} else {
for _, binding := range bindings {
for i, binding := range bindings {
// convert policy binding from v1alpha1 to v1beta1
var namespaceSelector, objectSelector, paramSelector metav1.LabelSelector
if binding.Spec.MatchResources.NamespaceSelector != nil {
namespaceSelector = *binding.Spec.MatchResources.NamespaceSelector
}
if binding.Spec.MatchResources.ObjectSelector != nil {
objectSelector = *binding.Spec.MatchResources.ObjectSelector
var resourceRules, excludeResourceRules []v1alpha1.NamedRuleWithOperations
var matchPolicy *v1alpha1.MatchPolicyType
if binding.Spec.MatchResources != nil {
if binding.Spec.MatchResources.NamespaceSelector != nil {
namespaceSelector = *binding.Spec.MatchResources.NamespaceSelector
}
if binding.Spec.MatchResources.ObjectSelector != nil {
objectSelector = *binding.Spec.MatchResources.ObjectSelector
}
resourceRules = binding.Spec.MatchResources.ResourceRules
excludeResourceRules = binding.Spec.MatchResources.ExcludeResourceRules
matchPolicy = binding.Spec.MatchResources.MatchPolicy
}
var paramRef v1beta1.ParamRef
@ -157,9 +164,9 @@ func Validate(policyData PolicyData, resource unstructured.Unstructured, client
MatchResources: &v1beta1.MatchResources{
NamespaceSelector: &namespaceSelector,
ObjectSelector: &objectSelector,
ResourceRules: convertRules(binding.Spec.MatchResources.ResourceRules),
ExcludeResourceRules: convertRules(binding.Spec.MatchResources.ExcludeResourceRules),
MatchPolicy: (*v1beta1.MatchPolicyType)(binding.Spec.MatchResources.MatchPolicy),
ResourceRules: convertRules(resourceRules),
ExcludeResourceRules: convertRules(excludeResourceRules),
MatchPolicy: (*v1beta1.MatchPolicyType)(matchPolicy),
},
ValidationActions: convertValidationActions(binding.Spec.ValidationActions),
},
@ -174,20 +181,20 @@ func Validate(policyData PolicyData, resource unstructured.Unstructured, client
resPath := fmt.Sprintf("%s/%s/%s", a.GetNamespace(), a.GetKind().Kind, a.GetName())
logger.V(3).Info("validate resource %s against policy %s with binding %s", resPath, policy.GetName(), binding.GetName())
return validateResource(policy, resource, a)
return validateResource(policy, &bindings[i], resource, a)
}
}
} else {
a = admission.NewAttributesRecord(resource.DeepCopyObject(), nil, resource.GroupVersionKind(), resource.GetNamespace(), resource.GetName(), gvr, "", admission.Create, nil, false, nil)
resPath := fmt.Sprintf("%s/%s/%s", a.GetNamespace(), a.GetKind().Kind, a.GetName())
logger.V(3).Info("validate resource %s against policy %s", resPath, policy.GetName())
return validateResource(policy, resource, a)
return validateResource(policy, nil, resource, a)
}
return engineResponse, nil
}
func validateResource(policy v1alpha1.ValidatingAdmissionPolicy, resource unstructured.Unstructured, a admission.Attributes) (engineapi.EngineResponse, error) {
func validateResource(policy v1alpha1.ValidatingAdmissionPolicy, binding *v1alpha1.ValidatingAdmissionPolicyBinding, resource unstructured.Unstructured, a admission.Attributes) (engineapi.EngineResponse, error) {
startTime := time.Now()
engineResponse := engineapi.NewEngineResponse(resource, engineapi.NewValidatingAdmissionPolicy(policy), nil)
@ -244,6 +251,10 @@ func validateResource(policy v1alpha1.ValidatingAdmissionPolicy, resource unstru
if isPass {
ruleResp = engineapi.RulePass(policy.GetName(), engineapi.Validation, "")
}
if binding != nil {
ruleResp = ruleResp.WithBinding(binding)
}
policyResp.Add(engineapi.NewExecutionStats(startTime, time.Now()), *ruleResp)
engineResponse = engineResponse.WithPolicyResponse(policyResp)

View file

@ -0,0 +1,11 @@
## Description
This test checks that policy reports are generated successfully as a result of applying the ValidatingAdmissionPolicy with its binding to a resource.
## Steps
1. - Create a `staging-ns-2` namespace whose label is `environment: staging-ns-2`
1. - Create a Deployment named `deployment-3` with 7 replicas in the `staging-ns-2` namespace.
1. - Create a ValidatingAdmissionPolicy that checks deployment replicas to be less than or equal to 5.
- Create a ValidatingAdmissionPolicyBinding that matches resources whose namespace has a label of `environment: staging`.
1. - A policy report is generated for `deployment-3` with a fail result.

View file

@ -0,0 +1,27 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
creationTimestamp: null
name: with-validating-admission-policy-binding-fail
spec:
steps:
- name: step-01
try:
- apply:
file: ns.yaml
- assert:
file: ns.yaml
- name: step-02
try:
- apply:
file: deployment.yaml
- assert:
file: deployment-assert.yaml
- name: step-03
try:
- apply:
file: policy.yaml
- name: step-04
try:
- assert:
file: report-assert.yaml

View file

@ -0,0 +1,5 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-3
namespace: staging-ns-2

View file

@ -0,0 +1,22 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-3
namespace: staging-ns-2
labels:
app: nginx
spec:
replicas: 7
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

View file

@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: staging-ns-2
labels:
environment: staging-ns-2

View file

@ -0,0 +1,30 @@
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "check-deployment-replicas-04"
spec:
matchConstraints:
resourceRules:
- apiGroups:
- apps
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- deployments
validations:
- expression: object.spec.replicas <= 5
---
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "check-deployment-replicas-binding-04"
spec:
policyName: "check-deployment-replicas-04"
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
environment: staging-ns-2

View file

@ -0,0 +1,26 @@
apiVersion: wgpolicyk8s.io/v1alpha2
kind: PolicyReport
metadata:
labels:
app.kubernetes.io/managed-by: kyverno
namespace: staging-ns-2
ownerReferences:
- apiVersion: apps/v1
kind: Deployment
name: deployment-3
results:
- message: 'failed expression: object.spec.replicas <= 5'
policy: check-deployment-replicas-04
result: fail
source: ValidatingAdmissionPolicy
scope:
apiVersion: apps/v1
kind: Deployment
name: deployment-3
namespace: staging-ns-2
summary:
error: 0
fail: 1
pass: 0
skip: 0
warn: 0

View file

@ -0,0 +1,11 @@
## Description
This test checks that policy reports are generated successfully as a result of applying the ValidatingAdmissionPolicy with its binding to a resource.
## Steps
1. - Create a Deployment named `deployment-1` with 7 replicas in the `default` namespace.
- Create a Deployment named `deployment-2` with 3 replicas in the `default` namespace.
1. - Create a ValidatingAdmissionPolicy that checks deployment replicas to be less than or equal to 5.
- Create a ValidatingAdmissionPolicyBinding that matches resources whose namespace has a label of `environment: staging`.
1. - No policy reports generated for both `deployment-1` and `deployment-2`.

View file

@ -0,0 +1,21 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
creationTimestamp: null
name: with-validating-admission-policy-binding-no-reports
spec:
steps:
- name: step-01
try:
- apply:
file: deployment.yaml
- assert:
file: deployment-assert.yaml
- name: step-02
try:
- apply:
file: policy.yaml
- name: step-03
try:
- error:
file: report-error.yaml

View file

@ -0,0 +1,11 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-1
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-2
namespace: default

View file

@ -0,0 +1,45 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-1
namespace: default
labels:
app: nginx
spec:
replicas: 7
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-2
namespace: default
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

View file

@ -0,0 +1,30 @@
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "check-deployment-replicas-05"
spec:
matchConstraints:
resourceRules:
- apiGroups:
- apps
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- deployments
validations:
- expression: object.spec.replicas <= 5
---
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "check-deployment-replicas-binding-05"
spec:
policyName: "check-deployment-replicas-05"
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
environment: staging

View file

@ -0,0 +1,52 @@
apiVersion: wgpolicyk8s.io/v1alpha2
kind: PolicyReport
metadata:
labels:
app.kubernetes.io/managed-by: kyverno
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: Deployment
name: deployment-1
results:
- message: 'failed expression: object.spec.replicas <= 5'
policy: check-deployment-replicas-05
result: fail
source: ValidatingAdmissionPolicy
scope:
apiVersion: apps/v1
kind: Deployment
name: deployment-1
namespace: default
summary:
error: 0
fail: 1
pass: 0
skip: 0
warn: 0
---
apiVersion: wgpolicyk8s.io/v1alpha2
kind: PolicyReport
metadata:
labels:
app.kubernetes.io/managed-by: kyverno
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: Deployment
name: deployment-2
results:
- policy: check-deployment-replicas-05
result: pass
source: ValidatingAdmissionPolicy
scope:
apiVersion: apps/v1
kind: Deployment
name: deployment-2
namespace: default
summary:
error: 0
fail: 0
pass: 1
skip: 0
warn: 0

View file

@ -0,0 +1,11 @@
## Description
This test checks that policy reports are generated successfully as a result of applying the ValidatingAdmissionPolicy with its binding to a resource.
## Steps
1. - Create a `staging-ns-1` namespace whose label is `environment: staging-ns-1`
1. - Create a Deployment named `deployment-4` with 3 replicas in the `staging-ns-1` namespace.
1. - Create a ValidatingAdmissionPolicy that checks deployment replicas to be less than or equal to 5.
- Create a ValidatingAdmissionPolicyBinding that matches resources whose namespace has a label of `environment: staging`.
1. - A policy report is generated for `deployment-4` with a pass result.

View file

@ -0,0 +1,27 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
creationTimestamp: null
name: with-validating-admission-policy-binding-pass
spec:
steps:
- name: step-01
try:
- apply:
file: ns.yaml
- assert:
file: ns.yaml
- name: step-02
try:
- apply:
file: deployment.yaml
- assert:
file: deployment-assert.yaml
- name: step-03
try:
- apply:
file: policy.yaml
- name: step-04
try:
- assert:
file: report-assert.yaml

View file

@ -0,0 +1,5 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-4
namespace: staging-ns-1

View file

@ -0,0 +1,22 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-4
namespace: staging-ns-1
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

View file

@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: staging-ns-1
labels:
environment: staging-ns-1

View file

@ -0,0 +1,30 @@
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicy
metadata:
name: "check-deployment-replicas-03"
spec:
matchConstraints:
resourceRules:
- apiGroups:
- apps
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- deployments
validations:
- expression: object.spec.replicas <= 5
---
apiVersion: admissionregistration.k8s.io/v1alpha1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "check-deployment-replicas-binding-03"
spec:
policyName: "check-deployment-replicas-03"
validationActions: [Deny]
matchResources:
namespaceSelector:
matchLabels:
environment: staging-ns-1

View file

@ -0,0 +1,25 @@
apiVersion: wgpolicyk8s.io/v1alpha2
kind: PolicyReport
metadata:
labels:
app.kubernetes.io/managed-by: kyverno
namespace: staging-ns-1
ownerReferences:
- apiVersion: apps/v1
kind: Deployment
name: deployment-4
results:
- policy: check-deployment-replicas-03
result: pass
source: ValidatingAdmissionPolicy
scope:
apiVersion: apps/v1
kind: Deployment
name: deployment-4
namespace: staging-ns-1
summary:
error: 0
fail: 0
pass: 1
skip: 0
warn: 0