mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
rebase with master
This commit is contained in:
commit
d43b4d93c2
19 changed files with 592 additions and 424 deletions
|
@ -96,9 +96,9 @@ spec :
|
|||
- ^(name): "*"
|
||||
resources:
|
||||
requests:
|
||||
memory: "$(<=./../../lim(its/mem)ory)"
|
||||
lim(its:
|
||||
mem)ory: "2048Mi"
|
||||
memory: "$(<=./../../limits/memory)"
|
||||
limits:
|
||||
memory: "2048Mi"
|
||||
````
|
||||
|
||||
### Allow OR across overlay pattern
|
||||
|
|
8
main.go
8
main.go
|
@ -37,7 +37,8 @@ func main() {
|
|||
printVersionInfo()
|
||||
// profile cpu and memory consuption
|
||||
prof = enableProfiling(cpu, memory)
|
||||
|
||||
// cleanUp Channel
|
||||
cleanUp := make(chan struct{})
|
||||
// CLIENT CONFIG
|
||||
clientConfig, err := createClientConfig(kubeconfig)
|
||||
if err != nil {
|
||||
|
@ -136,7 +137,7 @@ func main() {
|
|||
// -- annotations on resources with update details on mutation JSON patches
|
||||
// -- generate policy violation resource
|
||||
// -- generate events on policy and resource
|
||||
server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, webhookRegistrationClient, pc.GetPolicyStatusAggregator(), filterK8Resources)
|
||||
server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), pInformer.Kyverno().V1alpha1().PolicyViolations(), egen, webhookRegistrationClient, pc.GetPolicyStatusAggregator(), filterK8Resources, cleanUp)
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to create webhook server: %v\n", err)
|
||||
}
|
||||
|
@ -157,6 +158,9 @@ func main() {
|
|||
<-stopCh
|
||||
disableProfiling(prof)
|
||||
server.Stop()
|
||||
// resource cleanup
|
||||
// remove webhook configurations
|
||||
<-cleanUp
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -15,9 +15,13 @@ const (
|
|||
ValidatingWebhookConfigurationDebug = "kyverno-validating-webhook-cfg-debug"
|
||||
ValidatingWebhookName = "nirmata.kyverno.validating-webhook"
|
||||
|
||||
PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg"
|
||||
PolicyValidatingWebhookConfigurationDebug = "kyverno-policy-validating-webhook-cfg-debug"
|
||||
PolicyValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook"
|
||||
PolicyValidatingWebhookConfigurationName = "kyverno-policy-validating-webhook-cfg"
|
||||
PolicyValidatingWebhookConfigurationDebugName = "kyverno-policy-validating-webhook-cfg-debug"
|
||||
PolicyValidatingWebhookName = "nirmata.kyverno.policy-validating-webhook"
|
||||
|
||||
PolicyMutatingWebhookConfigurationName = "kyverno-policy-mutating-webhook-cfg"
|
||||
PolicyMutatingWebhookConfigurationDebugName = "kyverno-policy-mutating-webhook-cfg-debug"
|
||||
PolicyMutatingWebhookName = "nirmata.kyverno.policy-mutating-webhook"
|
||||
|
||||
// Due to kubernetes issue, we must use next literal constants instead of deployment TypeMeta fields
|
||||
// Issue: https://github.com/kubernetes/kubernetes/pull/63972
|
||||
|
@ -31,6 +35,7 @@ var (
|
|||
MutatingWebhookServicePath = "/mutate"
|
||||
ValidatingWebhookServicePath = "/validate"
|
||||
PolicyValidatingWebhookServicePath = "/policyvalidate"
|
||||
PolicyMutatingWebhookServicePath = "/policymutate"
|
||||
KubePolicyAppLabels = map[string]string{
|
||||
"app": "kyverno",
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ func processPatches(rule kyverno.Rule, resource []byte) (allPatches [][]byte, er
|
|||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
patchedDocument, err = ApplyPatchNew(patchedDocument, patchRaw)
|
||||
patches := [][]byte{patchRaw}
|
||||
patchedDocument, err = ApplyPatches(patchedDocument, patches)
|
||||
// TODO: continue on error if one of the patches fails, will add the failure event in such case
|
||||
if patch.Operation == "remove" {
|
||||
glog.Info(err)
|
||||
|
@ -152,7 +152,7 @@ func processPatchesNew(rule kyverno.Rule, resource unstructured.Unstructured) (r
|
|||
// error while processing JSON patches
|
||||
if len(errs) > 0 {
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprint("failed to process JSON patches: %v", func() string {
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", func() string {
|
||||
var str []string
|
||||
for _, err := range errs {
|
||||
str = append(str, err.Error())
|
||||
|
@ -163,7 +163,7 @@ func processPatchesNew(rule kyverno.Rule, resource unstructured.Unstructured) (r
|
|||
}
|
||||
err = patchedResource.UnmarshalJSON(resourceRaw)
|
||||
if err != nil {
|
||||
glog.Info("failed to unmarshall resource to undstructured: %v", err)
|
||||
glog.Infof("failed to unmarshall resource to undstructured: %v", err)
|
||||
response.Success = false
|
||||
response.Message = fmt.Sprintf("failed to process JSON patches: %v", err)
|
||||
return response, resource
|
||||
|
|
|
@ -51,8 +51,10 @@ func ValidateValueWithPattern(value, pattern interface{}) bool {
|
|||
return validateValueWithStringPatterns(value, typedPattern)
|
||||
case nil:
|
||||
return validateValueWithNilPattern(value)
|
||||
case map[string]interface{}, []interface{}:
|
||||
glog.Warning("Maps and arrays as patterns are not supported")
|
||||
case map[string]interface{}:
|
||||
return validateValueWithMapPattern(value, typedPattern)
|
||||
case []interface{}:
|
||||
glog.Warning("Arrays as patterns are not supported")
|
||||
return false
|
||||
default:
|
||||
glog.Warningf("Unknown type as pattern: %T\n", pattern)
|
||||
|
@ -60,6 +62,17 @@ func ValidateValueWithPattern(value, pattern interface{}) bool {
|
|||
}
|
||||
}
|
||||
|
||||
func validateValueWithMapPattern(value interface{}, typedPattern map[string]interface{}) bool {
|
||||
// verify the type of the resource value is map[string]interface,
|
||||
// we only check for existance of object, not the equality of content and value
|
||||
_, ok := value.(map[string]interface{})
|
||||
if !ok {
|
||||
glog.Warningf("Expected map[string]interface{}, found %T\n", value)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Handler for int values during validation process
|
||||
func validateValueWithIntPattern(value interface{}, pattern int64) bool {
|
||||
switch typedValue := value.(type) {
|
||||
|
|
|
@ -1571,8 +1571,8 @@ func TestValidate_ServiceTest(t *testing.T) {
|
|||
|
||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
||||
assert.NilError(t, err)
|
||||
res := Validate(policy, *resourceUnstructured)
|
||||
assert.Assert(t, len(res.RuleInfos) == 0)
|
||||
er := ValidateNew(policy, *resourceUnstructured)
|
||||
assert.Assert(t, len(er.PolicyResponse.Rules) == 0)
|
||||
}
|
||||
|
||||
func TestValidate_MapHasFloats(t *testing.T) {
|
||||
|
@ -1668,6 +1668,6 @@ func TestValidate_MapHasFloats(t *testing.T) {
|
|||
|
||||
resourceUnstructured, err := ConvertToUnstructured(rawResource)
|
||||
assert.NilError(t, err)
|
||||
res := Validate(policy, *resourceUnstructured)
|
||||
assert.Assert(t, len(res.RuleInfos) == 0)
|
||||
er := ValidateNew(policy, *resourceUnstructured)
|
||||
assert.Assert(t, len(er.PolicyResponse.Rules) == 0)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
@ -100,42 +99,31 @@ func applyPolicyOnRaw(policy *kyverno.Policy, rawResource []byte, gvk *metav1.Gr
|
|||
|
||||
rname := engine.ParseNameFromObject(rawResource)
|
||||
rns := engine.ParseNamespaceFromObject(rawResource)
|
||||
policyInfo := info.NewPolicyInfo(policy.Name,
|
||||
gvk.Kind,
|
||||
rname,
|
||||
rns,
|
||||
policy.Spec.ValidationFailureAction)
|
||||
|
||||
resource, err := ConvertToUnstructured(rawResource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//TODO check if the kind information is present resource
|
||||
// Process Mutation
|
||||
engineResponse := engine.Mutate(*policy, *resource)
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
if !policyInfo.IsSuccessful() {
|
||||
engineResponse := engine.MutateNew(*policy, *resource)
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns)
|
||||
for _, r := range engineResponse.RuleInfos {
|
||||
glog.Warning(r.Msgs)
|
||||
for _, r := range engineResponse.PolicyResponse.Rules {
|
||||
glog.Warning(r.Message)
|
||||
}
|
||||
} else if len(engineResponse.Patches) > 0 {
|
||||
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
|
||||
glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
|
||||
patchedResource, err = engine.ApplyPatches(rawResource, engineResponse.Patches)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to apply mutation patches:\n%v", err)
|
||||
}
|
||||
// Process Validation
|
||||
engineResponse := engine.Validate(*policy, *resource)
|
||||
|
||||
policyInfo.AddRuleInfos(engineResponse.RuleInfos)
|
||||
if !policyInfo.IsSuccessful() {
|
||||
// Process Validation
|
||||
engineResponse := engine.ValidateNew(*policy, *resource)
|
||||
|
||||
if !engineResponse.IsSuccesful() {
|
||||
glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns)
|
||||
for _, r := range engineResponse.RuleInfos {
|
||||
glog.Warning(r.Msgs)
|
||||
for _, r := range engineResponse.PolicyResponse.Rules {
|
||||
glog.Warning(r.Message)
|
||||
}
|
||||
return patchedResource, fmt.Errorf("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns)
|
||||
} else if len(engineResponse.RuleInfos) > 0 {
|
||||
} else if len(engineResponse.PolicyResponse.Rules) > 0 {
|
||||
glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, gvk.Kind, rname, rns)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ func applyPolicy(policy kyverno.Policy, resource unstructured.Unstructured, poli
|
|||
engineResponse, err = mutation(policy, resource, policyStatus)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
if err != nil {
|
||||
glog.Error("unable to process mutation rules: %v", err)
|
||||
glog.Errorf("unable to process mutation rules: %v", err)
|
||||
}
|
||||
gatherStat(policy.Name, engineResponse.PolicyResponse)
|
||||
//send stats
|
||||
|
|
|
@ -465,17 +465,17 @@ func (pc *PolicyController) handleWebhookRegistration(delete bool, policy *kyver
|
|||
// check empty policy first, then rule type in terms of O(time)
|
||||
if policies == nil {
|
||||
glog.V(3).Infoln("No policy found in the cluster, deregistering webhook")
|
||||
pc.webhookRegistrationClient.DeregisterMutatingWebhook()
|
||||
pc.webhookRegistrationClient.RemoveResourceMutatingWebhookConfiguration()
|
||||
} else if !HasMutateOrValidatePolicies(policies) {
|
||||
glog.V(3).Infoln("No muatate/validate policy found in the cluster, deregistering webhook")
|
||||
pc.webhookRegistrationClient.DeregisterMutatingWebhook()
|
||||
pc.webhookRegistrationClient.RemoveResourceMutatingWebhookConfiguration()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if webhookList == nil && HasMutateOrValidate(*policy) {
|
||||
glog.V(3).Infoln("Found policy without mutatingwebhook, registering webhook")
|
||||
pc.webhookRegistrationClient.RegisterMutatingWebhook()
|
||||
pc.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
120
pkg/webhookconfig/common.go
Normal file
120
pkg/webhookconfig/common.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
package webhookconfig
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
rest "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
func (wrc *WebhookRegistrationClient) readCaData() []byte {
|
||||
var caData []byte
|
||||
// Check if ca is defined in the secret tls-ca
|
||||
// assume the key and signed cert have been defined in secret tls.kyverno
|
||||
if caData = wrc.client.ReadRootCASecret(); len(caData) != 0 {
|
||||
glog.V(4).Infof("read CA from secret")
|
||||
return caData
|
||||
}
|
||||
glog.V(4).Infof("failed to read CA from secret, reading from kubeconfig")
|
||||
// load the CA from kubeconfig
|
||||
if caData = extractCA(wrc.clientConfig); len(caData) != 0 {
|
||||
glog.V(4).Infof("read CA from kubeconfig")
|
||||
return caData
|
||||
}
|
||||
glog.V(4).Infof("failed to read CA from kubeconfig")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractCA used for extraction CA from config
|
||||
func extractCA(config *rest.Config) (result []byte) {
|
||||
fileName := config.TLSClientConfig.CAFile
|
||||
|
||||
if fileName != "" {
|
||||
result, err := ioutil.ReadFile(fileName)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return config.TLSClientConfig.CAData
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) constructOwner() v1.OwnerReference {
|
||||
kubePolicyDeployment, err := wrc.client.GetKubePolicyDeployment()
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("Error when constructing OwnerReference, err: %v\n", err)
|
||||
return v1.OwnerReference{}
|
||||
}
|
||||
|
||||
return v1.OwnerReference{
|
||||
APIVersion: config.DeploymentAPIVersion,
|
||||
Kind: config.DeploymentKind,
|
||||
Name: kubePolicyDeployment.ObjectMeta.Name,
|
||||
UID: kubePolicyDeployment.ObjectMeta.UID,
|
||||
}
|
||||
}
|
||||
|
||||
func generateDebugWebhook(name, url string, caData []byte, validate bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.Webhook {
|
||||
return admregapi.Webhook{
|
||||
Name: name,
|
||||
ClientConfig: admregapi.WebhookClientConfig{
|
||||
URL: &url,
|
||||
CABundle: caData,
|
||||
},
|
||||
Rules: []admregapi.RuleWithOperations{
|
||||
admregapi.RuleWithOperations{
|
||||
Operations: operationTypes,
|
||||
Rule: admregapi.Rule{
|
||||
APIGroups: []string{
|
||||
apiGroups,
|
||||
},
|
||||
APIVersions: []string{
|
||||
apiVersions,
|
||||
},
|
||||
Resources: []string{
|
||||
resource,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
func generateWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32, resource, apiGroups, apiVersions string, operationTypes []admregapi.OperationType) admregapi.Webhook {
|
||||
return admregapi.Webhook{
|
||||
Name: name,
|
||||
ClientConfig: admregapi.WebhookClientConfig{
|
||||
Service: &admregapi.ServiceReference{
|
||||
Namespace: config.KubePolicyNamespace,
|
||||
Name: config.WebhookServiceName,
|
||||
Path: &servicePath,
|
||||
},
|
||||
CABundle: caData,
|
||||
},
|
||||
Rules: []admregapi.RuleWithOperations{
|
||||
admregapi.RuleWithOperations{
|
||||
Operations: operationTypes,
|
||||
Rule: admregapi.Rule{
|
||||
APIGroups: []string{
|
||||
apiGroups,
|
||||
},
|
||||
APIVersions: []string{
|
||||
apiVersions,
|
||||
},
|
||||
Resources: []string{
|
||||
resource,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
}
|
||||
}
|
110
pkg/webhookconfig/policy.go
Normal file
110
pkg/webhookconfig/policy.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
package webhookconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.PolicyValidatingWebhookConfigurationName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
OwnerReferences: []v1.OwnerReference{
|
||||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
generateWebhook(
|
||||
config.PolicyValidatingWebhookName,
|
||||
config.PolicyValidatingWebhookServicePath,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
"policies/*",
|
||||
"kyverno.io",
|
||||
"v1alpha1",
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
|
||||
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.PolicyValidatingWebhookServicePath)
|
||||
glog.V(4).Infof("Debug PolicyValidatingWebhookConfig is registered with url %s\n", url)
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.PolicyValidatingWebhookConfigurationDebugName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
generateDebugWebhook(
|
||||
config.PolicyValidatingWebhookName,
|
||||
url,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
"policies/*",
|
||||
"kyverno.io",
|
||||
"v1alpha1",
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) contructPolicyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.PolicyMutatingWebhookConfigurationName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
OwnerReferences: []v1.OwnerReference{
|
||||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
generateWebhook(
|
||||
config.PolicyMutatingWebhookName,
|
||||
config.PolicyMutatingWebhookServicePath,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
"policies/*",
|
||||
"kyverno.io",
|
||||
"v1alpha1",
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
func (wrc *WebhookRegistrationClient) contructDebugPolicyMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.PolicyMutatingWebhookServicePath)
|
||||
glog.V(4).Infof("Debug PolicyMutatingWebhookConfig is registered with url %s\n", url)
|
||||
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.PolicyMutatingWebhookConfigurationDebugName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
generateDebugWebhook(
|
||||
config.PolicyMutatingWebhookName,
|
||||
url,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
"policies/*",
|
||||
"kyverno.io",
|
||||
"v1alpha1",
|
||||
[]admregapi.OperationType{admregapi.Create, admregapi.Update},
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -2,9 +2,7 @@ package webhookconfig
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
|
@ -56,19 +54,57 @@ func (wrc *WebhookRegistrationClient) Register() error {
|
|||
}
|
||||
|
||||
// For the case if cluster already has this configs
|
||||
wrc.DeregisterAll()
|
||||
// remove previously create webhookconfigurations if any
|
||||
// webhook configurations are created dynamically based on the policy resources
|
||||
wrc.removeWebhookConfigurations()
|
||||
|
||||
// register policy validating webhook during inital start
|
||||
return wrc.RegisterPolicyValidatingWebhook()
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) RegisterMutatingWebhook() error {
|
||||
mutatingWebhookConfig, err := wrc.constructMutatingWebhookConfig(wrc.clientConfig)
|
||||
if err != nil {
|
||||
// Static Webhook configuration on Policy CRD
|
||||
// create Policy CRD validating webhook configuration resource
|
||||
// used for validating Policy CR
|
||||
if err := wrc.createPolicyValidatingWebhookConfiguration(); err != nil {
|
||||
return err
|
||||
}
|
||||
// create Policy CRD validating webhook configuration resource
|
||||
// used for defauling values in Policy CR
|
||||
if err := wrc.createPolicyMutatingWebhookConfiguration(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err = wrc.registrationClient.MutatingWebhookConfigurations().Create(mutatingWebhookConfig); err != nil {
|
||||
// RemovePolicyWebhookConfigurations removes webhook configurations for reosurces and policy
|
||||
// called during webhook server shutdown
|
||||
func (wrc *WebhookRegistrationClient) RemovePolicyWebhookConfigurations(cleanUp chan<- struct{}) {
|
||||
//TODO: dupliate, but a placeholder to perform more error handlind during cleanup
|
||||
wrc.removeWebhookConfigurations()
|
||||
// close channel to notify cleanup is complete
|
||||
close(cleanUp)
|
||||
}
|
||||
|
||||
//CreateResourceMutatingWebhookConfiguration create a Mutatingwebhookconfiguration resource for all resource type
|
||||
// used to forward request to kyverno webhooks to apply policeis
|
||||
// Mutationg webhook is be used for Mutating & Validating purpose
|
||||
func (wrc *WebhookRegistrationClient) CreateResourceMutatingWebhookConfiguration() 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.contructDebugMutatingWebhookConfig(caData)
|
||||
} else {
|
||||
// clientConfig - service
|
||||
config = wrc.constructMutatingWebhookConfig(caData)
|
||||
}
|
||||
|
||||
if _, err := wrc.registrationClient.MutatingWebhookConfigurations().Create(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -76,384 +112,126 @@ func (wrc *WebhookRegistrationClient) RegisterMutatingWebhook() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) RegisterValidatingWebhook() error {
|
||||
validationWebhookConfig, err := wrc.constructValidatingWebhookConfig(wrc.clientConfig)
|
||||
if err != nil {
|
||||
//registerPolicyValidatingWebhookConfiguration create a Validating webhook configuration for Policy CRD
|
||||
func (wrc *WebhookRegistrationClient) createPolicyValidatingWebhookConfiguration() error {
|
||||
var caData []byte
|
||||
var config *admregapi.ValidatingWebhookConfiguration
|
||||
|
||||
// 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.contructDebugPolicyValidatingWebhookConfig(caData)
|
||||
} else {
|
||||
// clientConfig - service
|
||||
config = wrc.contructPolicyValidatingWebhookConfig(caData)
|
||||
}
|
||||
|
||||
// create validating webhook configuration resource
|
||||
if _, err := wrc.registrationClient.ValidatingWebhookConfigurations().Create(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = wrc.registrationClient.ValidatingWebhookConfigurations().Create(validationWebhookConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wrc.ValidationRegistered.Set()
|
||||
glog.V(4).Infof("created Validating Webhook Configuration %s ", config.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) RegisterPolicyValidatingWebhook() error {
|
||||
policyValidationWebhookConfig, err := wrc.contructPolicyValidatingWebhookConfig()
|
||||
if err != nil {
|
||||
func (wrc *WebhookRegistrationClient) createPolicyMutatingWebhookConfiguration() 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.contructDebugPolicyMutatingWebhookConfig(caData)
|
||||
} else {
|
||||
// clientConfig - service
|
||||
config = wrc.contructPolicyMutatingWebhookConfig(caData)
|
||||
}
|
||||
|
||||
// create mutating webhook configuration resource
|
||||
if _, err := wrc.registrationClient.MutatingWebhookConfigurations().Create(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = wrc.registrationClient.ValidatingWebhookConfigurations().Create(policyValidationWebhookConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(3).Infoln("Policy validating webhook registered")
|
||||
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
|
||||
func (wrc *WebhookRegistrationClient) DeregisterAll() {
|
||||
wrc.DeregisterMutatingWebhook()
|
||||
wrc.deregisterValidatingWebhook()
|
||||
func (wrc *WebhookRegistrationClient) removeWebhookConfigurations() {
|
||||
startTime := time.Now()
|
||||
glog.V(4).Infof("Started cleaning up webhookconfigurations")
|
||||
defer func() {
|
||||
glog.V(4).Infof("Finished cleaning up webhookcongfigurations (%v)", time.Since(startTime))
|
||||
}()
|
||||
// mutating and validating webhook configuration for Kubernetes resources
|
||||
wrc.RemoveResourceMutatingWebhookConfiguration()
|
||||
|
||||
// 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 validatingConfig string
|
||||
if wrc.serverIP != "" {
|
||||
err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationDebug, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
}
|
||||
validatingConfig = config.PolicyValidatingWebhookConfigurationDebugName
|
||||
} else {
|
||||
validatingConfig = config.PolicyValidatingWebhookConfigurationName
|
||||
}
|
||||
err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.PolicyValidatingWebhookConfigurationName, &v1.DeleteOptions{})
|
||||
glog.V(4).Infof("removing webhook configuration %s", validatingConfig)
|
||||
err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(validatingConfig, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(mutatingConfig, &v1.DeleteOptions{}); err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) deregister() {
|
||||
wrc.DeregisterMutatingWebhook()
|
||||
wrc.deregisterValidatingWebhook()
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) DeregisterMutatingWebhook() {
|
||||
//RemoveResourceMutatingWebhookConfiguration removes mutating webhook configuration for all resources
|
||||
func (wrc *WebhookRegistrationClient) RemoveResourceMutatingWebhookConfiguration() {
|
||||
var configName string
|
||||
if wrc.serverIP != "" {
|
||||
err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationDebug, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
wrc.MutationRegistered.UnSet()
|
||||
}
|
||||
return
|
||||
configName = config.MutatingWebhookConfigurationDebug
|
||||
} else {
|
||||
configName = config.MutatingWebhookConfigurationName
|
||||
}
|
||||
|
||||
err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(config.MutatingWebhookConfigurationName, &v1.DeleteOptions{})
|
||||
// delete webhook configuration
|
||||
err := wrc.registrationClient.MutatingWebhookConfigurations().Delete(configName, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
} else {
|
||||
wrc.MutationRegistered.UnSet()
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) deregisterValidatingWebhook() {
|
||||
if wrc.serverIP != "" {
|
||||
err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationDebug, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
}
|
||||
wrc.ValidationRegistered.UnSet()
|
||||
return
|
||||
}
|
||||
|
||||
err := wrc.registrationClient.ValidatingWebhookConfigurations().Delete(config.ValidatingWebhookConfigurationName, &v1.DeleteOptions{})
|
||||
if err != nil && !errorsapi.IsNotFound(err) {
|
||||
glog.Error(err)
|
||||
}
|
||||
wrc.ValidationRegistered.UnSet()
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(configuration *rest.Config) (*admregapi.MutatingWebhookConfiguration, error) {
|
||||
var caData []byte
|
||||
// Check if ca is defined in the secret tls-ca
|
||||
// assume the key and signed cert have been defined in secret tls.kyverno
|
||||
caData = wrc.client.ReadRootCASecret()
|
||||
if len(caData) == 0 {
|
||||
// load the CA from kubeconfig
|
||||
caData = extractCA(configuration)
|
||||
}
|
||||
if len(caData) == 0 {
|
||||
return nil, errors.New("Unable to extract CA data from configuration")
|
||||
}
|
||||
|
||||
if wrc.serverIP != "" {
|
||||
return wrc.contructDebugMutatingWebhookConfig(caData), nil
|
||||
}
|
||||
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.MutatingWebhookConfigurationName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
OwnerReferences: []v1.OwnerReference{
|
||||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
constructWebhook(
|
||||
config.MutatingWebhookName,
|
||||
config.MutatingWebhookServicePath,
|
||||
caData,
|
||||
false,
|
||||
wrc.timeoutSeconds,
|
||||
),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) contructDebugMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.MutatingWebhookServicePath)
|
||||
glog.V(3).Infof("Debug MutatingWebhookConfig is registered with url %s\n", url)
|
||||
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.MutatingWebhookConfigurationDebug,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
constructDebugWebhook(
|
||||
config.MutatingWebhookName,
|
||||
url,
|
||||
caData,
|
||||
false,
|
||||
wrc.timeoutSeconds),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) constructValidatingWebhookConfig(configuration *rest.Config) (*admregapi.ValidatingWebhookConfiguration, error) {
|
||||
// Check if ca is defined in the secret tls-ca
|
||||
// assume the key and signed cert have been defined in secret tls.kyverno
|
||||
caData := wrc.client.ReadRootCASecret()
|
||||
if len(caData) == 0 {
|
||||
// load the CA from kubeconfig
|
||||
caData = extractCA(configuration)
|
||||
}
|
||||
if len(caData) == 0 {
|
||||
return nil, errors.New("Unable to extract CA data from configuration")
|
||||
}
|
||||
|
||||
if wrc.serverIP != "" {
|
||||
return wrc.contructDebugValidatingWebhookConfig(caData), nil
|
||||
}
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.ValidatingWebhookConfigurationName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
OwnerReferences: []v1.OwnerReference{
|
||||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
constructWebhook(
|
||||
config.ValidatingWebhookName,
|
||||
config.ValidatingWebhookServicePath,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) contructDebugValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
|
||||
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.ValidatingWebhookServicePath)
|
||||
glog.V(3).Infof("Debug ValidatingWebhookConfig is registered with url %s\n", url)
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.ValidatingWebhookConfigurationDebug,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
constructDebugWebhook(
|
||||
config.ValidatingWebhookName,
|
||||
url,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) contructPolicyValidatingWebhookConfig() (*admregapi.ValidatingWebhookConfiguration, error) {
|
||||
// Check if ca is defined in the secret tls-ca
|
||||
// assume the key and signed cert have been defined in secret tls.kyverno
|
||||
caData := wrc.client.ReadRootCASecret()
|
||||
if len(caData) == 0 {
|
||||
// load the CA from kubeconfig
|
||||
caData = extractCA(wrc.clientConfig)
|
||||
}
|
||||
if len(caData) == 0 {
|
||||
return nil, errors.New("Unable to extract CA data from configuration")
|
||||
}
|
||||
|
||||
if wrc.serverIP != "" {
|
||||
return wrc.contructDebugPolicyValidatingWebhookConfig(caData), nil
|
||||
}
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.PolicyValidatingWebhookConfigurationName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
OwnerReferences: []v1.OwnerReference{
|
||||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
constructWebhook(
|
||||
config.PolicyValidatingWebhookName,
|
||||
config.PolicyValidatingWebhookServicePath,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) contructDebugPolicyValidatingWebhookConfig(caData []byte) *admregapi.ValidatingWebhookConfiguration {
|
||||
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.PolicyValidatingWebhookServicePath)
|
||||
glog.V(3).Infof("Debug PolicyValidatingWebhookConfig is registered with url %s\n", url)
|
||||
|
||||
return &admregapi.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.PolicyValidatingWebhookConfigurationDebug,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
constructDebugWebhook(
|
||||
config.PolicyValidatingWebhookName,
|
||||
url,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func constructWebhook(name, servicePath string, caData []byte, validation bool, timeoutSeconds int32) admregapi.Webhook {
|
||||
resource := "*/*"
|
||||
apiGroups := "*"
|
||||
apiversions := "*"
|
||||
if servicePath == config.PolicyValidatingWebhookServicePath {
|
||||
resource = "policies/*"
|
||||
apiGroups = "kyverno.io"
|
||||
apiversions = "v1alpha1"
|
||||
}
|
||||
operationtypes := []admregapi.OperationType{
|
||||
admregapi.Create,
|
||||
admregapi.Update,
|
||||
}
|
||||
// // Add operation DELETE for validation
|
||||
// if validation {
|
||||
// operationtypes = append(operationtypes, admregapi.Delete)
|
||||
|
||||
// }
|
||||
|
||||
return admregapi.Webhook{
|
||||
Name: name,
|
||||
ClientConfig: admregapi.WebhookClientConfig{
|
||||
Service: &admregapi.ServiceReference{
|
||||
Namespace: config.KubePolicyNamespace,
|
||||
Name: config.WebhookServiceName,
|
||||
Path: &servicePath,
|
||||
},
|
||||
CABundle: caData,
|
||||
},
|
||||
Rules: []admregapi.RuleWithOperations{
|
||||
admregapi.RuleWithOperations{
|
||||
Operations: operationtypes,
|
||||
Rule: admregapi.Rule{
|
||||
APIGroups: []string{
|
||||
apiGroups,
|
||||
},
|
||||
APIVersions: []string{
|
||||
apiversions,
|
||||
},
|
||||
Resources: []string{
|
||||
resource,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
func constructDebugWebhook(name, url string, caData []byte, validation bool, timeoutSeconds int32) admregapi.Webhook {
|
||||
resource := "*/*"
|
||||
apiGroups := "*"
|
||||
apiversions := "*"
|
||||
|
||||
if strings.Contains(url, config.PolicyValidatingWebhookServicePath) {
|
||||
resource = "policies/*"
|
||||
apiGroups = "kyverno.io"
|
||||
apiversions = "v1alpha1"
|
||||
}
|
||||
operationtypes := []admregapi.OperationType{
|
||||
admregapi.Create,
|
||||
admregapi.Update,
|
||||
}
|
||||
// // Add operation DELETE for validation
|
||||
// if validation {
|
||||
// operationtypes = append(operationtypes, admregapi.Delete)
|
||||
// }
|
||||
|
||||
return admregapi.Webhook{
|
||||
Name: name,
|
||||
ClientConfig: admregapi.WebhookClientConfig{
|
||||
URL: &url,
|
||||
CABundle: caData,
|
||||
},
|
||||
Rules: []admregapi.RuleWithOperations{
|
||||
admregapi.RuleWithOperations{
|
||||
Operations: operationtypes,
|
||||
Rule: admregapi.Rule{
|
||||
APIGroups: []string{
|
||||
apiGroups,
|
||||
},
|
||||
APIVersions: []string{
|
||||
apiversions,
|
||||
},
|
||||
Resources: []string{
|
||||
resource,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) constructOwner() v1.OwnerReference {
|
||||
kubePolicyDeployment, err := wrc.client.GetKubePolicyDeployment()
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("Error when constructing OwnerReference, err: %v\n", err)
|
||||
return v1.OwnerReference{}
|
||||
}
|
||||
|
||||
return v1.OwnerReference{
|
||||
APIVersion: config.DeploymentAPIVersion,
|
||||
Kind: config.DeploymentKind,
|
||||
Name: kubePolicyDeployment.ObjectMeta.Name,
|
||||
UID: kubePolicyDeployment.ObjectMeta.UID,
|
||||
}
|
||||
}
|
||||
|
||||
// ExtractCA used for extraction CA from config
|
||||
func extractCA(config *rest.Config) (result []byte) {
|
||||
fileName := config.TLSClientConfig.CAFile
|
||||
|
||||
if fileName != "" {
|
||||
result, err := ioutil.ReadFile(fileName)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return config.TLSClientConfig.CAData
|
||||
}
|
||||
|
|
60
pkg/webhookconfig/resource.go
Normal file
60
pkg/webhookconfig/resource.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package webhookconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
admregapi "k8s.io/api/admissionregistration/v1beta1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (wrc *WebhookRegistrationClient) contructDebugMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
url := fmt.Sprintf("https://%s%s", wrc.serverIP, config.MutatingWebhookServicePath)
|
||||
glog.V(3).Infof("Debug MutatingWebhookConfig is registered with url %s\n", url)
|
||||
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.MutatingWebhookConfigurationDebug,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
generateDebugWebhook(
|
||||
config.MutatingWebhookName,
|
||||
url,
|
||||
caData,
|
||||
true,
|
||||
wrc.timeoutSeconds,
|
||||
"*/*",
|
||||
"*",
|
||||
"*",
|
||||
[]admregapi.OperationType{admregapi.Create},
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wrc *WebhookRegistrationClient) constructMutatingWebhookConfig(caData []byte) *admregapi.MutatingWebhookConfiguration {
|
||||
return &admregapi.MutatingWebhookConfiguration{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: config.MutatingWebhookConfigurationName,
|
||||
Labels: config.KubePolicyAppLabels,
|
||||
OwnerReferences: []v1.OwnerReference{
|
||||
wrc.constructOwner(),
|
||||
},
|
||||
},
|
||||
Webhooks: []admregapi.Webhook{
|
||||
generateWebhook(
|
||||
config.MutatingWebhookName,
|
||||
config.MutatingWebhookServicePath,
|
||||
caData,
|
||||
false,
|
||||
wrc.timeoutSeconds,
|
||||
"*/*",
|
||||
"*",
|
||||
"*",
|
||||
[]admregapi.OperationType{admregapi.Create},
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -139,7 +139,7 @@ func generateAnnotationsFromPolicyResponse(policyResponse engine.PolicyResponse)
|
|||
}
|
||||
patch, err := json.Marshal(rulePatches)
|
||||
if err != nil {
|
||||
glog.Info("failed to marshall: %v", err)
|
||||
glog.Infof("failed to marshall: %v", err)
|
||||
return nil
|
||||
}
|
||||
return patch
|
||||
|
|
84
pkg/webhooks/policymutation.go
Normal file
84
pkg/webhooks/policymutation.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package webhooks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/utils"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (ws *WebhookServer) handlePolicyMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
var policy *kyverno.Policy
|
||||
raw := request.Object.Raw
|
||||
|
||||
//TODO: can this happen? wont this be picked by OpenAPI spec schema ?
|
||||
if err := json.Unmarshal(raw, &policy); err != nil {
|
||||
glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
Message: fmt.Sprintf("failed to default value, check kyverno controller logs for details: %v", err),
|
||||
},
|
||||
}
|
||||
}
|
||||
// Generate JSON Patches for defaults
|
||||
patches, updateMsgs := generateJSONPatchesForDefaults(policy)
|
||||
if patches != nil {
|
||||
patchType := v1beta1.PatchTypeJSONPatch
|
||||
glog.V(4).Infof("defaulted values %v policy %s", updateMsgs, policy.Name)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{
|
||||
Message: strings.Join(updateMsgs, "'"),
|
||||
},
|
||||
Patch: patches,
|
||||
PatchType: &patchType,
|
||||
}
|
||||
}
|
||||
glog.V(4).Infof("nothing to default for policy %s", policy.Name)
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
}
|
||||
|
||||
func generateJSONPatchesForDefaults(policy *kyverno.Policy) ([]byte, []string) {
|
||||
var patches [][]byte
|
||||
var updateMsgs []string
|
||||
|
||||
// default 'ValidationFailureAction'
|
||||
if patch, updateMsg := defaultvalidationFailureAction(policy); patch != nil {
|
||||
patches = append(patches, patch)
|
||||
updateMsgs = append(updateMsgs, updateMsg)
|
||||
}
|
||||
|
||||
return utils.JoinPatches(patches), updateMsgs
|
||||
}
|
||||
|
||||
func defaultvalidationFailureAction(policy *kyverno.Policy) ([]byte, string) {
|
||||
// default ValidationFailureAction to "enforce" if not specified
|
||||
if policy.Spec.ValidationFailureAction == "" {
|
||||
glog.V(4).Infof("defaulting policy %s 'ValidationFailureAction' to '%s'", policy.Name, BlockChanges)
|
||||
jsonPatch := struct {
|
||||
Path string `json:"path"`
|
||||
Op string `json:"op"`
|
||||
Value string `json:"value"`
|
||||
}{
|
||||
"/spec/validationFailureAction",
|
||||
"add",
|
||||
BlockChanges, //enforce
|
||||
}
|
||||
patchByte, err := json.Marshal(jsonPatch)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to set default 'ValidationFailureAction' to '%s' for policy %s", BlockChanges, policy.Name)
|
||||
return nil, ""
|
||||
}
|
||||
glog.V(4).Infof("generate JSON Patch to set default 'ValidationFailureAction' to '%s' for policy %s", BlockChanges, policy.Name)
|
||||
return patchByte, fmt.Sprintf("default 'ValidationFailureAction' to '%s'", BlockChanges)
|
||||
}
|
||||
return nil, ""
|
||||
}
|
|
@ -13,16 +13,13 @@ import (
|
|||
)
|
||||
|
||||
//HandlePolicyValidation performs the validation check on policy resource
|
||||
func (ws *WebhookServer) HandlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
var policy *kyverno.Policy
|
||||
admissionResp := &v1beta1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
}
|
||||
// nothing to do on DELETE
|
||||
if request.Operation == v1beta1.Delete {
|
||||
return admissionResp
|
||||
}
|
||||
|
||||
//TODO: can this happen? wont this be picked by OpenAPI spec schema ?
|
||||
raw := request.Object.Raw
|
||||
if err := json.Unmarshal(raw, &policy); err != nil {
|
||||
glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err)
|
||||
|
|
|
@ -41,6 +41,7 @@ type WebhookServer struct {
|
|||
// API to send policy stats for aggregation
|
||||
policyStatus policy.PolicyStatusInterface
|
||||
filterK8Resources []utils.K8Resource
|
||||
cleanUp chan<- struct{}
|
||||
}
|
||||
|
||||
// NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
|
||||
|
@ -54,7 +55,8 @@ func NewWebhookServer(
|
|||
eventGen event.Interface,
|
||||
webhookRegistrationClient *webhookconfig.WebhookRegistrationClient,
|
||||
policyStatus policy.PolicyStatusInterface,
|
||||
filterK8Resources string) (*WebhookServer, error) {
|
||||
filterK8Resources string,
|
||||
cleanUp chan<- struct{}) (*WebhookServer, error) {
|
||||
|
||||
if tlsPair == nil {
|
||||
return nil, errors.New("NewWebhookServer is not initialized properly")
|
||||
|
@ -79,11 +81,13 @@ func NewWebhookServer(
|
|||
webhookRegistrationClient: webhookRegistrationClient,
|
||||
policyStatus: policyStatus,
|
||||
filterK8Resources: utils.ParseKinds(filterK8Resources),
|
||||
cleanUp: cleanUp,
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(config.MutatingWebhookServicePath, ws.serve)
|
||||
mux.HandleFunc(config.ValidatingWebhookServicePath, ws.serve)
|
||||
mux.HandleFunc(config.PolicyValidatingWebhookServicePath, ws.serve)
|
||||
mux.HandleFunc(config.PolicyMutatingWebhookServicePath, ws.serve)
|
||||
|
||||
ws.server = http.Server{
|
||||
Addr: ":443", // Listen on port for HTTPS requests
|
||||
|
@ -113,9 +117,11 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
|||
// Resource UPDATE
|
||||
switch r.URL.Path {
|
||||
case config.MutatingWebhookServicePath:
|
||||
admissionReview.Response = ws.HandleAdmissionRequest(admissionReview.Request)
|
||||
admissionReview.Response = ws.handleAdmissionRequest(admissionReview.Request)
|
||||
case config.PolicyValidatingWebhookServicePath:
|
||||
admissionReview.Response = ws.HandlePolicyValidation(admissionReview.Request)
|
||||
admissionReview.Response = ws.handlePolicyValidation(admissionReview.Request)
|
||||
case config.PolicyMutatingWebhookServicePath:
|
||||
admissionReview.Response = ws.handlePolicyMutation(admissionReview.Request)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +139,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
func (ws *WebhookServer) handleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
|
||||
// MUTATION
|
||||
ok, patches, msg := ws.HandleMutation(request)
|
||||
if !ok {
|
||||
|
@ -177,9 +183,8 @@ func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionReques
|
|||
func (ws *WebhookServer) RunAsync() {
|
||||
go func(ws *WebhookServer) {
|
||||
glog.V(3).Infof("serving on %s\n", ws.server.Addr)
|
||||
err := ws.server.ListenAndServeTLS("", "")
|
||||
if err != nil {
|
||||
glog.Fatalf("error serving TLS: %v\n", err)
|
||||
if err := ws.server.ListenAndServeTLS("", ""); err != http.ErrServerClosed {
|
||||
glog.Infof("HTTP server error: %v", err)
|
||||
}
|
||||
}(ws)
|
||||
glog.Info("Started Webhook Server")
|
||||
|
@ -193,6 +198,10 @@ func (ws *WebhookServer) Stop() {
|
|||
glog.Info("Server Shutdown error: ", err)
|
||||
ws.server.Close()
|
||||
}
|
||||
// cleanUp
|
||||
// remove the static webhookconfigurations for policy CRD
|
||||
ws.webhookRegistrationClient.RemovePolicyWebhookConfigurations(ws.cleanUp)
|
||||
|
||||
}
|
||||
|
||||
// bodyToAdmissionReview creates AdmissionReview object from request body
|
||||
|
|
|
@ -52,7 +52,7 @@ func getErrorMsg(engineReponses []engine.EngineResponseNew) string {
|
|||
var str []string
|
||||
for _, er := range engineReponses {
|
||||
if !er.IsSuccesful() {
|
||||
str = append(str, fmt.Sprintf("failed policy %s"), er.PolicyResponse.Policy)
|
||||
str = append(str, fmt.Sprintf("failed policy %s", er.PolicyResponse.Policy))
|
||||
for _, rule := range er.PolicyResponse.Rules {
|
||||
if !rule.Success {
|
||||
str = append(str, rule.ToString())
|
||||
|
|
|
@ -33,7 +33,7 @@ func (ws *WebhookServer) registerWebhookConfigurations(policy kyverno.Policy) er
|
|||
}
|
||||
|
||||
if !ws.webhookRegistrationClient.MutationRegistered.IsSet() {
|
||||
if err := ws.webhookRegistrationClient.RegisterMutatingWebhook(); err != nil {
|
||||
if err := ws.webhookRegistrationClient.CreateResourceMutatingWebhookConfiguration(); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.Infof("Mutating webhook registered")
|
||||
|
@ -47,7 +47,7 @@ func (ws *WebhookServer) deregisterWebhookConfigurations(policy kyverno.Policy)
|
|||
|
||||
// deregister webhook if no mutate/validate policy found in cluster
|
||||
if !HasMutateOrValidatePolicies(policies) {
|
||||
ws.webhookRegistrationClient.DeregisterMutatingWebhook()
|
||||
ws.webhookRegistrationClient.RemoveResourceMutatingWebhookConfiguration()
|
||||
glog.Infoln("Mutating webhook deregistered")
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue