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:
parent
c40dab9ea0
commit
9ed14cb779
28 changed files with 585 additions and 38 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-3
|
||||
namespace: staging-ns-2
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: staging-ns-2
|
||||
labels:
|
||||
environment: staging-ns-2
|
|
@ -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
|
|
@ -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
|
|
@ -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`.
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-4
|
||||
namespace: staging-ns-1
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: staging-ns-1
|
||||
labels:
|
||||
environment: staging-ns-1
|
|
@ -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
|
|
@ -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
|
Loading…
Add table
Reference in a new issue