mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
* fix: display a message when the controller has no permissions for VAPs * fix: add a warning when a Kyverno policy is created --------- Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> Co-authored-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
parent
ef90f0b07a
commit
3de7c54a86
7 changed files with 109 additions and 24 deletions
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/kyverno/kyverno/cmd/internal"
|
||||
"github.com/kyverno/kyverno/pkg/auth/checker"
|
||||
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
|
@ -181,6 +182,7 @@ func createrLeaderControllers(
|
|||
leaderControllers = append(leaderControllers, internal.NewController(exceptionWebhookControllerName, exceptionWebhookController, 1))
|
||||
|
||||
if generateVAPs {
|
||||
checker := checker.NewSelfChecker(kubeClient.AuthorizationV1().SelfSubjectAccessReviews())
|
||||
vapController := vapcontroller.NewController(
|
||||
kubeClient,
|
||||
kyvernoClient,
|
||||
|
@ -190,6 +192,7 @@ func createrLeaderControllers(
|
|||
kubeInformer.Admissionregistration().V1alpha1().ValidatingAdmissionPolicies(),
|
||||
kubeInformer.Admissionregistration().V1alpha1().ValidatingAdmissionPolicyBindings(),
|
||||
eventGenerator,
|
||||
checker,
|
||||
)
|
||||
leaderControllers = append(leaderControllers, internal.NewController(vapcontroller.ControllerName, vapController, vapcontroller.Workers))
|
||||
}
|
||||
|
@ -200,17 +203,16 @@ func main() {
|
|||
var (
|
||||
// TODO: this has been added to backward support command line arguments
|
||||
// will be removed in future and the configuration will be set only via configmaps
|
||||
serverIP string
|
||||
webhookTimeout int
|
||||
maxQueuedEvents int
|
||||
omitEvents string
|
||||
autoUpdateWebhooks bool
|
||||
webhookRegistrationTimeout time.Duration
|
||||
admissionReports bool
|
||||
dumpPayload bool
|
||||
servicePort int
|
||||
backgroundServiceAccountName string
|
||||
generateValidatingAdmissionPolicy bool
|
||||
serverIP string
|
||||
webhookTimeout int
|
||||
maxQueuedEvents int
|
||||
omitEvents string
|
||||
autoUpdateWebhooks bool
|
||||
webhookRegistrationTimeout time.Duration
|
||||
admissionReports bool
|
||||
dumpPayload bool
|
||||
servicePort int
|
||||
backgroundServiceAccountName string
|
||||
)
|
||||
flagset := flag.NewFlagSet("kyverno", flag.ExitOnError)
|
||||
flagset.BoolVar(&dumpPayload, "dumpPayload", false, "Set this flag to activate/deactivate debug mode.")
|
||||
|
@ -222,8 +224,8 @@ func main() {
|
|||
flagset.DurationVar(&webhookRegistrationTimeout, "webhookRegistrationTimeout", 120*time.Second, "Timeout for webhook registration, e.g., 30s, 1m, 5m.")
|
||||
flagset.Func(toggle.ProtectManagedResourcesFlagName, toggle.ProtectManagedResourcesDescription, toggle.ProtectManagedResources.Parse)
|
||||
flagset.Func(toggle.ForceFailurePolicyIgnoreFlagName, toggle.ForceFailurePolicyIgnoreDescription, toggle.ForceFailurePolicyIgnore.Parse)
|
||||
flagset.Func(toggle.GenerateValidatingAdmissionPolicyFlagName, toggle.GenerateValidatingAdmissionPolicyDescription, toggle.GenerateValidatingAdmissionPolicy.Parse)
|
||||
flagset.BoolVar(&admissionReports, "admissionReports", true, "Enable or disable admission reports.")
|
||||
flagset.BoolVar(&generateValidatingAdmissionPolicy, "generateValidatingAdmissionPolicy", false, "Set this flag 'true' to generate validating admission policies.")
|
||||
flagset.IntVar(&servicePort, "servicePort", 443, "Port used by the Kyverno Service resource and for webhook configurations.")
|
||||
flagset.StringVar(&backgroundServiceAccountName, "backgroundServiceAccountName", "", "Background service account name.")
|
||||
flagset.StringVar(&caSecretName, "caSecretName", "", "Name of the secret containing CA.")
|
||||
|
@ -261,6 +263,7 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
// check if validating admission policies are registered in the API server
|
||||
generateValidatingAdmissionPolicy := toggle.FromContext(context.TODO()).GenerateValidatingAdmissionPolicy()
|
||||
if generateValidatingAdmissionPolicy {
|
||||
groupVersion := schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1alpha1"}
|
||||
if _, err := setup.KyvernoDynamicClient.GetKubeClient().Discovery().ServerResourcesForGroupVersion(groupVersion.String()); err != nil {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/go-logr/logr"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"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"
|
||||
kyvernov1listers "github.com/kyverno/kyverno/pkg/client/listers/kyverno/v1"
|
||||
|
@ -16,6 +17,7 @@ import (
|
|||
controllerutils "github.com/kyverno/kyverno/pkg/utils/controller"
|
||||
datautils "github.com/kyverno/kyverno/pkg/utils/data"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
"github.com/kyverno/kyverno/pkg/utils/validatingadmissionpolicy"
|
||||
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -48,6 +50,7 @@ type controller struct {
|
|||
queue workqueue.RateLimitingInterface
|
||||
|
||||
eventGen event.Interface
|
||||
checker checker.AuthChecker
|
||||
}
|
||||
|
||||
func NewController(
|
||||
|
@ -59,6 +62,7 @@ func NewController(
|
|||
vapInformer admissionregistrationv1alpha1informers.ValidatingAdmissionPolicyInformer,
|
||||
vapbindingInformer admissionregistrationv1alpha1informers.ValidatingAdmissionPolicyBindingInformer,
|
||||
eventGen event.Interface,
|
||||
checker checker.AuthChecker,
|
||||
) controllers.Controller {
|
||||
queue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), ControllerName)
|
||||
c := &controller{
|
||||
|
@ -70,6 +74,7 @@ func NewController(
|
|||
vapbindingLister: vapbindingInformer.Lister(),
|
||||
queue: queue,
|
||||
eventGen: eventGen,
|
||||
checker: checker,
|
||||
}
|
||||
|
||||
// Set up an event handler for when Kyverno policies change
|
||||
|
@ -307,6 +312,20 @@ func (c *controller) reconcile(ctx context.Context, logger logr.Logger, key, nam
|
|||
return nil
|
||||
}
|
||||
|
||||
// check if the controller has the required permissions to generate validating admission policies.
|
||||
if !validatingadmissionpolicy.HasValidatingAdmissionPolicyPermission(c.checker) {
|
||||
logger.Info("doesn't have required permissions for generating ValidatingAdmissionPolicies")
|
||||
c.updateClusterPolicyStatus(ctx, *policy, false, "doesn't have required permissions for generating ValidatingAdmissionPolicies")
|
||||
return nil
|
||||
}
|
||||
|
||||
// check if the controller has the required permissions to generate validating admission policy bindings.
|
||||
if !validatingadmissionpolicy.HasValidatingAdmissionPolicyBindingPermission(c.checker) {
|
||||
logger.Info("doesn't have required permissions for generating ValidatingAdmissionPolicyBindings")
|
||||
c.updateClusterPolicyStatus(ctx, *policy, false, "doesn't have required permissions for generating ValidatingAdmissionPolicyBindings")
|
||||
return nil
|
||||
}
|
||||
|
||||
if ok, msg := canGenerateVAP(spec); !ok {
|
||||
c.updateClusterPolicyStatus(ctx, *policy, false, msg)
|
||||
return nil
|
||||
|
|
|
@ -10,6 +10,7 @@ type Toggles interface {
|
|||
ProtectManagedResources() bool
|
||||
ForceFailurePolicyIgnore() bool
|
||||
EnableDeferredLoading() bool
|
||||
GenerateValidatingAdmissionPolicy() bool
|
||||
}
|
||||
|
||||
type defaultToggles struct{}
|
||||
|
@ -26,6 +27,10 @@ func (defaultToggles) EnableDeferredLoading() bool {
|
|||
return EnableDeferredLoading.enabled()
|
||||
}
|
||||
|
||||
func (defaultToggles) GenerateValidatingAdmissionPolicy() bool {
|
||||
return GenerateValidatingAdmissionPolicy.enabled()
|
||||
}
|
||||
|
||||
type contextKey struct{}
|
||||
|
||||
func NewContext(ctx context.Context, toggles Toggles) context.Context {
|
||||
|
|
|
@ -21,12 +21,18 @@ const (
|
|||
EnableDeferredLoadingDescription = "enable deferred loading of context variables"
|
||||
enableDeferredLoadingEnvVar = "FLAG_ENABLE_DEFERRED_LOADING"
|
||||
defaultEnableDeferredLoading = true
|
||||
// generate validating admission policies
|
||||
GenerateValidatingAdmissionPolicyFlagName = "generateValidatingAdmissionPolicy"
|
||||
GenerateValidatingAdmissionPolicyDescription = "Set the flag to 'true', to generate validating admission policies."
|
||||
generateValidatingAdmissionPolicyEnvVar = "FLAG_GENERATE_VALIDATING_ADMISSION_POLICY"
|
||||
defaultGenerateValidatingAdmissionPolicy = false
|
||||
)
|
||||
|
||||
var (
|
||||
ProtectManagedResources = newToggle(defaultProtectManagedResources, protectManagedResourcesEnvVar)
|
||||
ForceFailurePolicyIgnore = newToggle(defaultForceFailurePolicyIgnore, forceFailurePolicyIgnoreEnvVar)
|
||||
EnableDeferredLoading = newToggle(defaultEnableDeferredLoading, enableDeferredLoadingEnvVar)
|
||||
ProtectManagedResources = newToggle(defaultProtectManagedResources, protectManagedResourcesEnvVar)
|
||||
ForceFailurePolicyIgnore = newToggle(defaultForceFailurePolicyIgnore, forceFailurePolicyIgnoreEnvVar)
|
||||
EnableDeferredLoading = newToggle(defaultEnableDeferredLoading, enableDeferredLoadingEnvVar)
|
||||
GenerateValidatingAdmissionPolicy = newToggle(defaultGenerateValidatingAdmissionPolicy, generateValidatingAdmissionPolicyEnvVar)
|
||||
)
|
||||
|
||||
type ToggleFlag interface {
|
||||
|
|
30
pkg/utils/validatingadmissionpolicy/permissions.go
Normal file
30
pkg/utils/validatingadmissionpolicy/permissions.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package validatingadmissionpolicy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/auth/checker"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func hasPermissions(resource schema.GroupVersionResource, s checker.AuthChecker) bool {
|
||||
can, err := checker.Check(context.TODO(), s, resource.Group, resource.Version, resource.Resource, "", "", "create", "update", "list", "delete")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return can
|
||||
}
|
||||
|
||||
// HasValidatingAdmissionPolicyPermission check if the admission controller has the required permissions to generate
|
||||
// Kubernetes ValidatingAdmissionPolicy
|
||||
func HasValidatingAdmissionPolicyPermission(s checker.AuthChecker) bool {
|
||||
gvr := schema.GroupVersionResource{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Resource: "validatingadmissionpolicies"}
|
||||
return hasPermissions(gvr, s)
|
||||
}
|
||||
|
||||
// HasValidatingAdmissionPolicyBindingPermission check if the admission controller has the required permissions to generate
|
||||
// Kubernetes ValidatingAdmissionPolicyBinding
|
||||
func HasValidatingAdmissionPolicyBindingPermission(s checker.AuthChecker) bool {
|
||||
gvr := schema.GroupVersionResource{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Resource: "validatingadmissionpolicybindings"}
|
||||
return hasPermissions(gvr, s)
|
||||
}
|
|
@ -6,11 +6,14 @@ import (
|
|||
"slices"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
authChecker "github.com/kyverno/kyverno/pkg/auth/checker"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/logging"
|
||||
"github.com/kyverno/kyverno/pkg/policy/generate"
|
||||
"github.com/kyverno/kyverno/pkg/policy/mutate"
|
||||
"github.com/kyverno/kyverno/pkg/policy/validate"
|
||||
"github.com/kyverno/kyverno/pkg/toggle"
|
||||
"github.com/kyverno/kyverno/pkg/utils/validatingadmissionpolicy"
|
||||
)
|
||||
|
||||
// Validation provides methods to validate a rule
|
||||
|
@ -22,9 +25,9 @@ type Validation interface {
|
|||
// - Mutate
|
||||
// - Validation
|
||||
// - Generate
|
||||
func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mock bool, username string) error {
|
||||
func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mock bool, username string) (string, error) {
|
||||
if rule == nil {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
var checker Validation
|
||||
|
@ -32,7 +35,7 @@ func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mo
|
|||
if rule.HasMutate() {
|
||||
checker = mutate.NewMutateFactory(rule.Mutation, client, username)
|
||||
if path, err := checker.Validate(context.TODO()); err != nil {
|
||||
return fmt.Errorf("path: spec.rules[%d].mutate.%s.: %v", idx, path, err)
|
||||
return "", fmt.Errorf("path: spec.rules[%d].mutate.%s.: %v", idx, path, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +43,21 @@ func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mo
|
|||
if rule.HasValidate() {
|
||||
checker = validate.NewValidateFactory(&rule.Validation)
|
||||
if path, err := checker.Validate(context.TODO()); err != nil {
|
||||
return fmt.Errorf("path: spec.rules[%d].validate.%s.: %v", idx, path, err)
|
||||
return "", fmt.Errorf("path: spec.rules[%d].validate.%s.: %v", idx, path, err)
|
||||
}
|
||||
|
||||
// In case generateValidatingAdmissionPolicy flag is set to true, check the required permissions.
|
||||
if toggle.FromContext(context.TODO()).GenerateValidatingAdmissionPolicy() {
|
||||
authCheck := authChecker.NewSelfChecker(client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews())
|
||||
// check if the controller has the required permissions to generate validating admission policies.
|
||||
if !validatingadmissionpolicy.HasValidatingAdmissionPolicyPermission(authCheck) {
|
||||
return "doesn't have required permissions for generating ValidatingAdmissionPolicies", nil
|
||||
}
|
||||
|
||||
// check if the controller has the required permissions to generate validating admission policy bindings.
|
||||
if !validatingadmissionpolicy.HasValidatingAdmissionPolicyBindingPermission(authCheck) {
|
||||
return "doesn't have required permissions for generating ValidatingAdmissionPolicyBindings", nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,19 +69,19 @@ func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mo
|
|||
if mock {
|
||||
checker = generate.NewFakeGenerate(rule.Generation)
|
||||
if path, err := checker.Validate(context.TODO()); err != nil {
|
||||
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
|
||||
return "", fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
|
||||
}
|
||||
} else {
|
||||
checker = generate.NewGenerateFactory(client, rule.Generation, username, logging.GlobalLogger())
|
||||
if path, err := checker.Validate(context.TODO()); err != nil {
|
||||
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
|
||||
return "", fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
|
||||
}
|
||||
}
|
||||
|
||||
if slices.Contains(rule.MatchResources.Kinds, rule.Generation.Kind) {
|
||||
return fmt.Errorf("generation kind and match resource kind should not be the same")
|
||||
return "", fmt.Errorf("generation kind and match resource kind should not be the same")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -299,8 +299,13 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
|
|||
}
|
||||
}
|
||||
|
||||
if err := validateActions(i, &rules[i], client, mock, username); err != nil {
|
||||
msg, err := validateActions(i, &rules[i], client, mock, username)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
} else {
|
||||
if len(msg) != 0 {
|
||||
warnings = append(warnings, msg)
|
||||
}
|
||||
}
|
||||
|
||||
if rule.HasVerifyImages() {
|
||||
|
|
Loading…
Add table
Reference in a new issue