1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00
kyverno/pkg/config/config.go
Vyom Yadav c01b5cc381
fix: Don't check for subresource existence when it is the trigger. (#6544)
Signed-off-by: Vyom-Yadav <jackhammervyom@gmail.com>
Co-authored-by: shuting <shuting@nirmata.com>
2023-03-29 15:54:42 +00:00

373 lines
13 KiB
Go

package config
import (
"context"
"strconv"
"sync"
valid "github.com/asaskevich/govalidator"
osutils "github.com/kyverno/kyverno/pkg/utils/os"
"github.com/kyverno/kyverno/pkg/utils/wildcard"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
// These constants MUST be equal to the corresponding names in service definition in definitions/install.yaml
// webhook configuration names
const (
// PolicyValidatingWebhookConfigurationName default policy validating webhook configuration name
PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg"
// ValidatingWebhookConfigurationName ...
ValidatingWebhookConfigurationName = "kyverno-resource-validating-webhook-cfg"
// ExceptionValidatingWebhookConfigurationName ...
ExceptionValidatingWebhookConfigurationName = "kyverno-exception-validating-webhook-cfg"
// CleanupValidatingWebhookConfigurationName ...
CleanupValidatingWebhookConfigurationName = "kyverno-cleanup-validating-webhook-cfg"
// PolicyMutatingWebhookConfigurationName default policy mutating webhook configuration name
PolicyMutatingWebhookConfigurationName = "kyverno-policy-mutating-webhook-cfg"
// MutatingWebhookConfigurationName default resource mutating webhook configuration name
MutatingWebhookConfigurationName = "kyverno-resource-mutating-webhook-cfg"
// VerifyMutatingWebhookConfigurationName default verify mutating webhook configuration name
VerifyMutatingWebhookConfigurationName = "kyverno-verify-mutating-webhook-cfg"
)
// webhook names
const (
// PolicyValidatingWebhookName default policy validating webhook name
PolicyValidatingWebhookName = "validate-policy.kyverno.svc"
// ValidatingWebhookName ...
ValidatingWebhookName = "validate.kyverno.svc"
// PolicyMutatingWebhookName default policy mutating webhook name
PolicyMutatingWebhookName = "mutate-policy.kyverno.svc"
// MutatingWebhookName default resource mutating webhook name
MutatingWebhookName = "mutate.kyverno.svc"
// VerifyMutatingWebhookName default verify mutating webhook name
VerifyMutatingWebhookName = "monitor-webhooks.kyverno.svc"
)
// paths
const (
// PolicyValidatingWebhookServicePath is the path for policy validation webhook(used to validate policy resource)
PolicyValidatingWebhookServicePath = "/policyvalidate"
// ValidatingWebhookServicePath is the path for validation webhook
ValidatingWebhookServicePath = "/validate"
// ExceptionValidatingWebhookServicePath is the path for policy exception validation webhook(used to validate policy exception resource)
ExceptionValidatingWebhookServicePath = "/exceptionvalidate"
// CleanupValidatingWebhookServicePath is the path for cleanup policy validation webhook(used to validate cleanup policy resource)
CleanupValidatingWebhookServicePath = "/validate"
// PolicyMutatingWebhookServicePath is the path for policy mutation webhook(used to default)
PolicyMutatingWebhookServicePath = "/policymutate"
// MutatingWebhookServicePath is the path for mutation webhook
MutatingWebhookServicePath = "/mutate"
// VerifyMutatingWebhookServicePath is the path for verify webhook(used to veryfing if admission control is enabled and active)
VerifyMutatingWebhookServicePath = "/verifymutate"
// LivenessServicePath is the path for check liveness health
LivenessServicePath = "/health/liveness"
// ReadinessServicePath is the path for check readness health
ReadinessServicePath = "/health/readiness"
// MetricsPath is the path for exposing metrics
MetricsPath = "/metrics"
)
const (
// Due to kubernetes issue, we must use next literal constants instead of deployment TypeMeta fields
// Issue: https://github.com/kubernetes/kubernetes/pull/63972
// When the issue is closed, we should use TypeMeta struct instead of this constants
// ClusterRoleAPIVersion define the default clusterrole resource apiVersion
ClusterRoleAPIVersion = "rbac.authorization.k8s.io/v1"
// ClusterRoleKind define the default clusterrole resource kind
ClusterRoleKind = "ClusterRole"
)
var (
// kyvernoNamespace is the Kyverno namespace
kyvernoNamespace = osutils.GetEnvWithFallback("KYVERNO_NAMESPACE", "kyverno")
// kyvernoServiceAccountName is the Kyverno service account name
kyvernoServiceAccountName = osutils.GetEnvWithFallback("KYVERNO_SERVICEACCOUNT_NAME", "kyverno")
// kyvernoDeploymentName is the Kyverno deployment name
kyvernoDeploymentName = osutils.GetEnvWithFallback("KYVERNO_DEPLOYMENT", "kyverno")
// kyvernoServiceName is the Kyverno service name
kyvernoServiceName = osutils.GetEnvWithFallback("KYVERNO_SVC", "kyverno-svc")
// kyvernoPodName is the Kyverno pod name
kyvernoPodName = osutils.GetEnvWithFallback("KYVERNO_POD_NAME", "kyverno")
// kyvernoConfigMapName is the Kyverno configmap name
kyvernoConfigMapName = osutils.GetEnvWithFallback("INIT_CONFIG", "kyverno")
// defaultExcludedUsernames are the usernames excluded by default when matching an incoming admission request
defaultExcludedUsernames []string
// defaultExcludedGroups are the groups excluded by default when matching an incoming admission request
defaultExcludedGroups []string = []string{"system:serviceaccounts:kube-system", "system:nodes"}
// kyvernoDryRunNamespace is the namespace for DryRun option of YAML verification
kyvernoDryrunNamespace = osutils.GetEnvWithFallback("KYVERNO_DRYRUN_NAMESPACE", "kyverno-dryrun")
)
func KyvernoNamespace() string {
return kyvernoNamespace
}
func KyvernoDryRunNamespace() string {
return kyvernoDryrunNamespace
}
func KyvernoServiceAccountName() string {
return kyvernoServiceAccountName
}
func KyvernoDeploymentName() string {
return kyvernoDeploymentName
}
func KyvernoServiceName() string {
return kyvernoServiceName
}
func KyvernoPodName() string {
return kyvernoPodName
}
func KyvernoConfigMapName() string {
return kyvernoConfigMapName
}
// Configuration to be used by consumer to check filters
type Configuration interface {
// GetDefaultRegistry return default image registry
GetDefaultRegistry() string
// GetEnableDefaultRegistryMutation return if should mutate image registry
GetEnableDefaultRegistryMutation() bool
// ToFilter checks if the given resource is set to be filtered in the configuration
ToFilter(kind, namespace, name string) bool
// GetExcludedGroups return exclude groups
GetExcludedGroups() []string
// GetExcludedUsernames return exclude usernames
GetExcludedUsernames() []string
// GetExcludedBackgroundUsernames return exclude usernames for mutateExisting and generate policies
GetExcludedBackgroundUsernames() []string
// GetGenerateSuccessEvents return if should generate success events
GetGenerateSuccessEvents() bool
// GetWebhooks returns the webhook configs
GetWebhooks() []WebhookConfig
// GetWebhookAnnotations returns annotations to set on webhook configs
GetWebhookAnnotations() map[string]string
// Load loads configuration from a configmap
Load(cm *corev1.ConfigMap)
}
// configuration stores the configuration
type configuration struct {
defaultRegistry string
enableDefaultRegistryMutation bool
excludedGroups []string
excludedUsernames []string
excludeBackgroundUsernames []string
filters []filter
generateSuccessEvents bool
webhooks []WebhookConfig
webhookAnnotations map[string]string
mux sync.RWMutex
}
// NewDefaultConfiguration ...
func NewDefaultConfiguration() *configuration {
return &configuration{
defaultRegistry: "docker.io",
enableDefaultRegistryMutation: true,
excludedGroups: defaultExcludedGroups,
excludedUsernames: defaultExcludedUsernames,
}
}
// NewConfiguration ...
func NewConfiguration(client kubernetes.Interface) (Configuration, error) {
cd := NewDefaultConfiguration()
if cm, err := client.CoreV1().ConfigMaps(kyvernoNamespace).Get(context.TODO(), kyvernoConfigMapName, metav1.GetOptions{}); err != nil {
if !errors.IsNotFound(err) {
return nil, err
}
} else {
cd.load(cm)
}
return cd, nil
}
func (cd *configuration) ToFilter(kind, namespace, name string) bool {
cd.mux.RLock()
defer cd.mux.RUnlock()
for _, f := range cd.filters {
if wildcard.Match(f.Kind, kind) && wildcard.Match(f.Namespace, namespace) && wildcard.Match(f.Name, name) {
return true
}
if kind == "Namespace" {
// [Namespace,kube-system,*] || [*,kube-system,*]
if (f.Kind == "Namespace" || f.Kind == "*") && wildcard.Match(f.Namespace, name) {
return true
}
}
}
return false
}
func (cd *configuration) GetDefaultRegistry() string {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.defaultRegistry
}
func (cd *configuration) GetEnableDefaultRegistryMutation() bool {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.enableDefaultRegistryMutation
}
func (cd *configuration) GetExcludedUsernames() []string {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.excludedUsernames
}
func (cd *configuration) GetExcludedBackgroundUsernames() []string {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.excludeBackgroundUsernames
}
func (cd *configuration) GetExcludedGroups() []string {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.excludedGroups
}
func (cd *configuration) GetGenerateSuccessEvents() bool {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.generateSuccessEvents
}
func (cd *configuration) GetWebhooks() []WebhookConfig {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.webhooks
}
func (cd *configuration) GetWebhookAnnotations() map[string]string {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.webhookAnnotations
}
func (cd *configuration) Load(cm *corev1.ConfigMap) {
if cm != nil {
cd.load(cm)
} else {
cd.unload()
}
}
func (cd *configuration) load(cm *corev1.ConfigMap) {
logger := logger.WithValues("name", cm.Name, "namespace", cm.Namespace)
if cm.Data == nil {
return
}
cd.mux.Lock()
defer cd.mux.Unlock()
// reset
cd.filters = []filter{}
cd.excludedUsernames = []string{}
cd.excludedGroups = []string{}
cd.generateSuccessEvents = false
cd.webhooks = nil
cd.excludedGroups = append(cd.excludedGroups, defaultExcludedGroups...)
cd.excludedUsernames = append(cd.excludedUsernames, defaultExcludedUsernames...)
// load filters
cd.filters = parseKinds(cm.Data["resourceFilters"])
newDefaultRegistry, ok := cm.Data["defaultRegistry"]
if !ok {
logger.V(6).Info("configuration: No defaultRegistry defined in ConfigMap")
} else {
if valid.IsDNSName(newDefaultRegistry) {
logger.V(4).Info("Updated defaultRegistry config parameter.", "oldDefaultRegistry", cd.defaultRegistry, "newDefaultRegistry", newDefaultRegistry)
cd.defaultRegistry = newDefaultRegistry
} else {
logger.V(4).Info("defaultRegistry didn't change because the provided config value isn't a valid DNS hostname")
}
}
enableDefaultRegistryMutation, ok := cm.Data["enableDefaultRegistryMutation"]
if !ok {
logger.V(6).Info("configuration: No enableDefaultRegistryMutation defined in ConfigMap")
} else {
newEnableDefaultRegistryMutation, err := strconv.ParseBool(enableDefaultRegistryMutation)
if err != nil {
logger.V(4).Info("configuration: Invalid value for enableDefaultRegistryMutation defined in ConfigMap. enableDefaultRegistryMutation didn't change")
}
logger.V(4).Info("Updated enableDefaultRegistryMutation config parameter", "oldEnableDefaultRegistryMutation", cd.enableDefaultRegistryMutation, "newEnableDefaultRegistryMutation", newEnableDefaultRegistryMutation)
cd.enableDefaultRegistryMutation = newEnableDefaultRegistryMutation
}
// load excludeGroupRole
excludedGroups, ok := cm.Data["excludeGroupRole"]
if !ok {
logger.V(6).Info("configuration: No excludeGroupRole defined in ConfigMap")
} else {
cd.excludedGroups = parseRbac(excludedGroups)
}
// load excludeUsername
excludedUsernames, ok := cm.Data["excludeUsername"]
if !ok {
logger.V(6).Info("configuration: No excludeUsername defined in ConfigMap")
} else {
cd.excludedUsernames = parseRbac(excludedUsernames)
}
// load excludeBackgroundUsernames
excludeBackgroundUsernames, ok := cm.Data["excludeBackgroundUsernames"]
if !ok {
logger.V(6).Info("configuration: No excludeBackgroundUsernames defined in ConfigMap")
} else {
cd.excludeBackgroundUsernames = parseRbac(excludeBackgroundUsernames)
}
// load generateSuccessEvents
generateSuccessEvents, ok := cm.Data["generateSuccessEvents"]
if ok {
generateSuccessEvents, err := strconv.ParseBool(generateSuccessEvents)
if err != nil {
logger.Error(err, "failed to parse generateSuccessEvents")
} else {
cd.generateSuccessEvents = generateSuccessEvents
}
}
// load webhooks
webhooks, ok := cm.Data["webhooks"]
if ok {
webhooks, err := parseWebhooks(webhooks)
if err != nil {
logger.Error(err, "failed to parse webhooks")
} else {
cd.webhooks = webhooks
}
}
// load webhook annotations
webhookAnnotations, ok := cm.Data["webhookAnnotations"]
if ok {
webhookAnnotations, err := parseWebhookAnnotations(webhookAnnotations)
if err != nil {
logger.Error(err, "failed to parse webhook annotations")
} else {
cd.webhookAnnotations = webhookAnnotations
}
}
}
func (cd *configuration) unload() {
cd.mux.Lock()
defer cd.mux.Unlock()
cd.filters = []filter{}
cd.defaultRegistry = "docker.io"
cd.enableDefaultRegistryMutation = true
cd.excludedUsernames = []string{}
cd.excludedGroups = []string{}
cd.generateSuccessEvents = false
cd.webhooks = nil
cd.webhookAnnotations = nil
cd.excludedGroups = append(cd.excludedGroups, defaultExcludedGroups...)
cd.excludedUsernames = append(cd.excludedUsernames, defaultExcludedUsernames...)
}