mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 10:28:36 +00:00
feat: skip generating VAP when an exception is defined (#9386)
Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
parent
b4acbdea2c
commit
a791d9ac35
12 changed files with 183 additions and 1 deletions
|
@ -47,6 +47,10 @@ func (p *PolicyException) Contains(policy string, rule string) bool {
|
|||
return p.Spec.Contains(policy, rule)
|
||||
}
|
||||
|
||||
func (p *PolicyException) GetKind() string {
|
||||
return "PolicyException"
|
||||
}
|
||||
|
||||
// PolicyExceptionSpec stores policy exception spec
|
||||
type PolicyExceptionSpec struct {
|
||||
// Background controls if exceptions are applied to existing policies during a background scan.
|
||||
|
|
|
@ -40,6 +40,10 @@ func (p *PolicyException) Contains(policy string, rule string) bool {
|
|||
return p.Spec.Contains(policy, rule)
|
||||
}
|
||||
|
||||
func (p *PolicyException) GetKind() string {
|
||||
return "PolicyException"
|
||||
}
|
||||
|
||||
// Exception stores infos about a policy and rules
|
||||
type Exception = kyvernov2beta1.Exception
|
||||
|
||||
|
|
|
@ -47,6 +47,10 @@ func (p *PolicyException) Contains(policy string, rule string) bool {
|
|||
return p.Spec.Contains(policy, rule)
|
||||
}
|
||||
|
||||
func (p *PolicyException) GetKind() string {
|
||||
return "PolicyException"
|
||||
}
|
||||
|
||||
// PolicyExceptionSpec stores policy exception spec
|
||||
type PolicyExceptionSpec struct {
|
||||
// Background controls if exceptions are applied to existing policies during a background scan.
|
||||
|
|
|
@ -192,6 +192,7 @@ func createrLeaderControllers(
|
|||
kyvernoClient,
|
||||
dynamicClient.Discovery(),
|
||||
kyvernoInformer.Kyverno().V1().ClusterPolicies(),
|
||||
kyvernoInformer.Kyverno().V2beta1().PolicyExceptions(),
|
||||
kubeInformer.Admissionregistration().V1alpha1().ValidatingAdmissionPolicies(),
|
||||
kubeInformer.Admissionregistration().V1alpha1().ValidatingAdmissionPolicyBindings(),
|
||||
eventGenerator,
|
||||
|
|
|
@ -2,14 +2,18 @@ package validatingadmissionpolicygenerate
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
|
||||
"github.com/kyverno/kyverno/pkg/auth/checker"
|
||||
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernov1informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
|
||||
kyvernov2beta1informers "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v2beta1"
|
||||
kyvernov1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
|
||||
kyvernov2beta1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v2beta1"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/controllers"
|
||||
"github.com/kyverno/kyverno/pkg/event"
|
||||
|
@ -43,6 +47,7 @@ type controller struct {
|
|||
|
||||
// listers
|
||||
cpolLister kyvernov1listers.ClusterPolicyLister
|
||||
polexLister kyvernov2beta1listers.PolicyExceptionLister
|
||||
vapLister admissionregistrationv1alpha1listers.ValidatingAdmissionPolicyLister
|
||||
vapbindingLister admissionregistrationv1alpha1listers.ValidatingAdmissionPolicyBindingLister
|
||||
|
||||
|
@ -58,6 +63,7 @@ func NewController(
|
|||
kyvernoClient versioned.Interface,
|
||||
discoveryClient dclient.IDiscovery,
|
||||
cpolInformer kyvernov1informers.ClusterPolicyInformer,
|
||||
polexInformer kyvernov2beta1informers.PolicyExceptionInformer,
|
||||
vapInformer admissionregistrationv1alpha1informers.ValidatingAdmissionPolicyInformer,
|
||||
vapbindingInformer admissionregistrationv1alpha1informers.ValidatingAdmissionPolicyBindingInformer,
|
||||
eventGen event.Interface,
|
||||
|
@ -69,6 +75,7 @@ func NewController(
|
|||
kyvernoClient: kyvernoClient,
|
||||
discoveryClient: discoveryClient,
|
||||
cpolLister: cpolInformer.Lister(),
|
||||
polexLister: polexInformer.Lister(),
|
||||
vapLister: vapInformer.Lister(),
|
||||
vapbindingLister: vapbindingInformer.Lister(),
|
||||
queue: queue,
|
||||
|
@ -81,6 +88,11 @@ func NewController(
|
|||
logger.Error(err, "failed to register event handlers")
|
||||
}
|
||||
|
||||
// Set up an event handler for when policy exceptions change
|
||||
if _, err := controllerutils.AddEventHandlersT(polexInformer.Informer(), c.addException, c.updateException, c.deleteException); err != nil {
|
||||
logger.Error(err, "failed to register event handlers")
|
||||
}
|
||||
|
||||
// Set up an event handler for when validating admission policies change
|
||||
if _, err := controllerutils.AddEventHandlersT(vapInformer.Informer(), c.addVAP, c.updateVAP, c.deleteVAP); err != nil {
|
||||
logger.Error(err, "failed to register event handlers")
|
||||
|
@ -135,6 +147,46 @@ func (c *controller) enqueuePolicy(obj kyvernov1.PolicyInterface) {
|
|||
c.queue.Add(key)
|
||||
}
|
||||
|
||||
func (c *controller) addException(obj *kyvernov2beta1.PolicyException) {
|
||||
logger.Info("policy exception created", "uid", obj.GetUID(), "kind", obj.GetKind(), "name", obj.GetName())
|
||||
c.enqueueException(obj)
|
||||
}
|
||||
|
||||
func (c *controller) updateException(old, obj *kyvernov2beta1.PolicyException) {
|
||||
if datautils.DeepEqual(old.Spec, obj.Spec) {
|
||||
return
|
||||
}
|
||||
logger.Info("policy exception updated", "uid", obj.GetUID(), "kind", obj.GetKind(), "name", obj.GetName())
|
||||
c.enqueueException(obj)
|
||||
}
|
||||
|
||||
func (c *controller) deleteException(obj *kyvernov2beta1.PolicyException) {
|
||||
polex := kubeutils.GetObjectWithTombstone(obj).(*kyvernov2beta1.PolicyException)
|
||||
|
||||
logger.Info("policy exception deleted", "uid", polex.GetUID(), "kind", polex.GetKind(), "name", polex.GetName())
|
||||
c.enqueueException(obj)
|
||||
}
|
||||
|
||||
func (c *controller) enqueueException(obj *kyvernov2beta1.PolicyException) {
|
||||
for _, exception := range obj.Spec.Exceptions {
|
||||
// skip adding namespaced policies in the queue.
|
||||
// skip adding policies with multiple rules in the queue.
|
||||
if strings.Contains(exception.PolicyName, "/") || len(exception.RuleNames) > 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
cpol, err := c.getClusterPolicy(exception.PolicyName)
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return
|
||||
}
|
||||
logger.Error(err, "unable to get the policy from policy informer")
|
||||
return
|
||||
}
|
||||
c.enqueuePolicy(cpol)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) addVAP(obj *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) {
|
||||
c.enqueueVAP(obj)
|
||||
}
|
||||
|
@ -334,7 +386,12 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, nam
|
|||
|
||||
observedVAP, vapErr := c.getValidatingAdmissionPolicy(vapName)
|
||||
observedVAPbinding, vapBindingErr := c.getValidatingAdmissionPolicyBinding(vapBindingName)
|
||||
if ok, msg := canGenerateVAP(spec); !ok {
|
||||
|
||||
hasExceptions, err := c.hasExceptions(name, spec.Rules[0].Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok, msg := canGenerateVAP(spec); !ok || hasExceptions {
|
||||
// delete the ValidatingAdmissionPolicy if exist
|
||||
if vapErr == nil {
|
||||
err = c.client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Delete(ctx, vapName, metav1.DeleteOptions{})
|
||||
|
@ -349,6 +406,10 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, nam
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if msg == "" {
|
||||
msg = "skip generating ValidatingAdmissionPolicy: a policy exception is configured."
|
||||
}
|
||||
c.updateClusterPolicyStatus(ctx, *policy, false, msg)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,8 +2,23 @@ package validatingadmissionpolicygenerate
|
|||
|
||||
import (
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
// hasExceptions checks if there is an exception that match both the policy and the rule.
|
||||
func (c *controller) hasExceptions(policyName, rule string) (bool, error) {
|
||||
polexs, err := c.polexLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, polex := range polexs {
|
||||
if polex.Contains(policyName, rule) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func checkResources(resource kyvernov1.ResourceDescription) (bool, string) {
|
||||
var msg string
|
||||
if len(resource.Namespaces) != 0 || len(resource.Annotations) != 0 {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: cpol-with-exceptions
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- apply:
|
||||
file: exception.yaml
|
||||
- name: step-03
|
||||
try:
|
||||
- sleep:
|
||||
duration: 15s
|
||||
- name: step-04
|
||||
try:
|
||||
- error:
|
||||
file: validatingadmissionpolicy.yaml
|
||||
- error:
|
||||
file: validatingadmissionpolicybinding.yaml
|
|
@ -0,0 +1,16 @@
|
|||
apiVersion: kyverno.io/v2beta1
|
||||
kind: PolicyException
|
||||
metadata:
|
||||
name: policy-exception
|
||||
spec:
|
||||
exceptions:
|
||||
- policyName: disallow-host-path-t11
|
||||
ruleNames:
|
||||
- host-path
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
names:
|
||||
- important-tool*
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t11
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-path-t11
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: false
|
||||
rules:
|
||||
- name: host-path
|
||||
match:
|
||||
all:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
- StatefulSet
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
selector:
|
||||
matchLabels:
|
||||
app: critical
|
||||
validate:
|
||||
cel:
|
||||
expressions:
|
||||
- expression: "!has(object.spec.template.spec.volumes) || object.spec.template.spec.volumes.all(volume, !has(volume.hostPath))"
|
||||
message: "HostPath volumes are forbidden. The field spec.template.spec.volumes[*].hostPath must be unset."
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t11
|
||||
spec: {}
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicyBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
name: disallow-host-path-t11-binding
|
||||
spec: {}
|
Loading…
Add table
Reference in a new issue