mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
refactor: introduce policy context interface in engine api (#6177)
* refactor: introduce policy context interface in engine api Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more interface funcs Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * interface Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * rename Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * merge main Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>
This commit is contained in:
parent
848596ca8d
commit
8290112b84
16 changed files with 326 additions and 245 deletions
|
@ -461,10 +461,7 @@ OuterLoop:
|
||||||
log.Log.Error(err, "failed to add image variables to context")
|
log.Log.Error(err, "failed to add image variables to context")
|
||||||
}
|
}
|
||||||
|
|
||||||
subresources := make([]struct {
|
subresources := make([]engineapi.SubResource, 0)
|
||||||
APIResource metav1.APIResource
|
|
||||||
ParentResource metav1.APIResource
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
// If --cluster flag is not set, then we need to add subresources to the context
|
// If --cluster flag is not set, then we need to add subresources to the context
|
||||||
if c.Client == nil {
|
if c.Client == nil {
|
||||||
|
|
45
pkg/engine/api/policycontext.go
Normal file
45
pkg/engine/api/policycontext.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
|
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
||||||
|
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
|
||||||
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||||
|
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExcludeFunc is a function used to determine if a resource is excluded
|
||||||
|
type ExcludeFunc = func(kind, namespace, name string) bool
|
||||||
|
|
||||||
|
type SubResource struct {
|
||||||
|
APIResource metav1.APIResource
|
||||||
|
ParentResource metav1.APIResource
|
||||||
|
}
|
||||||
|
|
||||||
|
type PolicyContext interface {
|
||||||
|
Policy() kyvernov1.PolicyInterface
|
||||||
|
NewResource() unstructured.Unstructured
|
||||||
|
OldResource() unstructured.Unstructured
|
||||||
|
AdmissionInfo() kyvernov1beta1.RequestInfo
|
||||||
|
NamespaceLabels() map[string]string
|
||||||
|
SubResource() string
|
||||||
|
SubresourcesInPolicy() []SubResource
|
||||||
|
ExcludeGroupRole() []string
|
||||||
|
AdmissionOperation() bool
|
||||||
|
RequestResource() metav1.GroupVersionResource
|
||||||
|
Element() unstructured.Unstructured
|
||||||
|
SetElement(element unstructured.Unstructured)
|
||||||
|
|
||||||
|
JSONContext() enginecontext.Interface
|
||||||
|
Client() dclient.Interface
|
||||||
|
Copy() PolicyContext
|
||||||
|
|
||||||
|
FindExceptions(rule string) ([]*kyvernov2alpha1.PolicyException, error)
|
||||||
|
ExcludeResourceFunc() ExcludeFunc
|
||||||
|
ResolveConfigMap(ctx context.Context, namespace string, name string) (*corev1.ConfigMap, error)
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ import (
|
||||||
// 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
|
// 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed
|
||||||
func ApplyBackgroundChecks(
|
func ApplyBackgroundChecks(
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
policyContext *PolicyContext,
|
policyContext engineapi.PolicyContext,
|
||||||
) (resp *engineapi.EngineResponse) {
|
) (resp *engineapi.EngineResponse) {
|
||||||
policyStartTime := time.Now()
|
policyStartTime := time.Now()
|
||||||
return filterRules(contextLoader, policyContext, policyStartTime)
|
return filterRules(contextLoader, policyContext, policyStartTime)
|
||||||
|
@ -27,18 +27,20 @@ func ApplyBackgroundChecks(
|
||||||
|
|
||||||
func filterRules(
|
func filterRules(
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
policyContext *PolicyContext,
|
policyContext engineapi.PolicyContext,
|
||||||
startTime time.Time,
|
startTime time.Time,
|
||||||
) *engineapi.EngineResponse {
|
) *engineapi.EngineResponse {
|
||||||
kind := policyContext.newResource.GetKind()
|
newResource := policyContext.NewResource()
|
||||||
name := policyContext.newResource.GetName()
|
policy := policyContext.Policy()
|
||||||
namespace := policyContext.newResource.GetNamespace()
|
kind := newResource.GetKind()
|
||||||
apiVersion := policyContext.newResource.GetAPIVersion()
|
name := newResource.GetName()
|
||||||
|
namespace := newResource.GetNamespace()
|
||||||
|
apiVersion := newResource.GetAPIVersion()
|
||||||
resp := &engineapi.EngineResponse{
|
resp := &engineapi.EngineResponse{
|
||||||
PolicyResponse: engineapi.PolicyResponse{
|
PolicyResponse: engineapi.PolicyResponse{
|
||||||
Policy: engineapi.PolicySpec{
|
Policy: engineapi.PolicySpec{
|
||||||
Name: policyContext.policy.GetName(),
|
Name: policy.GetName(),
|
||||||
Namespace: policyContext.policy.GetNamespace(),
|
Namespace: policy.GetNamespace(),
|
||||||
},
|
},
|
||||||
PolicyStats: engineapi.PolicyStats{
|
PolicyStats: engineapi.PolicyStats{
|
||||||
ExecutionStats: engineapi.ExecutionStats{
|
ExecutionStats: engineapi.ExecutionStats{
|
||||||
|
@ -54,13 +56,13 @@ func filterRules(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if policyContext.excludeResourceFunc(kind, namespace, name) {
|
if policyContext.ExcludeResourceFunc()(kind, namespace, name) {
|
||||||
logging.WithName("ApplyBackgroundChecks").Info("resource excluded", "kind", kind, "namespace", namespace, "name", name)
|
logging.WithName("ApplyBackgroundChecks").Info("resource excluded", "kind", kind, "namespace", namespace, "name", name)
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
applyRules := policyContext.policy.GetSpec().GetApplyRules()
|
applyRules := policy.GetSpec().GetApplyRules()
|
||||||
for _, rule := range autogen.ComputeRules(policyContext.policy) {
|
for _, rule := range autogen.ComputeRules(policy) {
|
||||||
if ruleResp := filterRule(contextLoader, rule, policyContext); ruleResp != nil {
|
if ruleResp := filterRule(contextLoader, rule, policyContext); ruleResp != nil {
|
||||||
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
|
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
|
||||||
if applyRules == kyvernov1.ApplyOne && ruleResp.Status != engineapi.RuleStatusSkip {
|
if applyRules == kyvernov1.ApplyOne && ruleResp.Status != engineapi.RuleStatusSkip {
|
||||||
|
@ -75,7 +77,7 @@ func filterRules(
|
||||||
func filterRule(
|
func filterRule(
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
rule kyvernov1.Rule,
|
rule kyvernov1.Rule,
|
||||||
policyContext *PolicyContext,
|
policyContext engineapi.PolicyContext,
|
||||||
) *engineapi.RuleResponse {
|
) *engineapi.RuleResponse {
|
||||||
if !rule.HasGenerate() && !rule.IsMutateExisting() {
|
if !rule.HasGenerate() && !rule.IsMutateExisting() {
|
||||||
return nil
|
return nil
|
||||||
|
@ -99,21 +101,21 @@ func filterRule(
|
||||||
|
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
policy := policyContext.policy
|
policy := policyContext.Policy()
|
||||||
newResource := policyContext.newResource
|
newResource := policyContext.NewResource()
|
||||||
oldResource := policyContext.oldResource
|
oldResource := policyContext.OldResource()
|
||||||
admissionInfo := policyContext.admissionInfo
|
admissionInfo := policyContext.AdmissionInfo()
|
||||||
ctx := policyContext.jsonContext
|
ctx := policyContext.JSONContext()
|
||||||
excludeGroupRole := policyContext.excludeGroupRole
|
excludeGroupRole := policyContext.ExcludeGroupRole()
|
||||||
namespaceLabels := policyContext.namespaceLabels
|
namespaceLabels := policyContext.NamespaceLabels()
|
||||||
|
|
||||||
logger = logging.WithName(string(ruleType)).WithValues("policy", policy.GetName(),
|
logger = logging.WithName(string(ruleType)).WithValues("policy", policy.GetName(),
|
||||||
"kind", newResource.GetKind(), "namespace", newResource.GetNamespace(), "name", newResource.GetName())
|
"kind", newResource.GetKind(), "namespace", newResource.GetNamespace(), "name", newResource.GetName())
|
||||||
|
|
||||||
if err := MatchesResourceDescription(subresourceGVKToAPIResource, newResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, "", policyContext.subresource); err != nil {
|
if err := MatchesResourceDescription(subresourceGVKToAPIResource, newResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, "", policyContext.SubResource()); err != nil {
|
||||||
if ruleType == engineapi.Generation {
|
if ruleType == engineapi.Generation {
|
||||||
// if the oldResource matched, return "false" to delete GR for it
|
// if the oldResource matched, return "false" to delete GR for it
|
||||||
if err = MatchesResourceDescription(subresourceGVKToAPIResource, oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, "", policyContext.subresource); err == nil {
|
if err = MatchesResourceDescription(subresourceGVKToAPIResource, oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, "", policyContext.SubResource()); err == nil {
|
||||||
return &engineapi.RuleResponse{
|
return &engineapi.RuleResponse{
|
||||||
Name: rule.Name,
|
Name: rule.Name,
|
||||||
Type: ruleType,
|
Type: ruleType,
|
||||||
|
@ -129,8 +131,8 @@ func filterRule(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
policyContext.jsonContext.Checkpoint()
|
policyContext.JSONContext().Checkpoint()
|
||||||
defer policyContext.jsonContext.Restore()
|
defer policyContext.JSONContext().Restore()
|
||||||
|
|
||||||
if err := LoadContext(context.TODO(), contextLoader, rule.Context, policyContext, rule.Name); err != nil {
|
if err := LoadContext(context.TODO(), contextLoader, rule.Context, policyContext, rule.Name); err != nil {
|
||||||
logger.V(4).Info("cannot add external data to the context", "reason", err.Error())
|
logger.V(4).Info("cannot add external data to the context", "reason", err.Error())
|
||||||
|
|
|
@ -3,20 +3,22 @@ package engine
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetSubresourceGVKToAPIResourceMap returns a map of subresource GVK to APIResource. This is used to determine if a resource is a subresource.
|
// GetSubresourceGVKToAPIResourceMap returns a map of subresource GVK to APIResource. This is used to determine if a resource is a subresource.
|
||||||
func GetSubresourceGVKToAPIResourceMap(kindsInPolicy []string, ctx *PolicyContext) map[string]*metav1.APIResource {
|
func GetSubresourceGVKToAPIResourceMap(kindsInPolicy []string, ctx engineapi.PolicyContext) map[string]*metav1.APIResource {
|
||||||
subresourceGVKToAPIResource := make(map[string]*metav1.APIResource)
|
subresourceGVKToAPIResource := make(map[string]*metav1.APIResource)
|
||||||
for _, gvk := range kindsInPolicy {
|
for _, gvk := range kindsInPolicy {
|
||||||
gv, k := kubeutils.GetKindFromGVK(gvk)
|
gv, k := kubeutils.GetKindFromGVK(gvk)
|
||||||
parentKind, subresource := kubeutils.SplitSubresource(k)
|
parentKind, subresource := kubeutils.SplitSubresource(k)
|
||||||
// Len of subresources is non zero only when validation request was sent from CLI without connecting to the cluster.
|
// Len of subresources is non zero only when validation request was sent from CLI without connecting to the cluster.
|
||||||
if len(ctx.subresourcesInPolicy) != 0 {
|
subresourcesInPolicy := ctx.SubresourcesInPolicy()
|
||||||
|
if len(subresourcesInPolicy) != 0 {
|
||||||
if subresource != "" {
|
if subresource != "" {
|
||||||
for _, subresourceInPolicy := range ctx.subresourcesInPolicy {
|
for _, subresourceInPolicy := range subresourcesInPolicy {
|
||||||
parentResourceGroupVersion := metav1.GroupVersion{
|
parentResourceGroupVersion := metav1.GroupVersion{
|
||||||
Group: subresourceInPolicy.ParentResource.Group,
|
Group: subresourceInPolicy.ParentResource.Group,
|
||||||
Version: subresourceInPolicy.ParentResource.Version,
|
Version: subresourceInPolicy.ParentResource.Version,
|
||||||
|
@ -31,7 +33,7 @@ func GetSubresourceGVKToAPIResourceMap(kindsInPolicy []string, ctx *PolicyContex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // Complete kind may be a subresource, for eg- 'PodExecOptions'
|
} else { // Complete kind may be a subresource, for eg- 'PodExecOptions'
|
||||||
for _, subresourceInPolicy := range ctx.subresourcesInPolicy {
|
for _, subresourceInPolicy := range subresourcesInPolicy {
|
||||||
// Subresources which can be just specified by kind, for eg- 'PodExecOptions'
|
// Subresources which can be just specified by kind, for eg- 'PodExecOptions'
|
||||||
// have different kind than their parent resource. Otherwise for subresources which
|
// have different kind than their parent resource. Otherwise for subresources which
|
||||||
// have same kind as parent resource, need to be specified as Kind/Subresource, eg - 'Pod/status'
|
// have same kind as parent resource, need to be specified as Kind/Subresource, eg - 'Pod/status'
|
||||||
|
@ -48,9 +50,9 @@ func GetSubresourceGVKToAPIResourceMap(kindsInPolicy []string, ctx *PolicyContex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ctx.client != nil {
|
} else if ctx.Client() != nil {
|
||||||
// find the resource from API client
|
// find the resource from API client
|
||||||
apiResource, _, _, err := ctx.client.Discovery().FindResource(gv, k)
|
apiResource, _, _, err := ctx.Client().Discovery().FindResource(gv, k)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if kubeutils.IsSubresource(apiResource.Name) {
|
if kubeutils.IsSubresource(apiResource.Name) {
|
||||||
subresourceGVKToAPIResource[gvk] = apiResource
|
subresourceGVKToAPIResource[gvk] = apiResource
|
||||||
|
|
|
@ -3,6 +3,7 @@ package engine
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
@ -37,10 +38,7 @@ func Test_GetSubresourceGVKToAPIResourceMap(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
policyContext := NewPolicyContext().
|
policyContext := NewPolicyContext().
|
||||||
WithSubresourcesInPolicy([]struct {
|
WithSubresourcesInPolicy([]engineapi.SubResource{
|
||||||
APIResource metav1.APIResource
|
|
||||||
ParentResource metav1.APIResource
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
APIResource: podStatusAPIResource,
|
APIResource: podStatusAPIResource,
|
||||||
ParentResource: podAPIResource,
|
ParentResource: podAPIResource,
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
// GenerateResponse checks for validity of generate rule on the resource
|
// GenerateResponse checks for validity of generate rule on the resource
|
||||||
func GenerateResponse(
|
func GenerateResponse(
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
policyContext *PolicyContext,
|
policyContext engineapi.PolicyContext,
|
||||||
gr kyvernov1beta1.UpdateRequest,
|
gr kyvernov1beta1.UpdateRequest,
|
||||||
) (resp *engineapi.EngineResponse) {
|
) (resp *engineapi.EngineResponse) {
|
||||||
policyStartTime := time.Now()
|
policyStartTime := time.Now()
|
||||||
|
@ -22,19 +22,19 @@ func GenerateResponse(
|
||||||
|
|
||||||
func filterGenerateRules(
|
func filterGenerateRules(
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
policyContext *PolicyContext,
|
policyContext engineapi.PolicyContext,
|
||||||
policyNameKey string,
|
policyNameKey string,
|
||||||
startTime time.Time,
|
startTime time.Time,
|
||||||
) *engineapi.EngineResponse {
|
) *engineapi.EngineResponse {
|
||||||
kind := policyContext.newResource.GetKind()
|
newResource := policyContext.NewResource()
|
||||||
name := policyContext.newResource.GetName()
|
kind := newResource.GetKind()
|
||||||
namespace := policyContext.newResource.GetNamespace()
|
name := newResource.GetName()
|
||||||
apiVersion := policyContext.newResource.GetAPIVersion()
|
namespace := newResource.GetNamespace()
|
||||||
|
apiVersion := newResource.GetAPIVersion()
|
||||||
pNamespace, pName, err := cache.SplitMetaNamespaceKey(policyNameKey)
|
pNamespace, pName, err := cache.SplitMetaNamespaceKey(policyNameKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Error(err, "failed to spilt name and namespace", policyNameKey)
|
logging.Error(err, "failed to spilt name and namespace", policyNameKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &engineapi.EngineResponse{
|
resp := &engineapi.EngineResponse{
|
||||||
PolicyResponse: engineapi.PolicyResponse{
|
PolicyResponse: engineapi.PolicyResponse{
|
||||||
Policy: engineapi.PolicySpec{
|
Policy: engineapi.PolicySpec{
|
||||||
|
@ -54,13 +54,12 @@ func filterGenerateRules(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if policyContext.ExcludeResourceFunc()(kind, namespace, name) {
|
||||||
if policyContext.excludeResourceFunc(kind, namespace, name) {
|
|
||||||
logging.WithName("Generate").Info("resource excluded", "kind", kind, "namespace", namespace, "name", name)
|
logging.WithName("Generate").Info("resource excluded", "kind", kind, "namespace", namespace, "name", name)
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rule := range autogen.ComputeRules(policyContext.policy) {
|
for _, rule := range autogen.ComputeRules(policyContext.Policy()) {
|
||||||
if ruleResp := filterRule(contextLoader, rule, policyContext); ruleResp != nil {
|
if ruleResp := filterRule(contextLoader, rule, policyContext); ruleResp != nil {
|
||||||
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
|
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,15 +47,15 @@ func getMatchingImages(images map[string]map[string]apiutils.ImageInfo, rule *ky
|
||||||
return imageInfos, strings.Join(imageRefs, ",")
|
return imageInfos, strings.Join(imageRefs, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractMatchingImages(policyContext *PolicyContext, rule *kyvernov1.Rule, cfg config.Configuration) ([]apiutils.ImageInfo, string, error) {
|
func extractMatchingImages(policyContext engineapi.PolicyContext, rule *kyvernov1.Rule, cfg config.Configuration) ([]apiutils.ImageInfo, string, error) {
|
||||||
var (
|
var (
|
||||||
images map[string]map[string]apiutils.ImageInfo
|
images map[string]map[string]apiutils.ImageInfo
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
images = policyContext.jsonContext.ImageInfo()
|
newResource := policyContext.NewResource()
|
||||||
|
images = policyContext.JSONContext().ImageInfo()
|
||||||
if rule.ImageExtractors != nil {
|
if rule.ImageExtractors != nil {
|
||||||
images, err = policyContext.jsonContext.GenerateCustomImageInfo(
|
images, err = policyContext.JSONContext().GenerateCustomImageInfo(&newResource, rule.ImageExtractors, cfg)
|
||||||
&policyContext.newResource, rule.ImageExtractors, cfg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// if we get an error while generating custom images from image extractors,
|
// if we get an error while generating custom images from image extractors,
|
||||||
// don't check for matching images in imageExtractors
|
// don't check for matching images in imageExtractors
|
||||||
|
@ -70,13 +70,13 @@ func VerifyAndPatchImages(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
rclient registryclient.Client,
|
rclient registryclient.Client,
|
||||||
policyContext *PolicyContext,
|
policyContext engineapi.PolicyContext,
|
||||||
cfg config.Configuration,
|
cfg config.Configuration,
|
||||||
) (*engineapi.EngineResponse, *ImageVerificationMetadata) {
|
) (*engineapi.EngineResponse, *ImageVerificationMetadata) {
|
||||||
resp := &engineapi.EngineResponse{}
|
resp := &engineapi.EngineResponse{}
|
||||||
|
|
||||||
policy := policyContext.policy
|
policy := policyContext.Policy()
|
||||||
patchedResource := policyContext.newResource
|
patchedResource := policyContext.NewResource()
|
||||||
logger := logging.WithName("EngineVerifyImages").WithValues("policy", policy.GetName(),
|
logger := logging.WithName("EngineVerifyImages").WithValues("policy", policy.GetName(),
|
||||||
"kind", patchedResource.GetKind(), "namespace", patchedResource.GetNamespace(), "name", patchedResource.GetName())
|
"kind", patchedResource.GetKind(), "namespace", patchedResource.GetNamespace(), "name", patchedResource.GetName())
|
||||||
|
|
||||||
|
@ -88,11 +88,11 @@ func VerifyAndPatchImages(
|
||||||
"applied", resp.PolicyResponse.RulesAppliedCount, "successful", resp.IsSuccessful())
|
"applied", resp.PolicyResponse.RulesAppliedCount, "successful", resp.IsSuccessful())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
policyContext.jsonContext.Checkpoint()
|
policyContext.JSONContext().Checkpoint()
|
||||||
defer policyContext.jsonContext.Restore()
|
defer policyContext.JSONContext().Restore()
|
||||||
|
|
||||||
ivm := &ImageVerificationMetadata{}
|
ivm := &ImageVerificationMetadata{}
|
||||||
rules := autogen.ComputeRules(policyContext.policy)
|
rules := autogen.ComputeRules(policyContext.Policy())
|
||||||
applyRules := policy.GetSpec().GetApplyRules()
|
applyRules := policy.GetSpec().GetApplyRules()
|
||||||
|
|
||||||
for i := range rules {
|
for i := range rules {
|
||||||
|
@ -138,13 +138,13 @@ func VerifyAndPatchImages(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
policyContext.jsonContext.Restore()
|
policyContext.JSONContext().Restore()
|
||||||
if err := LoadContext(ctx, contextLoader, rule.Context, policyContext, rule.Name); err != nil {
|
if err := LoadContext(ctx, contextLoader, rule.Context, policyContext, rule.Name); err != nil {
|
||||||
appendResponse(resp, rule, fmt.Sprintf("failed to load context: %s", err.Error()), engineapi.RuleStatusError)
|
appendResponse(resp, rule, fmt.Sprintf("failed to load context: %s", err.Error()), engineapi.RuleStatusError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ruleCopy, err := substituteVariables(rule, policyContext.jsonContext, logger)
|
ruleCopy, err := substituteVariables(rule, policyContext.JSONContext(), logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
appendResponse(resp, rule, fmt.Sprintf("failed to substitute variables: %s", err.Error()), engineapi.RuleStatusError)
|
appendResponse(resp, rule, fmt.Sprintf("failed to substitute variables: %s", err.Error()), engineapi.RuleStatusError)
|
||||||
return
|
return
|
||||||
|
@ -203,7 +203,7 @@ func substituteVariables(rule *kyvernov1.Rule, ctx enginecontext.EvalInterface,
|
||||||
type imageVerifier struct {
|
type imageVerifier struct {
|
||||||
logger logr.Logger
|
logger logr.Logger
|
||||||
rclient registryclient.Client
|
rclient registryclient.Client
|
||||||
policyContext *PolicyContext
|
policyContext engineapi.PolicyContext
|
||||||
rule *kyvernov1.Rule
|
rule *kyvernov1.Rule
|
||||||
resp *engineapi.EngineResponse
|
resp *engineapi.EngineResponse
|
||||||
ivm *ImageVerificationMetadata
|
ivm *ImageVerificationMetadata
|
||||||
|
@ -228,13 +228,13 @@ func (iv *imageVerifier) verify(ctx context.Context, imageVerify kyvernov1.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
pointer := jsonpointer.ParsePath(imageInfo.Pointer).JMESPath()
|
pointer := jsonpointer.ParsePath(imageInfo.Pointer).JMESPath()
|
||||||
changed, err := iv.policyContext.jsonContext.HasChanged(pointer)
|
changed, err := iv.policyContext.JSONContext().HasChanged(pointer)
|
||||||
if err == nil && !changed {
|
if err == nil && !changed {
|
||||||
iv.logger.V(4).Info("no change in image, skipping check", "image", image)
|
iv.logger.V(4).Info("no change in image, skipping check", "image", image)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
verified, err := isImageVerified(iv.policyContext.newResource, image, iv.logger)
|
verified, err := isImageVerified(iv.policyContext.NewResource(), image, iv.logger)
|
||||||
if err == nil && verified {
|
if err == nil && verified {
|
||||||
iv.logger.Info("image was previously verified, skipping check", "image", image)
|
iv.logger.Info("image was previously verified, skipping check", "image", image)
|
||||||
continue
|
continue
|
||||||
|
@ -292,15 +292,17 @@ func (iv *imageVerifier) handleMutateDigest(ctx context.Context, digest string,
|
||||||
return patch, digest, nil
|
return patch, digest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasImageVerifiedAnnotationChanged(ctx *PolicyContext, log logr.Logger) bool {
|
func hasImageVerifiedAnnotationChanged(ctx engineapi.PolicyContext, log logr.Logger) bool {
|
||||||
if reflect.DeepEqual(ctx.newResource, unstructured.Unstructured{}) ||
|
newResource := ctx.NewResource()
|
||||||
reflect.DeepEqual(ctx.oldResource, unstructured.Unstructured{}) {
|
oldResource := ctx.OldResource()
|
||||||
|
if reflect.DeepEqual(newResource, unstructured.Unstructured{}) ||
|
||||||
|
reflect.DeepEqual(oldResource, unstructured.Unstructured{}) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
key := imageVerifyAnnotationKey
|
key := imageVerifyAnnotationKey
|
||||||
newValue := ctx.newResource.GetAnnotations()[key]
|
newValue := newResource.GetAnnotations()[key]
|
||||||
oldValue := ctx.oldResource.GetAnnotations()[key]
|
oldValue := oldResource.GetAnnotations()[key]
|
||||||
result := newValue != oldValue
|
result := newValue != oldValue
|
||||||
if result {
|
if result {
|
||||||
log.V(2).Info("annotation mismatch", "oldValue", oldValue, "newValue", newValue, "key", key)
|
log.V(2).Info("annotation mismatch", "oldValue", oldValue, "newValue", newValue, "key", key)
|
||||||
|
@ -333,7 +335,7 @@ func (iv *imageVerifier) verifyImage(
|
||||||
iv.logger.V(2).Info("verifying image signatures", "image", image,
|
iv.logger.V(2).Info("verifying image signatures", "image", image,
|
||||||
"attestors", len(imageVerify.Attestors), "attestations", len(imageVerify.Attestations))
|
"attestors", len(imageVerify.Attestors), "attestations", len(imageVerify.Attestations))
|
||||||
|
|
||||||
if err := iv.policyContext.jsonContext.AddImageInfo(imageInfo, cfg); err != nil {
|
if err := iv.policyContext.JSONContext().AddImageInfo(imageInfo, cfg); err != nil {
|
||||||
iv.logger.Error(err, "failed to add image to context")
|
iv.logger.Error(err, "failed to add image to context")
|
||||||
msg := fmt.Sprintf("failed to add image to context %s: %s", image, err.Error())
|
msg := fmt.Sprintf("failed to add image to context %s: %s", image, err.Error())
|
||||||
return ruleResponse(*iv.rule, engineapi.ImageVerify, msg, engineapi.RuleStatusError), ""
|
return ruleResponse(*iv.rule, engineapi.ImageVerify, msg, engineapi.RuleStatusError), ""
|
||||||
|
@ -698,10 +700,10 @@ func (iv *imageVerifier) checkAttestations(a kyvernov1.Attestation, s map[string
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
iv.policyContext.jsonContext.Checkpoint()
|
iv.policyContext.JSONContext().Checkpoint()
|
||||||
defer iv.policyContext.jsonContext.Restore()
|
defer iv.policyContext.JSONContext().Restore()
|
||||||
|
|
||||||
return evaluateConditions(a.Conditions, iv.policyContext.jsonContext, s, iv.logger)
|
return evaluateConditions(a.Conditions, iv.policyContext.JSONContext(), s, iv.logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
func evaluateConditions(
|
func evaluateConditions(
|
||||||
|
|
|
@ -19,7 +19,7 @@ func processImageValidationRule(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
log logr.Logger,
|
log logr.Logger,
|
||||||
enginectx *PolicyContext,
|
enginectx engineapi.PolicyContext,
|
||||||
rule *kyvernov1.Rule,
|
rule *kyvernov1.Rule,
|
||||||
cfg config.Configuration,
|
cfg config.Configuration,
|
||||||
) *engineapi.RuleResponse {
|
) *engineapi.RuleResponse {
|
||||||
|
@ -51,7 +51,7 @@ func processImageValidationRule(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !preconditionsPassed {
|
if !preconditionsPassed {
|
||||||
if enginectx.policy.GetSpec().ValidationFailureAction.Audit() {
|
if enginectx.Policy().GetSpec().ValidationFailureAction.Audit() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ func processImageValidationRule(
|
||||||
|
|
||||||
for _, v := range rule.VerifyImages {
|
for _, v := range rule.VerifyImages {
|
||||||
imageVerify := v.Convert()
|
imageVerify := v.Convert()
|
||||||
for _, infoMap := range enginectx.jsonContext.ImageInfo() {
|
for _, infoMap := range enginectx.JSONContext().ImageInfo() {
|
||||||
for name, imageInfo := range infoMap {
|
for name, imageInfo := range infoMap {
|
||||||
image := imageInfo.String()
|
image := imageInfo.String()
|
||||||
log = log.WithValues("rule", rule.Name)
|
log = log.WithValues("rule", rule.Name)
|
||||||
|
@ -82,24 +82,22 @@ func processImageValidationRule(
|
||||||
return ruleResponse(*rule, engineapi.Validation, "image verified", engineapi.RuleStatusPass)
|
return ruleResponse(*rule, engineapi.Validation, "image verified", engineapi.RuleStatusPass)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateImage(ctx *PolicyContext, imageVerify *kyvernov1.ImageVerification, name string, imageInfo apiutils.ImageInfo, log logr.Logger) error {
|
func validateImage(ctx engineapi.PolicyContext, imageVerify *kyvernov1.ImageVerification, name string, imageInfo apiutils.ImageInfo, log logr.Logger) error {
|
||||||
image := imageInfo.String()
|
image := imageInfo.String()
|
||||||
if imageVerify.VerifyDigest && imageInfo.Digest == "" {
|
if imageVerify.VerifyDigest && imageInfo.Digest == "" {
|
||||||
log.V(2).Info("missing digest", "image", imageInfo.String())
|
log.V(2).Info("missing digest", "image", imageInfo.String())
|
||||||
return fmt.Errorf("missing digest for %s", image)
|
return fmt.Errorf("missing digest for %s", image)
|
||||||
}
|
}
|
||||||
|
newResource := ctx.NewResource()
|
||||||
if imageVerify.Required && !reflect.DeepEqual(ctx.newResource, unstructured.Unstructured{}) {
|
if imageVerify.Required && !reflect.DeepEqual(newResource, unstructured.Unstructured{}) {
|
||||||
verified, err := isImageVerified(ctx.newResource, image, log)
|
verified, err := isImageVerified(newResource, image, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !verified {
|
if !verified {
|
||||||
return fmt.Errorf("unverified image %s", image)
|
return fmt.Errorf("unverified image %s", image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -163,7 +163,7 @@ var cfg = config.NewDefaultConfiguration()
|
||||||
func doVerifyAndPatchImages(
|
func doVerifyAndPatchImages(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rclient registryclient.Client,
|
rclient registryclient.Client,
|
||||||
pContext *PolicyContext,
|
pContext engineapi.PolicyContext,
|
||||||
cfg config.Configuration,
|
cfg config.Configuration,
|
||||||
) (*engineapi.EngineResponse, *ImageVerificationMetadata) {
|
) (*engineapi.EngineResponse, *ImageVerificationMetadata) {
|
||||||
return VerifyAndPatchImages(
|
return VerifyAndPatchImages(
|
||||||
|
|
|
@ -17,13 +17,14 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/logging"
|
"github.com/kyverno/kyverno/pkg/logging"
|
||||||
"github.com/kyverno/kyverno/pkg/registryclient"
|
"github.com/kyverno/kyverno/pkg/registryclient"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContextLoaderFactory = func(pContext *PolicyContext, ruleName string) engineapi.ContextLoader
|
type ContextLoaderFactory = func(pContext engineapi.PolicyContext, ruleName string) engineapi.ContextLoader
|
||||||
|
|
||||||
func LegacyContextLoaderFactory(rclient registryclient.Client) ContextLoaderFactory {
|
func LegacyContextLoaderFactory(rclient registryclient.Client) ContextLoaderFactory {
|
||||||
if store.IsMock() {
|
if store.IsMock() {
|
||||||
return func(pContext *PolicyContext, ruleName string) engineapi.ContextLoader {
|
return func(pContext engineapi.PolicyContext, ruleName string) engineapi.ContextLoader {
|
||||||
policy := pContext.Policy()
|
policy := pContext.Policy()
|
||||||
return &mockContextLoader{
|
return &mockContextLoader{
|
||||||
logger: logging.WithName("MockContextLoaderFactory"),
|
logger: logging.WithName("MockContextLoaderFactory"),
|
||||||
|
@ -31,16 +32,16 @@ func LegacyContextLoaderFactory(rclient registryclient.Client) ContextLoaderFact
|
||||||
ruleName: ruleName,
|
ruleName: ruleName,
|
||||||
client: pContext.Client(),
|
client: pContext.Client(),
|
||||||
rclient: rclient,
|
rclient: rclient,
|
||||||
cmResolver: pContext.informerCacheResolvers,
|
cmResolver: pContext.ResolveConfigMap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return func(pContext *PolicyContext, ruleName string) engineapi.ContextLoader {
|
return func(pContext engineapi.PolicyContext, ruleName string) engineapi.ContextLoader {
|
||||||
return &contextLoader{
|
return &contextLoader{
|
||||||
logger: logging.WithName("LegacyContextLoaderFactory"),
|
logger: logging.WithName("LegacyContextLoaderFactory"),
|
||||||
client: pContext.Client(),
|
client: pContext.Client(),
|
||||||
rclient: rclient,
|
rclient: rclient,
|
||||||
cmResolver: pContext.informerCacheResolvers,
|
cmResolver: pContext.ResolveConfigMap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +50,7 @@ type contextLoader struct {
|
||||||
logger logr.Logger
|
logger logr.Logger
|
||||||
rclient registryclient.Client
|
rclient registryclient.Client
|
||||||
client dclient.Interface
|
client dclient.Interface
|
||||||
cmResolver engineapi.ConfigmapResolver
|
cmResolver func(context.Context, string, string) (*corev1.ConfigMap, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *contextLoader) Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, enginectx enginecontext.Interface) error {
|
func (l *contextLoader) Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, enginectx enginecontext.Interface) error {
|
||||||
|
@ -81,7 +82,7 @@ type mockContextLoader struct {
|
||||||
ruleName string
|
ruleName string
|
||||||
rclient registryclient.Client
|
rclient registryclient.Client
|
||||||
client dclient.Interface
|
client dclient.Interface
|
||||||
cmResolver engineapi.ConfigmapResolver
|
cmResolver func(context.Context, string, string) (*corev1.ConfigMap, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *mockContextLoader) Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, enginectx enginecontext.Interface) error {
|
func (l *mockContextLoader) Load(ctx context.Context, contextEntries []kyvernov1.ContextEntry, enginectx enginecontext.Interface) error {
|
||||||
|
@ -122,7 +123,7 @@ func (l *mockContextLoader) Load(ctx context.Context, contextEntries []kyvernov1
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadContext(ctx context.Context, factory ContextLoaderFactory, contextEntries []kyvernov1.ContextEntry, pContext *PolicyContext, ruleName string) error {
|
func LoadContext(ctx context.Context, factory ContextLoaderFactory, contextEntries []kyvernov1.ContextEntry, pContext engineapi.PolicyContext, ruleName string) error {
|
||||||
return factory(pContext, ruleName).Load(ctx, contextEntries, pContext.JSONContext())
|
return factory(pContext, ruleName).Load(ctx, contextEntries, pContext.JSONContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +301,7 @@ func applyJMESPath(jmesPath string, data interface{}) (interface{}, error) {
|
||||||
return jp.Search(data)
|
return jp.Search(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver engineapi.ConfigmapResolver) error {
|
func loadConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver func(context.Context, string, string) (*corev1.ConfigMap, error)) error {
|
||||||
data, err := fetchConfigMap(ctx, logger, entry, enginectx, resolver)
|
data, err := fetchConfigMap(ctx, logger, entry, enginectx, resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to retrieve config map for context entry %s: %v", entry.Name, err)
|
return fmt.Errorf("failed to retrieve config map for context entry %s: %v", entry.Name, err)
|
||||||
|
@ -312,7 +313,7 @@ func loadConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.Cont
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver engineapi.ConfigmapResolver) ([]byte, error) {
|
func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.ContextEntry, enginectx enginecontext.Interface, resolver func(context.Context, string, string) (*corev1.ConfigMap, error)) ([]byte, error) {
|
||||||
contextData := make(map[string]interface{})
|
contextData := make(map[string]interface{})
|
||||||
|
|
||||||
name, err := variables.SubstituteAll(logger, enginectx, entry.ConfigMap.Name)
|
name, err := variables.SubstituteAll(logger, enginectx, entry.ConfigMap.Name)
|
||||||
|
@ -329,7 +330,7 @@ func fetchConfigMap(ctx context.Context, logger logr.Logger, entry kyvernov1.Con
|
||||||
namespace = "default"
|
namespace = "default"
|
||||||
}
|
}
|
||||||
|
|
||||||
obj, err := resolver.Get(ctx, namespace.(string), name.(string))
|
obj, err := resolver(ctx, namespace.(string), name.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get configmap %s/%s : %v", namespace, name, err)
|
return nil, fmt.Errorf("failed to get configmap %s/%s : %v", namespace, name, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ const (
|
||||||
//go:embed resources/default-config.yaml
|
//go:embed resources/default-config.yaml
|
||||||
var defaultConfigBytes []byte
|
var defaultConfigBytes []byte
|
||||||
|
|
||||||
func processYAMLValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) *engineapi.RuleResponse {
|
func processYAMLValidationRule(log logr.Logger, ctx engineapi.PolicyContext, rule *kyvernov1.Rule) *engineapi.RuleResponse {
|
||||||
if isDeleteRequest(ctx) {
|
if isDeleteRequest(ctx) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func processYAMLValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyvern
|
||||||
return ruleResp
|
return ruleResp
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleVerifyManifest(ctx *PolicyContext, rule *kyvernov1.Rule, logger logr.Logger) *engineapi.RuleResponse {
|
func handleVerifyManifest(ctx engineapi.PolicyContext, rule *kyvernov1.Rule, logger logr.Logger) *engineapi.RuleResponse {
|
||||||
verified, reason, err := verifyManifest(ctx, *rule.Validation.Manifests, logger)
|
verified, reason, err := verifyManifest(ctx, *rule.Validation.Manifests, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.V(3).Info("verifyManifest return err", "error", err.Error())
|
logger.V(3).Info("verifyManifest return err", "error", err.Error())
|
||||||
|
@ -56,9 +56,9 @@ func handleVerifyManifest(ctx *PolicyContext, rule *kyvernov1.Rule, logger logr.
|
||||||
return ruleResponse(*rule, engineapi.Validation, reason, engineapi.RuleStatusPass)
|
return ruleResponse(*rule, engineapi.Validation, reason, engineapi.RuleStatusPass)
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyManifest(policyContext *PolicyContext, verifyRule kyvernov1.Manifests, logger logr.Logger) (bool, string, error) {
|
func verifyManifest(policyContext engineapi.PolicyContext, verifyRule kyvernov1.Manifests, logger logr.Logger) (bool, string, error) {
|
||||||
// load AdmissionRequest
|
// load AdmissionRequest
|
||||||
request, err := policyContext.jsonContext.Query("request")
|
request, err := policyContext.JSONContext().Query("request")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", errors.Wrapf(err, "failed to get a request from policyContext")
|
return false, "", errors.Wrapf(err, "failed to get a request from policyContext")
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ func verifyManifest(policyContext *PolicyContext, verifyRule kyvernov1.Manifests
|
||||||
}
|
}
|
||||||
if !vo.DisableDryRun {
|
if !vo.DisableDryRun {
|
||||||
// check if kyverno can 'create' dryrun resource
|
// check if kyverno can 'create' dryrun resource
|
||||||
ok, err := checkDryRunPermission(policyContext.client, adreq.Kind.Kind, vo.DryRunNamespace)
|
ok, err := checkDryRunPermission(policyContext.Client(), adreq.Kind.Kind, vo.DryRunNamespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.V(1).Info("failed to check permissions to 'create' resource. disabled DryRun option.", "dryrun namespace", vo.DryRunNamespace, "kind", adreq.Kind.Kind, "error", err.Error())
|
logger.V(1).Info("failed to check permissions to 'create' resource. disabled DryRun option.", "dryrun namespace", vo.DryRunNamespace, "kind", adreq.Kind.Kind, "error", err.Error())
|
||||||
vo.DisableDryRun = true
|
vo.DisableDryRun = true
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/variables"
|
"github.com/kyverno/kyverno/pkg/engine/variables"
|
||||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||||
"github.com/kyverno/kyverno/pkg/utils/wildcard"
|
"github.com/kyverno/kyverno/pkg/utils/wildcard"
|
||||||
|
@ -23,7 +24,7 @@ type resourceInfo struct {
|
||||||
parentResourceGVR metav1.GroupVersionResource
|
parentResourceGVR metav1.GroupVersionResource
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadTargets(targets []kyvernov1.ResourceSpec, ctx *PolicyContext, logger logr.Logger) ([]resourceInfo, error) {
|
func loadTargets(targets []kyvernov1.ResourceSpec, ctx engineapi.PolicyContext, logger logr.Logger) ([]resourceInfo, error) {
|
||||||
var targetObjects []resourceInfo
|
var targetObjects []resourceInfo
|
||||||
var errors []error
|
var errors []error
|
||||||
|
|
||||||
|
@ -46,23 +47,23 @@ func loadTargets(targets []kyvernov1.ResourceSpec, ctx *PolicyContext, logger lo
|
||||||
return targetObjects, multierr.Combine(errors...)
|
return targetObjects, multierr.Combine(errors...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveSpec(i int, target kyvernov1.ResourceSpec, ctx *PolicyContext, logger logr.Logger) (kyvernov1.ResourceSpec, error) {
|
func resolveSpec(i int, target kyvernov1.ResourceSpec, ctx engineapi.PolicyContext, logger logr.Logger) (kyvernov1.ResourceSpec, error) {
|
||||||
kind, err := variables.SubstituteAll(logger, ctx.jsonContext, target.Kind)
|
kind, err := variables.SubstituteAll(logger, ctx.JSONContext(), target.Kind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Kind %s: %v", i, target.Kind, err)
|
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Kind %s: %v", i, target.Kind, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
apiversion, err := variables.SubstituteAll(logger, ctx.jsonContext, target.APIVersion)
|
apiversion, err := variables.SubstituteAll(logger, ctx.JSONContext(), target.APIVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].APIVersion %s: %v", i, target.APIVersion, err)
|
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].APIVersion %s: %v", i, target.APIVersion, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace, err := variables.SubstituteAll(logger, ctx.jsonContext, target.Namespace)
|
namespace, err := variables.SubstituteAll(logger, ctx.JSONContext(), target.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Namespace %s: %v", i, target.Namespace, err)
|
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Namespace %s: %v", i, target.Namespace, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
name, err := variables.SubstituteAll(logger, ctx.jsonContext, target.Name)
|
name, err := variables.SubstituteAll(logger, ctx.JSONContext(), target.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Name %s: %v", i, target.Name, err)
|
return kyvernov1.ResourceSpec{}, fmt.Errorf("failed to substitute variables in target[%d].Name %s: %v", i, target.Name, err)
|
||||||
}
|
}
|
||||||
|
@ -75,17 +76,18 @@ func resolveSpec(i int, target kyvernov1.ResourceSpec, ctx *PolicyContext, logge
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTargets(target kyvernov1.ResourceSpec, ctx *PolicyContext) ([]resourceInfo, error) {
|
func getTargets(target kyvernov1.ResourceSpec, ctx engineapi.PolicyContext) ([]resourceInfo, error) {
|
||||||
var targetObjects []resourceInfo
|
var targetObjects []resourceInfo
|
||||||
namespace := target.Namespace
|
namespace := target.Namespace
|
||||||
name := target.Name
|
name := target.Name
|
||||||
|
policy := ctx.Policy()
|
||||||
|
|
||||||
// if it's namespaced policy, targets has to be loaded only from the policy's namespace
|
// if it's namespaced policy, targets has to be loaded only from the policy's namespace
|
||||||
if ctx.policy.IsNamespaced() {
|
if policy.IsNamespaced() {
|
||||||
namespace = ctx.policy.GetNamespace()
|
namespace = policy.GetNamespace()
|
||||||
}
|
}
|
||||||
|
client := ctx.Client()
|
||||||
apiResource, parentAPIResource, _, err := ctx.client.Discovery().FindResource(target.APIVersion, target.Kind)
|
apiResource, parentAPIResource, _, err := client.Discovery().FindResource(target.APIVersion, target.Kind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -102,14 +104,14 @@ func getTargets(target kyvernov1.ResourceSpec, ctx *PolicyContext) ([]resourceIn
|
||||||
Version: parentAPIResource.Version,
|
Version: parentAPIResource.Version,
|
||||||
}.String()
|
}.String()
|
||||||
subresourceName = strings.Split(apiResource.Name, "/")[1]
|
subresourceName = strings.Split(apiResource.Name, "/")[1]
|
||||||
obj, err = ctx.client.GetResource(context.TODO(), apiVersion, parentAPIResource.Kind, namespace, name, subresourceName)
|
obj, err = client.GetResource(context.TODO(), apiVersion, parentAPIResource.Kind, namespace, name, subresourceName)
|
||||||
parentResourceGVR = metav1.GroupVersionResource{
|
parentResourceGVR = metav1.GroupVersionResource{
|
||||||
Group: parentAPIResource.Group,
|
Group: parentAPIResource.Group,
|
||||||
Version: parentAPIResource.Version,
|
Version: parentAPIResource.Version,
|
||||||
Resource: parentAPIResource.Name,
|
Resource: parentAPIResource.Name,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
obj, err = ctx.client.GetResource(context.TODO(), target.APIVersion, target.Kind, namespace, name)
|
obj, err = client.GetResource(context.TODO(), target.APIVersion, target.Kind, namespace, name)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get target %s/%s %s/%s : %v", target.APIVersion, target.Kind, namespace, name, err)
|
return nil, fmt.Errorf("failed to get target %s/%s %s/%s : %v", target.APIVersion, target.Kind, namespace, name, err)
|
||||||
|
@ -123,7 +125,7 @@ func getTargets(target kyvernov1.ResourceSpec, ctx *PolicyContext) ([]resourceIn
|
||||||
Group: parentAPIResource.Group,
|
Group: parentAPIResource.Group,
|
||||||
Version: parentAPIResource.Version,
|
Version: parentAPIResource.Version,
|
||||||
}.String()
|
}.String()
|
||||||
objList, err := ctx.client.ListResource(context.TODO(), apiVersion, parentAPIResource.Kind, "", nil)
|
objList, err := client.ListResource(context.TODO(), apiVersion, parentAPIResource.Kind, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -138,7 +140,7 @@ func getTargets(target kyvernov1.ResourceSpec, ctx *PolicyContext) ([]resourceIn
|
||||||
for i := range parentObjects {
|
for i := range parentObjects {
|
||||||
parentObj := parentObjects[i]
|
parentObj := parentObjects[i]
|
||||||
subresourceName := strings.Split(apiResource.Name, "/")[1]
|
subresourceName := strings.Split(apiResource.Name, "/")[1]
|
||||||
obj, err := ctx.client.GetResource(context.TODO(), parentObj.GetAPIVersion(), parentAPIResource.Kind, parentObj.GetNamespace(), parentObj.GetName(), subresourceName)
|
obj, err := client.GetResource(context.TODO(), parentObj.GetAPIVersion(), parentAPIResource.Kind, parentObj.GetNamespace(), parentObj.GetName(), subresourceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -151,7 +153,7 @@ func getTargets(target kyvernov1.ResourceSpec, ctx *PolicyContext) ([]resourceIn
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// list all targets if wildcard is specified
|
// list all targets if wildcard is specified
|
||||||
objList, err := ctx.client.ListResource(context.TODO(), target.APIVersion, target.Kind, "", nil)
|
objList, err := client.ListResource(context.TODO(), target.APIVersion, target.Kind, "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,15 +24,15 @@ import (
|
||||||
func Mutate(
|
func Mutate(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
policyContext *PolicyContext,
|
policyContext engineapi.PolicyContext,
|
||||||
) (resp *engineapi.EngineResponse) {
|
) (resp *engineapi.EngineResponse) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
policy := policyContext.policy
|
policy := policyContext.Policy()
|
||||||
resp = &engineapi.EngineResponse{
|
resp = &engineapi.EngineResponse{
|
||||||
Policy: policy,
|
Policy: policy,
|
||||||
}
|
}
|
||||||
matchedResource := policyContext.newResource
|
matchedResource := policyContext.NewResource()
|
||||||
enginectx := policyContext.jsonContext
|
enginectx := policyContext.JSONContext()
|
||||||
var skippedRules []string
|
var skippedRules []string
|
||||||
|
|
||||||
logger := logging.WithName("EngineMutate").WithValues("policy", policy.GetName(), "kind", matchedResource.GetKind(),
|
logger := logging.WithName("EngineMutate").WithValues("policy", policy.GetName(), "kind", matchedResource.GetKind(),
|
||||||
|
@ -43,8 +43,8 @@ func Mutate(
|
||||||
startMutateResultResponse(resp, policy, matchedResource)
|
startMutateResultResponse(resp, policy, matchedResource)
|
||||||
defer endMutateResultResponse(logger, resp, startTime)
|
defer endMutateResultResponse(logger, resp, startTime)
|
||||||
|
|
||||||
policyContext.jsonContext.Checkpoint()
|
policyContext.JSONContext().Checkpoint()
|
||||||
defer policyContext.jsonContext.Restore()
|
defer policyContext.JSONContext().Restore()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
applyRules := policy.GetSpec().GetApplyRules()
|
applyRules := policy.GetSpec().GetApplyRules()
|
||||||
|
@ -62,13 +62,13 @@ func Mutate(
|
||||||
func(ctx context.Context, span trace.Span) {
|
func(ctx context.Context, span trace.Span) {
|
||||||
logger := logger.WithValues("rule", rule.Name)
|
logger := logger.WithValues("rule", rule.Name)
|
||||||
var excludeResource []string
|
var excludeResource []string
|
||||||
if len(policyContext.excludeGroupRole) > 0 {
|
if len(policyContext.ExcludeGroupRole()) > 0 {
|
||||||
excludeResource = policyContext.excludeGroupRole
|
excludeResource = policyContext.ExcludeGroupRole()
|
||||||
}
|
}
|
||||||
|
|
||||||
kindsInPolicy := append(rule.MatchResources.GetKinds(), rule.ExcludeResources.GetKinds()...)
|
kindsInPolicy := append(rule.MatchResources.GetKinds(), rule.ExcludeResources.GetKinds()...)
|
||||||
subresourceGVKToAPIResource := GetSubresourceGVKToAPIResourceMap(kindsInPolicy, policyContext)
|
subresourceGVKToAPIResource := GetSubresourceGVKToAPIResourceMap(kindsInPolicy, policyContext)
|
||||||
if err = MatchesResourceDescription(subresourceGVKToAPIResource, matchedResource, rule, policyContext.admissionInfo, excludeResource, policyContext.namespaceLabels, policyContext.policy.GetNamespace(), policyContext.subresource); err != nil {
|
if err = MatchesResourceDescription(subresourceGVKToAPIResource, matchedResource, rule, policyContext.AdmissionInfo(), excludeResource, policyContext.NamespaceLabels(), policyContext.Policy().GetNamespace(), policyContext.SubResource()); err != nil {
|
||||||
logger.V(4).Info("rule not matched", "reason", err.Error())
|
logger.V(4).Info("rule not matched", "reason", err.Error())
|
||||||
skippedRules = append(skippedRules, rule.Name)
|
skippedRules = append(skippedRules, rule.Name)
|
||||||
return
|
return
|
||||||
|
@ -82,8 +82,8 @@ func Mutate(
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.V(3).Info("processing mutate rule", "applyRules", applyRules)
|
logger.V(3).Info("processing mutate rule", "applyRules", applyRules)
|
||||||
resource, err := policyContext.jsonContext.Query("request.object")
|
resource, err := policyContext.JSONContext().Query("request.object")
|
||||||
policyContext.jsonContext.Reset()
|
policyContext.JSONContext().Reset()
|
||||||
if err == nil && resource != nil {
|
if err == nil && resource != nil {
|
||||||
if err := enginectx.AddResource(resource.(map[string]interface{})); err != nil {
|
if err := enginectx.AddResource(resource.(map[string]interface{})); err != nil {
|
||||||
logger.Error(err, "unable to update resource object")
|
logger.Error(err, "unable to update resource object")
|
||||||
|
@ -103,7 +103,7 @@ func Mutate(
|
||||||
|
|
||||||
ruleCopy := rule.DeepCopy()
|
ruleCopy := rule.DeepCopy()
|
||||||
var patchedResources []resourceInfo
|
var patchedResources []resourceInfo
|
||||||
if !policyContext.admissionOperation && rule.IsMutateExisting() {
|
if !policyContext.AdmissionOperation() && rule.IsMutateExisting() {
|
||||||
targets, err := loadTargets(ruleCopy.Mutation.Targets, policyContext, logger)
|
targets, err := loadTargets(ruleCopy.Mutation.Targets, policyContext, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rr := ruleResponse(rule, engineapi.Mutation, err.Error(), engineapi.RuleStatusError)
|
rr := ruleResponse(rule, engineapi.Mutation, err.Error(), engineapi.RuleStatusError)
|
||||||
|
@ -113,12 +113,12 @@ func Mutate(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var parentResourceGVR metav1.GroupVersionResource
|
var parentResourceGVR metav1.GroupVersionResource
|
||||||
if policyContext.subresource != "" {
|
if policyContext.SubResource() != "" {
|
||||||
parentResourceGVR = policyContext.requestResource
|
parentResourceGVR = policyContext.RequestResource()
|
||||||
}
|
}
|
||||||
patchedResources = append(patchedResources, resourceInfo{
|
patchedResources = append(patchedResources, resourceInfo{
|
||||||
unstructured: matchedResource,
|
unstructured: matchedResource,
|
||||||
subresource: policyContext.subresource,
|
subresource: policyContext.SubResource(),
|
||||||
parentResourceGVR: parentResourceGVR,
|
parentResourceGVR: parentResourceGVR,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -128,9 +128,9 @@ func Mutate(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !policyContext.admissionOperation && rule.IsMutateExisting() {
|
if !policyContext.AdmissionOperation() && rule.IsMutateExisting() {
|
||||||
policyContext := policyContext.Copy()
|
policyContext := policyContext.Copy()
|
||||||
if err := policyContext.jsonContext.AddTargetResource(patchedResource.unstructured.Object); err != nil {
|
if err := policyContext.JSONContext().AddTargetResource(patchedResource.unstructured.Object); err != nil {
|
||||||
logging.Error(err, "failed to add target resource to the context")
|
logging.Error(err, "failed to add target resource to the context")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ func Mutate(
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func mutateResource(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructured.Unstructured, logger logr.Logger) *mutate.Response {
|
func mutateResource(rule *kyvernov1.Rule, ctx engineapi.PolicyContext, resource unstructured.Unstructured, logger logr.Logger) *mutate.Response {
|
||||||
preconditionsPassed, err := checkPreconditions(logger, ctx, rule.GetAnyAllConditions())
|
preconditionsPassed, err := checkPreconditions(logger, ctx, rule.GetAnyAllConditions())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mutate.NewErrorResponse("failed to evaluate preconditions", err)
|
return mutate.NewErrorResponse("failed to evaluate preconditions", err)
|
||||||
|
@ -201,7 +201,7 @@ func mutateResource(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructu
|
||||||
|
|
||||||
type forEachMutator struct {
|
type forEachMutator struct {
|
||||||
rule *kyvernov1.Rule
|
rule *kyvernov1.Rule
|
||||||
policyContext *PolicyContext
|
policyContext engineapi.PolicyContext
|
||||||
foreach []kyvernov1.ForEachMutation
|
foreach []kyvernov1.ForEachMutation
|
||||||
resource resourceInfo
|
resource resourceInfo
|
||||||
nesting int
|
nesting int
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
||||||
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
|
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
|
||||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||||
"github.com/kyverno/kyverno/pkg/config"
|
"github.com/kyverno/kyverno/pkg/config"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/api"
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
enginectx "github.com/kyverno/kyverno/pkg/engine/context"
|
enginectx "github.com/kyverno/kyverno/pkg/engine/context"
|
||||||
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
admissionv1 "k8s.io/api/admission/v1"
|
admissionv1 "k8s.io/api/admission/v1"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExcludeFunc is a function used to determine if a resource is excluded
|
|
||||||
type ExcludeFunc = func(kind, namespace, name string) bool
|
|
||||||
|
|
||||||
type PolicyExceptionLister interface {
|
type PolicyExceptionLister interface {
|
||||||
// List lists all PolicyExceptions in the indexer.
|
// List lists all PolicyExceptions in the indexer.
|
||||||
// Objects returned here must be treated as read-only.
|
// Objects returned here must be treated as read-only.
|
||||||
|
@ -59,7 +59,7 @@ type PolicyContext struct {
|
||||||
// Config handler
|
// Config handler
|
||||||
excludeGroupRole []string
|
excludeGroupRole []string
|
||||||
|
|
||||||
excludeResourceFunc ExcludeFunc
|
excludeResourceFunc engineapi.ExcludeFunc
|
||||||
|
|
||||||
// jsonContext is the variable context
|
// jsonContext is the variable context
|
||||||
jsonContext enginectx.Interface
|
jsonContext enginectx.Interface
|
||||||
|
@ -71,7 +71,7 @@ type PolicyContext struct {
|
||||||
admissionOperation bool
|
admissionOperation bool
|
||||||
|
|
||||||
// informerCacheResolvers - used to get resources from informer cache
|
// informerCacheResolvers - used to get resources from informer cache
|
||||||
informerCacheResolvers api.ConfigmapResolver
|
informerCacheResolvers engineapi.ConfigmapResolver
|
||||||
|
|
||||||
// subresource is the subresource being requested, if any (for example, "status" or "scale")
|
// subresource is the subresource being requested, if any (for example, "status" or "scale")
|
||||||
subresource string
|
subresource string
|
||||||
|
@ -79,16 +79,13 @@ type PolicyContext struct {
|
||||||
// subresourcesInPolicy represents the APIResources that are subresources along with their parent resource.
|
// subresourcesInPolicy represents the APIResources that are subresources along with their parent resource.
|
||||||
// This is used to determine if a resource is a subresource. It is only used when the policy context is populated
|
// This is used to determine if a resource is a subresource. It is only used when the policy context is populated
|
||||||
// by kyverno CLI. In all other cases when connected to a cluster, this is empty.
|
// by kyverno CLI. In all other cases when connected to a cluster, this is empty.
|
||||||
subresourcesInPolicy []struct {
|
subresourcesInPolicy []engineapi.SubResource
|
||||||
APIResource metav1.APIResource
|
|
||||||
ParentResource metav1.APIResource
|
|
||||||
}
|
|
||||||
|
|
||||||
// peLister list all policy exceptions
|
// peLister list all policy exceptions
|
||||||
peLister PolicyExceptionLister
|
peLister PolicyExceptionLister
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// engineapi.PolicyContext interface
|
||||||
|
|
||||||
func (c *PolicyContext) Policy() kyvernov1.PolicyInterface {
|
func (c *PolicyContext) Policy() kyvernov1.PolicyInterface {
|
||||||
return c.policy
|
return c.policy
|
||||||
|
@ -106,10 +103,50 @@ func (c *PolicyContext) AdmissionInfo() kyvernov1beta1.RequestInfo {
|
||||||
return c.admissionInfo
|
return c.admissionInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *PolicyContext) NamespaceLabels() map[string]string {
|
||||||
|
return c.namespaceLabels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PolicyContext) SubResource() string {
|
||||||
|
return c.subresource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PolicyContext) SubresourcesInPolicy() []engineapi.SubResource {
|
||||||
|
return c.subresourcesInPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PolicyContext) ExcludeGroupRole() []string {
|
||||||
|
return c.excludeGroupRole
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PolicyContext) AdmissionOperation() bool {
|
||||||
|
return c.admissionOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PolicyContext) RequestResource() metav1.GroupVersionResource {
|
||||||
|
return c.requestResource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PolicyContext) Element() unstructured.Unstructured {
|
||||||
|
return c.element
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PolicyContext) SetElement(element unstructured.Unstructured) {
|
||||||
|
c.element = element
|
||||||
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) JSONContext() enginectx.Interface {
|
func (c *PolicyContext) JSONContext() enginectx.Interface {
|
||||||
return c.jsonContext
|
return c.jsonContext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *PolicyContext) Client() dclient.Interface {
|
||||||
|
return c.client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c PolicyContext) Copy() engineapi.PolicyContext {
|
||||||
|
return c.copy()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) FindExceptions(rule string) ([]*kyvernov2alpha1.PolicyException, error) {
|
func (c *PolicyContext) FindExceptions(rule string) ([]*kyvernov2alpha1.PolicyException, error) {
|
||||||
if c.peLister == nil {
|
if c.peLister == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -131,44 +168,48 @@ func (c *PolicyContext) FindExceptions(rule string) ([]*kyvernov2alpha1.PolicyEx
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) Client() dclient.Interface {
|
func (c *PolicyContext) ExcludeResourceFunc() engineapi.ExcludeFunc {
|
||||||
return c.client
|
return c.excludeResourceFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PolicyContext) ResolveConfigMap(ctx context.Context, namespace string, name string) (*corev1.ConfigMap, error) {
|
||||||
|
return c.informerCacheResolvers.Get(ctx, namespace, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mutators
|
// Mutators
|
||||||
|
|
||||||
func (c *PolicyContext) WithPolicy(policy kyvernov1.PolicyInterface) *PolicyContext {
|
func (c *PolicyContext) WithPolicy(policy kyvernov1.PolicyInterface) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.policy = policy
|
copy.policy = policy
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithNamespaceLabels(namespaceLabels map[string]string) *PolicyContext {
|
func (c *PolicyContext) WithNamespaceLabels(namespaceLabels map[string]string) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.namespaceLabels = namespaceLabels
|
copy.namespaceLabels = namespaceLabels
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithAdmissionInfo(admissionInfo kyvernov1beta1.RequestInfo) *PolicyContext {
|
func (c *PolicyContext) WithAdmissionInfo(admissionInfo kyvernov1beta1.RequestInfo) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.admissionInfo = admissionInfo
|
copy.admissionInfo = admissionInfo
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithRequestResource(requestResource metav1.GroupVersionResource) *PolicyContext {
|
func (c *PolicyContext) WithRequestResource(requestResource metav1.GroupVersionResource) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.requestResource = requestResource
|
copy.requestResource = requestResource
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithNewResource(resource unstructured.Unstructured) *PolicyContext {
|
func (c *PolicyContext) WithNewResource(resource unstructured.Unstructured) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.newResource = resource
|
copy.newResource = resource
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithOldResource(resource unstructured.Unstructured) *PolicyContext {
|
func (c *PolicyContext) WithOldResource(resource unstructured.Unstructured) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.oldResource = resource
|
copy.oldResource = resource
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
@ -178,19 +219,19 @@ func (c *PolicyContext) WithResources(newResource unstructured.Unstructured, old
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithClient(client dclient.Interface) *PolicyContext {
|
func (c *PolicyContext) WithClient(client dclient.Interface) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.client = client
|
copy.client = client
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithExcludeGroupRole(excludeGroupRole ...string) *PolicyContext {
|
func (c *PolicyContext) WithExcludeGroupRole(excludeGroupRole ...string) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.excludeGroupRole = excludeGroupRole
|
copy.excludeGroupRole = excludeGroupRole
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithExcludeResourceFunc(excludeResourceFunc ExcludeFunc) *PolicyContext {
|
func (c *PolicyContext) WithExcludeResourceFunc(excludeResourceFunc engineapi.ExcludeFunc) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.excludeResourceFunc = excludeResourceFunc
|
copy.excludeResourceFunc = excludeResourceFunc
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
@ -200,39 +241,39 @@ func (c *PolicyContext) WithConfiguration(configuration config.Configuration) *P
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithAdmissionOperation(admissionOperation bool) *PolicyContext {
|
func (c *PolicyContext) WithAdmissionOperation(admissionOperation bool) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.admissionOperation = admissionOperation
|
copy.admissionOperation = admissionOperation
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithInformerCacheResolver(informerCacheResolver api.ConfigmapResolver) *PolicyContext {
|
func (c *PolicyContext) WithInformerCacheResolver(informerCacheResolver engineapi.ConfigmapResolver) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.informerCacheResolvers = informerCacheResolver
|
copy.informerCacheResolvers = informerCacheResolver
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithSubresource(subresource string) *PolicyContext {
|
func (c *PolicyContext) WithSubresource(subresource string) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.subresource = subresource
|
copy.subresource = subresource
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithSubresourcesInPolicy(subresourcesInPolicy []struct {
|
func (c *PolicyContext) WithSubresourcesInPolicy(subresourcesInPolicy []engineapi.SubResource) *PolicyContext {
|
||||||
APIResource metav1.APIResource
|
copy := c.copy()
|
||||||
ParentResource metav1.APIResource
|
|
||||||
},
|
|
||||||
) *PolicyContext {
|
|
||||||
copy := c.Copy()
|
|
||||||
copy.subresourcesInPolicy = subresourcesInPolicy
|
copy.subresourcesInPolicy = subresourcesInPolicy
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PolicyContext) WithExceptions(peLister PolicyExceptionLister) *PolicyContext {
|
func (c *PolicyContext) WithExceptions(peLister PolicyExceptionLister) *PolicyContext {
|
||||||
copy := c.Copy()
|
copy := c.copy()
|
||||||
copy.peLister = peLister
|
copy.peLister = peLister
|
||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c PolicyContext) copy() *PolicyContext {
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
func NewPolicyContextWithJsonContext(jsonContext enginectx.Interface) *PolicyContext {
|
func NewPolicyContextWithJsonContext(jsonContext enginectx.Interface) *PolicyContext {
|
||||||
return &PolicyContext{
|
return &PolicyContext{
|
||||||
|
@ -253,7 +294,7 @@ func NewPolicyContextFromAdmissionRequest(
|
||||||
admissionInfo kyvernov1beta1.RequestInfo,
|
admissionInfo kyvernov1beta1.RequestInfo,
|
||||||
configuration config.Configuration,
|
configuration config.Configuration,
|
||||||
client dclient.Interface,
|
client dclient.Interface,
|
||||||
informerCacheResolver api.ConfigmapResolver,
|
informerCacheResolver engineapi.ConfigmapResolver,
|
||||||
polexLister PolicyExceptionLister,
|
polexLister PolicyExceptionLister,
|
||||||
) (*PolicyContext, error) {
|
) (*PolicyContext, error) {
|
||||||
ctx, err := newVariablesContext(request, &admissionInfo)
|
ctx, err := newVariablesContext(request, &admissionInfo)
|
||||||
|
@ -282,10 +323,6 @@ func NewPolicyContextFromAdmissionRequest(
|
||||||
return policyContext, nil
|
return policyContext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c PolicyContext) Copy() *PolicyContext {
|
|
||||||
return &c
|
|
||||||
}
|
|
||||||
|
|
||||||
func newVariablesContext(request *admissionv1.AdmissionRequest, userRequestInfo *kyvernov1beta1.RequestInfo) (enginectx.Interface, error) {
|
func newVariablesContext(request *admissionv1.AdmissionRequest, userRequestInfo *kyvernov1beta1.RequestInfo) (enginectx.Interface, error) {
|
||||||
ctx := enginectx.NewContext()
|
ctx := enginectx.NewContext()
|
||||||
if err := ctx.AddRequest(request); err != nil {
|
if err := ctx.AddRequest(request); err != nil {
|
||||||
|
|
|
@ -325,8 +325,8 @@ func ManagedPodResource(policy kyvernov1.PolicyInterface, resource unstructured.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkPreconditions(logger logr.Logger, ctx *PolicyContext, anyAllConditions apiextensions.JSON) (bool, error) {
|
func checkPreconditions(logger logr.Logger, ctx engineapi.PolicyContext, anyAllConditions apiextensions.JSON) (bool, error) {
|
||||||
preconditions, err := variables.SubstituteAllInPreconditions(logger, ctx.jsonContext, anyAllConditions)
|
preconditions, err := variables.SubstituteAllInPreconditions(logger, ctx.JSONContext(), anyAllConditions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.Wrapf(err, "failed to substitute variables in preconditions")
|
return false, errors.Wrapf(err, "failed to substitute variables in preconditions")
|
||||||
}
|
}
|
||||||
|
@ -336,7 +336,7 @@ func checkPreconditions(logger logr.Logger, ctx *PolicyContext, anyAllConditions
|
||||||
return false, errors.Wrapf(err, "failed to parse preconditions")
|
return false, errors.Wrapf(err, "failed to parse preconditions")
|
||||||
}
|
}
|
||||||
|
|
||||||
pass := variables.EvaluateConditions(logger, ctx.jsonContext, typeConditions)
|
pass := variables.EvaluateConditions(logger, ctx.JSONContext(), typeConditions)
|
||||||
return pass, nil
|
return pass, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ import (
|
||||||
func Validate(
|
func Validate(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
policyContext *PolicyContext,
|
policyContext engineapi.PolicyContext,
|
||||||
cfg config.Configuration,
|
cfg config.Configuration,
|
||||||
) (resp *engineapi.EngineResponse) {
|
) (resp *engineapi.EngineResponse) {
|
||||||
resp = &engineapi.EngineResponse{}
|
resp = &engineapi.EngineResponse{}
|
||||||
|
@ -53,46 +53,48 @@ func Validate(
|
||||||
}()
|
}()
|
||||||
|
|
||||||
resp = validateResource(ctx, contextLoader, logger, policyContext, cfg)
|
resp = validateResource(ctx, contextLoader, logger, policyContext, cfg)
|
||||||
resp.NamespaceLabels = policyContext.namespaceLabels
|
resp.NamespaceLabels = policyContext.NamespaceLabels()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildLogger(ctx *PolicyContext) logr.Logger {
|
func buildLogger(ctx engineapi.PolicyContext) logr.Logger {
|
||||||
logger := logging.WithName("EngineValidate").WithValues("policy", ctx.policy.GetName())
|
logger := logging.WithName("EngineValidate").WithValues("policy", ctx.Policy().GetName())
|
||||||
if reflect.DeepEqual(ctx.newResource, unstructured.Unstructured{}) {
|
newResource := ctx.NewResource()
|
||||||
logger = logger.WithValues("kind", ctx.oldResource.GetKind(), "namespace", ctx.oldResource.GetNamespace(), "name", ctx.oldResource.GetName())
|
oldResource := ctx.OldResource()
|
||||||
|
if reflect.DeepEqual(newResource, unstructured.Unstructured{}) {
|
||||||
|
logger = logger.WithValues("kind", oldResource.GetKind(), "namespace", oldResource.GetNamespace(), "name", oldResource.GetName())
|
||||||
} else {
|
} else {
|
||||||
logger = logger.WithValues("kind", ctx.newResource.GetKind(), "namespace", ctx.newResource.GetNamespace(), "name", ctx.newResource.GetName())
|
logger = logger.WithValues("kind", newResource.GetKind(), "namespace", newResource.GetNamespace(), "name", newResource.GetName())
|
||||||
}
|
}
|
||||||
|
|
||||||
return logger
|
return logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildResponse(ctx *PolicyContext, resp *engineapi.EngineResponse, startTime time.Time) {
|
func buildResponse(ctx engineapi.PolicyContext, resp *engineapi.EngineResponse, startTime time.Time) {
|
||||||
if reflect.DeepEqual(resp, engineapi.EngineResponse{}) {
|
if reflect.DeepEqual(resp, engineapi.EngineResponse{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if reflect.DeepEqual(resp.PatchedResource, unstructured.Unstructured{}) {
|
if reflect.DeepEqual(resp.PatchedResource, unstructured.Unstructured{}) {
|
||||||
// for delete requests patched resource will be oldResource since newResource is empty
|
// for delete requests patched resource will be oldResource since newResource is empty
|
||||||
resource := ctx.newResource
|
resource := ctx.NewResource()
|
||||||
if reflect.DeepEqual(ctx.newResource, unstructured.Unstructured{}) {
|
if reflect.DeepEqual(resource, unstructured.Unstructured{}) {
|
||||||
resource = ctx.oldResource
|
resource = ctx.OldResource()
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.PatchedResource = resource
|
resp.PatchedResource = resource
|
||||||
}
|
}
|
||||||
|
policy := ctx.Policy()
|
||||||
resp.Policy = ctx.policy
|
resp.Policy = policy
|
||||||
resp.PolicyResponse.Policy.Name = ctx.policy.GetName()
|
resp.PolicyResponse.Policy.Name = policy.GetName()
|
||||||
resp.PolicyResponse.Policy.Namespace = ctx.policy.GetNamespace()
|
resp.PolicyResponse.Policy.Namespace = policy.GetNamespace()
|
||||||
resp.PolicyResponse.Resource.Name = resp.PatchedResource.GetName()
|
resp.PolicyResponse.Resource.Name = resp.PatchedResource.GetName()
|
||||||
resp.PolicyResponse.Resource.Namespace = resp.PatchedResource.GetNamespace()
|
resp.PolicyResponse.Resource.Namespace = resp.PatchedResource.GetNamespace()
|
||||||
resp.PolicyResponse.Resource.Kind = resp.PatchedResource.GetKind()
|
resp.PolicyResponse.Resource.Kind = resp.PatchedResource.GetKind()
|
||||||
resp.PolicyResponse.Resource.APIVersion = resp.PatchedResource.GetAPIVersion()
|
resp.PolicyResponse.Resource.APIVersion = resp.PatchedResource.GetAPIVersion()
|
||||||
resp.PolicyResponse.ValidationFailureAction = ctx.policy.GetSpec().ValidationFailureAction
|
resp.PolicyResponse.ValidationFailureAction = policy.GetSpec().ValidationFailureAction
|
||||||
|
|
||||||
for _, v := range ctx.policy.GetSpec().ValidationFailureActionOverrides {
|
for _, v := range policy.GetSpec().ValidationFailureActionOverrides {
|
||||||
newOverrides := engineapi.ValidationFailureActionOverride{Action: v.Action, Namespaces: v.Namespaces, NamespaceSelector: v.NamespaceSelector}
|
newOverrides := engineapi.ValidationFailureActionOverride{Action: v.Action, Namespaces: v.Namespaces, NamespaceSelector: v.NamespaceSelector}
|
||||||
resp.PolicyResponse.ValidationFailureActionOverrides = append(resp.PolicyResponse.ValidationFailureActionOverrides, newOverrides)
|
resp.PolicyResponse.ValidationFailureActionOverrides = append(resp.PolicyResponse.ValidationFailureActionOverrides, newOverrides)
|
||||||
}
|
}
|
||||||
|
@ -105,24 +107,26 @@ func validateResource(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
log logr.Logger,
|
log logr.Logger,
|
||||||
enginectx *PolicyContext,
|
enginectx engineapi.PolicyContext,
|
||||||
cfg config.Configuration,
|
cfg config.Configuration,
|
||||||
) *engineapi.EngineResponse {
|
) *engineapi.EngineResponse {
|
||||||
resp := &engineapi.EngineResponse{}
|
resp := &engineapi.EngineResponse{}
|
||||||
|
|
||||||
enginectx.jsonContext.Checkpoint()
|
enginectx.JSONContext().Checkpoint()
|
||||||
defer enginectx.jsonContext.Restore()
|
defer enginectx.JSONContext().Restore()
|
||||||
|
|
||||||
rules := autogen.ComputeRules(enginectx.policy)
|
rules := autogen.ComputeRules(enginectx.Policy())
|
||||||
matchCount := 0
|
matchCount := 0
|
||||||
applyRules := enginectx.policy.GetSpec().GetApplyRules()
|
applyRules := enginectx.Policy().GetSpec().GetApplyRules()
|
||||||
|
newResource := enginectx.NewResource()
|
||||||
|
oldResource := enginectx.OldResource()
|
||||||
|
|
||||||
if enginectx.policy.IsNamespaced() {
|
if enginectx.Policy().IsNamespaced() {
|
||||||
polNs := enginectx.policy.GetNamespace()
|
polNs := enginectx.Policy().GetNamespace()
|
||||||
if enginectx.newResource.Object != nil && (enginectx.newResource.GetNamespace() != polNs || enginectx.newResource.GetNamespace() == "") {
|
if enginectx.NewResource().Object != nil && (newResource.GetNamespace() != polNs || newResource.GetNamespace() == "") {
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
if enginectx.oldResource.Object != nil && (enginectx.oldResource.GetNamespace() != polNs || enginectx.oldResource.GetNamespace() == "") {
|
if enginectx.OldResource().Object != nil && (oldResource.GetNamespace() != polNs || oldResource.GetNamespace() == "") {
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +134,7 @@ func validateResource(
|
||||||
for i := range rules {
|
for i := range rules {
|
||||||
rule := &rules[i]
|
rule := &rules[i]
|
||||||
log.V(3).Info("processing validation rule", "matchCount", matchCount, "applyRules", applyRules)
|
log.V(3).Info("processing validation rule", "matchCount", matchCount, "applyRules", applyRules)
|
||||||
enginectx.jsonContext.Reset()
|
enginectx.JSONContext().Reset()
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
ruleResp := tracing.ChildSpan1(
|
ruleResp := tracing.ChildSpan1(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -156,7 +160,7 @@ func validateResource(
|
||||||
return ruleResp
|
return ruleResp
|
||||||
}
|
}
|
||||||
log.V(3).Info("processing validation rule", "matchCount", matchCount, "applyRules", applyRules)
|
log.V(3).Info("processing validation rule", "matchCount", matchCount, "applyRules", applyRules)
|
||||||
enginectx.jsonContext.Reset()
|
enginectx.JSONContext().Reset()
|
||||||
if hasValidate && !hasYAMLSignatureVerify {
|
if hasValidate && !hasYAMLSignatureVerify {
|
||||||
return processValidationRule(ctx, contextLoader, log, enginectx, rule)
|
return processValidationRule(ctx, contextLoader, log, enginectx, rule)
|
||||||
} else if hasValidateImage {
|
} else if hasValidateImage {
|
||||||
|
@ -182,7 +186,7 @@ func processValidationRule(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
log logr.Logger,
|
log logr.Logger,
|
||||||
policyContext *PolicyContext,
|
policyContext engineapi.PolicyContext,
|
||||||
rule *kyvernov1.Rule,
|
rule *kyvernov1.Rule,
|
||||||
) *engineapi.RuleResponse {
|
) *engineapi.RuleResponse {
|
||||||
v := newValidator(log, contextLoader, policyContext, rule)
|
v := newValidator(log, contextLoader, policyContext, rule)
|
||||||
|
@ -205,7 +209,7 @@ func addRuleResponse(log logr.Logger, resp *engineapi.EngineResponse, ruleResp *
|
||||||
|
|
||||||
type validator struct {
|
type validator struct {
|
||||||
log logr.Logger
|
log logr.Logger
|
||||||
policyContext *PolicyContext
|
policyContext engineapi.PolicyContext
|
||||||
rule *kyvernov1.Rule
|
rule *kyvernov1.Rule
|
||||||
contextEntries []kyvernov1.ContextEntry
|
contextEntries []kyvernov1.ContextEntry
|
||||||
anyAllConditions apiextensions.JSON
|
anyAllConditions apiextensions.JSON
|
||||||
|
@ -218,7 +222,7 @@ type validator struct {
|
||||||
nesting int
|
nesting int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newValidator(log logr.Logger, contextLoader ContextLoaderFactory, ctx *PolicyContext, rule *kyvernov1.Rule) *validator {
|
func newValidator(log logr.Logger, contextLoader ContextLoaderFactory, ctx engineapi.PolicyContext, rule *kyvernov1.Rule) *validator {
|
||||||
ruleCopy := rule.DeepCopy()
|
ruleCopy := rule.DeepCopy()
|
||||||
return &validator{
|
return &validator{
|
||||||
log: log,
|
log: log,
|
||||||
|
@ -240,7 +244,7 @@ func newForEachValidator(
|
||||||
contextLoader ContextLoaderFactory,
|
contextLoader ContextLoaderFactory,
|
||||||
nesting int,
|
nesting int,
|
||||||
rule *kyvernov1.Rule,
|
rule *kyvernov1.Rule,
|
||||||
ctx *PolicyContext,
|
ctx engineapi.PolicyContext,
|
||||||
log logr.Logger,
|
log logr.Logger,
|
||||||
) (*validator, error) {
|
) (*validator, error) {
|
||||||
ruleCopy := rule.DeepCopy()
|
ruleCopy := rule.DeepCopy()
|
||||||
|
@ -325,24 +329,20 @@ func (v *validator) validateForEach(ctx context.Context) *engineapi.RuleResponse
|
||||||
if resp.Status != engineapi.RuleStatusPass {
|
if resp.Status != engineapi.RuleStatusPass {
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
applyCount += count
|
applyCount += count
|
||||||
}
|
}
|
||||||
|
|
||||||
if applyCount == 0 {
|
if applyCount == 0 {
|
||||||
if v.forEach == nil {
|
if v.forEach == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return ruleResponse(*v.rule, engineapi.Validation, "rule skipped", engineapi.RuleStatusSkip)
|
return ruleResponse(*v.rule, engineapi.Validation, "rule skipped", engineapi.RuleStatusSkip)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ruleResponse(*v.rule, engineapi.Validation, "rule passed", engineapi.RuleStatusPass)
|
return ruleResponse(*v.rule, engineapi.Validation, "rule passed", engineapi.RuleStatusPass)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *validator) validateElements(ctx context.Context, foreach kyvernov1.ForEachValidation, elements []interface{}, elementScope *bool) (*engineapi.RuleResponse, int) {
|
func (v *validator) validateElements(ctx context.Context, foreach kyvernov1.ForEachValidation, elements []interface{}, elementScope *bool) (*engineapi.RuleResponse, int) {
|
||||||
v.policyContext.jsonContext.Checkpoint()
|
v.policyContext.JSONContext().Checkpoint()
|
||||||
defer v.policyContext.jsonContext.Restore()
|
defer v.policyContext.JSONContext().Restore()
|
||||||
applyCount := 0
|
applyCount := 0
|
||||||
|
|
||||||
for index, element := range elements {
|
for index, element := range elements {
|
||||||
|
@ -388,7 +388,7 @@ func (v *validator) validateElements(ctx context.Context, foreach kyvernov1.ForE
|
||||||
return ruleResponse(*v.rule, engineapi.Validation, "", engineapi.RuleStatusPass), applyCount
|
return ruleResponse(*v.rule, engineapi.Validation, "", engineapi.RuleStatusPass), applyCount
|
||||||
}
|
}
|
||||||
|
|
||||||
func addElementToContext(ctx *PolicyContext, element interface{}, index, nesting int, elementScope *bool) error {
|
func addElementToContext(ctx engineapi.PolicyContext, element interface{}, index, nesting int, elementScope *bool) error {
|
||||||
data, err := variables.DocumentToUntyped(element)
|
data, err := variables.DocumentToUntyped(element)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -412,11 +412,10 @@ func addElementToContext(ctx *PolicyContext, element interface{}, index, nesting
|
||||||
}
|
}
|
||||||
scoped = *elementScope
|
scoped = *elementScope
|
||||||
}
|
}
|
||||||
|
|
||||||
if scoped {
|
if scoped {
|
||||||
u := unstructured.Unstructured{}
|
u := unstructured.Unstructured{}
|
||||||
u.SetUnstructuredContent(dataMap)
|
u.SetUnstructuredContent(dataMap)
|
||||||
ctx.element = u
|
ctx.SetElement(u)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -437,7 +436,7 @@ func (v *validator) loadContext(ctx context.Context) error {
|
||||||
|
|
||||||
func (v *validator) validateDeny() *engineapi.RuleResponse {
|
func (v *validator) validateDeny() *engineapi.RuleResponse {
|
||||||
anyAllCond := v.deny.GetAnyAllConditions()
|
anyAllCond := v.deny.GetAnyAllConditions()
|
||||||
anyAllCond, err := variables.SubstituteAll(v.log, v.policyContext.jsonContext, anyAllCond)
|
anyAllCond, err := variables.SubstituteAll(v.log, v.policyContext.JSONContext(), anyAllCond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ruleError(v.rule, engineapi.Validation, "failed to substitute variables in deny conditions", err)
|
return ruleError(v.rule, engineapi.Validation, "failed to substitute variables in deny conditions", err)
|
||||||
}
|
}
|
||||||
|
@ -451,7 +450,7 @@ func (v *validator) validateDeny() *engineapi.RuleResponse {
|
||||||
return ruleError(v.rule, engineapi.Validation, "invalid deny conditions", err)
|
return ruleError(v.rule, engineapi.Validation, "invalid deny conditions", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
deny := variables.EvaluateConditions(v.log, v.policyContext.jsonContext, denyConditions)
|
deny := variables.EvaluateConditions(v.log, v.policyContext.JSONContext(), denyConditions)
|
||||||
if deny {
|
if deny {
|
||||||
return ruleResponse(*v.rule, engineapi.Validation, v.getDenyMessage(deny), engineapi.RuleStatusFail)
|
return ruleResponse(*v.rule, engineapi.Validation, v.getDenyMessage(deny), engineapi.RuleStatusFail)
|
||||||
}
|
}
|
||||||
|
@ -467,7 +466,7 @@ func (v *validator) getDenyMessage(deny bool) string {
|
||||||
if msg == "" {
|
if msg == "" {
|
||||||
return fmt.Sprintf("validation error: rule %s failed", v.rule.Name)
|
return fmt.Sprintf("validation error: rule %s failed", v.rule.Name)
|
||||||
}
|
}
|
||||||
raw, err := variables.SubstituteAll(v.log, v.policyContext.jsonContext, msg)
|
raw, err := variables.SubstituteAll(v.log, v.policyContext.JSONContext(), msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
@ -480,12 +479,13 @@ func (v *validator) getDenyMessage(deny bool) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSpec(v *validator) (podSpec *corev1.PodSpec, metadata *metav1.ObjectMeta, err error) {
|
func getSpec(v *validator) (podSpec *corev1.PodSpec, metadata *metav1.ObjectMeta, err error) {
|
||||||
kind := v.policyContext.newResource.GetKind()
|
newResource := v.policyContext.NewResource()
|
||||||
|
kind := newResource.GetKind()
|
||||||
|
|
||||||
if kind == "DaemonSet" || kind == "Deployment" || kind == "Job" || kind == "StatefulSet" || kind == "ReplicaSet" || kind == "ReplicationController" {
|
if kind == "DaemonSet" || kind == "Deployment" || kind == "Job" || kind == "StatefulSet" || kind == "ReplicaSet" || kind == "ReplicationController" {
|
||||||
var deployment appsv1.Deployment
|
var deployment appsv1.Deployment
|
||||||
|
|
||||||
resourceBytes, err := v.policyContext.newResource.MarshalJSON()
|
resourceBytes, err := newResource.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -499,7 +499,7 @@ func getSpec(v *validator) (podSpec *corev1.PodSpec, metadata *metav1.ObjectMeta
|
||||||
} else if kind == "CronJob" {
|
} else if kind == "CronJob" {
|
||||||
var cronJob batchv1.CronJob
|
var cronJob batchv1.CronJob
|
||||||
|
|
||||||
resourceBytes, err := v.policyContext.newResource.MarshalJSON()
|
resourceBytes, err := newResource.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -512,7 +512,7 @@ func getSpec(v *validator) (podSpec *corev1.PodSpec, metadata *metav1.ObjectMeta
|
||||||
} else if kind == "Pod" {
|
} else if kind == "Pod" {
|
||||||
var pod corev1.Pod
|
var pod corev1.Pod
|
||||||
|
|
||||||
resourceBytes, err := v.policyContext.newResource.MarshalJSON()
|
resourceBytes, err := newResource.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -566,22 +566,22 @@ func (v *validator) validatePodSecurity() *engineapi.RuleResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *validator) validateResourceWithRule() *engineapi.RuleResponse {
|
func (v *validator) validateResourceWithRule() *engineapi.RuleResponse {
|
||||||
if !isEmptyUnstructured(&v.policyContext.element) {
|
element := v.policyContext.Element()
|
||||||
return v.validatePatterns(v.policyContext.element)
|
if !isEmptyUnstructured(&element) {
|
||||||
|
return v.validatePatterns(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDeleteRequest(v.policyContext) {
|
if isDeleteRequest(v.policyContext) {
|
||||||
v.log.V(3).Info("skipping validation on deleted resource")
|
v.log.V(3).Info("skipping validation on deleted resource")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
resp := v.validatePatterns(v.policyContext.NewResource())
|
||||||
resp := v.validatePatterns(v.policyContext.newResource)
|
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDeleteRequest(ctx *PolicyContext) bool {
|
func isDeleteRequest(ctx engineapi.PolicyContext) bool {
|
||||||
|
newResource := ctx.NewResource()
|
||||||
// if the OldResource is not empty, and the NewResource is empty, the request is a DELETE
|
// if the OldResource is not empty, and the NewResource is empty, the request is a DELETE
|
||||||
return isEmptyUnstructured(&ctx.newResource)
|
return isEmptyUnstructured(&newResource)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEmptyUnstructured(u *unstructured.Unstructured) bool {
|
func isEmptyUnstructured(u *unstructured.Unstructured) bool {
|
||||||
|
@ -597,14 +597,14 @@ func isEmptyUnstructured(u *unstructured.Unstructured) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// matches checks if either the new or old resource satisfies the filter conditions defined in the rule
|
// matches checks if either the new or old resource satisfies the filter conditions defined in the rule
|
||||||
func matches(logger logr.Logger, rule *kyvernov1.Rule, ctx *PolicyContext, subresourceGVKToAPIResource map[string]*metav1.APIResource) bool {
|
func matches(logger logr.Logger, rule *kyvernov1.Rule, ctx engineapi.PolicyContext, subresourceGVKToAPIResource map[string]*metav1.APIResource) bool {
|
||||||
err := MatchesResourceDescription(subresourceGVKToAPIResource, ctx.newResource, *rule, ctx.admissionInfo, ctx.excludeGroupRole, ctx.namespaceLabels, "", ctx.subresource)
|
err := MatchesResourceDescription(subresourceGVKToAPIResource, ctx.NewResource(), *rule, ctx.AdmissionInfo(), ctx.ExcludeGroupRole(), ctx.NamespaceLabels(), "", ctx.SubResource())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(ctx.OldResource, unstructured.Unstructured{}) {
|
if !reflect.DeepEqual(ctx.OldResource, unstructured.Unstructured{}) {
|
||||||
err := MatchesResourceDescription(subresourceGVKToAPIResource, ctx.oldResource, *rule, ctx.admissionInfo, ctx.excludeGroupRole, ctx.namespaceLabels, "", ctx.subresource)
|
err := MatchesResourceDescription(subresourceGVKToAPIResource, ctx.OldResource(), *rule, ctx.AdmissionInfo(), ctx.ExcludeGroupRole(), ctx.NamespaceLabels(), "", ctx.SubResource())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -728,7 +728,7 @@ func (v *validator) buildErrorMessage(err error, path string) string {
|
||||||
return fmt.Sprintf("validation error: rule %s execution error: %s", v.rule.Name, err.Error())
|
return fmt.Sprintf("validation error: rule %s execution error: %s", v.rule.Name, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
msgRaw, sErr := variables.SubstituteAll(v.log, v.policyContext.jsonContext, v.rule.Validation.Message)
|
msgRaw, sErr := variables.SubstituteAll(v.log, v.policyContext.JSONContext(), v.rule.Validation.Message)
|
||||||
if sErr != nil {
|
if sErr != nil {
|
||||||
v.log.V(2).Info("failed to substitute variables in message", "error", sErr)
|
v.log.V(2).Info("failed to substitute variables in message", "error", sErr)
|
||||||
return fmt.Sprintf("validation error: variables substitution error in rule %s execution error: %s", v.rule.Name, err.Error())
|
return fmt.Sprintf("validation error: variables substitution error in rule %s execution error: %s", v.rule.Name, err.Error())
|
||||||
|
@ -759,7 +759,7 @@ func buildAnyPatternErrorMessage(rule *kyvernov1.Rule, errors []string) string {
|
||||||
|
|
||||||
func (v *validator) substitutePatterns() error {
|
func (v *validator) substitutePatterns() error {
|
||||||
if v.pattern != nil {
|
if v.pattern != nil {
|
||||||
i, err := variables.SubstituteAll(v.log, v.policyContext.jsonContext, v.pattern)
|
i, err := variables.SubstituteAll(v.log, v.policyContext.JSONContext(), v.pattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -769,7 +769,7 @@ func (v *validator) substitutePatterns() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.anyPattern != nil {
|
if v.anyPattern != nil {
|
||||||
i, err := variables.SubstituteAll(v.log, v.policyContext.jsonContext, v.anyPattern)
|
i, err := variables.SubstituteAll(v.log, v.policyContext.JSONContext(), v.anyPattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -785,19 +785,17 @@ func (v *validator) substituteDeny() error {
|
||||||
if v.deny == nil {
|
if v.deny == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
i, err := variables.SubstituteAll(v.log, v.policyContext.JSONContext(), v.deny)
|
||||||
i, err := variables.SubstituteAll(v.log, v.policyContext.jsonContext, v.deny)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
v.deny = i.(*kyvernov1.Deny)
|
v.deny = i.(*kyvernov1.Deny)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchesException checks if an exception applies to the resource being admitted
|
// matchesException checks if an exception applies to the resource being admitted
|
||||||
func matchesException(
|
func matchesException(
|
||||||
policyContext *PolicyContext,
|
policyContext engineapi.PolicyContext,
|
||||||
rule *kyvernov1.Rule,
|
rule *kyvernov1.Rule,
|
||||||
subresourceGVKToAPIResource map[string]*metav1.APIResource,
|
subresourceGVKToAPIResource map[string]*metav1.APIResource,
|
||||||
) (*kyvernov2alpha1.PolicyException, error) {
|
) (*kyvernov2alpha1.PolicyException, error) {
|
||||||
|
@ -807,13 +805,13 @@ func matchesException(
|
||||||
}
|
}
|
||||||
for _, candidate := range candidates {
|
for _, candidate := range candidates {
|
||||||
err := matched.CheckMatchesResources(
|
err := matched.CheckMatchesResources(
|
||||||
policyContext.newResource,
|
policyContext.NewResource(),
|
||||||
candidate.Spec.Match,
|
candidate.Spec.Match,
|
||||||
policyContext.namespaceLabels,
|
policyContext.NamespaceLabels(),
|
||||||
subresourceGVKToAPIResource,
|
subresourceGVKToAPIResource,
|
||||||
policyContext.subresource,
|
policyContext.SubResource(),
|
||||||
policyContext.admissionInfo,
|
policyContext.AdmissionInfo(),
|
||||||
policyContext.excludeGroupRole,
|
policyContext.ExcludeGroupRole(),
|
||||||
)
|
)
|
||||||
// if there's no error it means a match
|
// if there's no error it means a match
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -825,7 +823,7 @@ func matchesException(
|
||||||
|
|
||||||
// hasPolicyExceptions returns nil when there are no matching exceptions.
|
// hasPolicyExceptions returns nil when there are no matching exceptions.
|
||||||
// A rule response is returned when an exception is matched, or there is an error.
|
// A rule response is returned when an exception is matched, or there is an error.
|
||||||
func hasPolicyExceptions(ctx *PolicyContext, rule *kyvernov1.Rule, subresourceGVKToAPIResource map[string]*metav1.APIResource, log logr.Logger) *engineapi.RuleResponse {
|
func hasPolicyExceptions(ctx engineapi.PolicyContext, rule *kyvernov1.Rule, subresourceGVKToAPIResource map[string]*metav1.APIResource, log logr.Logger) *engineapi.RuleResponse {
|
||||||
// if matches, check if there is a corresponding policy exception
|
// if matches, check if there is a corresponding policy exception
|
||||||
exception, err := matchesException(ctx, rule, subresourceGVKToAPIResource)
|
exception, err := matchesException(ctx, rule, subresourceGVKToAPIResource)
|
||||||
// if we found an exception
|
// if we found an exception
|
||||||
|
|
Loading…
Reference in a new issue