1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-29 10:55:05 +00:00

Merge pull request #855 from nirmata/bugfix/fixes_791_792_832

Bugfix/fixes 791 792 832
This commit is contained in:
Jim Bugwadia 2020-05-17 19:30:11 -07:00 committed by GitHub
commit 8c4c98d1a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 358 additions and 228 deletions

View file

@ -53,6 +53,10 @@ docker-push-initContainer:
.PHONY: docker-build-kyverno docker-tag-repo-kyverno docker-push-kyverno
KYVERNO_PATH := cmd/kyverno
KYVERNO_IMAGE := kyverno
local:
go build -ldflags=$(LD_FLAGS) $(PWD)/$(KYVERNO_PATH)/
kyverno:
GOOS=$(GOOS) go build -o $(PWD)/$(KYVERNO_PATH)/kyverno -ldflags=$(LD_FLAGS) $(PWD)/$(KYVERNO_PATH)/main.go

View file

@ -33,6 +33,8 @@ import (
log "sigs.k8s.io/controller-runtime/pkg/log"
)
const resyncPeriod = 15 * time.Minute
var (
kubeconfig string
serverIP string
@ -61,15 +63,11 @@ func main() {
// Generate CSR with CN as FQDN due to https://github.com/nirmata/kyverno/issues/542
flag.BoolVar(&fqdncn, "fqdn-as-cn", false, "use FQDN as Common Name in CSR")
flag.Parse()
version.PrintVersionInfo(log.Log)
// cleanUp Channel
cleanUp := make(chan struct{})
// handle os signals
stopCh := signal.SetupSignalHandler()
// CLIENT CONFIG
clientConfig, err := config.CreateClientConfig(kubeconfig, log.Log)
if err != nil {
setupLog.Error(err, "Failed to build kubeconfig")
@ -88,39 +86,31 @@ func main() {
// DYNAMIC CLIENT
// - client for all registered resources
// - invalidate local cache of registered resource every 10 seconds
client, err := dclient.NewClient(clientConfig, 10*time.Second, stopCh, log.Log)
client, err := dclient.NewClient(clientConfig, 5*time.Minute, stopCh, log.Log)
if err != nil {
setupLog.Error(err, "Failed to create client")
os.Exit(1)
}
// CRD CHECK
// - verify if the CRD for Policy & PolicyViolation are available
if !utils.CRDInstalled(client.DiscoveryClient, log.Log) {
setupLog.Error(fmt.Errorf("pre-requisite CRDs not installed"), "Failed to create watch on kyverno CRDs")
os.Exit(1)
}
// KUBERNETES CLIENT
kubeClient, err := utils.NewKubeClient(clientConfig)
if err != nil {
setupLog.Error(err, "Failed to create kubernetes client")
os.Exit(1)
}
// TODO(shuting): To be removed for v1.2.0
// TODO: To be removed for v1.2.0
utils.CleanupOldCrd(client, log.Log)
// KUBERNETES RESOURCES INFORMER
// watches namespace resource
// - cache resync time: 10 seconds
kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(
kubeClient,
10*time.Second)
// KUBERNETES Dynamic informer
// - cahce resync time: 10 seconds
kubedynamicInformer := client.NewDynamicSharedInformerFactory(10 * time.Second)
kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, resyncPeriod)
kubedynamicInformer := client.NewDynamicSharedInformerFactory(resyncPeriod)
// WERBHOOK REGISTRATION CLIENT
webhookRegistrationClient := webhookconfig.NewWebhookRegistrationClient(
clientConfig,
client,
@ -143,10 +133,7 @@ func main() {
// watches CRD resources:
// - Policy
// - PolicyVolation
// - cache resync time: 10 seconds
pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(
pclient,
10*time.Second)
pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, resyncPeriod)
// Configuration Data
// dynamically load the configuration from configMap
@ -187,9 +174,8 @@ func main() {
// POLICY CONTROLLER
// - reconciliation policy and policy violation
// - process policy on existing resources
// - status aggregator: receives stats when a policy is applied
// & updates the policy status
pc, err := policy.NewPolicyController(pclient,
// - status aggregator: receives stats when a policy is applied & updates the policy status
policyCtrl, err := policy.NewPolicyController(pclient,
client,
pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().ClusterPolicyViolations(),
@ -201,6 +187,7 @@ func main() {
rWebhookWatcher,
log.Log.WithName("PolicyController"),
)
if err != nil {
setupLog.Error(err, "Failed to create policy controller")
os.Exit(1)
@ -222,6 +209,7 @@ func main() {
statusSync.Listener,
log.Log.WithName("GenerateController"),
)
// GENERATE REQUEST CLEANUP
// -- cleans up the generate requests that have not been processed(i.e. state = [Pending, Failed]) for more than defined timeout
grcc := generatecleanup.NewController(
@ -257,7 +245,7 @@ func main() {
}
// Sync openAPI definitions of resources
openApiSync := openapi.NewCRDSync(client, openAPIController)
openAPISync := openapi.NewCRDSync(client, openAPIController)
// WEBHOOOK
// - https server to provide endpoints called based on rules defined in Mutating & Validation webhook configuration
@ -284,10 +272,12 @@ func main() {
log.Log.WithName("WebhookServer"),
openAPIController,
)
if err != nil {
setupLog.Error(err, "Failed to create webhook server")
os.Exit(1)
}
// Start the components
pInformer.Start(stopCh)
kubeInformer.Start(stopCh)
@ -296,13 +286,13 @@ func main() {
go rWebhookWatcher.Run(stopCh)
go configData.Run(stopCh)
go policyMetaStore.Run(stopCh)
go pc.Run(1, stopCh)
go policyCtrl.Run(3, stopCh)
go egen.Run(1, stopCh)
go grc.Run(1, stopCh)
go grcc.Run(1, stopCh)
go pvgen.Run(1, stopCh)
go statusSync.Run(1, stopCh)
openApiSync.Run(1, stopCh)
openAPISync.Run(1, stopCh)
// verifys if the admission control is enabled and active
// resync: 60 seconds
@ -319,8 +309,10 @@ func main() {
defer func() {
cancel()
}()
// cleanup webhookconfigurations followed by webhook shutdown
server.Stop(ctx)
// resource cleanup
// remove webhook configurations
<-cleanUp

1
go.mod
View file

@ -14,6 +14,7 @@ require (
github.com/json-iterator/go v1.1.9 // indirect
github.com/julienschmidt/httprouter v1.3.0
github.com/minio/minio v0.0.0-20200114012931-30922148fbb5
github.com/rogpeppe/godef v1.1.2 // indirect
github.com/spf13/cobra v0.0.5
github.com/tevino/abool v0.0.0-20170917061928-9b9efcf221b5
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect

9
go.sum
View file

@ -1,3 +1,5 @@
9fans.net/go v0.0.0-20181112161441-237454027057 h1:OcHlKWkAMJEF1ndWLGxp5dnJQkYM/YImUOvsBoz6h5E=
9fans.net/go v0.0.0-20181112161441-237454027057/go.mod h1:diCsxrliIURU9xsYtjCp5AbpQKqdhKmf0ujWDUSkfoY=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@ -732,6 +734,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/go-internal v1.0.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/godef v1.1.2 h1:c5mCx0EcCORJOdVMREX7Lgh1raTxAHFmOfXdEB9u8Jw=
github.com/rogpeppe/godef v1.1.2/go.mod h1:WtY9A/ovuQ+UakAJ1/CEqwwulX/WJjb2kgkokCHi/GY=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rubenv/sql-migrate v0.0.0-20190212093014-1007f53448d7/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@ -872,6 +876,7 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0=
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -886,6 +891,7 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1036,7 +1042,10 @@ golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200226224502-204d844ad48d h1:loGv/4fxITSrCD4t2P8ZF4oUC4RlRFDAsczcoUS2g6c=
golang.org/x/tools v0.0.0-20200226224502-204d844ad48d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
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=
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=

View file

@ -1,6 +1,7 @@
package checker
import (
"fmt"
"sync"
"time"
@ -32,10 +33,11 @@ func (t *LastReqTime) Time() time.Time {
return t.t
}
//SetTime stes the lastrequest time
//SetTime updates the lastrequest time
func (t *LastReqTime) SetTime(tm time.Time) {
t.mu.Lock()
defer t.mu.Unlock()
t.t = tm
}
@ -52,6 +54,7 @@ func checkIfPolicyWithMutateAndGenerateExists(pLister kyvernolister.ClusterPolic
if err != nil {
log.Error(err, "failed to list cluster policies")
}
for _, policy := range policies {
if policy.HasMutateOrValidateOrGenerate() {
// as there exists one policy with mutate or validate rule
@ -59,13 +62,14 @@ func checkIfPolicyWithMutateAndGenerateExists(pLister kyvernolister.ClusterPolic
return true
}
}
return false
}
//Run runs the checker and verify the resource update
func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister, eventGen event.Interface, client *dclient.Client, defaultResync time.Duration, deadline time.Duration, stopCh <-chan struct{}) {
logger := t.log
logger.V(2).Info("tarting default resync for webhook checker", "resyncTime", defaultResync)
logger.V(4).Info("starting default resync for webhook checker", "resyncTime", defaultResync)
maxDeadline := deadline * time.Duration(MaxRetryCount)
ticker := time.NewTicker(defaultResync)
/// interface to update and increment kyverno webhook status via annotations
@ -85,35 +89,37 @@ func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister, eventGen ev
for {
select {
case <-ticker.C:
// if there are no policies then we dont have a webhook on resource.
// we indirectly check if the resource
if !checkIfPolicyWithMutateAndGenerateExists(pLister, logger) {
continue
}
// get current time
timeDiff := time.Since(t.Time())
if timeDiff > maxDeadline {
logger.Info("request exceeded max deadline", "deadline", maxDeadline)
logger.Info("Admission Control failing: Webhook is not receiving requests forwarded by api-server as per webhook configurations")
// set the status unavailable
err := fmt.Errorf("Admission control configuration error")
logger.Error(err, "webhook check failed", "deadline", maxDeadline)
if err := statuscontrol.FailedStatus(); err != nil {
logger.Error(err, "failed to set 'failed' status")
logger.Error(err, "error setting webhook check status to failed")
}
continue
}
if timeDiff > deadline {
logger.Info("Admission Control failing: Webhook is not receiving requests forwarded by api-server as per webhook configurations")
logger.V(3).Info("webhook check deadline exceeded", "deadline", deadline)
// send request to update the kyverno deployment
if err := statuscontrol.IncrementAnnotation(); err != nil {
logger.Error(err, "failed to increment annotation")
}
continue
}
// if the status was false before then we update it to true
// send request to update the kyverno deployment
if err := statuscontrol.SuccessStatus(); err != nil {
logger.Error(err, "failed to update success status")
logger.Error(err, "error setting webhook check status to success")
}
case <-stopCh:
// handler termination signal
logger.V(2).Info("stopping default resync for webhook checker")

View file

@ -13,7 +13,7 @@ const deployName string = "kyverno"
const deployNamespace string = "kyverno"
const annCounter string = "kyverno.io/generationCounter"
const annWebhookStats string = "kyverno.io/webhookActive"
const annWebhookStatus string = "kyverno.io/webhookActive"
//StatusInterface provides api to update webhook active annotations on kyverno deployments
type StatusInterface interface {
@ -52,37 +52,42 @@ func NewVerifyControl(client *dclient.Client, eventGen event.Interface, log logr
}
func (vc StatusControl) setStatus(status string) error {
logger := vc.log
logger.Info(fmt.Sprintf("setting deployment %s in ns %s annotation %s to %s", deployName, deployNamespace, annWebhookStats, status))
logger := vc.log.WithValues("name", deployName, "namespace", deployNamespace)
var ann map[string]string
var err error
deploy, err := vc.client.GetResource("Deployment", deployNamespace, deployName)
if err != nil {
logger.Error(err, "failed to get deployment resource")
logger.Error(err, "failed to get deployment")
return err
}
ann = deploy.GetAnnotations()
if ann == nil {
ann = map[string]string{}
ann[annWebhookStats] = status
ann[annWebhookStatus] = status
}
webhookAction, ok := ann[annWebhookStats]
deployStatus, ok := ann[annWebhookStatus]
if ok {
// annotatiaion is present
if webhookAction == status {
logger.V(4).Info(fmt.Sprintf("annotation %s already set to '%s'", annWebhookStats, status))
if deployStatus == status {
logger.V(4).Info(fmt.Sprintf("annotation %s already set to '%s'", annWebhookStatus, status))
return nil
}
}
// set the status
ann[annWebhookStats] = status
logger.Info("updating deployment annotation", "key", annWebhookStatus, "val", status)
ann[annWebhookStatus] = status
deploy.SetAnnotations(ann)
// update counter
_, err = vc.client.UpdateResource("Deployment", deployNamespace, deploy, false)
if err != nil {
logger.Error(err, fmt.Sprintf("failed to update annotation %s for deployment %s in namespace %s", annWebhookStats, deployName, deployNamespace))
logger.Error(err, "failed to update deployment annotation", "key", annWebhookStatus, "val", status)
return err
}
// create event on kyverno deployment
createStatusUpdateEvent(status, vc.eventGen)
return nil
@ -101,34 +106,42 @@ func createStatusUpdateEvent(status string, eventGen event.Interface) {
//IncrementAnnotation ...
func (vc StatusControl) IncrementAnnotation() error {
logger := vc.log
logger.Info(fmt.Sprintf("setting deployment %s in ns %s annotation %s", deployName, deployNamespace, annCounter))
var ann map[string]string
var err error
deploy, err := vc.client.GetResource("Deployment", deployNamespace, deployName)
if err != nil {
logger.Error(err, "failed to get deployment %s in namespace %s", deployName, deployNamespace)
logger.Error(err, "failed to find deployment %s in namespace %s", deployName, deployNamespace)
return err
}
ann = deploy.GetAnnotations()
if ann == nil {
ann = map[string]string{}
}
if ann[annCounter] == "" {
ann[annCounter] = "0"
}
counter, err := strconv.Atoi(ann[annCounter])
if err != nil {
logger.Error(err, "Failed to parse string")
logger.Error(err, "Failed to parse string", "name", annCounter, "value", ann[annCounter])
return err
}
// increment counter
counter++
ann[annCounter] = strconv.Itoa(counter)
logger.Info("incrementing annotation", "old", annCounter, "new", counter)
logger.V(3).Info("updating webhook test annotation", "key", annCounter, "value", counter, "deployment", deployName, "namespace", deployNamespace)
deploy.SetAnnotations(ann)
// update counter
_, err = vc.client.UpdateResource("Deployment", deployNamespace, deploy, false)
if err != nil {
logger.Error(err, fmt.Sprintf("failed to update annotation %s for deployment %s in namespace %s", annCounter, deployName, deployNamespace))
return err
}
return nil
}

View file

@ -221,6 +221,7 @@ func convertToCSR(obj *unstructured.Unstructured) (*certificates.CertificateSign
//IDiscovery provides interface to mange Kind and GVR mapping
type IDiscovery interface {
FindResource(kind string) (*meta.APIResource, schema.GroupVersionResource, error)
GetGVRFromKind(kind string) schema.GroupVersionResource
GetServerVersion() (*version.Info, error)
OpenAPISchema() (*openapi_v2.Document, error)
@ -257,56 +258,67 @@ func (c ServerPreferredResources) Poll(resync time.Duration, stopCh <-chan struc
}
}
// OpenAPISchema returns the API server OpenAPI schema document
func (c ServerPreferredResources) OpenAPISchema() (*openapi_v2.Document, error) {
return c.cachedClient.OpenAPISchema()
}
//GetGVRFromKind get the Group Version Resource from kind
// if kind is not found in first attempt we invalidate the cache,
// the retry will then fetch the new registered resources and check again
// if not found after 2 attempts, we declare kind is not found
// kind is Case sensitive
// GetGVRFromKind get the Group Version Resource from kind
func (c ServerPreferredResources) GetGVRFromKind(kind string) schema.GroupVersionResource {
var gvr schema.GroupVersionResource
var err error
gvr, err = loadServerResources(kind, c.cachedClient, c.log)
if err != nil && !c.cachedClient.Fresh() {
// invalidate cahce & re-try once more
c.cachedClient.Invalidate()
gvr, err = loadServerResources(kind, c.cachedClient, c.log)
if err == nil {
return gvr
}
_, gvr, err := c.FindResource(kind)
if err != nil {
c.log.Info("schema not found", "kind", kind)
return schema.GroupVersionResource{}
}
return gvr
}
//GetServerVersion returns the server version of the cluster
// GetServerVersion returns the server version of the cluster
func (c ServerPreferredResources) GetServerVersion() (*version.Info, error) {
return c.cachedClient.ServerVersion()
}
func loadServerResources(k string, cdi discovery.CachedDiscoveryInterface, log logr.Logger) (schema.GroupVersionResource, error) {
logger := log.WithName("loadServerResources")
emptyGVR := schema.GroupVersionResource{}
serverresources, err := cdi.ServerPreferredResources()
if err != nil {
logger.V(4).Info("failed to get registered preferred resources", "err", err.Error())
// FindResource finds an API resource that matches 'kind'. If the resource is not
// found and the Cache is not fresh, the cache is invalidated and a retry is attempted
func (c ServerPreferredResources) FindResource(kind string) (*meta.APIResource, schema.GroupVersionResource, error) {
r, gvr, err := c.findResource(kind)
if err == nil {
return r, gvr, nil
}
if !c.cachedClient.Fresh() {
c.cachedClient.Invalidate()
if r, gvr, err = c.findResource(kind); err == nil {
return r, gvr, nil
}
}
return nil, schema.GroupVersionResource{}, err
}
func (c ServerPreferredResources) findResource(k string) (*meta.APIResource, schema.GroupVersionResource, error) {
serverresources, err := c.cachedClient.ServerPreferredResources()
if err != nil {
c.log.Error(err, "failed to get registered preferred resources")
return nil, schema.GroupVersionResource{}, err
}
for _, serverresource := range serverresources {
for _, resource := range serverresource.APIResources {
// skip the resource names with "/", to avoid comparison with subresources
// skip the resource names with "/", to avoid comparison with subresources
if resource.Kind == k && !strings.Contains(resource.Name, "/") {
gv, err := schema.ParseGroupVersion(serverresource.GroupVersion)
if err != nil {
logger.Error(err, "failed to parse groupVersion from schema", "groupVersion", serverresource.GroupVersion)
return emptyGVR, err
c.log.Error(err, "failed to parse groupVersion", "groupVersion", serverresource.GroupVersion)
return nil, schema.GroupVersionResource{}, err
}
return gv.WithResource(resource.Name), nil
return &resource, gv.WithResource(resource.Name), nil
}
}
}
return emptyGVR, fmt.Errorf("kind '%s' not found", k)
return nil, schema.GroupVersionResource{}, fmt.Errorf("kind '%s' not found", k)
}

View file

@ -1,10 +1,12 @@
package client
import (
"fmt"
"strings"
openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -76,6 +78,10 @@ func (c *fakeDiscoveryClient) GetGVRFromKind(kind string) schema.GroupVersionRes
return c.getGVR(resource)
}
func (c *fakeDiscoveryClient) FindResource(kind string) (*meta.APIResource, schema.GroupVersionResource, error) {
return nil, schema.GroupVersionResource{}, fmt.Errorf("Not implemented")
}
func (c *fakeDiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) {
return nil, nil
}

View file

@ -21,10 +21,14 @@ import (
//TODO: generation rules
func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, logger logr.Logger) (responses []response.EngineResponse) {
startTime := time.Now()
logger.Info("start applying policy", "startTime", startTime)
defer func() {
logger.Info("finisnhed applying policy", "processingTime", time.Since(startTime))
name := resource.GetKind() + "/" + resource.GetName()
ns := resource.GetNamespace()
if ns != "" {
name = ns + "/" + name
}
logger.V(3).Info("applyPolicy", "resource", name, "processingTime", time.Since(startTime))
}()
var engineResponses []response.EngineResponse

View file

@ -206,6 +206,7 @@ func (pc *PolicyController) updatePolicy(old, cur interface{}) {
return
}
}
logger.V(4).Info("updating policy", "name", oldP.Name)
pc.enqueuePolicy(curP)
}
@ -225,11 +226,13 @@ func (pc *PolicyController) deletePolicy(obj interface{}) {
return
}
}
logger.V(4).Info("deleting policy", "name", p.Name)
// Unregister from policy meta-store
if err := pc.pMetaStore.UnRegister(*p); err != nil {
logger.Error(err, "failed to unregister policy", "name", p.Name)
}
// we process policies that are not set of background processing as we need to perform policy violation
// cleanup when a policy is deleted.
pc.enqueuePolicy(p)
@ -263,6 +266,7 @@ func (pc *PolicyController) Run(workers int, stopCh <-chan struct{}) {
for i := 0; i < workers; i++ {
go wait.Until(pc.worker, time.Second, stopCh)
}
<-stopCh
}
@ -315,49 +319,54 @@ func (pc *PolicyController) syncPolicy(key string) error {
defer func() {
logger.V(4).Info("finished syncing policy", "key", key, "processingTime", time.Since(startTime))
}()
policy, err := pc.pLister.Get(key)
if errors.IsNotFound(err) {
logger.V(2).Info("policy deleted", "key", key)
// delete cluster policy violation
if err := pc.deleteClusterPolicyViolations(key); err != nil {
return err
}
// delete namespaced policy violation
if err := pc.deleteNamespacedPolicyViolations(key); err != nil {
return err
}
go pc.deletePolicyViolations(key)
// remove webhook configurations if there are no policies
if err := pc.removeResourceWebhookConfiguration(); err != nil {
// do not fail, if unable to delete resource webhook config
logger.Error(err, "failed to remove resource webhook configurations")
}
return nil
}
if err != nil {
return err
}
pc.resourceWebhookWatcher.RegisterResourceWebhook()
// process policies on existing resources
engineResponses := pc.processExistingResources(*policy)
// report errors
pc.cleanupAndReport(engineResponses)
return nil
}
func (pc *PolicyController) deletePolicyViolations(key string) {
if err := pc.deleteClusterPolicyViolations(key); err != nil {
pc.log.Error(err, "failed to delete policy violation", "key", key)
}
if err := pc.deleteNamespacedPolicyViolations(key); err != nil {
pc.log.Error(err, "failed to delete policy violation", "key", key)
}
}
func (pc *PolicyController) deleteClusterPolicyViolations(policy string) error {
cpvList, err := pc.getClusterPolicyViolationForPolicy(policy)
if err != nil {
return err
}
for _, cpv := range cpvList {
if err := pc.pvControl.DeleteClusterPolicyViolation(cpv.Name); err != nil {
return err
pc.log.Error(err, "failed to delete policy violation", "name", cpv.Name)
}
}
return nil
}
@ -366,11 +375,13 @@ func (pc *PolicyController) deleteNamespacedPolicyViolations(policy string) erro
if err != nil {
return err
}
for _, nspv := range nspvList {
if err := pc.pvControl.DeleteNamespacedPolicyViolation(nspv.Namespace, nspv.Name); err != nil {
return err
pc.log.Error(err, "failed to delete policy violation", "name", nspv.Name)
}
}
return nil
}

View file

@ -58,26 +58,35 @@ func listResources(client *client.Client, policy kyverno.ClusterPolicy, configHa
resourceMap := map[string]unstructured.Unstructured{}
for _, rule := range policy.Spec.Rules {
// resources that match
for _, k := range rule.MatchResources.Kinds {
var namespaces []string
if len(rule.MatchResources.Namespaces) > 0 {
namespaces = append(namespaces, rule.MatchResources.Namespaces...)
log.V(4).Info("namespaces included", "namespaces", rule.MatchResources.Namespaces)
} else {
log.V(4).Info("processing all namespaces", "rule", rule.Name)
// get all namespaces
namespaces = getAllNamespaces(client, log)
resourceSchema, _, err := client.DiscoveryClient.FindResource(k)
if err != nil {
log.Error(err, "failed to find resource", "kind", k)
continue
}
// get resources in the namespaces
for _, ns := range namespaces {
rMap := getResourcesPerNamespace(k, client, ns, rule, configHandler, log)
if !resourceSchema.Namespaced {
rMap := getResourcesPerNamespace(k, client, "", rule, configHandler, log)
mergeresources(resourceMap, rMap)
}
} else {
var namespaces []string
if len(rule.MatchResources.Namespaces) > 0 {
log.V(4).Info("namespaces included", "namespaces", rule.MatchResources.Namespaces)
namespaces = append(namespaces, rule.MatchResources.Namespaces...)
} else {
log.V(4).Info("processing all namespaces", "rule", rule.Name)
namespaces = getAllNamespaces(client, log)
}
for _, ns := range namespaces {
rMap := getResourcesPerNamespace(k, client, ns, rule, configHandler, log)
mergeresources(resourceMap, rMap)
}
}
}
}
return resourceMap
}

View file

@ -60,7 +60,7 @@ func (wrc *WebhookRegistrationClient) constructDebugVerifyMutatingWebhookConfig(
func (wrc *WebhookRegistrationClient) removeVerifyWebhookMutatingWebhookConfig(wg *sync.WaitGroup) {
defer wg.Done()
// Mutating webhook configuration
var err error
var mutatingConfig string
if wrc.serverIP != "" {
@ -68,14 +68,18 @@ func (wrc *WebhookRegistrationClient) removeVerifyWebhookMutatingWebhookConfig(w
} else {
mutatingConfig = config.VerifyMutatingWebhookConfigurationName
}
logger := wrc.log.WithValues("name", mutatingConfig)
logger.V(4).Info("removing webhook configuration")
err = wrc.client.DeleteResource(MutatingWebhookConfigurationKind, "", mutatingConfig, false)
if errorsapi.IsNotFound(err) {
logger.Error(err, "verify webhook configuration, does not exits. not deleting")
} else if err != nil {
logger.Error(err, "failed to delete verify wwebhook configuration")
} else {
logger.V(4).Info("successfully deleted verify webhook configuration")
logger.V(5).Info("verify webhook configuration not found")
return
}
if err != nil {
logger.Error(err, "failed to delete verify wwebhook configuration")
return
}
logger.V(4).Info("successfully deleted verify webhook configuration")
}

View file

@ -51,7 +51,7 @@ func NewWebhookRegistrationClient(
func (wrc *WebhookRegistrationClient) Register() error {
logger := wrc.log.WithName("Register")
if wrc.serverIP != "" {
logger.Info("Registering webhook", "url", fmt.Sprintf("https://%s", wrc.serverIP))
logger.V(4).Info("Registering webhook", "url", fmt.Sprintf("https://%s", wrc.serverIP))
}
// For the case if cluster already has this configs
@ -249,20 +249,23 @@ func (wrc *WebhookRegistrationClient) createVerifyMutatingWebhookConfiguration()
// Register will fail if the config exists, so there is no need to fail on error
func (wrc *WebhookRegistrationClient) removeWebhookConfigurations() {
startTime := time.Now()
wrc.log.Info("Started cleaning up webhookconfigurations")
wrc.log.Info("removing prior webhook configurations")
defer func() {
wrc.log.V(4).Info("Finished cleaning up webhookcongfigurations", "processingTime", time.Since(startTime))
wrc.log.V(4).Info("removed webhookcongfigurations", "processingTime", time.Since(startTime))
}()
var wg sync.WaitGroup
wg.Add(5)
// mutating and validating webhook configuration for Kubernetes resources
go wrc.removeResourceMutatingWebhookConfiguration(&wg)
go wrc.removeResourceValidatingWebhookConfiguration(&wg)
// mutating and validating webhook configurtion for Policy CRD resource
go wrc.removePolicyMutatingWebhookConfiguration(&wg)
go wrc.removePolicyValidatingWebhookConfiguration(&wg)
// mutating webhook configuration for verifying webhook
go wrc.removeVerifyWebhookMutatingWebhookConfig(&wg)
@ -285,48 +288,53 @@ func (wrc *WebhookRegistrationClient) removeResourceValidatingWebhookConfigurati
}
}
// delete policy mutating webhookconfigurations
// handle wait group
func (wrc *WebhookRegistrationClient) removePolicyMutatingWebhookConfiguration(wg *sync.WaitGroup) {
defer wg.Done()
// Mutating webhook configuration
var mutatingConfig string
if wrc.serverIP != "" {
mutatingConfig = config.PolicyMutatingWebhookConfigurationDebugName
} else {
mutatingConfig = config.PolicyMutatingWebhookConfigurationName
}
logger := wrc.log.WithValues("name", mutatingConfig)
logger.V(4).Info("removing mutating webhook configuration")
err := wrc.client.DeleteResource(MutatingWebhookConfigurationKind, "", mutatingConfig, false)
if errorsapi.IsNotFound(err) {
logger.Error(err, "policy mutating webhook configuration does not exist, not deleting")
} else if err != nil {
logger.Error(err, "failed to delete policy mutating webhook configuration")
} else {
logger.V(4).Info("successfully deleted policy mutating webhook configutation")
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.V(4).Info("successfully deleted policy mutating webhook configutation")
}
// delete policy validating webhookconfigurations
// handle wait group
func (wrc *WebhookRegistrationClient) removePolicyValidatingWebhookConfiguration(wg *sync.WaitGroup) {
defer wg.Done()
// Validating webhook configuration
var validatingConfig string
if wrc.serverIP != "" {
validatingConfig = config.PolicyValidatingWebhookConfigurationDebugName
} else {
validatingConfig = config.PolicyValidatingWebhookConfigurationName
}
logger := wrc.log.WithValues("name", validatingConfig)
logger.V(4).Info("removing validating webhook configuration")
err := wrc.client.DeleteResource(ValidatingWebhookConfigurationKind, "", validatingConfig, false)
if errorsapi.IsNotFound(err) {
logger.Error(err, "policy validating webhook configuration does not exist, not deleting")
} else if err != nil {
logger.Error(err, "failed to delete policy validating webhook configuration")
} else {
logger.V(4).Info("successfully deleted policy validating webhook configutation")
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.V(4).Info("successfully deleted policy validating webhook configutation")
}

View file

@ -12,7 +12,7 @@ import (
func (wrc *WebhookRegistrationClient) constructDebugMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
logger := wrc.log
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.MutatingWebhookServicePath)
logger.V(4).Info("Debug MutatingWebhookConfig registed", "url", url)
logger.V(4).Info("Debug MutatingWebhookConfig registered", "url", url)
return &admregapi.MutatingWebhookConfiguration{
ObjectMeta: v1.ObjectMeta{
Name: config.MutatingWebhookConfigurationDebugName,
@ -57,7 +57,7 @@ func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(caData []by
}
}
//GetResourceMutatingWebhookConfigName provi
//GetResourceMutatingWebhookConfigName returns the webhook configuration name
func (wrc *WebhookRegistrationClient) GetResourceMutatingWebhookConfigName() string {
if wrc.serverIP != "" {
return config.MutatingWebhookConfigurationDebugName
@ -72,14 +72,16 @@ func (wrc *WebhookRegistrationClient) RemoveResourceMutatingWebhookConfiguration
// delete webhook configuration
err := wrc.client.DeleteResource(MutatingWebhookConfigurationKind, "", configName, false)
if errors.IsNotFound(err) {
logger.Error(err, "resource does not exit")
logger.V(5).Info("webhook configuration not found")
return nil
}
if err != nil {
logger.V(4).Info("failed to delete resource")
logger.V(4).Info("failed to delete webhook configuration")
return err
}
logger.V(4).Info("deleted resource")
logger.V(4).Info("deleted webhook configuration")
return nil
}
@ -130,25 +132,30 @@ func (wrc *WebhookRegistrationClient) constructValidatingWebhookConfig(caData []
}
}
// GetResourceValidatingWebhookConfigName returns the webhook configuration name
func (wrc *WebhookRegistrationClient) GetResourceValidatingWebhookConfigName() string {
if wrc.serverIP != "" {
return config.ValidatingWebhookConfigurationDebugName
}
return config.ValidatingWebhookConfigurationName
}
// RemoveResourceValidatingWebhookConfiguration deletes an existing webhook configuration
func (wrc *WebhookRegistrationClient) RemoveResourceValidatingWebhookConfiguration() error {
configName := wrc.GetResourceValidatingWebhookConfigName()
logger := wrc.log.WithValues("kind", ValidatingWebhookConfigurationKind, "name", configName)
err := wrc.client.DeleteResource(ValidatingWebhookConfigurationKind, "", configName, false)
if errors.IsNotFound(err) {
logger.Error(err, "resource does not exist; deleted already")
logger.V(5).Info("webhook configuration not found")
return nil
}
if err != nil {
logger.Error(err, "failed to delete the resource")
logger.Error(err, "failed to delete the webhook configuration")
return err
}
logger.Info("resource deleted")
logger.Info("webhook configuration deleted")
return nil
}

View file

@ -14,11 +14,11 @@ import (
//ResourceWebhookRegister manages the resource webhook registration
type ResourceWebhookRegister struct {
// pendingCreation indicates the status of resource webhook creation
pendingCreation *abool.AtomicBool
LastReqTime *checker.LastReqTime
mwebhookconfigSynced cache.InformerSynced
vwebhookconfigSynced cache.InformerSynced
// list/get mutatingwebhookconfigurations
pendingMutateWebhookCreation *abool.AtomicBool
pendingValidateWebhookCreation *abool.AtomicBool
LastReqTime *checker.LastReqTime
mwebhookconfigSynced cache.InformerSynced
vwebhookconfigSynced cache.InformerSynced
mWebhookConfigLister mconfiglister.MutatingWebhookConfigurationLister
vWebhookConfigLister mconfiglister.ValidatingWebhookConfigurationLister
webhookRegistrationClient *WebhookRegistrationClient
@ -36,7 +36,8 @@ func NewResourceWebhookRegister(
log logr.Logger,
) *ResourceWebhookRegister {
return &ResourceWebhookRegister{
pendingCreation: abool.New(),
pendingMutateWebhookCreation: abool.New(),
pendingValidateWebhookCreation: abool.New(),
LastReqTime: lastReqTime,
mwebhookconfigSynced: mconfigwebhookinformer.Informer().HasSynced,
mWebhookConfigLister: mconfigwebhookinformer.Lister(),
@ -50,51 +51,60 @@ func NewResourceWebhookRegister(
//RegisterResourceWebhook registers a resource webhook
func (rww *ResourceWebhookRegister) RegisterResourceWebhook() {
logger := rww.log
// drop the request if creation is in processing
if rww.pendingCreation.IsSet() {
logger.V(3).Info("resource webhook configuration is in pending creation, skip the request")
timeDiff := time.Since(rww.LastReqTime.Time())
if timeDiff < checker.DefaultDeadline {
if !rww.pendingMutateWebhookCreation.IsSet() {
go rww.createMutatingWebhook()
}
if !rww.pendingValidateWebhookCreation.IsSet() {
go rww.createValidateWebhook()
}
}
}
func (rww *ResourceWebhookRegister) createMutatingWebhook() {
rww.pendingMutateWebhookCreation.Set()
defer rww.pendingMutateWebhookCreation.UnSet()
mutatingConfigName := rww.webhookRegistrationClient.GetResourceMutatingWebhookConfigName()
mutatingConfig, _ := rww.mWebhookConfigLister.Get(mutatingConfigName)
if mutatingConfig != nil {
rww.log.V(5).Info("mutating webhoook configuration exists", "name", mutatingConfigName)
} else {
err := rww.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration()
if err != nil {
rww.log.Error(err, "failed to create resource mutating webhook configuration, re-queue creation request")
rww.RegisterResourceWebhook()
return
}
rww.log.V(2).Info("created mutating webhook", "name", mutatingConfigName)
}
}
func (rww *ResourceWebhookRegister) createValidateWebhook() {
rww.pendingValidateWebhookCreation.Set()
defer rww.pendingValidateWebhookCreation.UnSet()
if rww.RunValidationInMutatingWebhook == "true" {
rww.log.V(2).Info("validation is configured to run during mutate webhook")
return
}
timeDiff := time.Since(rww.LastReqTime.Time())
if timeDiff < checker.DefaultDeadline {
logger.V(3).Info("verified webhook status, creating webhook configuration")
go func() {
mutatingConfigName := rww.webhookRegistrationClient.GetResourceMutatingWebhookConfigName()
mutatingConfig, _ := rww.mWebhookConfigLister.Get(mutatingConfigName)
if mutatingConfig != nil {
logger.V(4).Info("mutating webhoook configuration already exists")
} else {
rww.pendingCreation.Set()
err1 := rww.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration()
rww.pendingCreation.UnSet()
if err1 != nil {
logger.Error(err1, "failed to create resource mutating webhook configuration, re-queue creation request")
rww.RegisterResourceWebhook()
return
}
logger.V(3).Info("successfully created mutating webhook configuration for resources")
}
validatingConfigName := rww.webhookRegistrationClient.GetResourceValidatingWebhookConfigName()
validatingConfig, _ := rww.vWebhookConfigLister.Get(validatingConfigName)
if validatingConfig != nil {
rww.log.V(4).Info("validating webhoook configuration exists", "name", validatingConfigName)
} else {
err := rww.webhookRegistrationClient.CreateResourceValidatingWebhookConfiguration()
if err != nil {
rww.log.Error(err, "failed to create resource validating webhook configuration; re-queue creation request")
rww.RegisterResourceWebhook()
return
}
if rww.RunValidationInMutatingWebhook != "true" {
validatingConfigName := rww.webhookRegistrationClient.GetResourceValidatingWebhookConfigName()
validatingConfig, _ := rww.vWebhookConfigLister.Get(validatingConfigName)
if validatingConfig != nil {
logger.V(4).Info("validating webhoook configuration already exists")
} else {
rww.pendingCreation.Set()
err2 := rww.webhookRegistrationClient.CreateResourceValidatingWebhookConfiguration()
rww.pendingCreation.UnSet()
if err2 != nil {
logger.Error(err2, "failed to create resource validating webhook configuration; re-queue creation request")
rww.RegisterResourceWebhook()
return
}
logger.V(3).Info("successfully created validating webhook configuration for resources")
}
}
}()
rww.log.V(2).Info("created validating webhook", "name", validatingConfigName)
}
}
@ -121,7 +131,7 @@ func (rww *ResourceWebhookRegister) RemoveResourceWebhookConfiguration() error {
if err != nil {
return err
}
logger.V(3).Info("emoved mutating resource webhook configuration")
logger.V(3).Info("removed mutating resource webhook configuration")
}
if rww.RunValidationInMutatingWebhook != "true" {

View file

@ -18,9 +18,17 @@ import (
// HandleMutation handles mutating webhook admission request
// return value: generated patches
func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resource unstructured.Unstructured, policies []kyverno.ClusterPolicy, roles, clusterRoles []string) []byte {
logger := ws.log.WithValues("action", "mutation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
logger.V(4).Info("incoming request")
func (ws *WebhookServer) HandleMutation(
request *v1beta1.AdmissionRequest,
resource unstructured.Unstructured,
policies []kyverno.ClusterPolicy, roles, clusterRoles []string) []byte {
resourceName := request.Kind.Kind + "/" + request.Name
if request.Namespace != "" {
resourceName = request.Namespace + "/" + resourceName
}
logger := ws.log.WithValues("action", "mutate", "resource", resourceName, "operation", request.Operation)
var patches [][]byte
var engineResponses []response.EngineResponse
@ -59,17 +67,23 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
policyContext.Policy = policy
engineResponse := engine.Mutate(policyContext)
if engineResponse.PolicyResponse.RulesAppliedCount <= 0 {
continue
}
engineResponses = append(engineResponses, engineResponse)
ws.statusListener.Send(mutateStats{resp: engineResponse})
if !engineResponse.IsSuccesful() {
logger.V(4).Info("failed to apply policy", "policy", policy.Name)
logger.Info("failed to apply policy", "policy", policy.Name)
continue
}
err := ws.openAPIController.ValidateResource(*engineResponse.PatchedResource.DeepCopy(), engineResponse.PatchedResource.GetKind())
if err != nil {
logger.Error(err, "failed to validate resource")
logger.Error(err, "validation error", "policy", policy.Name)
continue
}
// gather patches
patches = append(patches, engineResponse.GetPatches()...)
if len(engineResponse.GetPatches()) != 0 {
@ -88,6 +102,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
// generate violation when response fails
pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses, logger)
ws.pvGenerator.Add(pvInfos...)
// REPORTING EVENTS
// Scenario 1:
// some/all policies failed to apply on the resource. a policy volation is generated.

View file

@ -81,7 +81,7 @@ func generateJSONPatchesForDefaults(policy *kyverno.ClusterPolicy, log logr.Logg
}
func defaultBackgroundFlag(policy *kyverno.ClusterPolicy, log logr.Logger) ([]byte, string) {
// default 'Background' flag to 'true' if not specified
// set 'Background' flag to 'true' if not specified
defaultVal := true
if policy.Spec.Background == nil {
log.V(4).Info("setting default value", "spec.background", true)
@ -94,19 +94,22 @@ func defaultBackgroundFlag(policy *kyverno.ClusterPolicy, log logr.Logger) ([]by
"add",
&defaultVal,
}
patchByte, err := json.Marshal(jsonPatch)
if err != nil {
log.Error(err, "failed to set default value", "spec.background", true)
return nil, ""
}
log.Info("generated JSON Patch to set default", "spec.background", true)
log.V(3).Info("generated JSON Patch to set default", "spec.background", true)
return patchByte, fmt.Sprintf("default 'Background' to '%s'", strconv.FormatBool(true))
}
return nil, ""
}
func defaultvalidationFailureAction(policy *kyverno.ClusterPolicy, log logr.Logger) ([]byte, string) {
// default ValidationFailureAction to "audit" if not specified
// set ValidationFailureAction to "audit" if not specified
if policy.Spec.ValidationFailureAction == "" {
log.V(4).Info("setting defautl value", "spec.validationFailureAction", Audit)
jsonPatch := struct {
@ -116,16 +119,19 @@ func defaultvalidationFailureAction(policy *kyverno.ClusterPolicy, log logr.Logg
}{
"/spec/validationFailureAction",
"add",
Audit, //audit
Audit,
}
patchByte, err := json.Marshal(jsonPatch)
if err != nil {
log.Error(err, "failed to default value", "spec.validationFailureAction", Audit)
return nil, ""
}
log.Info("generated JSON Patch to set default", "spec.validationFailureAction", Audit)
log.V(3).Info("generated JSON Patch to set default", "spec.validationFailureAction", Audit)
return patchByte, fmt.Sprintf("default 'ValidationFailureAction' to '%s'", Audit)
}
return nil, ""
}

View file

@ -37,7 +37,6 @@ import (
)
// WebhookServer contains configured TLS server with MutationWebhook.
// MutationWebhook gets policies from policyController and takes control of the cluster with kubeclient.
type WebhookServer struct {
server http.Server
client *client.Client
@ -150,15 +149,16 @@ func NewWebhookServer(
}
func (ws *WebhookServer) handlerFunc(handler func(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse, filter bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
return func(rw http.ResponseWriter, r *http.Request) {
startTime := time.Now()
// for every request received on the ep update last request time,
// this is used to verify admission control
ws.lastReqTime.SetTime(time.Now())
admissionReview := ws.bodyToAdmissionReview(r, w)
ws.lastReqTime.SetTime(startTime)
admissionReview := ws.bodyToAdmissionReview(r, rw)
if admissionReview == nil {
ws.log.Info("failed to parse admission review request", "request", r)
return
}
logger := ws.log.WithValues("kind", admissionReview.Request.Kind, "namespace", admissionReview.Request.Namespace, "name", admissionReview.Request.Name)
defer func() {
logger.V(4).Info("request processed", "processingTime", time.Since(startTime))
@ -166,29 +166,32 @@ func (ws *WebhookServer) handlerFunc(handler func(request *v1beta1.AdmissionRequ
admissionReview.Response = &v1beta1.AdmissionResponse{
Allowed: true,
UID: admissionReview.Request.UID,
}
// Do not process the admission requests for kinds that are in filterKinds for filtering
request := admissionReview.Request
if filter {
if !ws.configHandler.ToFilter(request.Kind.Kind, request.Namespace, request.Name) {
admissionReview.Response = handler(request)
}
} else {
admissionReview.Response = handler(request)
}
admissionReview.Response.UID = request.UID
responseJSON, err := json.Marshal(admissionReview)
if err != nil {
http.Error(w, fmt.Sprintf("Could not encode response: %v", err), http.StatusInternalServerError)
if filter && ws.configHandler.ToFilter(request.Kind.Kind, request.Namespace, request.Name) {
writeResponse(rw, admissionReview)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if _, err := w.Write(responseJSON); err != nil {
http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError)
}
admissionReview.Response = handler(request)
writeResponse(rw, admissionReview)
return
}
}
func writeResponse(rw http.ResponseWriter, admissionReview *v1beta1.AdmissionReview) {
responseJSON, err := json.Marshal(admissionReview)
if err != nil {
http.Error(rw, fmt.Sprintf("Could not encode response: %v", err), http.StatusInternalServerError)
return
}
rw.Header().Set("Content-Type", "application/json; charset=utf-8")
if _, err := rw.Write(responseJSON); err != nil {
http.Error(rw, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError)
}
}
@ -364,6 +367,7 @@ func (ws *WebhookServer) RunAsync(stopCh <-chan struct{}) {
}
}(ws)
logger.Info("starting")
// verifys if the admission control is enabled and active
// resync: 60 seconds
// deadline: 60 seconds (send request)

View file

@ -17,9 +17,17 @@ import (
// HandleValidation handles validating webhook admission request
// If there are no errors in validating rule we apply generation rules
// patchedResource is the (resource + patches) after applying mutation rules
func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, policies []kyverno.ClusterPolicy, patchedResource []byte, roles, clusterRoles []string) (bool, string) {
logger := ws.log.WithValues("action", "validation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
logger.V(4).Info("incoming request")
func (ws *WebhookServer) HandleValidation(
request *v1beta1.AdmissionRequest,
policies []kyverno.ClusterPolicy,
patchedResource []byte, roles, clusterRoles []string) (bool, string) {
resourceName := request.Kind.Kind + "/" + request.Name
if request.Namespace != "" {
resourceName = request.Namespace + "/" + resourceName
}
logger := ws.log.WithValues("action", "validate", "resource", resourceName, "operation", request.Operation)
// Get new and old resource
newR, oldR, err := extractResources(patchedResource, request)
@ -28,6 +36,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
logger.Error(err, "failed to extract resource")
return true, ""
}
userRequestInfo := kyverno.RequestInfo{
Roles: roles,
ClusterRoles: clusterRoles,