mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-06 16:06:56 +00:00
* add resource lister to even handler Signed-off-by: Shuting Zhao <shutting06@gmail.com> * use lister to get Kyverno deployment Signed-off-by: Shuting Zhao <shutting06@gmail.com> * add lister for webhook configs Signed-off-by: Shuting Zhao <shutting06@gmail.com>
437 lines
12 KiB
Go
437 lines
12 KiB
Go
package webhookconfig
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/go-logr/logr"
|
|
"github.com/kyverno/kyverno/pkg/config"
|
|
client "github.com/kyverno/kyverno/pkg/dclient"
|
|
"github.com/kyverno/kyverno/pkg/resourcecache"
|
|
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
|
errorsapi "k8s.io/apimachinery/pkg/api/errors"
|
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
rest "k8s.io/client-go/rest"
|
|
)
|
|
|
|
const (
|
|
kindMutating string = "MutatingWebhookConfiguration"
|
|
kindValidating string = "ValidatingWebhookConfiguration"
|
|
)
|
|
|
|
// Register manages webhook registration. There are five webhooks:
|
|
// 1. Policy Validation
|
|
// 2. Policy Mutation
|
|
// 3. Resource Validation
|
|
// 4. Resource Mutation
|
|
// 5. Webhook Status Mutation
|
|
type Register struct {
|
|
client *client.Client
|
|
clientConfig *rest.Config
|
|
resCache resourcecache.ResourceCache
|
|
serverIP string // when running outside a cluster
|
|
timeoutSeconds int32
|
|
log logr.Logger
|
|
}
|
|
|
|
// NewRegister creates new Register instance
|
|
func NewRegister(
|
|
clientConfig *rest.Config,
|
|
client *client.Client,
|
|
resCache resourcecache.ResourceCache,
|
|
serverIP string,
|
|
webhookTimeout int32,
|
|
log logr.Logger) *Register {
|
|
return &Register{
|
|
clientConfig: clientConfig,
|
|
client: client,
|
|
resCache: resCache,
|
|
serverIP: serverIP,
|
|
timeoutSeconds: webhookTimeout,
|
|
log: log.WithName("Register"),
|
|
}
|
|
}
|
|
|
|
// Register clean up the old webhooks and re-creates admission webhooks configs on cluster
|
|
func (wrc *Register) Register() error {
|
|
logger := wrc.log
|
|
if wrc.serverIP != "" {
|
|
logger.Info("Registering webhook", "url", fmt.Sprintf("https://%s", wrc.serverIP))
|
|
}
|
|
|
|
wrc.removeWebhookConfigurations()
|
|
|
|
errors := make([]string, 0)
|
|
if err := wrc.createVerifyMutatingWebhookConfiguration(); err != nil {
|
|
errors = append(errors, err.Error())
|
|
}
|
|
|
|
if err := wrc.createPolicyValidatingWebhookConfiguration(); err != nil {
|
|
errors = append(errors, err.Error())
|
|
}
|
|
|
|
if err := wrc.createPolicyMutatingWebhookConfiguration(); err != nil {
|
|
errors = append(errors, err.Error())
|
|
}
|
|
|
|
if err := wrc.createResourceValidatingWebhookConfiguration(); err != nil {
|
|
errors = append(errors, err.Error())
|
|
}
|
|
|
|
if err := wrc.createResourceMutatingWebhookConfiguration(); err != nil {
|
|
errors = append(errors, err.Error())
|
|
}
|
|
|
|
if len(errors) > 0 {
|
|
return fmt.Errorf("%s", strings.Join(errors, ","))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Check returns an error if any of the webhooks are not configured
|
|
func (wrc *Register) Check() error {
|
|
mutatingCache, _ := wrc.resCache.GetGVRCache(kindMutating)
|
|
validatingCache, _ := wrc.resCache.GetGVRCache(kindValidating)
|
|
|
|
if _, err := mutatingCache.Lister().Get(wrc.getVerifyWebhookMutatingWebhookName()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := mutatingCache.Lister().Get(wrc.getResourceMutatingWebhookConfigName()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := validatingCache.Lister().Get(wrc.getResourceValidatingWebhookConfigName()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := mutatingCache.Lister().Get(wrc.getPolicyMutatingWebhookConfigurationName()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := validatingCache.Lister().Get(wrc.getPolicyValidatingWebhookConfigurationName()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Remove removes all webhook configurations
|
|
func (wrc *Register) Remove(cleanUp chan<- struct{}) {
|
|
wrc.removeWebhookConfigurations()
|
|
close(cleanUp)
|
|
}
|
|
|
|
func (wrc *Register) createResourceMutatingWebhookConfiguration() error {
|
|
|
|
var caData []byte
|
|
var config *admregapi.MutatingWebhookConfiguration
|
|
|
|
if caData = wrc.readCaData(); caData == nil {
|
|
return errors.New("Unable to extract CA data from configuration")
|
|
}
|
|
|
|
if wrc.serverIP != "" {
|
|
config = wrc.constructDebugMutatingWebhookConfig(caData)
|
|
} else {
|
|
config = wrc.constructMutatingWebhookConfig(caData)
|
|
}
|
|
|
|
logger := wrc.log.WithValues("kind", kindMutating, "name", config.Name)
|
|
|
|
_, err := wrc.client.CreateResource("", kindMutating, "", *config, false)
|
|
if errorsapi.IsAlreadyExists(err) {
|
|
logger.V(6).Info("resource mutating webhook configuration already exists", "name", config.Name)
|
|
return nil
|
|
}
|
|
|
|
if err != nil {
|
|
logger.Error(err, "failed to create resource mutating webhook configuration", "name", config.Name)
|
|
return err
|
|
}
|
|
|
|
logger.Info("created webhook")
|
|
return nil
|
|
}
|
|
|
|
func (wrc *Register) createResourceValidatingWebhookConfiguration() error {
|
|
var caData []byte
|
|
var config *admregapi.ValidatingWebhookConfiguration
|
|
|
|
if caData = wrc.readCaData(); caData == nil {
|
|
return errors.New("Unable to extract CA data from configuration")
|
|
}
|
|
if wrc.serverIP != "" {
|
|
config = wrc.constructDebugValidatingWebhookConfig(caData)
|
|
} else {
|
|
config = wrc.constructValidatingWebhookConfig(caData)
|
|
}
|
|
|
|
logger := wrc.log.WithValues("kind", kindValidating, "name", config.Name)
|
|
|
|
_, err := wrc.client.CreateResource("", kindValidating, "", *config, false)
|
|
if errorsapi.IsAlreadyExists(err) {
|
|
logger.V(6).Info("resource validating webhook configuration already exists", "name", config.Name)
|
|
return nil
|
|
}
|
|
|
|
if err != nil {
|
|
logger.Error(err, "failed to create resource")
|
|
return err
|
|
}
|
|
|
|
logger.Info("created webhook")
|
|
return nil
|
|
}
|
|
|
|
//registerPolicyValidatingWebhookConfiguration create a Validating webhook configuration for Policy CRD
|
|
func (wrc *Register) createPolicyValidatingWebhookConfiguration() error {
|
|
var caData []byte
|
|
var config *admregapi.ValidatingWebhookConfiguration
|
|
|
|
// read certificate data
|
|
if caData = wrc.readCaData(); caData == nil {
|
|
return errors.New("Unable to extract CA data from configuration")
|
|
}
|
|
|
|
if wrc.serverIP != "" {
|
|
config = wrc.contructDebugPolicyValidatingWebhookConfig(caData)
|
|
} else {
|
|
config = wrc.contructPolicyValidatingWebhookConfig(caData)
|
|
}
|
|
|
|
if _, err := wrc.client.CreateResource("", kindValidating, "", *config, false); err != nil {
|
|
if errorsapi.IsAlreadyExists(err) {
|
|
wrc.log.V(6).Info("webhook already exists", "kind", kindValidating, "name", config.Name)
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
wrc.log.Info("created webhook", "kind", kindValidating, "name", config.Name)
|
|
return nil
|
|
}
|
|
|
|
func (wrc *Register) createPolicyMutatingWebhookConfiguration() error {
|
|
var caData []byte
|
|
var config *admregapi.MutatingWebhookConfiguration
|
|
|
|
if caData = wrc.readCaData(); caData == nil {
|
|
return errors.New("Unable to extract CA data from configuration")
|
|
}
|
|
|
|
if wrc.serverIP != "" {
|
|
config = wrc.contructDebugPolicyMutatingWebhookConfig(caData)
|
|
} else {
|
|
config = wrc.contructPolicyMutatingWebhookConfig(caData)
|
|
}
|
|
|
|
// create mutating webhook configuration resource
|
|
if _, err := wrc.client.CreateResource("", kindMutating, "", *config, false); err != nil {
|
|
if errorsapi.IsAlreadyExists(err) {
|
|
wrc.log.V(6).Info("webhook already exists", "kind", kindMutating, "name", config.Name)
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
wrc.log.Info("created webhook", "kind", kindMutating, "name", config.Name)
|
|
return nil
|
|
}
|
|
|
|
func (wrc *Register) createVerifyMutatingWebhookConfiguration() error {
|
|
var caData []byte
|
|
var config *admregapi.MutatingWebhookConfiguration
|
|
|
|
if caData = wrc.readCaData(); caData == nil {
|
|
return errors.New("Unable to extract CA data from configuration")
|
|
}
|
|
|
|
if wrc.serverIP != "" {
|
|
config = wrc.constructDebugVerifyMutatingWebhookConfig(caData)
|
|
} else {
|
|
config = wrc.constructVerifyMutatingWebhookConfig(caData)
|
|
}
|
|
|
|
if _, err := wrc.client.CreateResource("", kindMutating, "", *config, false); err != nil {
|
|
if errorsapi.IsAlreadyExists(err) {
|
|
wrc.log.V(6).Info("webhook already exists", "kind", kindMutating, "name", config.Name)
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
wrc.log.Info("created webhook", "kind", kindMutating, "name", config.Name)
|
|
return nil
|
|
}
|
|
|
|
func (wrc *Register) removeWebhookConfigurations() {
|
|
startTime := time.Now()
|
|
wrc.log.Info("deleting all webhook configurations")
|
|
defer func() {
|
|
wrc.log.V(4).Info("removed webhook configurations", "processingTime", time.Since(startTime).String())
|
|
}()
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(5)
|
|
|
|
go wrc.removeResourceMutatingWebhookConfiguration(&wg)
|
|
go wrc.removeResourceValidatingWebhookConfiguration(&wg)
|
|
go wrc.removePolicyMutatingWebhookConfiguration(&wg)
|
|
go wrc.removePolicyValidatingWebhookConfiguration(&wg)
|
|
go wrc.removeVerifyWebhookMutatingWebhookConfig(&wg)
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func (wrc *Register) removePolicyMutatingWebhookConfiguration(wg *sync.WaitGroup) {
|
|
defer wg.Done()
|
|
|
|
mutatingConfig := wrc.getPolicyMutatingWebhookConfigurationName()
|
|
|
|
logger := wrc.log.WithValues("kind", kindMutating, "name", mutatingConfig)
|
|
err := wrc.client.DeleteResource("", kindMutating, "", mutatingConfig, false)
|
|
if errorsapi.IsNotFound(err) {
|
|
logger.V(5).Info("policy mutating webhook configuration not found")
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
logger.Error(err, "failed to delete policy mutating webhook configuration")
|
|
return
|
|
}
|
|
|
|
logger.Info("webhook configuration deleted")
|
|
}
|
|
|
|
func (wrc *Register) getPolicyMutatingWebhookConfigurationName() string {
|
|
var mutatingConfig string
|
|
if wrc.serverIP != "" {
|
|
mutatingConfig = config.PolicyMutatingWebhookConfigurationDebugName
|
|
} else {
|
|
mutatingConfig = config.PolicyMutatingWebhookConfigurationName
|
|
}
|
|
return mutatingConfig
|
|
}
|
|
|
|
func (wrc *Register) removePolicyValidatingWebhookConfiguration(wg *sync.WaitGroup) {
|
|
defer wg.Done()
|
|
|
|
validatingConfig := wrc.getPolicyValidatingWebhookConfigurationName()
|
|
|
|
logger := wrc.log.WithValues("kind", kindValidating, "name", validatingConfig)
|
|
logger.V(4).Info("removing validating webhook configuration")
|
|
err := wrc.client.DeleteResource("", kindValidating, "", validatingConfig, false)
|
|
if errorsapi.IsNotFound(err) {
|
|
logger.V(5).Info("policy validating webhook configuration not found")
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
logger.Error(err, "failed to delete policy validating webhook configuration")
|
|
return
|
|
}
|
|
|
|
logger.Info("webhook configuration deleted")
|
|
}
|
|
|
|
func (wrc *Register) getPolicyValidatingWebhookConfigurationName() string {
|
|
var validatingConfig string
|
|
if wrc.serverIP != "" {
|
|
validatingConfig = config.PolicyValidatingWebhookConfigurationDebugName
|
|
} else {
|
|
validatingConfig = config.PolicyValidatingWebhookConfigurationName
|
|
}
|
|
return validatingConfig
|
|
}
|
|
|
|
func (wrc *Register) constructVerifyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
|
return &admregapi.MutatingWebhookConfiguration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: config.VerifyMutatingWebhookConfigurationName,
|
|
OwnerReferences: []v1.OwnerReference{
|
|
wrc.constructOwner(),
|
|
},
|
|
},
|
|
Webhooks: []admregapi.MutatingWebhook{
|
|
generateMutatingWebhook(
|
|
config.VerifyMutatingWebhookName,
|
|
config.VerifyMutatingWebhookServicePath,
|
|
caData,
|
|
true,
|
|
wrc.timeoutSeconds,
|
|
[]string{"deployments/*"},
|
|
"apps",
|
|
"v1",
|
|
[]admregapi.OperationType{admregapi.Update},
|
|
),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (wrc *Register) constructDebugVerifyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
|
logger := wrc.log
|
|
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.VerifyMutatingWebhookServicePath)
|
|
logger.V(4).Info("Debug VerifyMutatingWebhookConfig is registered with url", "url", url)
|
|
return &admregapi.MutatingWebhookConfiguration{
|
|
ObjectMeta: v1.ObjectMeta{
|
|
Name: config.VerifyMutatingWebhookConfigurationDebugName,
|
|
},
|
|
Webhooks: []admregapi.MutatingWebhook{
|
|
generateDebugMutatingWebhook(
|
|
config.VerifyMutatingWebhookName,
|
|
url,
|
|
caData,
|
|
true,
|
|
wrc.timeoutSeconds,
|
|
[]string{"deployments/*"},
|
|
"apps",
|
|
"v1",
|
|
[]admregapi.OperationType{admregapi.Update},
|
|
),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (wrc *Register) removeVerifyWebhookMutatingWebhookConfig(wg *sync.WaitGroup) {
|
|
defer wg.Done()
|
|
|
|
var err error
|
|
mutatingConfig := wrc.getVerifyWebhookMutatingWebhookName()
|
|
|
|
logger := wrc.log.WithValues("kind", kindMutating, "name", mutatingConfig)
|
|
err = wrc.client.DeleteResource("", kindMutating, "", mutatingConfig, false)
|
|
if errorsapi.IsNotFound(err) {
|
|
logger.V(5).Info("verify webhook configuration not found")
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
logger.Error(err, "failed to delete verify webhook configuration")
|
|
return
|
|
}
|
|
|
|
logger.Info("webhook configuration deleted")
|
|
}
|
|
|
|
func (wrc *Register) getVerifyWebhookMutatingWebhookName() string {
|
|
var mutatingConfig string
|
|
if wrc.serverIP != "" {
|
|
mutatingConfig = config.VerifyMutatingWebhookConfigurationDebugName
|
|
} else {
|
|
mutatingConfig = config.VerifyMutatingWebhookConfigurationName
|
|
}
|
|
return mutatingConfig
|
|
}
|
|
|
|
// GetWebhookTimeOut returns the value of webhook timeout
|
|
func (wrc *Register) GetWebhookTimeOut() time.Duration {
|
|
return time.Duration(wrc.timeoutSeconds)
|
|
}
|