mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-30 19:35:06 +00:00
add checker to verify if mutatingwebhook is enabled or not + refactoring
This commit is contained in:
parent
968aead2ad
commit
e022084dd0
12 changed files with 432 additions and 143 deletions
8
main.go
8
main.go
|
@ -31,9 +31,6 @@ var (
|
|||
filterK8Resources string
|
||||
)
|
||||
|
||||
// TODO: tune resync time differently for each informer
|
||||
const defaultReSyncTime = 10 * time.Second
|
||||
|
||||
func main() {
|
||||
defer glog.Flush()
|
||||
printVersionInfo()
|
||||
|
@ -162,7 +159,10 @@ func main() {
|
|||
go egen.Run(1, stopCh)
|
||||
go nsc.Run(1, stopCh)
|
||||
|
||||
//TODO add WG for the go routines?
|
||||
// verifys if the admission control is enabled and active
|
||||
// resync: 60 seconds
|
||||
// deadline: 60 seconds (send request)
|
||||
// max deadline: deadline*3 (set the deployment annotation as false)
|
||||
server.RunAsync(stopCh)
|
||||
|
||||
<-stopCh
|
||||
|
|
114
pkg/checker/checker.go
Normal file
114
pkg/checker/checker.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
package checker
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
|
||||
dclient "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
//MaxRetryCount defines the max deadline count
|
||||
const MaxRetryCount int = 3
|
||||
|
||||
// LastReqTime
|
||||
type LastReqTime struct {
|
||||
t time.Time
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func (t *LastReqTime) Time() time.Time {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
return t.t
|
||||
}
|
||||
|
||||
func (t *LastReqTime) SetTime(tm time.Time) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
glog.V(4).Info("updating last request time")
|
||||
t.t = tm
|
||||
}
|
||||
|
||||
func NewLastReqTime() *LastReqTime {
|
||||
return &LastReqTime{
|
||||
t: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func checkIfPolicyWithMutateAndGenerateExists(pLister kyvernolister.ClusterPolicyLister) bool {
|
||||
policies, err := pLister.ListResources(labels.NewSelector())
|
||||
if err != nil {
|
||||
glog.Error()
|
||||
}
|
||||
for _, policy := range policies {
|
||||
if policy.HasMutateOrValidate() {
|
||||
// as there exists one policy with mutate or validate rule
|
||||
// so there must be a webhook configuration on resource
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//Run runs the checker and verify the resource update
|
||||
func (t *LastReqTime) Run(pLister kyvernolister.ClusterPolicyLister, client *dclient.Client, defaultResync time.Duration, deadline time.Duration, stopCh <-chan struct{}) {
|
||||
glog.V(2).Infof("starting default resync for webhook checker with resync time %d", defaultResync)
|
||||
maxDeadline := deadline * time.Duration(MaxRetryCount)
|
||||
ticker := time.NewTicker(defaultResync)
|
||||
var statuscontrol StatusInterface
|
||||
/// interface to update and increment kyverno webhook status via annotations
|
||||
statuscontrol = NewVerifyControl(client)
|
||||
// send the initial update status
|
||||
if checkIfPolicyWithMutateAndGenerateExists(pLister) {
|
||||
if err := statuscontrol.SuccessStatus(); err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
defer ticker.Stop()
|
||||
// - has recieved request -> set webhookstatus as "True"
|
||||
// - no requests recieved
|
||||
// -> if greater than deadline, send update request
|
||||
// -> if greater than maxDeadline, send failed status update
|
||||
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) {
|
||||
continue
|
||||
}
|
||||
// get current time
|
||||
timeDiff := time.Since(t.Time())
|
||||
if timeDiff > maxDeadline {
|
||||
glog.Infof("failed to recieve any request for more than %v ", maxDeadline)
|
||||
glog.Info("Admission Control failing: Webhook is not recieving requests forwarded by api-server as per webhook configurations")
|
||||
// set the status unavailable
|
||||
if err := statuscontrol.FailedStatus(); err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if timeDiff > deadline {
|
||||
glog.Info("Admission Control failing: Webhook is not recieving requests forwarded by api-server as per webhook configurations")
|
||||
// send request to update the kyverno deployment
|
||||
if err := statuscontrol.IncrementAnnotation(); err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
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 {
|
||||
glog.Error(err)
|
||||
}
|
||||
case <-stopCh:
|
||||
// handler termination signal
|
||||
glog.V(2).Infof("stopping default resync for webhook checker")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
114
pkg/checker/status.go
Normal file
114
pkg/checker/status.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
package checker
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/glog"
|
||||
dclient "github.com/nirmata/kyverno/pkg/dclient"
|
||||
)
|
||||
|
||||
const deployName string = "kyverno"
|
||||
const deployNamespace string = "kyverno"
|
||||
|
||||
const annCounter string = "kyverno.io/generationCounter"
|
||||
const annWebhookStats string = "kyverno.io/webhookActive"
|
||||
|
||||
//StatusInterface provides api to update webhook active annotations on kyverno deployments
|
||||
type StatusInterface interface {
|
||||
// Increments generation counter annotation
|
||||
IncrementAnnotation() error
|
||||
// update annotation to inform webhook is active
|
||||
SuccessStatus() error
|
||||
// update annotation to inform webhook is inactive
|
||||
FailedStatus() error
|
||||
}
|
||||
|
||||
//StatusControl controls the webhook status
|
||||
type StatusControl struct {
|
||||
client *dclient.Client
|
||||
}
|
||||
|
||||
//SuccessStatus ...
|
||||
func (vc StatusControl) SuccessStatus() error {
|
||||
return vc.setStatus("true")
|
||||
}
|
||||
|
||||
//FailedStatus ...
|
||||
func (vc StatusControl) FailedStatus() error {
|
||||
return vc.setStatus("false")
|
||||
}
|
||||
|
||||
// NewVerifyControl ...
|
||||
func NewVerifyControl(client *dclient.Client) *StatusControl {
|
||||
return &StatusControl{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (vc StatusControl) setStatus(status string) error {
|
||||
glog.Infof("setting deployment %s in ns %s annotation %s to %s", deployName, deployNamespace, annWebhookStats, status)
|
||||
var ann map[string]string
|
||||
var err error
|
||||
deploy, err := vc.client.GetResource("Deployment", deployNamespace, deployName)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("failed to get deployment %s in namespace %s: %v", deployName, deployNamespace, err)
|
||||
return err
|
||||
}
|
||||
ann = deploy.GetAnnotations()
|
||||
if ann == nil {
|
||||
ann = map[string]string{}
|
||||
ann[annWebhookStats] = status
|
||||
}
|
||||
webhookAction, ok := ann[annWebhookStats]
|
||||
if ok {
|
||||
// annotatiaion is present
|
||||
if webhookAction == status {
|
||||
glog.V(4).Infof("annotation %s already set to '%s'", annWebhookStats, status)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// set the status
|
||||
ann[annWebhookStats] = status
|
||||
deploy.SetAnnotations(ann)
|
||||
// update counter
|
||||
_, err = vc.client.UpdateResource("Deployment", deployNamespace, deploy, false)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("failed to update annotation %s for deployment %s in namespace %s: %v", annWebhookStats, deployName, deployNamespace, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//IncrementAnnotation ...
|
||||
func (vc StatusControl) IncrementAnnotation() error {
|
||||
glog.Infof("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 {
|
||||
glog.V(4).Infof("failed to get deployment %s in namespace %s: %v", deployName, deployNamespace, err)
|
||||
return err
|
||||
}
|
||||
ann = deploy.GetAnnotations()
|
||||
if ann == nil {
|
||||
ann = map[string]string{}
|
||||
ann[annCounter] = "0"
|
||||
}
|
||||
counter, err := strconv.Atoi(ann[annCounter])
|
||||
if err != nil {
|
||||
glog.V(4).Infof("failed to parse string: %v", err)
|
||||
return err
|
||||
}
|
||||
// increment counter
|
||||
counter++
|
||||
ann[annCounter] = strconv.Itoa(counter)
|
||||
glog.Info("incrementing annotation %s counter to %d", annCounter, counter)
|
||||
deploy.SetAnnotations(ann)
|
||||
// update counter
|
||||
_, err = vc.client.UpdateResource("Deployment", deployNamespace, deploy, false)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("failed to update annotation %s for deployment %s in namespace %s: %v", annCounter, deployName, deployNamespace, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -15,6 +15,10 @@ const (
|
|||
// ValidatingWebhookConfigurationDebug = "kyverno-validating-webhook-cfg-debug"
|
||||
// ValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook"
|
||||
|
||||
VerifyMutatingWebhookConfigurationName = "kyverno-verify-mutating-webhook-cfg"
|
||||
VerifyMutatingWebhookConfigurationDebugName = "kyverno-verify-mutating-webhook-cfg-debug"
|
||||
VerifyMutatingWebhookName = "nirmata.kyverno.verify-mutating-webhook"
|
||||
|
||||
PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg"
|
||||
PolicyValidatingWebhookConfigurationDebugName = "kyverno-policy-validating-webhook-cfg-debug"
|
||||
PolicyValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook"
|
||||
|
@ -36,6 +40,7 @@ var (
|
|||
ValidatingWebhookServicePath = "/validate"
|
||||
PolicyValidatingWebhookServicePath = "/policyvalidate"
|
||||
PolicyMutatingWebhookServicePath = "/policymutate"
|
||||
VerifyMutatingWebhookServicePath = "/verifymutate"
|
||||
|
||||
SupportedKinds = []string{
|
||||
"ConfigMap",
|
||||
|
|
78
pkg/webhookconfig/checker.go
Normal file
78
pkg/webhookconfig/checker.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
package webhookconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
||||
errorsapi "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (wrc *WebhookRegistrationClient) constructVerifyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.VerifyMutatingWebhookConfigurationName,
|
||||
OwnerReferences: []v1.OwnerReference{
|
||||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
generateWebhook(
|
||||
config.VerifyMutatingWebhookName,
|
||||
config.VerifyMutatingWebhookServicePath,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
"deployments/*",
|
||||
"apps",
|
||||
"v1",
|
||||
[]admregapi.OperationType{admregapi.Update},
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) constructDebugVerifyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.VerifyMutatingWebhookServicePath)
|
||||
glog.V(4).Infof("Debug VerifyMutatingWebhookConfig is registered with url %s\n", url)
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.VerifyMutatingWebhookConfigurationDebugName,
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
generateDebugWebhook(
|
||||
config.VerifyMutatingWebhookName,
|
||||
url,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
"deployments/*",
|
||||
"apps",
|
||||
"v1",
|
||||
[]admregapi.OperationType{admregapi.Update},
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) removeVerifyWebhookMutatingWebhookConfig() {
|
||||
// Muating webhook configuration
|
||||
var err error
|
||||
var mutatingConfig string
|
||||
if wrc.serverIP != "" {
|
||||
mutatingConfig = config.VerifyMutatingWebhookConfigurationDebugName
|
||||
} else {
|
||||
mutatingConfig = config.VerifyMutatingWebhookConfigurationName
|
||||
}
|
||||
glog.V(4).Infof("removing webhook configuration %s", mutatingConfig)
|
||||
err = wrc.registrationClient.MutatingWebhookConfigurations().Delete(mutatingConfig, &v1.DeleteOptions{})
|
||||
if errorsapi.IsNotFound(err) {
|
||||
glog.V(4).Infof("verify webhook configuration %s, does not exits. not deleting", mutatingConfig)
|
||||
} else if err != nil {
|
||||
glog.Errorf("failed to delete verify webhook configuration %s: %v", mutatingConfig, err)
|
||||
} else {
|
||||
glog.V(4).Infof("succesfully deleted verify webhook configuration %s", mutatingConfig)
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
||||
errorsapi "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
|
@ -104,3 +105,43 @@ func (wrc *WebhookRegistrationClient) contructDebugPolicyMutatingWebhookConfig(c
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
// removePolicyWebhookConfigurations removes mutating and validating webhook configurations, if already presnt
|
||||
// webhookConfigurations are re-created later
|
||||
func (wrc *WebhookRegistrationClient) removePolicyWebhookConfigurations() {
|
||||
// Validating webhook configuration
|
||||
var err error
|
||||
var validatingConfig string
|
||||
if wrc.serverIP != "" {
|
||||
validatingConfig = config.PolicyValidatingWebhookConfigurationDebugName
|
||||
} else {
|
||||
validatingConfig = config.PolicyValidatingWebhookConfigurationName
|
||||
}
|
||||
glog.V(4).Infof("removing webhook configuration %s", validatingConfig)
|
||||
err = wrc.registrationClient.ValidatingWebhookConfigurations().Delete(validatingConfig, &v1.DeleteOptions{})
|
||||
if errorsapi.IsNotFound(err) {
|
||||
glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", validatingConfig)
|
||||
} else if err != nil {
|
||||
glog.Errorf("failed to delete policy webhook configuration %s: %v", validatingConfig, err)
|
||||
} else {
|
||||
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", validatingConfig)
|
||||
}
|
||||
|
||||
// Mutating webhook configuration
|
||||
var mutatingConfig string
|
||||
if wrc.serverIP != "" {
|
||||
mutatingConfig = config.PolicyMutatingWebhookConfigurationDebugName
|
||||
} else {
|
||||
mutatingConfig = config.PolicyMutatingWebhookConfigurationName
|
||||
}
|
||||
|
||||
glog.V(4).Infof("removing webhook configuration %s", mutatingConfig)
|
||||
err = wrc.registrationClient.MutatingWebhookConfigurations().Delete(mutatingConfig, &v1.DeleteOptions{})
|
||||
if errorsapi.IsNotFound(err) {
|
||||
glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", mutatingConfig)
|
||||
} else if err != nil {
|
||||
glog.Errorf("failed to delete policy webhook configuration %s: %v", mutatingConfig, err)
|
||||
} else {
|
||||
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", mutatingConfig)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,13 @@ func (wrc *WebhookRegistrationClient) Register() error {
|
|||
if err := wrc.createPolicyMutatingWebhookConfiguration(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create Verify mutating webhook configuration resource
|
||||
// that is used to check if admission control is enabled or not
|
||||
if err := wrc.createVerifyMutatingWebhookConfiguration(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -184,6 +191,36 @@ func (wrc *WebhookRegistrationClient) createPolicyMutatingWebhookConfiguration()
|
|||
return nil
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) createVerifyMutatingWebhookConfiguration() error {
|
||||
var caData []byte
|
||||
var config *admregapi.MutatingWebhookConfiguration
|
||||
|
||||
// read CA data from
|
||||
// 1) secret(config)
|
||||
// 2) kubeconfig
|
||||
if caData = wrc.readCaData(); caData == nil {
|
||||
return errors.New("Unable to extract CA data from configuration")
|
||||
}
|
||||
|
||||
// if serverIP is specified we assume its debug mode
|
||||
if wrc.serverIP != "" {
|
||||
// debug mode
|
||||
// clientConfig - URL
|
||||
config = wrc.constructDebugVerifyMutatingWebhookConfig(caData)
|
||||
} else {
|
||||
// clientConfig - service
|
||||
config = wrc.constructVerifyMutatingWebhookConfig(caData)
|
||||
}
|
||||
|
||||
// create mutating webhook configuration resource
|
||||
if _, err := wrc.registrationClient.MutatingWebhookConfigurations().Create(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("created Mutating Webhook Configuration %s ", config.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeregisterAll deletes webhook configs from cluster
|
||||
// This function does not fail on error:
|
||||
// Register will fail if the config exists, so there is no need to fail on error
|
||||
|
@ -198,44 +235,7 @@ func (wrc *WebhookRegistrationClient) removeWebhookConfigurations() {
|
|||
|
||||
// mutating and validating webhook configurtion for Policy CRD resource
|
||||
wrc.removePolicyWebhookConfigurations()
|
||||
}
|
||||
|
||||
// removePolicyWebhookConfigurations removes mutating and validating webhook configurations, if already presnt
|
||||
// webhookConfigurations are re-created later
|
||||
func (wrc *WebhookRegistrationClient) removePolicyWebhookConfigurations() {
|
||||
// Validating webhook configuration
|
||||
var err error
|
||||
var validatingConfig string
|
||||
if wrc.serverIP != "" {
|
||||
validatingConfig = config.PolicyValidatingWebhookConfigurationDebugName
|
||||
} else {
|
||||
validatingConfig = config.PolicyValidatingWebhookConfigurationName
|
||||
}
|
||||
glog.V(4).Infof("removing webhook configuration %s", validatingConfig)
|
||||
err = wrc.registrationClient.ValidatingWebhookConfigurations().Delete(validatingConfig, &v1.DeleteOptions{})
|
||||
if errorsapi.IsNotFound(err) {
|
||||
glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", validatingConfig)
|
||||
} else if err != nil {
|
||||
glog.Errorf("failed to delete policy webhook configuration %s: %v", validatingConfig, err)
|
||||
} else {
|
||||
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", validatingConfig)
|
||||
}
|
||||
|
||||
// Mutating webhook configuration
|
||||
var mutatingConfig string
|
||||
if wrc.serverIP != "" {
|
||||
mutatingConfig = config.PolicyMutatingWebhookConfigurationDebugName
|
||||
} else {
|
||||
mutatingConfig = config.PolicyMutatingWebhookConfigurationName
|
||||
}
|
||||
|
||||
glog.V(4).Infof("removing webhook configuration %s", mutatingConfig)
|
||||
err = wrc.registrationClient.MutatingWebhookConfigurations().Delete(mutatingConfig, &v1.DeleteOptions{})
|
||||
if errorsapi.IsNotFound(err) {
|
||||
glog.V(4).Infof("policy webhook configuration %s, does not exits. not deleting", mutatingConfig)
|
||||
} else if err != nil {
|
||||
glog.Errorf("failed to delete policy webhook configuration %s: %v", mutatingConfig, err)
|
||||
} else {
|
||||
glog.V(4).Infof("succesfully deleted policy webhook configuration %s", mutatingConfig)
|
||||
}
|
||||
|
||||
// muating webhook configuration use to verify if admission control flow is working or not
|
||||
wrc.removeVerifyWebhookMutatingWebhookConfig()
|
||||
}
|
||||
|
|
|
@ -1,91 +1,14 @@
|
|||
package webhooks
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
||||
"k8s.io/api/admission/v1beta1"
|
||||
)
|
||||
|
||||
const MaxRetryCount int = 3
|
||||
|
||||
// Last Request Time
|
||||
type LastReqTime struct {
|
||||
t time.Time
|
||||
mu sync.RWMutex
|
||||
RetryCount int
|
||||
}
|
||||
|
||||
func (t *LastReqTime) Time() time.Time {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
return t.t
|
||||
}
|
||||
|
||||
func (t *LastReqTime) SetTime(tm time.Time) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.t = tm
|
||||
t.RetryCount = MaxRetryCount
|
||||
}
|
||||
|
||||
func (t *LastReqTime) DecrementRetryCounter() {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.RetryCount--
|
||||
}
|
||||
|
||||
func NewLastReqTime() *LastReqTime {
|
||||
return &LastReqTime{
|
||||
t: time.Now(),
|
||||
func (ws *WebhookServer) handleVerifyRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
glog.V(4).Infof("Receive request in mutating webhook '/verify': Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *LastReqTime) checker(kyvernoClient *kyvernoclient.Clientset, defaultResync time.Duration, deadline time.Duration, stopCh <-chan struct{}) {
|
||||
sendDummyRequest := func(kyvernoClient *kyvernoclient.Clientset) {
|
||||
dummyPolicy := kyverno.ClusterPolicy{
|
||||
Spec: kyverno.Spec{
|
||||
Rules: []kyverno.Rule{
|
||||
kyverno.Rule{
|
||||
Name: "dummyPolicy",
|
||||
MatchResources: kyverno.MatchResources{
|
||||
ResourceDescription: kyverno.ResourceDescription{
|
||||
Kinds: []string{"Deployment"},
|
||||
},
|
||||
},
|
||||
Validation: kyverno.Validation{
|
||||
Message: "dummy validation policy rule",
|
||||
Pattern: "dummypattern",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
// this
|
||||
kyvernoClient.KyvernoV1alpha1().ClusterPolicies().Create(&dummyPolicy)
|
||||
}
|
||||
glog.V(2).Infof("starting default resync for webhook checker with resync time %d", defaultResync)
|
||||
ticker := time.NewTicker(defaultResync)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// get current time
|
||||
timeDiff := time.Since(t.Time())
|
||||
if timeDiff > deadline {
|
||||
if t.RetryCount == 0 {
|
||||
// set the status unavailable
|
||||
}
|
||||
t.DecrementRetryCounter()
|
||||
// send request again
|
||||
}
|
||||
|
||||
case <-stopCh:
|
||||
// handler termination signal
|
||||
break
|
||||
}
|
||||
}
|
||||
glog.V(2).Info("stopping default resync for webhook checker")
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
// HandleMutation handles mutating webhook admission request
|
||||
func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, []byte, string) {
|
||||
func (ws *WebhookServer) handleMutation(request *v1beta1.AdmissionRequest) (bool, []byte, string) {
|
||||
glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionReques
|
|||
Message: fmt.Sprintf("Failed to unmarshal policy admission request err %v", err),
|
||||
}}
|
||||
}
|
||||
|
||||
if err := policyvalidate.Validate(*policy); err != nil {
|
||||
admissionResp = &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/checker"
|
||||
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
|
||||
kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1alpha1"
|
||||
kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1alpha1"
|
||||
|
@ -43,6 +44,8 @@ type WebhookServer struct {
|
|||
configHandler config.Interface
|
||||
// channel for cleanup notification
|
||||
cleanUp chan<- struct{}
|
||||
// last request time
|
||||
lastReqTime *checker.LastReqTime
|
||||
}
|
||||
|
||||
// NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
|
||||
|
@ -83,6 +86,7 @@ func NewWebhookServer(
|
|||
policyStatus: policyStatus,
|
||||
configHandler: configHandler,
|
||||
cleanUp: cleanUp,
|
||||
lastReqTime: checker.NewLastReqTime(),
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(config.MutatingWebhookServicePath, ws.serve)
|
||||
|
@ -103,6 +107,9 @@ func NewWebhookServer(
|
|||
|
||||
// Main server endpoint for all requests
|
||||
func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
||||
// for every request recieved on the ep update last request time,
|
||||
// this is used to verify admission control
|
||||
ws.lastReqTime.SetTime(time.Now())
|
||||
admissionReview := ws.bodyToAdmissionReview(r, w)
|
||||
if admissionReview == nil {
|
||||
return
|
||||
|
@ -114,19 +121,24 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Do not process the admission requests for kinds that are in filterKinds for filtering
|
||||
request := admissionReview.Request
|
||||
if !ws.configHandler.ToFilter(request.Kind.Kind, request.Namespace, request.Name) {
|
||||
// Resource CREATE
|
||||
// Resource UPDATE
|
||||
switch r.URL.Path {
|
||||
case config.MutatingWebhookServicePath:
|
||||
switch r.URL.Path {
|
||||
case config.VerifyMutatingWebhookServicePath:
|
||||
// we do not apply filters as this endpoint is used explicity
|
||||
// to watch kyveno deployment and verify if admission control is enabled
|
||||
admissionReview.Response = ws.handleVerifyRequest(request)
|
||||
case config.MutatingWebhookServicePath:
|
||||
if !ws.configHandler.ToFilter(request.Kind.Kind, request.Namespace, request.Name) {
|
||||
admissionReview.Response = ws.handleAdmissionRequest(request)
|
||||
case config.PolicyValidatingWebhookServicePath:
|
||||
}
|
||||
case config.PolicyValidatingWebhookServicePath:
|
||||
if !ws.configHandler.ToFilter(request.Kind.Kind, request.Namespace, request.Name) {
|
||||
admissionReview.Response = ws.handlePolicyValidation(request)
|
||||
case config.PolicyMutatingWebhookServicePath:
|
||||
}
|
||||
case config.PolicyMutatingWebhookServicePath:
|
||||
if !ws.configHandler.ToFilter(request.Kind.Kind, request.Namespace, request.Name) {
|
||||
admissionReview.Response = ws.handlePolicyMutation(request)
|
||||
}
|
||||
}
|
||||
|
||||
admissionReview.Response.UID = request.UID
|
||||
|
||||
responseJSON, err := json.Marshal(admissionReview)
|
||||
|
@ -143,7 +155,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func (ws *WebhookServer) handleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
// MUTATION
|
||||
ok, patches, msg := ws.HandleMutation(request)
|
||||
ok, patches, msg := ws.handleMutation(request)
|
||||
if !ok {
|
||||
glog.V(4).Infof("Deny admission request: %v/%s/%s", request.Kind, request.Namespace, request.Name)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
|
@ -159,7 +171,7 @@ func (ws *WebhookServer) handleAdmissionRequest(request *v1beta1.AdmissionReques
|
|||
patchedResource := processResourceWithPatches(patches, request.Object.Raw)
|
||||
|
||||
// VALIDATION
|
||||
ok, msg = ws.HandleValidation(request, patchedResource)
|
||||
ok, msg = ws.handleValidation(request, patchedResource)
|
||||
if !ok {
|
||||
glog.V(4).Infof("Deny admission request: %v/%s/%s", request.Kind, request.Namespace, request.Name)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
|
@ -192,8 +204,11 @@ func (ws *WebhookServer) RunAsync(stopCh <-chan struct{}) {
|
|||
}
|
||||
}(ws)
|
||||
glog.Info("Started Webhook Server")
|
||||
|
||||
go checker(10*time.Second, stopCh)
|
||||
// verifys if the admission control is enabled and active
|
||||
// resync: 60 seconds
|
||||
// deadline: 60 seconds (send request)
|
||||
// max deadline: deadline*3 (set the deployment annotation as false)
|
||||
go ws.lastReqTime.Run(ws.pLister, ws.client, 60*time.Second, 60*time.Second, stopCh)
|
||||
}
|
||||
|
||||
// Stop TLS server and returns control after the server is shut down
|
||||
|
|
|
@ -11,10 +11,10 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// HandleValidation handles validating webhook admission request
|
||||
// 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, patchedResource []byte) (bool, string) {
|
||||
func (ws *WebhookServer) handleValidation(request *v1beta1.AdmissionRequest, patchedResource []byte) (bool, string) {
|
||||
glog.V(4).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
|
||||
request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue