1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-15 17:51:20 +00:00
kyverno/pkg/config/config.go

332 lines
13 KiB
Go
Raw Normal View History

package config
import (
"context"
"reflect"
"strconv"
"sync"
wildcard "github.com/kyverno/go-wildcard"
osutils "github.com/kyverno/kyverno/pkg/utils/os"
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
const (
// MutatingWebhookConfigurationName default resource mutating webhook configuration name
MutatingWebhookConfigurationName = "kyverno-resource-mutating-webhook-cfg"
// MutatingWebhookConfigurationDebugName default resource mutating webhook configuration name for debug mode
2019-09-04 20:43:12 +00:00
MutatingWebhookConfigurationDebugName = "kyverno-resource-mutating-webhook-cfg-debug"
// MutatingWebhookName default resource mutating webhook name
MutatingWebhookName = "mutate.kyverno.svc"
// ValidatingWebhookConfigurationName ...
ValidatingWebhookConfigurationName = "kyverno-resource-validating-webhook-cfg"
// ValidatingWebhookConfigurationDebugName ...
ValidatingWebhookConfigurationDebugName = "kyverno-resource-validating-webhook-cfg-debug"
// ValidatingWebhookName ...
ValidatingWebhookName = "validate.kyverno.svc"
// VerifyMutatingWebhookConfigurationName default verify mutating webhook configuration name
VerifyMutatingWebhookConfigurationName = "kyverno-verify-mutating-webhook-cfg"
// VerifyMutatingWebhookConfigurationDebugName default verify mutating webhook configuration name for debug mode
VerifyMutatingWebhookConfigurationDebugName = "kyverno-verify-mutating-webhook-cfg-debug"
// VerifyMutatingWebhookName default verify mutating webhook name
VerifyMutatingWebhookName = "monitor-webhooks.kyverno.svc"
// PolicyValidatingWebhookConfigurationName default policy validating webhook configuration name
PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg"
// PolicyValidatingWebhookConfigurationDebugName default policy validating webhook configuration name for debug mode
2019-08-27 21:59:17 +00:00
PolicyValidatingWebhookConfigurationDebugName = "kyverno-policy-validating-webhook-cfg-debug"
// PolicyValidatingWebhookName default policy validating webhook name
PolicyValidatingWebhookName = "validate-policy.kyverno.svc"
// PolicyMutatingWebhookConfigurationName default policy mutating webhook configuration name
PolicyMutatingWebhookConfigurationName = "kyverno-policy-mutating-webhook-cfg"
// PolicyMutatingWebhookConfigurationDebugName default policy mutating webhook configuration name for debug mode
PolicyMutatingWebhookConfigurationDebugName = "kyverno-policy-mutating-webhook-cfg-debug"
// PolicyMutatingWebhookName default policy mutating webhook name
PolicyMutatingWebhookName = "mutate-policy.kyverno.svc"
// 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"
// MutatingWebhookServicePath is the path for mutation webhook
MutatingWebhookServicePath = "/mutate"
// ValidatingWebhookServicePath is the path for validation webhook
ValidatingWebhookServicePath = "/validate"
// PolicyValidatingWebhookServicePath is the path for policy validation webhook(used to validate policy resource)
PolicyValidatingWebhookServicePath = "/policyvalidate"
// PolicyMutatingWebhookServicePath is the path for policy mutation webhook(used to default)
PolicyMutatingWebhookServicePath = "/policymutate"
// 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"
)
var (
// kyvernoNamespace is the Kyverno namespace
kyvernoNamespace = osutils.GetEnvWithFallback("KYVERNO_NAMESPACE", "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")
// defaultExcludeGroupRole ...
defaultExcludeGroupRole []string = []string{"system:serviceaccounts:kube-system", "system:nodes", "system:kube-scheduler"}
)
func KyvernoNamespace() string {
return kyvernoNamespace
}
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 {
// ToFilter checks if the given resource is set to be filtered in the configuration
ToFilter(kind, namespace, name string) bool
// GetExcludeGroupRole return exclude roles
GetExcludeGroupRole() []string
// GetExcludeUsername return exclude username
GetExcludeUsername() []string
// GetGenerateSuccessEvents return if should generate success events
GetGenerateSuccessEvents() bool
// RestrictDevelopmentUsername return exclude development username
RestrictDevelopmentUsername() []string
// FilterNamespaces filters exclude namespace
FilterNamespaces(namespaces []string) []string
// GetWebhooks returns the webhook configs
GetWebhooks() []WebhookConfig
// Load loads configuration from a configmap
Load(cm *corev1.ConfigMap)
}
// configuration stores the configuration
type configuration struct {
mux sync.RWMutex
filters []filter
excludeGroupRole []string
excludeUsername []string
restrictDevelopmentUsername []string
webhooks []WebhookConfig
generateSuccessEvents bool
reconcilePolicyReport chan<- bool
updateWebhookConfigurations chan<- bool
}
// NewConfiguration ...
func NewConfiguration(client kubernetes.Interface, reconcilePolicyReport, updateWebhookConfigurations chan<- bool) (Configuration, error) {
cd := &configuration{
reconcilePolicyReport: reconcilePolicyReport,
updateWebhookConfigurations: updateWebhookConfigurations,
restrictDevelopmentUsername: []string{"minikube-user", "kubernetes-admin"},
excludeGroupRole: defaultExcludeGroupRole,
}
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) GetExcludeGroupRole() []string {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.excludeGroupRole
}
func (cd *configuration) RestrictDevelopmentUsername() []string {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.restrictDevelopmentUsername
}
func (cd *configuration) GetExcludeUsername() []string {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.excludeUsername
}
func (cd *configuration) GetGenerateSuccessEvents() bool {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.generateSuccessEvents
}
func (cd *configuration) FilterNamespaces(namespaces []string) []string {
var results []string
for _, ns := range namespaces {
if !cd.ToFilter("", ns, "") {
results = append(results, ns)
}
}
return results
}
func (cd *configuration) GetWebhooks() []WebhookConfig {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.webhooks
}
func (cd *configuration) Load(cm *corev1.ConfigMap) {
reconcilePolicyReport, updateWebhook := true, true
if cm != nil {
logger.Info("load config", "name", cm.Name, "namespace", cm.Namespace)
reconcilePolicyReport, updateWebhook = cd.load(cm)
} else {
logger.Info("unload config")
cd.unload()
}
if reconcilePolicyReport {
logger.Info("resource filters changed, sending reconcile signal to the policy controller")
cd.reconcilePolicyReport <- true
}
if updateWebhook {
logger.Info("webhook configurations changed, updating webhook configurations")
cd.updateWebhookConfigurations <- true
}
}
func (cd *configuration) load(cm *corev1.ConfigMap) (reconcilePolicyReport, updateWebhook bool) {
logger := logger.WithValues("name", cm.Name, "namespace", cm.Namespace)
if cm.Data == nil {
logger.V(4).Info("configuration: No data defined in ConfigMap")
return
}
cd.mux.Lock()
defer cd.mux.Unlock()
filters, ok := cm.Data["resourceFilters"]
if !ok {
logger.V(4).Info("configuration: No resourceFilters defined in ConfigMap")
} else {
newFilters := parseKinds(filters)
if reflect.DeepEqual(newFilters, cd.filters) {
logger.V(4).Info("resourceFilters did not change")
} else {
logger.V(2).Info("Updated resource filters", "oldFilters", cd.filters, "newFilters", newFilters)
cd.filters = newFilters
reconcilePolicyReport = true
}
}
excludeGroupRole, ok := cm.Data["excludeGroupRole"]
if !ok {
logger.V(4).Info("configuration: No excludeGroupRole defined in ConfigMap")
}
newExcludeGroupRoles := parseRbac(excludeGroupRole)
newExcludeGroupRoles = append(newExcludeGroupRoles, defaultExcludeGroupRole...)
if reflect.DeepEqual(newExcludeGroupRoles, cd.excludeGroupRole) {
logger.V(4).Info("excludeGroupRole did not change")
} else {
logger.V(2).Info("Updated resource excludeGroupRoles", "oldExcludeGroupRole", cd.excludeGroupRole, "newExcludeGroupRole", newExcludeGroupRoles)
cd.excludeGroupRole = newExcludeGroupRoles
reconcilePolicyReport = true
}
excludeUsername, ok := cm.Data["excludeUsername"]
if !ok {
logger.V(4).Info("configuration: No excludeUsername defined in ConfigMap")
} else {
excludeUsernames := parseRbac(excludeUsername)
if reflect.DeepEqual(excludeUsernames, cd.excludeUsername) {
logger.V(4).Info("excludeGroupRole did not change")
} else {
logger.V(2).Info("Updated resource excludeUsernames", "oldExcludeUsername", cd.excludeUsername, "newExcludeUsername", excludeUsernames)
cd.excludeUsername = excludeUsernames
reconcilePolicyReport = true
}
}
webhooks, ok := cm.Data["webhooks"]
if !ok {
if len(cd.webhooks) > 0 {
cd.webhooks = nil
updateWebhook = true
logger.V(4).Info("configuration: Setting namespaceSelector to empty in the webhook configurations")
} else {
logger.V(4).Info("configuration: No webhook configurations defined in ConfigMap")
}
} else {
cfgs, err := parseWebhooks(webhooks)
if err != nil {
logger.Error(err, "unable to parse webhooks configurations")
return
}
if reflect.DeepEqual(cfgs, cd.webhooks) {
logger.V(4).Info("webhooks did not change")
} else {
logger.Info("Updated webhooks configurations", "oldWebhooks", cd.webhooks, "newWebhookd", cfgs)
cd.webhooks = cfgs
updateWebhook = true
}
}
generateSuccessEvents, ok := cm.Data["generateSuccessEvents"]
if !ok {
logger.V(4).Info("configuration: No generateSuccessEvents defined in ConfigMap")
} else {
generateSuccessEvents, err := strconv.ParseBool(generateSuccessEvents)
if err != nil {
logger.V(4).Info("configuration: generateSuccessEvents must be either true/false")
} else if generateSuccessEvents == cd.generateSuccessEvents {
logger.V(4).Info("generateSuccessEvents did not change")
} else {
logger.V(2).Info("Updated generateSuccessEvents", "oldGenerateSuccessEvents", cd.generateSuccessEvents, "newGenerateSuccessEvents", generateSuccessEvents)
cd.generateSuccessEvents = generateSuccessEvents
reconcilePolicyReport = true
}
}
return
}
func (cd *configuration) unload() {
cd.mux.Lock()
defer cd.mux.Unlock()
cd.filters = []filter{}
cd.excludeGroupRole = []string{}
cd.excludeGroupRole = append(cd.excludeGroupRole, defaultExcludeGroupRole...)
cd.excludeUsername = []string{}
cd.generateSuccessEvents = false
}