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

fix: cleanup policy validation (#5514)

* fix: cleanup policy validation

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>
Co-authored-by: Vyankatesh Kudtarkar <vyankateshkd@gmail.com>
This commit is contained in:
Charles-Edouard Brétéché 2022-12-01 09:02:21 +01:00 committed by GitHub
parent 1bf4455555
commit db9faf5835
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 100 deletions

View file

@ -5,9 +5,9 @@ import (
"time"
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/cmd/cleanup-controller/validate"
"github.com/kyverno/kyverno/pkg/clients/dclient"
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
validation "github.com/kyverno/kyverno/pkg/validation/cleanuppolicy"
admissionv1 "k8s.io/api/admission/v1"
)
@ -27,9 +27,9 @@ func (h *cleanupPolicyHandlers) Validate(ctx context.Context, logger logr.Logger
logger.Error(err, "failed to unmarshal policies from admission request")
return admissionutils.Response(request.UID, err)
}
err = validate.ValidateCleanupPolicy(logger, policy, h.client, false)
if err != nil {
if err := validation.Validate(ctx, logger, h.client, policy); err != nil {
logger.Error(err, "policy validation errors")
return admissionutils.Response(request.UID, err)
}
return admissionutils.Response(request.UID, err)
return nil
}

View file

@ -1,96 +0,0 @@
package validate
import (
"fmt"
"github.com/go-logr/logr"
kyvernov1alpha1 "github.com/kyverno/kyverno/api/kyverno/v1alpha1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/policy/generate"
"golang.org/x/net/context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/discovery"
)
// Cleanup provides implementation to validate permission for using DELETE operation by CleanupPolicy
type Cleanup struct {
// rule to hold CleanupPolicy specifications
spec kyvernov1alpha1.CleanupPolicySpec
// authCheck to check access for operations
authCheck generate.Operations
// logger
log logr.Logger
}
// NewCleanup returns a new instance of Cleanup validation checker
func NewCleanup(client dclient.Interface, cleanup kyvernov1alpha1.CleanupPolicySpec, log logr.Logger) *Cleanup {
c := Cleanup{
spec: cleanup,
authCheck: generate.NewAuth(client, log),
log: log,
}
return &c
}
// canIDelete returns a error if kyverno cannot perform operations
func (c *Cleanup) CanIDelete(ctx context.Context, kind, namespace string) error {
// Skip if there is variable defined
authCheck := c.authCheck
if !variables.IsVariable(kind) && !variables.IsVariable(namespace) {
// DELETE
ok, err := authCheck.CanIDelete(ctx, kind, namespace)
if err != nil {
// machinery error
return err
}
if !ok {
return fmt.Errorf("kyverno does not have permissions to 'delete' resource %s/%s. Update permissions in ClusterRole", kind, namespace)
}
} else {
c.log.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.")
}
return nil
}
// Validate checks the policy and rules declarations for required configurations
func ValidateCleanupPolicy(logger logr.Logger, cleanuppolicy kyvernov1alpha1.CleanupPolicyInterface, client dclient.Interface, mock bool) error {
// namespace := cleanuppolicy.GetNamespace()
var res []*metav1.APIResourceList
clusterResources := sets.NewString()
// Get all the cluster type kind supported by cluster
res, err := discovery.ServerPreferredResources(client.Discovery().DiscoveryInterface())
if err != nil {
if discovery.IsGroupDiscoveryFailedError(err) {
err := err.(*discovery.ErrGroupDiscoveryFailed)
for gv, err := range err.Groups {
logger.Error(err, "failed to list api resources", "group", gv)
}
} else {
return err
}
}
for _, resList := range res {
for _, r := range resList.APIResources {
if !r.Namespaced {
clusterResources.Insert(r.Kind)
}
}
}
if errs := cleanuppolicy.Validate(clusterResources); len(errs) != 0 {
return errs.ToAggregate()
}
// for kind := range clusterResources {
// checker := NewCleanup(client, *cleanuppolicy.GetSpec(), logging.GlobalLogger())
// if err := checker.CanIDelete(kind, namespace); err != nil {
// return fmt.Errorf("cannot delete kind %s in namespace %s", kind, namespace)
// }
// }
return nil
}

View file

@ -0,0 +1,76 @@
package cleanuppolicy
import (
"context"
"fmt"
"github.com/go-logr/logr"
kyvernov1alpha1 "github.com/kyverno/kyverno/api/kyverno/v1alpha1"
"github.com/kyverno/kyverno/pkg/auth"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/discovery"
)
// FetchClusteredResources retieves the list of clustered resources
func FetchClusteredResources(logger logr.Logger, client dclient.Interface) (sets.String, error) {
res, err := discovery.ServerPreferredResources(client.Discovery().DiscoveryInterface())
if err != nil {
if discovery.IsGroupDiscoveryFailedError(err) {
err := err.(*discovery.ErrGroupDiscoveryFailed)
for gv, err := range err.Groups {
logger.Error(err, "failed to list api resources", "group", gv)
}
} else {
return nil, err
}
}
clusterResources := sets.NewString()
for _, resList := range res {
for _, r := range resList.APIResources {
if !r.Namespaced {
clusterResources.Insert(r.Kind)
}
}
}
return clusterResources, nil
}
// Validate checks policy is valid
func Validate(ctx context.Context, logger logr.Logger, client dclient.Interface, policy kyvernov1alpha1.CleanupPolicyInterface) error {
clusteredResources, err := FetchClusteredResources(logger, client)
if err != nil {
return err
}
if err := validatePolicy(clusteredResources, policy); err != nil {
return err
}
if err := validateAuth(ctx, client, policy); err != nil {
return err
}
return nil
}
// validatePolicy checks the policy and rules declarations for required configurations
func validatePolicy(clusterResources sets.String, policy kyvernov1alpha1.CleanupPolicyInterface) error {
errs := policy.Validate(clusterResources)
return errs.ToAggregate()
}
// validateAuth checks the the delete action is allowed
func validateAuth(ctx context.Context, client dclient.Interface, policy kyvernov1alpha1.CleanupPolicyInterface) error {
namespace := policy.GetNamespace()
spec := policy.GetSpec()
kinds := sets.NewString(spec.MatchResources.GetKinds()...)
for kind := range kinds {
checker := auth.NewCanI(client, kind, namespace, "delete")
allowed, err := checker.RunAccessCheck(ctx)
if err != nil {
return err
}
if !allowed {
return fmt.Errorf("cleanup controller has no permission to delete kind %s", kind)
}
}
return nil
}