1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-29 02:45:06 +00:00

Customize namespaceSelector of Webhookconfigurations (#2003)

* customize namespaceSelector of webhook configurations from configMap

Signed-off-by: Shuting Zhao <shutting06@gmail.com>

* update webhook configurations base on UPDATEs of Kyverno ConfigMap

Signed-off-by: Shuting Zhao <shutting06@gmail.com>

* register webhook configurations with the namespaceSelector from ConfigMap

Signed-off-by: Shuting Zhao <shutting06@gmail.com>

* address golint comment

Signed-off-by: Shuting Zhao <shutting06@gmail.com>

* validate webhooks config format

Signed-off-by: Shuting Zhao <shutting06@gmail.com>

* fix NotDefined scenario

Signed-off-by: Shuting Zhao <shutting06@gmail.com>
This commit is contained in:
shuting 2021-06-14 13:01:40 -07:00 committed by GitHub
parent b486493f87
commit 6f07ea407f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 248 additions and 61 deletions

View file

@ -165,21 +165,6 @@ func main() {
if err != nil {
setupLog.Error(err, "ConfigMap lookup disabled: failed to create resource cache")
}
debug := serverIP != ""
webhookCfg := webhookconfig.NewRegister(
clientConfig,
client,
rCache,
serverIP,
int32(webhookTimeout),
debug,
log.Log)
webhookMonitor, err := webhookconfig.NewMonitor(kubeClient, log.Log.WithName("WebhookMonitor"))
if err != nil {
setupLog.Error(err, "failed to initialize webhookMonitor")
os.Exit(1)
}
// KYVERNO CRD INFORMER
// watches CRD resources:
@ -231,6 +216,22 @@ func main() {
os.Exit(1)
}
debug := serverIP != ""
webhookCfg := webhookconfig.NewRegister(
clientConfig,
client,
rCache,
serverIP,
int32(webhookTimeout),
debug,
log.Log)
webhookMonitor, err := webhookconfig.NewMonitor(kubeClient, log.Log.WithName("WebhookMonitor"))
if err != nil {
setupLog.Error(err, "failed to initialize webhookMonitor")
os.Exit(1)
}
// Configuration Data
// dynamically load the configuration from configMap
// - resource filters
@ -242,6 +243,7 @@ func main() {
excludeGroupRole,
excludeUsername,
prgen.ReconcileCh,
webhookCfg.UpdateWebhookChan,
log.Log.WithName("ConfigData"),
)
@ -350,10 +352,18 @@ func main() {
registerWebhookConfigurations := func() {
certManager.InitTLSPemPair()
// validate the ConfigMap format
if err := webhookCfg.ValidateWebhookConfigurations(config.KyvernoNamespace, configData.GetInitConfigMapName()); err != nil {
setupLog.Error(err, "invalid format of the Kyverno init ConfigMap, please correct the format of 'data.webhooks'")
os.Exit(1)
}
go webhookCfg.UpdateWebhookConfigurations(configData)
if registrationErr := registerWrapperRetry(); registrationErr != nil {
setupLog.Error(err, "Timeout registering admission control webhooks")
os.Exit(1)
}
webhookCfg.UpdateWebhookChan <- true
}
// leader election context
@ -366,7 +376,7 @@ func main() {
cancel()
}()
// register webhooks by the leader, it's a one-time job
// webhookconfigurations are registered by the leader only
webhookRegisterLeader, err := leaderelection.New("webhook-register", config.KyvernoNamespace, kubeClient, registerWebhookConfigurations, nil, log.Log.WithName("webhookRegister/LeaderElection"))
if err != nil {
setupLog.Error(err, "failed to elector leader")

15
go.sum
View file

@ -741,11 +741,7 @@ github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOms
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
<<<<<<< HEAD
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
=======
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
>>>>>>> 2c944c03 (updated verison to latest)
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU=
github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A=
@ -762,7 +758,6 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@ -779,7 +774,6 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -808,7 +802,6 @@ github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
@ -878,7 +871,6 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1115,7 +1107,6 @@ golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
@ -1135,7 +1126,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -1193,7 +1183,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@ -1207,9 +1196,7 @@ gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@ -1227,7 +1214,6 @@ gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@ -1260,7 +1246,6 @@ k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8Nld
k8s.io/apiserver v0.16.4/go.mod h1:kbLJOak655g6W7C+muqu1F76u9wnEycfKMqbVaXIdAc=
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
k8s.io/apiserver v0.21.0/go.mod h1:w2YSn4/WIwYuxG5zJmcqtRdtqgW/J2JRgFAqps3bBpg=
k8s.io/cli-runtime v0.20.2 h1:W0/FHdbApnl9oB7xdG643c/Zaf7TZT+43I+zKxwqvhU=
k8s.io/cli-runtime v0.20.2/go.mod h1:FjH6uIZZZP3XmwrXWeeYCbgxcrD6YXxoAykBaWH0VdM=
k8s.io/client-go v0.0.0-20190918160344-1fbdaa4c8d90/go.mod h1:J69/JveO6XESwVgG53q3Uz5OSfgsv4uxpScmmyYOOlk=
k8s.io/client-go v0.16.4/go.mod h1:ZgxhFDxSnoKY0J0U2/Y1C8obKDdlhGPZwA7oHH863Ok=

View file

@ -1,6 +1,7 @@
package config
import (
"encoding/json"
"os"
"reflect"
"regexp"
@ -10,6 +11,7 @@ import (
"github.com/go-logr/logr"
"github.com/minio/pkg/wildcard"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
informers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
@ -21,6 +23,10 @@ const cmNameEnv string = "INIT_CONFIG"
var defaultExcludeGroupRole []string = []string{"system:serviceaccounts:kube-system", "system:nodes", "system:kube-scheduler"}
type WebhookConfig struct {
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,5,opt,name=namespaceSelector"`
}
// ConfigData stores the configuration
type ConfigData struct {
client kubernetes.Interface
@ -30,8 +36,10 @@ type ConfigData struct {
excludeGroupRole []string
excludeUsername []string
restrictDevelopmentUsername []string
webhooks []WebhookConfig
cmSycned cache.InformerSynced
reconcilePolicyReport chan<- bool
updateWebhookConfigurations chan<- bool
log logr.Logger
}
@ -87,6 +95,16 @@ func (cd *ConfigData) FilterNamespaces(namespaces []string) []string {
return results
}
func (cd *ConfigData) GetWebhooks() []WebhookConfig {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.webhooks
}
func (cd *ConfigData) GetInitConfigMapName() string {
return cd.cmName
}
// Interface to be used by consumer to check filters
type Interface interface {
ToFilter(kind, namespace, name string) bool
@ -94,21 +112,24 @@ type Interface interface {
GetExcludeUsername() []string
RestrictDevelopmentUsername() []string
FilterNamespaces(namespaces []string) []string
GetWebhooks() []WebhookConfig
GetInitConfigMapName() string
}
// NewConfigData ...
func NewConfigData(rclient kubernetes.Interface, cmInformer informers.ConfigMapInformer, filterK8sResources, excludeGroupRole, excludeUsername string, reconcilePolicyReport chan<- bool, log logr.Logger) *ConfigData {
func NewConfigData(rclient kubernetes.Interface, cmInformer informers.ConfigMapInformer, filterK8sResources, excludeGroupRole, excludeUsername string, reconcilePolicyReport, updateWebhookConfigurations chan<- bool, log logr.Logger) *ConfigData {
// environment var is read at start only
if cmNameEnv == "" {
log.Info("ConfigMap name not defined in env:INIT_CONFIG: loading no default configuration")
}
cd := ConfigData{
client: rclient,
cmName: os.Getenv(cmNameEnv),
cmSycned: cmInformer.Informer().HasSynced,
reconcilePolicyReport: reconcilePolicyReport,
log: log,
client: rclient,
cmName: os.Getenv(cmNameEnv),
cmSycned: cmInformer.Informer().HasSynced,
reconcilePolicyReport: reconcilePolicyReport,
updateWebhookConfigurations: updateWebhookConfigurations,
log: log,
}
cd.restrictDevelopmentUsername = []string{"minikube-user", "kubernetes-admin"}
@ -156,7 +177,6 @@ func (cd *ConfigData) addCM(obj interface{}) {
return
}
cd.load(*cm)
// else load the configuration
}
func (cd *ConfigData) updateCM(old, cur interface{}) {
@ -165,11 +185,16 @@ func (cd *ConfigData) updateCM(old, cur interface{}) {
return
}
// if data has not changed then dont load configmap
changed := cd.load(*cm)
if changed {
reconcilePolicyReport, updateWebook := cd.load(*cm)
if reconcilePolicyReport {
cd.log.Info("resource filters changed, sending reconcile signal to the policy controller")
cd.reconcilePolicyReport <- true
}
if updateWebook {
cd.log.Info("webhook configurations changed, updating webhook configurations")
cd.updateWebhookConfigurations <- true
}
}
func (cd *ConfigData) deleteCM(obj interface{}) {
@ -195,7 +220,7 @@ func (cd *ConfigData) deleteCM(obj interface{}) {
cd.unload(*cm)
}
func (cd *ConfigData) load(cm v1.ConfigMap) (changed bool) {
func (cd *ConfigData) load(cm v1.ConfigMap) (reconcilePolicyReport, updateWebhook bool) {
logger := cd.log.WithValues("name", cm.Name, "namespace", cm.Namespace)
if cm.Data == nil {
logger.V(4).Info("configuration: No data defined in ConfigMap")
@ -215,7 +240,7 @@ func (cd *ConfigData) load(cm v1.ConfigMap) (changed bool) {
} else {
logger.V(2).Info("Updated resource filters", "oldFilters", cd.filters, "newFilters", newFilters)
cd.filters = newFilters
changed = true
reconcilePolicyReport = true
}
}
@ -230,7 +255,7 @@ func (cd *ConfigData) load(cm v1.ConfigMap) (changed bool) {
} else {
logger.V(2).Info("Updated resource excludeGroupRoles", "oldExcludeGroupRole", cd.excludeGroupRole, "newExcludeGroupRole", newExcludeGroupRoles)
cd.excludeGroupRole = newExcludeGroupRoles
changed = true
reconcilePolicyReport = true
}
excludeUsername, ok := cm.Data["excludeUsername"]
@ -243,11 +268,29 @@ func (cd *ConfigData) load(cm v1.ConfigMap) (changed bool) {
} else {
logger.V(2).Info("Updated resource excludeUsernames", "oldExcludeUsername", cd.excludeUsername, "newExcludeUsername", excludeUsernames)
cd.excludeUsername = excludeUsernames
changed = true
reconcilePolicyReport = true
}
}
return changed
webhooks, ok := cm.Data["webhooks"]
if !ok {
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
}
}
return
}
//TODO: this has been added to backward support command line arguments
@ -330,3 +373,12 @@ func parseKinds(list string) []k8Resource {
func parseRbac(list string) []string {
return strings.Split(list, ",")
}
func parseWebhooks(webhooks string) ([]WebhookConfig, error) {
webhookCfgs := make([]WebhookConfig, 0, 10)
if err := json.Unmarshal([]byte(webhooks), &webhookCfgs); err != nil {
return nil, err
}
return webhookCfgs, nil
}

View file

@ -1,7 +1,7 @@
package webhookconfig
import (
"errors"
"encoding/json"
"fmt"
"strings"
"sync"
@ -12,6 +12,7 @@ import (
client "github.com/kyverno/kyverno/pkg/dclient"
"github.com/kyverno/kyverno/pkg/resourcecache"
"github.com/kyverno/kyverno/pkg/tls"
"github.com/pkg/errors"
admregapi "k8s.io/api/admissionregistration/v1beta1"
corev1 "k8s.io/api/core/v1"
errorsapi "k8s.io/apimachinery/pkg/api/errors"
@ -40,6 +41,8 @@ type Register struct {
timeoutSeconds int32
log logr.Logger
debug bool
UpdateWebhookChan chan bool
}
// NewRegister creates new Register instance
@ -52,13 +55,14 @@ func NewRegister(
debug bool,
log logr.Logger) *Register {
return &Register{
clientConfig: clientConfig,
client: client,
resCache: resCache,
serverIP: serverIP,
timeoutSeconds: webhookTimeout,
log: log.WithName("Register"),
debug: debug,
clientConfig: clientConfig,
client: client,
resCache: resCache,
serverIP: serverIP,
timeoutSeconds: webhookTimeout,
log: log.WithName("Register"),
debug: debug,
UpdateWebhookChan: make(chan bool),
}
}
@ -147,6 +151,70 @@ func (wrc *Register) Remove(cleanUp chan<- struct{}) {
wrc.removeSecrets()
}
// UpdateWebhookConfigurations updates resource webhook configurations dynamically
// base on the UPDATEs of Kyverno init-config ConfigMap
//
// it currently updates namespaceSelector only, can be extend to update other fieids
func (wrc *Register) UpdateWebhookConfigurations(configHandler config.Interface) {
logger := wrc.log.WithName("UpdateWebhookConfigurations")
for {
<-wrc.UpdateWebhookChan
logger.Info("received the signal to update webhook configurations")
var nsSelector map[string]interface{}
webhookCfgs := configHandler.GetWebhooks()
if webhookCfgs != nil {
selector := webhookCfgs[0].NamespaceSelector
selectorBytes, err := json.Marshal(*selector)
if err != nil {
logger.Error(err, "failed to serialize namespaceSelector")
continue
}
if err = json.Unmarshal(selectorBytes, &nsSelector); err != nil {
logger.Error(err, "failed to convert namespaceSelector to the map")
continue
}
}
if err := wrc.updateResourceMutatingWebhookConfiguration(nsSelector); err != nil {
logger.Error(err, "unable to update mutatingWebhookConfigurations", "name", wrc.getResourceMutatingWebhookConfigName())
} else {
logger.Info("successfully updated mutatingWebhookConfigurations", "name", wrc.getResourceMutatingWebhookConfigName())
}
if err := wrc.updateResourceValidatingWebhookConfiguration(nsSelector); err != nil {
logger.Error(err, "unable to update validatingWebhookConfigurations", "name", wrc.getResourceValidatingWebhookConfigName())
} else {
logger.Info("successfully updated validatingWebhookConfigurations", "name", wrc.getResourceValidatingWebhookConfigName())
}
}
}
func (wrc *Register) ValidateWebhookConfigurations(namespace, name string) error {
logger := wrc.log.WithName("ValidateWebhookConfigurations")
cm, err := wrc.client.GetResource("", "ConfigMap", namespace, name)
if err != nil {
logger.Error(err, "unable to fetch ConfigMap", "namespace", namespace, "name", name)
return nil
}
webhooks, ok, err := unstructured.NestedString(cm.UnstructuredContent(), "data", "webhooks")
if err != nil {
logger.Error(err, "failed to fetch tag 'webhooks' from the ConfigMap")
return nil
}
if !ok {
logger.V(4).Info("webhook configurations not defined")
return nil
}
webhookCfgs := make([]config.WebhookConfig, 0, 10)
return json.Unmarshal([]byte(webhooks), &webhookCfgs)
}
// cleanupKyvernoResource returns true if Kyverno deployment is terminating
func (wrc *Register) cleanupKyvernoResource() bool {
logger := wrc.log.WithName("cleanupKyvernoResource")
@ -179,9 +247,9 @@ func (wrc *Register) createResourceMutatingWebhookConfiguration(caData []byte) e
var config *admregapi.MutatingWebhookConfiguration
if wrc.serverIP != "" {
config = wrc.constructDebugMutatingWebhookConfig(caData)
config = wrc.constructDefaultDebugMutatingWebhookConfig(caData)
} else {
config = wrc.constructMutatingWebhookConfig(caData)
config = wrc.constructDefaultMutatingWebhookConfig(caData)
}
logger := wrc.log.WithValues("kind", kindMutating, "name", config.Name)
@ -205,9 +273,9 @@ func (wrc *Register) createResourceValidatingWebhookConfiguration(caData []byte)
var config *admregapi.ValidatingWebhookConfiguration
if wrc.serverIP != "" {
config = wrc.constructDebugValidatingWebhookConfig(caData)
config = wrc.constructDefaultDebugValidatingWebhookConfig(caData)
} else {
config = wrc.constructValidatingWebhookConfig(caData)
config = wrc.constructDefaultValidatingWebhookConfig(caData)
}
logger := wrc.log.WithValues("kind", kindValidating, "name", config.Name)
@ -550,3 +618,75 @@ func (wrc *Register) checkEndpoint() error {
wrc.log.V(3).Info(err.Error(), "ns", config.KyvernoNamespace, "name", config.KyvernoServiceName)
return err
}
func (wrc *Register) updateResourceValidatingWebhookConfiguration(nsSelector map[string]interface{}) error {
validatingCache, _ := wrc.resCache.GetGVRCache(kindValidating)
resourceValidating, err := validatingCache.Lister().Get(wrc.getResourceValidatingWebhookConfigName())
if err != nil {
return errors.Wrapf(err, "unable to get validatingWebhookConfigurations")
}
webhooksUntyped, _, err := unstructured.NestedSlice(resourceValidating.UnstructuredContent(), "webhooks")
if err != nil {
return errors.Wrapf(err, "unable to load validatingWebhookConfigurations.webhooks")
}
var webhooks map[string]interface{}
var ok bool
if webhooksUntyped != nil {
webhooks, ok = webhooksUntyped[0].(map[string]interface{})
if !ok {
return errors.Wrapf(err, "type mismatched, expected map[string]interface{}, got %T", webhooksUntyped[0])
}
}
if err = unstructured.SetNestedMap(webhooks, nsSelector, "namespaceSelector"); err != nil {
return errors.Wrapf(err, "unable to set validatingWebhookConfigurations.webhooks[0].namespaceSelector")
}
if err = unstructured.SetNestedSlice(resourceValidating.UnstructuredContent(), []interface{}{webhooks}, "webhooks"); err != nil {
return errors.Wrapf(err, "unable to set validatingWebhookConfigurations.webhooks")
}
if _, err := wrc.client.UpdateResource(resourceValidating.GetAPIVersion(), resourceValidating.GetKind(), "", resourceValidating, false); err != nil {
return err
}
return nil
}
func (wrc *Register) updateResourceMutatingWebhookConfiguration(nsSelector map[string]interface{}) error {
mutatingCache, _ := wrc.resCache.GetGVRCache(kindMutating)
resourceMutating, err := mutatingCache.Lister().Get(wrc.getResourceMutatingWebhookConfigName())
if err != nil {
return errors.Wrapf(err, "unable to get mutatingWebhookConfigurations")
}
webhooksUntyped, _, err := unstructured.NestedSlice(resourceMutating.UnstructuredContent(), "webhooks")
if err != nil {
return errors.Wrapf(err, "unable to load mutatingWebhookConfigurations.webhooks")
}
var webhooks map[string]interface{}
var ok bool
if webhooksUntyped != nil {
webhooks, ok = webhooksUntyped[0].(map[string]interface{})
if !ok {
return errors.Wrapf(err, "type mismatched, expected map[string]interface{}, got %T", webhooksUntyped[0])
}
}
if err = unstructured.SetNestedMap(webhooks, nsSelector, "namespaceSelector"); err != nil {
return errors.Wrapf(err, "unable to set mutatingWebhookConfigurations.webhooks[0].namespaceSelector")
}
if err = unstructured.SetNestedSlice(resourceMutating.UnstructuredContent(), []interface{}{webhooks}, "webhooks"); err != nil {
return errors.Wrapf(err, "unable to set mutatingWebhookConfigurations.webhooks")
}
if _, err := wrc.client.UpdateResource(resourceMutating.GetAPIVersion(), resourceMutating.GetKind(), "", resourceMutating, false); err != nil {
return err
}
return nil
}

View file

@ -11,7 +11,7 @@ import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (wrc *Register) constructDebugMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
func (wrc *Register) constructDefaultDebugMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
logger := wrc.log
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.MutatingWebhookServicePath)
logger.V(4).Info("Debug MutatingWebhookConfig registered", "url", url)
@ -35,7 +35,7 @@ func (wrc *Register) constructDebugMutatingWebhookConfig(caData []byte) *admrega
}
}
func (wrc *Register) constructMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
func (wrc *Register) constructDefaultMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
webhookCfg := generateMutatingWebhook(
config.MutatingWebhookName,
@ -94,7 +94,7 @@ func (wrc *Register) removeResourceMutatingWebhookConfiguration(wg *sync.WaitGro
logger.Info("webhook configuration deleted")
}
func (wrc *Register) constructDebugValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
func (wrc *Register) constructDefaultDebugValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.ValidatingWebhookServicePath)
return &admregapi.ValidatingWebhookConfiguration{
@ -117,7 +117,7 @@ func (wrc *Register) constructDebugValidatingWebhookConfig(caData []byte) *admre
}
}
func (wrc *Register) constructValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
func (wrc *Register) constructDefaultValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
return &admregapi.ValidatingWebhookConfiguration{
ObjectMeta: v1.ObjectMeta{
Name: config.ValidatingWebhookConfigurationName,