diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go index 4b2edb9680..5f6f991dff 100644 --- a/cmd/cli/kubectl-kyverno/utils/common/common.go +++ b/cmd/cli/kubectl-kyverno/utils/common/common.go @@ -26,6 +26,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine/response" ut "github.com/kyverno/kyverno/pkg/engine/utils" "github.com/kyverno/kyverno/pkg/engine/variables" + "github.com/kyverno/kyverno/pkg/registryclient" yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml" yamlv2 "gopkg.in/yaml.v2" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -457,7 +458,7 @@ OuterLoop: WithAdmissionInfo(c.UserInfo). WithClient(c.Client) - mutateResponse := engine.Mutate(policyContext) + mutateResponse := engine.Mutate(registryclient.NewOrDie(), policyContext) if mutateResponse != nil { engineResponses = append(engineResponses, mutateResponse) } @@ -481,7 +482,7 @@ OuterLoop: var info Info var validateResponse *response.EngineResponse if policyHasValidate { - validateResponse = engine.Validate(policyContext) + validateResponse = engine.Validate(registryclient.NewOrDie(), policyContext) info = ProcessValidateEngineResponse(c.Policy, validateResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn) } @@ -489,7 +490,7 @@ OuterLoop: engineResponses = append(engineResponses, validateResponse) } - verifyImageResponse, _ := engine.VerifyAndPatchImages(policyContext) + verifyImageResponse, _ := engine.VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) if verifyImageResponse != nil && !verifyImageResponse.IsEmpty() { engineResponses = append(engineResponses, verifyImageResponse) info = ProcessValidateEngineResponse(c.Policy, verifyImageResponse, resPath, c.Rc, c.PolicyReport, c.AuditWarn) @@ -503,7 +504,7 @@ OuterLoop: } if policyHasGenerate { - generateResponse := engine.ApplyBackgroundChecks(policyContext) + generateResponse := engine.ApplyBackgroundChecks(registryclient.NewOrDie(), policyContext) if generateResponse != nil && !generateResponse.IsEmpty() { newRuleResponse, err := handleGeneratePolicy(generateResponse, *policyContext, c.RuleToCloneSourceResource) if err != nil { diff --git a/cmd/cli/kubectl-kyverno/utils/store/store.go b/cmd/cli/kubectl-kyverno/utils/store/store.go index 93e431f8fd..aa4dc55208 100644 --- a/cmd/cli/kubectl-kyverno/utils/store/store.go +++ b/cmd/cli/kubectl-kyverno/utils/store/store.go @@ -7,7 +7,7 @@ import ( var ( Mock bool - RegistryAccess bool + registryClient registryclient.Client AllowApiCalls bool ContextVar Context ForeachElement int @@ -32,13 +32,16 @@ func GetForeachElement() int { func SetRegistryAccess(access bool) { if access { - registryclient.DefaultClient.UseLocalKeychain() + registryClient = registryclient.NewOrDie(registryclient.WithLocalKeychain()) } - RegistryAccess = access } func GetRegistryAccess() bool { - return RegistryAccess + return registryClient != nil +} + +func GetRegistryClient() registryclient.Client { + return registryClient } func SetContext(context Context) { diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index c9b59f9edd..199a1734d3 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -61,23 +61,24 @@ const ( resyncPeriod = 15 * time.Minute ) -func setupRegistryClient(logger logr.Logger, kubeClient kubernetes.Interface, imagePullSecrets string, allowInsecureRegistry bool) error { +func setupRegistryClient(logger logr.Logger, kubeClient kubernetes.Interface, imagePullSecrets string, allowInsecureRegistry bool) (registryclient.Client, error) { logger = logger.WithName("registry-client") logger.Info("setup registry client...", "secrets", imagePullSecrets, "insecure", allowInsecureRegistry) var registryOptions []registryclient.Option secrets := strings.Split(imagePullSecrets, ",") if imagePullSecrets != "" && len(secrets) > 0 { - registryOptions = append(registryOptions, registryclient.WithKeychainPullSecrets(kubeClient, config.KyvernoNamespace(), "", secrets)) + registryOptions = append(registryOptions, registryclient.WithKeychainPullSecrets( + context.TODO(), + kubeClient, + config.KyvernoNamespace(), + "", + secrets..., + )) } if allowInsecureRegistry { registryOptions = append(registryOptions, registryclient.WithAllowInsecureRegistry()) } - client, err := registryclient.InitClient(registryOptions...) - if err != nil { - return err - } - registryclient.DefaultClient = client - return nil + return registryclient.New(registryOptions...) } func setupCosign(logger logr.Logger, imageSignatureRepository string) { @@ -115,6 +116,7 @@ func createNonLeaderControllers( kubeClient kubernetes.Interface, kyvernoClient versioned.Interface, dynamicClient dclient.Interface, + rclient registryclient.Client, configuration config.Configuration, policyCache policycache.Cache, eventGenerator event.Interface, @@ -136,6 +138,7 @@ func createNonLeaderControllers( updateRequestController := background.NewController( kyvernoClient, dynamicClient, + rclient, kyvernoInformer.Kyverno().V1().ClusterPolicies(), kyvernoInformer.Kyverno().V1().Policies(), kyvernoInformer.Kyverno().V1beta1().UpdateRequests(), @@ -162,6 +165,7 @@ func createReportControllers( backgroundScanWorkers int, client dclient.Interface, kyvernoClient versioned.Interface, + rclient registryclient.Client, metadataFactory metadatainformers.SharedInformerFactory, kubeInformer kubeinformers.SharedInformerFactory, kyvernoInformer kyvernoinformer.SharedInformerFactory, @@ -212,6 +216,7 @@ func createReportControllers( backgroundscancontroller.NewController( client, kyvernoClient, + rclient, metadataFactory, kyvernoV1.Policies(), kyvernoV1.ClusterPolicies(), @@ -247,6 +252,7 @@ func createrLeaderControllers( kubeClient kubernetes.Interface, kyvernoClient versioned.Interface, dynamicClient dclient.Interface, + rclient registryclient.Client, configuration config.Configuration, metricsConfig metrics.MetricsConfigManager, eventGenerator event.Interface, @@ -256,6 +262,7 @@ func createrLeaderControllers( policyCtrl, err := policy.NewPolicyController( kyvernoClient, dynamicClient, + rclient, kyvernoInformer.Kyverno().V1().ClusterPolicies(), kyvernoInformer.Kyverno().V1().Policies(), kyvernoInformer.Kyverno().V1beta1().UpdateRequests(), @@ -300,6 +307,7 @@ func createrLeaderControllers( backgroundScanWorkers, dynamicClient, kyvernoClient, + rclient, metadataInformer, kubeInformer, kyvernoInformer, @@ -390,7 +398,8 @@ func main() { os.Exit(1) } // setup registry client - if err := setupRegistryClient(logger, kubeClient, imagePullSecrets, allowInsecureRegistry); err != nil { + rclient, err := setupRegistryClient(logger, kubeClient, imagePullSecrets, allowInsecureRegistry) + if err != nil { logger.Error(err, "failed to setup registry client") os.Exit(1) } @@ -476,6 +485,7 @@ func main() { kubeClient, kyvernoClient, dClient, + rclient, configuration, policyCache, eventGenerator, @@ -531,6 +541,7 @@ func main() { kubeClient, kyvernoClient, dClient, + rclient, configuration, metricsConfig, eventGenerator, @@ -595,6 +606,7 @@ func main() { resourceHandlers := webhooksresource.NewHandlers( dClient, kyvernoClient, + rclient, configuration, metricsConfig, policyCache, diff --git a/pkg/background/generate/generate.go b/pkg/background/generate/generate.go index 4d65581566..9d3acb353c 100644 --- a/pkg/background/generate/generate.go +++ b/pkg/background/generate/generate.go @@ -27,6 +27,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine/utils" "github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/event" + "github.com/kyverno/kyverno/pkg/registryclient" kyvernoutils "github.com/kyverno/kyverno/pkg/utils" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" "golang.org/x/exp/slices" @@ -43,6 +44,7 @@ type GenerateController struct { client dclient.Interface kyvernoClient versioned.Interface statusControl common.StatusControlInterface + rclient registryclient.Client // listers urLister kyvernov1beta1listers.UpdateRequestNamespaceLister @@ -61,6 +63,7 @@ func NewGenerateController( client dclient.Interface, kyvernoClient versioned.Interface, statusControl common.StatusControlInterface, + rclient registryclient.Client, policyLister kyvernov1listers.ClusterPolicyLister, npolicyLister kyvernov1listers.PolicyLister, urLister kyvernov1beta1listers.UpdateRequestNamespaceLister, @@ -73,6 +76,7 @@ func NewGenerateController( client: client, kyvernoClient: kyvernoClient, statusControl: statusControl, + rclient: rclient, policyLister: policyLister, npolicyLister: npolicyLister, urLister: urLister, @@ -195,7 +199,7 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u } // check if the policy still applies to the resource - engineResponse := engine.GenerateResponse(policyContext, ur) + engineResponse := engine.GenerateResponse(c.rclient, policyContext, ur) if len(engineResponse.PolicyResponse.Rules) == 0 { logger.V(4).Info(doesNotApply) return nil, false, errors.New(doesNotApply) @@ -341,7 +345,7 @@ func (c *GenerateController) ApplyGeneratePolicy(log logr.Logger, policyContext } // add configmap json data to context - if err := engine.LoadContext(log, rule.Context, policyContext, rule.Name); err != nil { + if err := engine.LoadContext(log, c.rclient, rule.Context, policyContext, rule.Name); err != nil { log.Error(err, "cannot add configmaps to context") return nil, processExisting, err } diff --git a/pkg/background/mutate/mutate.go b/pkg/background/mutate/mutate.go index b83e7daaf2..570f12f5c2 100644 --- a/pkg/background/mutate/mutate.go +++ b/pkg/background/mutate/mutate.go @@ -15,6 +15,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/event" + "github.com/kyverno/kyverno/pkg/registryclient" "github.com/kyverno/kyverno/pkg/utils" "go.uber.org/multierr" yamlv2 "gopkg.in/yaml.v2" @@ -28,7 +29,7 @@ type MutateExistingController struct { // clients client dclient.Interface statusControl common.StatusControlInterface - + rclient registryclient.Client // listers policyLister kyvernov1listers.ClusterPolicyLister npolicyLister kyvernov1listers.PolicyLister @@ -43,6 +44,7 @@ type MutateExistingController struct { func NewMutateExistingController( client dclient.Interface, statusControl common.StatusControlInterface, + rclient registryclient.Client, policyLister kyvernov1listers.ClusterPolicyLister, npolicyLister kyvernov1listers.PolicyLister, dynamicConfig config.Configuration, @@ -52,6 +54,7 @@ func NewMutateExistingController( c := MutateExistingController{ client: client, statusControl: statusControl, + rclient: rclient, policyLister: policyLister, npolicyLister: npolicyLister, configuration: dynamicConfig, @@ -90,7 +93,7 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e continue } - er := engine.Mutate(policyContext) + er := engine.Mutate(c.rclient, policyContext) for _, r := range er.PolicyResponse.Rules { patched := r.PatchedTarget switch r.Status { diff --git a/pkg/background/update_request_controller.go b/pkg/background/update_request_controller.go index 58fc342ed0..d62d016362 100644 --- a/pkg/background/update_request_controller.go +++ b/pkg/background/update_request_controller.go @@ -19,6 +19,7 @@ import ( pkgCommon "github.com/kyverno/kyverno/pkg/common" "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/event" + "github.com/kyverno/kyverno/pkg/registryclient" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -46,6 +47,7 @@ type controller struct { // clients client dclient.Interface kyvernoClient versioned.Interface + rclient registryclient.Client // listers cpolLister kyvernov1listers.ClusterPolicyLister @@ -67,6 +69,7 @@ type controller struct { func NewController( kyvernoClient versioned.Interface, client dclient.Interface, + rclient registryclient.Client, cpolInformer kyvernov1informers.ClusterPolicyInformer, polInformer kyvernov1informers.PolicyInformer, urInformer kyvernov1beta1informers.UpdateRequestInformer, @@ -79,6 +82,7 @@ func NewController( c := controller{ client: client, kyvernoClient: kyvernoClient, + rclient: rclient, cpolLister: cpolInformer.Lister(), polLister: polInformer.Lister(), urLister: urLister, @@ -405,10 +409,10 @@ func (c *controller) processUR(ur *kyvernov1beta1.UpdateRequest) error { statusControl := common.NewStatusControl(c.kyvernoClient, c.urLister) switch ur.Spec.Type { case kyvernov1beta1.Mutate: - ctrl := mutate.NewMutateExistingController(c.client, statusControl, c.cpolLister, c.polLister, c.configuration, c.eventGen, logger) + ctrl := mutate.NewMutateExistingController(c.client, statusControl, c.rclient, c.cpolLister, c.polLister, c.configuration, c.eventGen, logger) return ctrl.ProcessUR(ur) case kyvernov1beta1.Generate: - ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.eventGen, logger) + ctrl := generate.NewGenerateController(c.client, c.kyvernoClient, statusControl, c.rclient, c.cpolLister, c.polLister, c.urLister, c.nsLister, c.configuration, c.eventGen, logger) return ctrl.ProcessUR(ur) } return nil diff --git a/pkg/controllers/report/background/controller.go b/pkg/controllers/report/background/controller.go index d6c8d5393b..92893e5f7d 100644 --- a/pkg/controllers/report/background/controller.go +++ b/pkg/controllers/report/background/controller.go @@ -16,6 +16,7 @@ import ( "github.com/kyverno/kyverno/pkg/controllers/report/resource" "github.com/kyverno/kyverno/pkg/controllers/report/utils" "github.com/kyverno/kyverno/pkg/engine/response" + "github.com/kyverno/kyverno/pkg/registryclient" controllerutils "github.com/kyverno/kyverno/pkg/utils/controller" reportutils "github.com/kyverno/kyverno/pkg/utils/report" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -41,6 +42,7 @@ type controller struct { // clients client dclient.Interface kyvernoClient versioned.Interface + rclient registryclient.Client // listers polLister kyvernov1listers.PolicyLister @@ -61,6 +63,7 @@ type controller struct { func NewController( client dclient.Interface, kyvernoClient versioned.Interface, + rclient registryclient.Client, metadataFactory metadatainformers.SharedInformerFactory, polInformer kyvernov1informers.PolicyInformer, cpolInformer kyvernov1informers.ClusterPolicyInformer, @@ -73,6 +76,7 @@ func NewController( c := controller{ client: client, kyvernoClient: kyvernoClient, + rclient: rclient, polLister: polInformer.Lister(), cpolLister: cpolInformer.Lister(), bgscanrLister: bgscanr.Lister(), @@ -214,7 +218,7 @@ func (c *controller) updateReport(ctx context.Context, meta metav1.Object, gvk s } // if the resource changed, we need to rebuild the report if !reportutils.CompareHash(meta, resource.Hash) { - scanner := utils.NewScanner(logger, c.client) + scanner := utils.NewScanner(logger, c.client, c.rclient) before, err := c.getReport(ctx, meta.GetNamespace(), meta.GetName()) if err != nil { return nil @@ -300,7 +304,7 @@ func (c *controller) updateReport(ctx context.Context, meta metav1.Object, gvk s } // creations if len(toCreate) > 0 { - scanner := utils.NewScanner(logger, c.client) + scanner := utils.NewScanner(logger, c.client, c.rclient) resource, err := c.client.GetResource(ctx, gvk.GroupVersion().String(), gvk.Kind, resource.Namespace, resource.Name) if err != nil { return err diff --git a/pkg/controllers/report/utils/scanner.go b/pkg/controllers/report/utils/scanner.go index 700dccf206..368284a344 100644 --- a/pkg/controllers/report/utils/scanner.go +++ b/pkg/controllers/report/utils/scanner.go @@ -7,6 +7,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/response" + "github.com/kyverno/kyverno/pkg/registryclient" "go.uber.org/multierr" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -14,6 +15,7 @@ import ( type scanner struct { logger logr.Logger client dclient.Interface + rclient registryclient.Client excludeGroupRole []string } @@ -26,10 +28,11 @@ type Scanner interface { ScanResource(unstructured.Unstructured, map[string]string, ...kyvernov1.PolicyInterface) map[kyvernov1.PolicyInterface]ScanResult } -func NewScanner(logger logr.Logger, client dclient.Interface, excludeGroupRole ...string) Scanner { +func NewScanner(logger logr.Logger, client dclient.Interface, rclient registryclient.Client, excludeGroupRole ...string) Scanner { return &scanner{ logger: logger, client: client, + rclient: rclient, excludeGroupRole: excludeGroupRole, } } @@ -81,7 +84,7 @@ func (s *scanner) validateResource(resource unstructured.Unstructured, nsLabels WithClient(s.client). WithNamespaceLabels(nsLabels). WithExcludeGroupRole(s.excludeGroupRole...) - return engine.Validate(policyCtx), nil + return engine.Validate(s.rclient, policyCtx), nil } func (s *scanner) validateImages(resource unstructured.Unstructured, nsLabels map[string]string, policy kyvernov1.PolicyInterface) (*response.EngineResponse, error) { @@ -104,7 +107,7 @@ func (s *scanner) validateImages(resource unstructured.Unstructured, nsLabels ma WithClient(s.client). WithNamespaceLabels(nsLabels). WithExcludeGroupRole(s.excludeGroupRole...) - response, _ := engine.VerifyAndPatchImages(policyCtx) + response, _ := engine.VerifyAndPatchImages(s.rclient, policyCtx) if len(response.PolicyResponse.Rules) > 0 { s.logger.Info("validateImages", "policy", policy, "response", response) } diff --git a/pkg/cosign/cosign.go b/pkg/cosign/cosign.go index 92d959c979..540ec7f657 100644 --- a/pkg/cosign/cosign.go +++ b/pkg/cosign/cosign.go @@ -59,13 +59,13 @@ type Response struct { type CosignError struct{} // VerifySignature verifies that the image has the expected signatures -func VerifySignature(opts Options) (*Response, error) { +func VerifySignature(rclient registryclient.Client, opts Options) (*Response, error) { ref, err := name.ParseReference(opts.ImageRef) if err != nil { return nil, fmt.Errorf("failed to parse image %s", opts.ImageRef) } - cosignOpts, err := buildCosignOptions(opts) + cosignOpts, err := buildCosignOptions(rclient, opts) if err != nil { return nil, err } @@ -110,7 +110,7 @@ func VerifySignature(opts Options) (*Response, error) { return &Response{Digest: digest}, nil } -func buildCosignOptions(opts Options) (*cosign.CheckOpts, error) { +func buildCosignOptions(rclient registryclient.Client, opts Options) (*cosign.CheckOpts, error) { var remoteOpts []remote.Option var err error signatureAlgorithmMap := map[string]crypto.Hash{ @@ -123,7 +123,7 @@ func buildCosignOptions(opts Options) (*cosign.CheckOpts, error) { if err != nil { return nil, errors.Wrap(err, "constructing client options") } - remoteOpts = append(remoteOpts, registryclient.BuildRemoteOption(registryclient.DefaultClient)) + remoteOpts = append(remoteOpts, rclient.BuildRemoteOption()) cosignOpts := &cosign.CheckOpts{ Annotations: map[string]interface{}{}, RegistryClientOpts: remoteOpts, @@ -254,8 +254,8 @@ func loadCertChain(pem []byte) ([]*x509.Certificate, error) { // FetchAttestations retrieves signed attestations and decodes them into in-toto statements // https://github.com/in-toto/attestation/blob/main/spec/README.md#statement -func FetchAttestations(opts Options) (*Response, error) { - cosignOpts, err := buildCosignOptions(opts) +func FetchAttestations(rclient registryclient.Client, opts Options) (*Response, error) { + cosignOpts, err := buildCosignOptions(rclient, opts) if err != nil { return nil, err } diff --git a/pkg/cosign/cosign_test.go b/pkg/cosign/cosign_test.go index f3a2c584cd..e3967fca20 100644 --- a/pkg/cosign/cosign_test.go +++ b/pkg/cosign/cosign_test.go @@ -8,6 +8,7 @@ import ( v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/types" + "github.com/kyverno/kyverno/pkg/registryclient" "github.com/sigstore/cosign/pkg/cosign" "github.com/sigstore/cosign/pkg/cosign/bundle" "github.com/sigstore/cosign/pkg/oci" @@ -76,15 +77,18 @@ func TestCosignKeyless(t *testing.T) { Subject: "jim", } - _, err := VerifySignature(opts) + client, err := registryclient.New() + assert.NilError(t, err) + + _, err = VerifySignature(client, opts) assert.ErrorContains(t, err, "subject mismatch: expected jim, received jim@nirmata.com") opts.Subject = "jim@nirmata.com" - _, err = VerifySignature(opts) + _, err = VerifySignature(client, opts) assert.ErrorContains(t, err, "issuer mismatch: expected https://github.com/, received https://github.com/login/oauth") opts.Issuer = "https://github.com/login/oauth" - _, err = VerifySignature(opts) + _, err = VerifySignature(client, opts) assert.NilError(t, err) } diff --git a/pkg/engine/background.go b/pkg/engine/background.go index 3781961a66..74b6b95159 100644 --- a/pkg/engine/background.go +++ b/pkg/engine/background.go @@ -9,6 +9,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/logging" + "github.com/kyverno/kyverno/pkg/registryclient" ) // ApplyBackgroundChecks checks for validity of generate and mutateExisting rules on the resource @@ -16,12 +17,12 @@ import ( // - the caller has to check the ruleResponse to determine whether the path exist // // 2. returns the list of rules that are applicable on this policy and resource, if 1 succeed -func ApplyBackgroundChecks(policyContext *PolicyContext) (resp *response.EngineResponse) { +func ApplyBackgroundChecks(rclient registryclient.Client, policyContext *PolicyContext) (resp *response.EngineResponse) { policyStartTime := time.Now() - return filterRules(policyContext, policyStartTime) + return filterRules(rclient, policyContext, policyStartTime) } -func filterRules(policyContext *PolicyContext, startTime time.Time) *response.EngineResponse { +func filterRules(rclient registryclient.Client, policyContext *PolicyContext, startTime time.Time) *response.EngineResponse { kind := policyContext.newResource.GetKind() name := policyContext.newResource.GetName() namespace := policyContext.newResource.GetNamespace() @@ -51,7 +52,7 @@ func filterRules(policyContext *PolicyContext, startTime time.Time) *response.En applyRules := policyContext.policy.GetSpec().GetApplyRules() for _, rule := range autogen.ComputeRules(policyContext.policy) { - if ruleResp := filterRule(rule, policyContext); ruleResp != nil { + if ruleResp := filterRule(rclient, rule, policyContext); ruleResp != nil { resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp) if applyRules == kyvernov1.ApplyOne && ruleResp.Status != response.RuleStatusSkip { break @@ -62,7 +63,7 @@ func filterRules(policyContext *PolicyContext, startTime time.Time) *response.En return resp } -func filterRule(rule kyvernov1.Rule, policyContext *PolicyContext) *response.RuleResponse { +func filterRule(rclient registryclient.Client, rule kyvernov1.Rule, policyContext *PolicyContext) *response.RuleResponse { if !rule.HasGenerate() && !rule.IsMutateExisting() { return nil } @@ -108,7 +109,7 @@ func filterRule(rule kyvernov1.Rule, policyContext *PolicyContext) *response.Rul policyContext.jsonContext.Checkpoint() defer policyContext.jsonContext.Restore() - if err = LoadContext(logger, rule.Context, policyContext, rule.Name); err != nil { + if err = LoadContext(logger, rclient, rule.Context, policyContext, rule.Name); err != nil { logger.V(4).Info("cannot add external data to the context", "reason", err.Error()) return nil } diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 521a6e17fe..c8e313f36f 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -7,16 +7,17 @@ import ( "github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/logging" + "github.com/kyverno/kyverno/pkg/registryclient" "k8s.io/client-go/tools/cache" ) // GenerateResponse checks for validity of generate rule on the resource -func GenerateResponse(policyContext *PolicyContext, gr kyvernov1beta1.UpdateRequest) (resp *response.EngineResponse) { +func GenerateResponse(rclient registryclient.Client, policyContext *PolicyContext, gr kyvernov1beta1.UpdateRequest) (resp *response.EngineResponse) { policyStartTime := time.Now() - return filterGenerateRules(policyContext, gr.Spec.Policy, policyStartTime) + return filterGenerateRules(rclient, policyContext, gr.Spec.Policy, policyStartTime) } -func filterGenerateRules(policyContext *PolicyContext, policyNameKey string, startTime time.Time) *response.EngineResponse { +func filterGenerateRules(rclient registryclient.Client, policyContext *PolicyContext, policyNameKey string, startTime time.Time) *response.EngineResponse { kind := policyContext.newResource.GetKind() name := policyContext.newResource.GetName() namespace := policyContext.newResource.GetNamespace() @@ -50,7 +51,7 @@ func filterGenerateRules(policyContext *PolicyContext, policyNameKey string, sta } for _, rule := range autogen.ComputeRules(policyContext.policy) { - if ruleResp := filterRule(rule, policyContext); ruleResp != nil { + if ruleResp := filterRule(rclient, rule, policyContext); ruleResp != nil { resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp) } } diff --git a/pkg/engine/imageVerify.go b/pkg/engine/imageVerify.go index fd7d87c166..a02fcb15a4 100644 --- a/pkg/engine/imageVerify.go +++ b/pkg/engine/imageVerify.go @@ -1,6 +1,7 @@ package engine import ( + "context" "encoding/json" "fmt" "net" @@ -12,7 +13,7 @@ import ( kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/cosign" - "github.com/kyverno/kyverno/pkg/engine/context" + enginecontext "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/logging" @@ -62,7 +63,7 @@ func extractMatchingImages(policyContext *PolicyContext, rule *kyvernov1.Rule) ( return matchingImages, imageRefs, nil } -func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineResponse, *ImageVerificationMetadata) { +func VerifyAndPatchImages(rclient registryclient.Client, policyContext *PolicyContext) (*response.EngineResponse, *ImageVerificationMetadata) { resp := &response.EngineResponse{} policy := policyContext.policy @@ -82,7 +83,7 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons defer policyContext.jsonContext.Restore() // update image registry secrets - if err := registryclient.DefaultClient.RefreshKeychainPullSecrets(); err != nil { + if err := rclient.RefreshKeychainPullSecrets(context.TODO()); err != nil { logger.Error(err, "failed to update image pull secrets") } @@ -116,7 +117,7 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons } policyContext.jsonContext.Restore() - if err := LoadContext(logger, rule.Context, policyContext, rule.Name); err != nil { + if err := LoadContext(logger, rclient, rule.Context, policyContext, rule.Name); err != nil { appendResponse(resp, rule, fmt.Sprintf("failed to load context: %s", err.Error()), response.RuleStatusError) continue } @@ -136,7 +137,7 @@ func VerifyAndPatchImages(policyContext *PolicyContext) (*response.EngineRespons } for _, imageVerify := range ruleCopy.VerifyImages { - iv.verify(imageVerify, ruleImages) + iv.verify(rclient, imageVerify, ruleImages) } if applyRules == kyvernov1.ApplyOne && resp.PolicyResponse.RulesAppliedCount > 0 { @@ -153,7 +154,7 @@ func appendResponse(resp *response.EngineResponse, rule *kyvernov1.Rule, msg str incrementErrorCount(resp) } -func substituteVariables(rule *kyvernov1.Rule, ctx context.EvalInterface, logger logr.Logger) (*kyvernov1.Rule, error) { +func substituteVariables(rule *kyvernov1.Rule, ctx enginecontext.EvalInterface, logger logr.Logger) (*kyvernov1.Rule, error) { // remove attestations as variables are not substituted in them ruleCopy := *rule.DeepCopy() for i := range ruleCopy.VerifyImages { @@ -184,7 +185,7 @@ type imageVerifier struct { // verify applies policy rules to each matching image. The policy rule results and annotation patches are // added to tme imageVerifier `resp` and `ivm` fields. -func (iv *imageVerifier) verify(imageVerify kyvernov1.ImageVerification, matchedImageInfos []apiutils.ImageInfo) { +func (iv *imageVerifier) verify(rclient registryclient.Client, imageVerify kyvernov1.ImageVerification, matchedImageInfos []apiutils.ImageInfo) { // for backward compatibility imageVerify = *imageVerify.Convert() @@ -213,10 +214,10 @@ func (iv *imageVerifier) verify(imageVerify kyvernov1.ImageVerification, matched continue } - ruleResp, digest := iv.verifyImage(imageVerify, imageInfo) + ruleResp, digest := iv.verifyImage(rclient, imageVerify, imageInfo) if imageVerify.MutateDigest { - patch, retrievedDigest, err := iv.handleMutateDigest(digest, imageInfo) + patch, retrievedDigest, err := iv.handleMutateDigest(rclient, digest, imageInfo) if err != nil { ruleResp = ruleError(iv.rule, response.ImageVerify, "failed to update digest", err) } else if patch != nil { @@ -242,13 +243,13 @@ func (iv *imageVerifier) verify(imageVerify kyvernov1.ImageVerification, matched } } -func (iv *imageVerifier) handleMutateDigest(digest string, imageInfo apiutils.ImageInfo) ([]byte, string, error) { +func (iv *imageVerifier) handleMutateDigest(rclient registryclient.Client, digest string, imageInfo apiutils.ImageInfo) ([]byte, string, error) { if imageInfo.Digest != "" { return nil, "", nil } if digest == "" { - desc, err := registryclient.DefaultClient.FetchImageDescriptor(imageInfo.String()) + desc, err := rclient.FetchImageDescriptor(context.TODO(), imageInfo.String()) if err != nil { return nil, "", err } @@ -292,7 +293,7 @@ func imageMatches(image string, imagePatterns []string) bool { return false } -func (iv *imageVerifier) verifyImage(imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo) (*response.RuleResponse, string) { +func (iv *imageVerifier) verifyImage(rclient registryclient.Client, imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo) (*response.RuleResponse, string) { if len(imageVerify.Attestors) <= 0 && len(imageVerify.Attestations) <= 0 { return nil, "" } @@ -308,16 +309,16 @@ func (iv *imageVerifier) verifyImage(imageVerify kyvernov1.ImageVerification, im } if len(imageVerify.Attestors) > 0 { - ruleResp, _, _ := iv.verifyAttestors(imageVerify.Attestors, imageVerify, imageInfo, "") + ruleResp, _, _ := iv.verifyAttestors(rclient, imageVerify.Attestors, imageVerify, imageInfo, "") if ruleResp.Status != response.RuleStatusPass { return ruleResp, "" } } - return iv.verifyAttestations(imageVerify, imageInfo) + return iv.verifyAttestations(rclient, imageVerify, imageInfo) } -func (iv *imageVerifier) verifyAttestors(attestors []kyvernov1.AttestorSet, imageVerify kyvernov1.ImageVerification, +func (iv *imageVerifier) verifyAttestors(rclient registryclient.Client, attestors []kyvernov1.AttestorSet, imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo, predicateType string, ) (*response.RuleResponse, *cosign.Response, []kyvernov1.AttestorSet) { var cosignResponse *cosign.Response @@ -328,7 +329,7 @@ func (iv *imageVerifier) verifyAttestors(attestors []kyvernov1.AttestorSet, imag var err error path := fmt.Sprintf(".attestors[%d]", i) iv.logger.V(4).Info("verifying attestors", "path", path) - cosignResponse, err = iv.verifyAttestorSet(attestorSet, imageVerify, imageInfo, path, predicateType) + cosignResponse, err = iv.verifyAttestorSet(rclient, attestorSet, imageVerify, imageInfo, path, predicateType) if err != nil { iv.logger.Error(err, "failed to verify image") msg := fmt.Sprintf("failed to verify image %s: %s", image, err.Error()) @@ -352,7 +353,7 @@ func (iv *imageVerifier) verifyAttestors(attestors []kyvernov1.AttestorSet, imag return ruleResponse(*iv.rule, response.ImageVerify, msg, response.RuleStatusPass, nil), cosignResponse, newAttestors } -func (iv *imageVerifier) verifyAttestations(imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo) (*response.RuleResponse, string) { +func (iv *imageVerifier) verifyAttestations(rclient registryclient.Client, imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo) (*response.RuleResponse, string) { image := imageInfo.String() for i, attestation := range imageVerify.Attestations { var attestationError error @@ -377,7 +378,7 @@ func (iv *imageVerifier) verifyAttestations(imageVerify kyvernov1.ImageVerificat for _, a := range entries { entryPath := fmt.Sprintf("%s.entries[%d]", attestorPath, i) opts, subPath := iv.buildOptionsAndPath(a, imageVerify, image, attestation) - cosignResp, err := cosign.FetchAttestations(*opts) + cosignResp, err := cosign.FetchAttestations(rclient, *opts) if err != nil { iv.logger.Error(err, "failed to fetch attestations") msg := fmt.Sprintf("failed to fetch attestations %s: %s", image, err.Error()) @@ -412,7 +413,7 @@ func (iv *imageVerifier) verifyAttestations(imageVerify kyvernov1.ImageVerificat return ruleResponse(*iv.rule, response.ImageVerify, msg, response.RuleStatusPass, nil), "" } -func (iv *imageVerifier) verifyAttestorSet(attestorSet kyvernov1.AttestorSet, imageVerify kyvernov1.ImageVerification, +func (iv *imageVerifier) verifyAttestorSet(rclient registryclient.Client, attestorSet kyvernov1.AttestorSet, imageVerify kyvernov1.ImageVerification, imageInfo apiutils.ImageInfo, path, predicateType string, ) (*cosign.Response, error) { var errorList []error @@ -433,11 +434,11 @@ func (iv *imageVerifier) verifyAttestorSet(attestorSet kyvernov1.AttestorSet, im entryError = errors.Wrapf(err, "failed to unmarshal nested attestor %s", attestorPath) } else { attestorPath += ".attestor" - cosignResp, entryError = iv.verifyAttestorSet(*nestedAttestorSet, imageVerify, imageInfo, attestorPath, predicateType) + cosignResp, entryError = iv.verifyAttestorSet(rclient, *nestedAttestorSet, imageVerify, imageInfo, attestorPath, predicateType) } } else { opts, subPath := iv.buildOptionsAndPath(a, imageVerify, image, kyvernov1.Attestation{PredicateType: predicateType}) - cosignResp, entryError = cosign.VerifySignature(*opts) + cosignResp, entryError = cosign.VerifySignature(rclient, *opts) if entryError != nil { entryError = errors.Wrapf(entryError, attestorPath+subPath) } @@ -636,7 +637,7 @@ func (iv *imageVerifier) checkAttestations(a kyvernov1.Attestation, s map[string func evaluateConditions( conditions []kyvernov1.AnyAllConditions, - ctx context.Interface, + ctx enginecontext.Interface, s map[string]interface{}, log logr.Logger, ) (bool, error) { @@ -645,7 +646,7 @@ func evaluateConditions( return false, fmt.Errorf("failed to extract predicate from statement: %v", s) } - if err := context.AddJSONObject(ctx, predicate); err != nil { + if err := enginecontext.AddJSONObject(ctx, predicate); err != nil { return false, errors.Wrapf(err, fmt.Sprintf("failed to add Statement to the context %v", s)) } diff --git a/pkg/engine/imageVerifyValidate.go b/pkg/engine/imageVerifyValidate.go index ca1d7fd142..940d48115f 100644 --- a/pkg/engine/imageVerifyValidate.go +++ b/pkg/engine/imageVerifyValidate.go @@ -8,12 +8,13 @@ import ( gojmespath "github.com/jmespath/go-jmespath" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/pkg/engine/response" + "github.com/kyverno/kyverno/pkg/registryclient" apiutils "github.com/kyverno/kyverno/pkg/utils/api" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func processImageValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse { +func processImageValidationRule(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse { if isDeleteRequest(ctx) { return nil } @@ -26,7 +27,7 @@ func processImageValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyver if len(matchingImages) == 0 { return ruleResponse(*rule, response.Validation, "image verified", response.RuleStatusSkip, nil) } - if err := LoadContext(log, rule.Context, ctx, rule.Name); err != nil { + if err := LoadContext(log, rclient, rule.Context, ctx, rule.Name); err != nil { if _, ok := err.(gojmespath.NotFoundError); ok { log.V(3).Info("failed to load context", "reason", err.Error()) } else { diff --git a/pkg/engine/imageVerify_test.go b/pkg/engine/imageVerify_test.go index e4ead76a3f..887b587a2d 100644 --- a/pkg/engine/imageVerify_test.go +++ b/pkg/engine/imageVerify_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/kyverno/kyverno/pkg/logging" + "github.com/kyverno/kyverno/pkg/registryclient" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" kubefake "k8s.io/client-go/kubernetes/fake" @@ -149,7 +150,7 @@ func Test_CosignMockAttest(t *testing.T) { err := cosign.SetMock("ghcr.io/jimbugwadia/pause2:latest", attestationPayloads) assert.NilError(t, err) - er, ivm := VerifyAndPatchImages(policyContext) + er, ivm := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass, fmt.Sprintf("expected: %v, got: %v, failure: %v", @@ -163,7 +164,7 @@ func Test_CosignMockAttest_fail(t *testing.T) { err := cosign.SetMock("ghcr.io/jimbugwadia/pause2:latest", attestationPayloads) assert.NilError(t, err) - er, _ := VerifyAndPatchImages(policyContext) + er, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail) } @@ -411,7 +412,7 @@ var testOtherKey = `-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD func Test_ConfigMapMissingSuccess(t *testing.T) { policyContext := buildContext(t, testConfigMapMissing, testConfigMapMissingResource, "") cosign.ClearMock() - err, _ := VerifyAndPatchImages(policyContext) + err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusSkip, err.PolicyResponse.Rules[0].Message) } @@ -423,7 +424,7 @@ func Test_ConfigMapMissingFailure(t *testing.T) { assert.NilError(t, err) policyContext.informerCacheResolvers = resolver cosign.ClearMock() - resp, _ := VerifyAndPatchImages(policyContext) + resp, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(resp.PolicyResponse.Rules), 1) assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusError, resp.PolicyResponse.Rules[0].Message) } @@ -431,7 +432,7 @@ func Test_ConfigMapMissingFailure(t *testing.T) { func Test_SignatureGoodSigned(t *testing.T) { policyContext := buildContext(t, testSampleSingleKeyPolicy, testSampleResource, "") cosign.ClearMock() - err, _ := VerifyAndPatchImages(policyContext) + err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass, err.PolicyResponse.Rules[0].Message) } @@ -440,7 +441,7 @@ func Test_SignatureUnsigned(t *testing.T) { cosign.ClearMock() unsigned := strings.Replace(testSampleResource, ":signed", ":unsigned", -1) policyContext := buildContext(t, testSampleSingleKeyPolicy, unsigned, "") - err, _ := VerifyAndPatchImages(policyContext) + err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail, err.PolicyResponse.Rules[0].Message) } @@ -449,7 +450,7 @@ func Test_SignatureWrongKey(t *testing.T) { cosign.ClearMock() otherKey := strings.Replace(testSampleResource, ":signed", ":signed-by-someone-else", -1) policyContext := buildContext(t, testSampleSingleKeyPolicy, otherKey, "") - err, _ := VerifyAndPatchImages(policyContext) + err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail, err.PolicyResponse.Rules[0].Message) } @@ -460,7 +461,7 @@ func Test_SignaturesMultiKey(t *testing.T) { policy = strings.Replace(policy, "KEY2", testVerifyImageKey, -1) policy = strings.Replace(policy, "COUNT", "0", -1) policyContext := buildContext(t, policy, testSampleResource, "") - err, _ := VerifyAndPatchImages(policyContext) + err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass, err.PolicyResponse.Rules[0].Message) } @@ -470,7 +471,7 @@ func Test_SignaturesMultiKeyFail(t *testing.T) { policy := strings.Replace(testSampleMultipleKeyPolicy, "KEY1", testVerifyImageKey, -1) policy = strings.Replace(policy, "COUNT", "0", -1) policyContext := buildContext(t, policy, testSampleResource, "") - err, _ := VerifyAndPatchImages(policyContext) + err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail, err.PolicyResponse.Rules[0].Message) } @@ -481,7 +482,7 @@ func Test_SignaturesMultiKeyOneGoodKey(t *testing.T) { policy = strings.Replace(policy, "KEY2", testOtherKey, -1) policy = strings.Replace(policy, "COUNT", "1", -1) policyContext := buildContext(t, policy, testSampleResource, "") - err, _ := VerifyAndPatchImages(policyContext) + err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass, err.PolicyResponse.Rules[0].Message) } @@ -492,7 +493,7 @@ func Test_SignaturesMultiKeyZeroGoodKey(t *testing.T) { policy = strings.Replace(policy, "KEY2", testOtherKey, -1) policy = strings.Replace(policy, "COUNT", "1", -1) policyContext := buildContext(t, policy, testSampleResource, "") - resp, _ := VerifyAndPatchImages(policyContext) + resp, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(resp.PolicyResponse.Rules), 1) assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusFail, resp.PolicyResponse.Rules[0].Message) } @@ -508,14 +509,14 @@ func Test_RuleSelectorImageVerify(t *testing.T) { applyAll := kyverno.ApplyAll spec.ApplyRules = &applyAll - resp, _ := VerifyAndPatchImages(policyContext) + resp, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(resp.PolicyResponse.Rules), 2) assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusPass, resp.PolicyResponse.Rules[0].Message) assert.Equal(t, resp.PolicyResponse.Rules[1].Status, response.RuleStatusFail, resp.PolicyResponse.Rules[1].Message) applyOne := kyverno.ApplyOne spec.ApplyRules = &applyOne - resp, _ = VerifyAndPatchImages(policyContext) + resp, _ = VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(resp.PolicyResponse.Rules), 1) assert.Equal(t, resp.PolicyResponse.Rules[0].Status, response.RuleStatusPass, resp.PolicyResponse.Rules[0].Message) } @@ -619,7 +620,7 @@ func Test_NestedAttestors(t *testing.T) { policy = strings.Replace(policy, "KEY2", testVerifyImageKey, -1) policy = strings.Replace(policy, "COUNT", "0", -1) policyContext := buildContext(t, policy, testSampleResource, "") - err, _ := VerifyAndPatchImages(policyContext) + err, _ := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass) @@ -627,7 +628,7 @@ func Test_NestedAttestors(t *testing.T) { policy = strings.Replace(policy, "KEY2", testOtherKey, -1) policy = strings.Replace(policy, "COUNT", "0", -1) policyContext = buildContext(t, policy, testSampleResource, "") - err, _ = VerifyAndPatchImages(policyContext) + err, _ = VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusFail) @@ -635,7 +636,7 @@ func Test_NestedAttestors(t *testing.T) { policy = strings.Replace(policy, "KEY2", testOtherKey, -1) policy = strings.Replace(policy, "COUNT", "1", -1) policyContext = buildContext(t, policy, testSampleResource, "") - err, _ = VerifyAndPatchImages(policyContext) + err, _ = VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(err.PolicyResponse.Rules), 1) assert.Equal(t, err.PolicyResponse.Rules[0].Status, response.RuleStatusPass) } @@ -728,7 +729,7 @@ func Test_MarkImageVerified(t *testing.T) { err := cosign.SetMock(image, attestationPayloads) assert.NilError(t, err) - engineResponse, verifiedImages := VerifyAndPatchImages(policyContext) + engineResponse, verifiedImages := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Assert(t, engineResponse != nil) assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1) assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, response.RuleStatusPass) @@ -821,7 +822,7 @@ func Test_ParsePEMDelimited(t *testing.T) { err := cosign.SetMock(image, signaturePayloads) assert.NilError(t, err) - engineResponse, verifiedImages := VerifyAndPatchImages(policyContext) + engineResponse, verifiedImages := VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) assert.Assert(t, engineResponse != nil) assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1) assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, response.RuleStatusPass) diff --git a/pkg/engine/jsonContext.go b/pkg/engine/jsonContext.go index 33bec04852..361c5c9c1a 100644 --- a/pkg/engine/jsonContext.go +++ b/pkg/engine/jsonContext.go @@ -14,7 +14,7 @@ import ( ) // LoadContext - Fetches and adds external data to the Context. -func LoadContext(logger logr.Logger, contextEntries []kyvernov1.ContextEntry, ctx *PolicyContext, ruleName string) error { +func LoadContext(logger logr.Logger, rclient registryclient.Client, contextEntries []kyvernov1.ContextEntry, ctx *PolicyContext, ruleName string) error { if len(contextEntries) == 0 { return nil } @@ -36,7 +36,8 @@ func LoadContext(logger logr.Logger, contextEntries []kyvernov1.ContextEntry, ct // Context Variable should be loaded after the values loaded from values file for _, entry := range contextEntries { if entry.ImageRegistry != nil && hasRegistryAccess { - if err := loadImageData(logger, entry, ctx); err != nil { + // rclient := store.GetRegistryClient() + if err := loadImageData(rclient, logger, entry, ctx); err != nil { return err } } else if entry.Variable != nil { @@ -68,7 +69,7 @@ func LoadContext(logger logr.Logger, contextEntries []kyvernov1.ContextEntry, ct return err } } else if entry.ImageRegistry != nil { - if err := loadImageData(logger, entry, ctx); err != nil { + if err := loadImageData(rclient, logger, entry, ctx); err != nil { return err } } else if entry.Variable != nil { @@ -140,11 +141,11 @@ func loadVariable(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyC } } -func loadImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) error { - if err := registryclient.DefaultClient.RefreshKeychainPullSecrets(); err != nil { +func loadImageData(rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) error { + if err := rclient.RefreshKeychainPullSecrets(context.TODO()); err != nil { return fmt.Errorf("unable to load image registry credentials, %w", err) } - imageData, err := fetchImageData(logger, entry, ctx) + imageData, err := fetchImageData(rclient, logger, entry, ctx) if err != nil { return err } @@ -158,7 +159,7 @@ func loadImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Policy return nil } -func fetchImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) (interface{}, error) { +func fetchImageData(rclient registryclient.Client, logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) (interface{}, error) { ref, err := variables.SubstituteAll(logger, ctx.jsonContext, entry.ImageRegistry.Reference) if err != nil { return nil, fmt.Errorf("ailed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.Reference, err) @@ -171,7 +172,7 @@ func fetchImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Polic if err != nil { return nil, fmt.Errorf("failed to substitute variables in context entry %s %s: %v", entry.Name, entry.ImageRegistry.JMESPath, err) } - imageData, err := fetchImageDataMap(refString) + imageData, err := fetchImageDataMap(rclient, refString) if err != nil { return nil, err } @@ -185,8 +186,8 @@ func fetchImageData(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *Polic } // FetchImageDataMap fetches image information from the remote registry. -func fetchImageDataMap(ref string) (interface{}, error) { - desc, err := registryclient.DefaultClient.FetchImageDescriptor(ref) +func fetchImageDataMap(rclient registryclient.Client, ref string) (interface{}, error) { + desc, err := rclient.FetchImageDescriptor(context.TODO(), ref) if err != nil { return nil, err } diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 8f53219abd..6f94dbbfc0 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -13,11 +13,12 @@ import ( "github.com/kyverno/kyverno/pkg/engine/mutate" "github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/logging" + "github.com/kyverno/kyverno/pkg/registryclient" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // Mutate performs mutation. Overlay first and then mutation patches -func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) { +func Mutate(rclient registryclient.Client, policyContext *PolicyContext) (resp *response.EngineResponse) { startTime := time.Now() policy := policyContext.policy resp = &response.EngineResponse{ @@ -69,7 +70,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) { logger.Error(err, "failed to query resource object") } - if err := LoadContext(logger, rule.Context, policyContext, rule.Name); err != nil { + if err := LoadContext(logger, rclient, rule.Context, policyContext, rule.Name); err != nil { if _, ok := err.(gojmespath.NotFoundError); ok { logger.V(3).Info("failed to load context", "reason", err.Error()) } else { @@ -108,7 +109,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) { logger.V(4).Info("apply rule to resource", "rule", rule.Name, "resource namespace", patchedResource.GetNamespace(), "resource name", patchedResource.GetName()) var ruleResp *response.RuleResponse if rule.Mutation.ForEachMutation != nil { - ruleResp, patchedResource = mutateForEach(ruleCopy, policyContext, patchedResource, logger) + ruleResp, patchedResource = mutateForEach(rclient, ruleCopy, policyContext, patchedResource, logger) } else { ruleResp, patchedResource = mutateResource(ruleCopy, policyContext, patchedResource, logger) } @@ -158,7 +159,7 @@ func mutateResource(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructu return ruleResp, mutateResp.PatchedResource } -func mutateForEach(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructured.Unstructured, logger logr.Logger) (*response.RuleResponse, unstructured.Unstructured) { +func mutateForEach(rclient registryclient.Client, rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructured.Unstructured, logger logr.Logger) (*response.RuleResponse, unstructured.Unstructured) { foreachList := rule.Mutation.ForEachMutation if foreachList == nil { return nil, resource @@ -169,7 +170,7 @@ func mutateForEach(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructur allPatches := make([][]byte, 0) for _, foreach := range foreachList { - if err := LoadContext(logger, rule.Context, ctx, rule.Name); err != nil { + if err := LoadContext(logger, rclient, rule.Context, ctx, rule.Name); err != nil { logger.Error(err, "failed to load context") return ruleError(rule, response.Mutation, "failed to load context", err), resource } @@ -189,7 +190,7 @@ func mutateForEach(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructur return ruleError(rule, response.Mutation, msg, err), resource } - mutateResp := mutateElements(rule.Name, foreach, ctx, elements, patchedResource, logger) + mutateResp := mutateElements(rclient, rule.Name, foreach, ctx, elements, patchedResource, logger) if mutateResp.Status == response.RuleStatusError { logger.Error(err, "failed to mutate elements") return buildRuleResponse(rule, mutateResp, nil), resource @@ -213,7 +214,7 @@ func mutateForEach(rule *kyvernov1.Rule, ctx *PolicyContext, resource unstructur return r, patchedResource } -func mutateElements(name string, foreach kyvernov1.ForEachMutation, ctx *PolicyContext, elements []interface{}, resource unstructured.Unstructured, logger logr.Logger) *mutate.Response { +func mutateElements(rclient registryclient.Client, name string, foreach kyvernov1.ForEachMutation, ctx *PolicyContext, elements []interface{}, resource unstructured.Unstructured, logger logr.Logger) *mutate.Response { ctx.jsonContext.Checkpoint() defer ctx.jsonContext.Restore() @@ -235,7 +236,7 @@ func mutateElements(name string, foreach kyvernov1.ForEachMutation, ctx *PolicyC return mutateError(err, fmt.Sprintf("failed to add element to mutate.foreach[%d].context", i)) } - if err := LoadContext(logger, foreach.Context, ctx, name); err != nil { + if err := LoadContext(logger, rclient, foreach.Context, ctx, name); err != nil { return mutateError(err, fmt.Sprintf("failed to load to mutate.foreach[%d].context", i)) } diff --git a/pkg/engine/mutation_test.go b/pkg/engine/mutation_test.go index 2f18417bfc..ec39af0517 100644 --- a/pkg/engine/mutation_test.go +++ b/pkg/engine/mutation_test.go @@ -13,6 +13,7 @@ import ( enginecontext "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/engine/utils" + "github.com/kyverno/kyverno/pkg/registryclient" "gotest.tools/assert" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -93,7 +94,7 @@ func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) { policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) t.Log(string(expectedPatch)) assert.Equal(t, len(er.PolicyResponse.Rules), 1) @@ -166,7 +167,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) { policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path")) } @@ -263,7 +264,7 @@ func Test_variableSubstitutionCLI(t *testing.T) { newResource: *resourceUnstructured, } - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) t.Log(string(expectedPatch)) @@ -372,7 +373,7 @@ func Test_chained_rules(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) containers, _, err := unstructured.NestedSlice(er.PatchedResource.Object, "spec", "containers") assert.NilError(t, err) assert.Equal(t, containers[0].(map[string]interface{})["image"], "otherregistry.corp.com/foo/bash:5.0") @@ -460,7 +461,7 @@ func Test_precondition(t *testing.T) { newResource: *resourceUnstructured, } - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) t.Log(string(expectedPatch)) t.Log(string(er.PolicyResponse.Rules[0].Patches[0])) if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) { @@ -557,7 +558,7 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) { newResource: *resourceUnstructured, } - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) t.Log(string(expectedPatch)) t.Log(string(er.PolicyResponse.Rules[0].Patches[0])) if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) { @@ -651,7 +652,7 @@ func Test_foreach(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass) @@ -758,7 +759,7 @@ func Test_foreach_element_mutation(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass) @@ -884,7 +885,7 @@ func Test_Container_InitContainer_foreach(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass) @@ -1011,7 +1012,7 @@ func Test_foreach_order_mutation_(t *testing.T) { err = enginecontext.MutateResourceWithImageInfo(resourceRaw, ctx) assert.NilError(t, err) - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusPass) @@ -1457,7 +1458,7 @@ func Test_mutate_existing_resources(t *testing.T) { newResource: *trigger, } } - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) for _, rr := range er.PolicyResponse.Rules { for i, p := range rr.Patches { @@ -1565,7 +1566,7 @@ func Test_RuleSelectorMutate(t *testing.T) { newResource: *resourceUnstructured, } - er := Mutate(policyContext) + er := Mutate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 2) assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) assert.Equal(t, len(er.PolicyResponse.Rules[1].Patches), 1) @@ -1580,7 +1581,7 @@ func Test_RuleSelectorMutate(t *testing.T) { applyOne := kyverno.ApplyOne policyContext.policy.GetSpec().ApplyRules = &applyOne - er = Mutate(policyContext) + er = Mutate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) @@ -1947,7 +1948,7 @@ func Test_SpecialCharacters(t *testing.T) { } // Mutate and make sure that we got the expected amount of rules. - patches := Mutate(policyContext).GetPatches() + patches := Mutate(registryclient.NewOrDie(), policyContext).GetPatches() if !reflect.DeepEqual(patches, tt.want) { t.Errorf("Mutate() got patches %s, expected %s", patches, tt.want) } diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index ae03700af8..c2902c2ec8 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -19,6 +19,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine/variables" "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/pss" + "github.com/kyverno/kyverno/pkg/registryclient" "github.com/kyverno/kyverno/pkg/utils" "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" @@ -30,7 +31,7 @@ import ( ) // Validate applies validation rules from policy on the resource -func Validate(policyContext *PolicyContext) (resp *response.EngineResponse) { +func Validate(rclient registryclient.Client, policyContext *PolicyContext) (resp *response.EngineResponse) { resp = &response.EngineResponse{} startTime := time.Now() @@ -41,7 +42,7 @@ func Validate(policyContext *PolicyContext) (resp *response.EngineResponse) { logger.V(4).Info("finished policy processing", "processingTime", resp.PolicyResponse.ProcessingTime.String(), "validationRulesApplied", resp.PolicyResponse.RulesAppliedCount) }() - resp = validateResource(logger, policyContext) + resp = validateResource(logger, rclient, policyContext) return } @@ -88,7 +89,7 @@ func buildResponse(ctx *PolicyContext, resp *response.EngineResponse, startTime resp.PolicyResponse.PolicyExecutionTimestamp = startTime.Unix() } -func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineResponse { +func validateResource(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext) *response.EngineResponse { resp := &response.EngineResponse{} ctx.jsonContext.Checkpoint() @@ -128,9 +129,9 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo var ruleResp *response.RuleResponse if hasValidate && !hasYAMLSignatureVerify { - ruleResp = processValidationRule(log, ctx, rule) + ruleResp = processValidationRule(log, rclient, ctx, rule) } else if hasValidateImage { - ruleResp = processImageValidationRule(log, ctx, rule) + ruleResp = processImageValidationRule(log, rclient, ctx, rule) } else if hasYAMLSignatureVerify { ruleResp = processYAMLValidationRule(log, ctx, rule) } @@ -146,7 +147,7 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo return resp } -func validateOldObject(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) (*response.RuleResponse, error) { +func validateOldObject(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) (*response.RuleResponse, error) { ctxCopy := ctx.Copy() ctxCopy.newResource = *ctxCopy.oldResource.DeepCopy() ctxCopy.oldResource = unstructured.Unstructured{} @@ -159,11 +160,11 @@ func validateOldObject(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule return nil, errors.Wrapf(err, "failed to replace old object in the JSON context") } - return processValidationRule(log, ctxCopy, rule), nil + return processValidationRule(log, rclient, ctxCopy, rule), nil } -func processValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse { - v := newValidator(log, ctx, rule) +func processValidationRule(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) *response.RuleResponse { + v := newValidator(log, rclient, ctx, rule) if rule.Validation.ForEachValidation != nil { return v.validateForEach() } @@ -195,9 +196,10 @@ type validator struct { anyPattern apiextensions.JSON deny *kyvernov1.Deny podSecurity *kyvernov1.PodSecurity + rclient registryclient.Client } -func newValidator(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) *validator { +func newValidator(log logr.Logger, rclient registryclient.Client, ctx *PolicyContext, rule *kyvernov1.Rule) *validator { ruleCopy := rule.DeepCopy() return &validator{ log: log, @@ -209,10 +211,11 @@ func newValidator(log logr.Logger, ctx *PolicyContext, rule *kyvernov1.Rule) *va anyPattern: ruleCopy.Validation.GetAnyPattern(), deny: ruleCopy.Validation.Deny, podSecurity: ruleCopy.Validation.PodSecurity, + rclient: rclient, } } -func newForeachValidator(foreach kyvernov1.ForEachValidation, rule *kyvernov1.Rule, ctx *PolicyContext, log logr.Logger) *validator { +func newForeachValidator(log logr.Logger, rclient registryclient.Client, foreach kyvernov1.ForEachValidation, rule *kyvernov1.Rule, ctx *PolicyContext) *validator { ruleCopy := rule.DeepCopy() anyAllConditions, err := utils.ToMap(foreach.AnyAllConditions) if err != nil { @@ -228,6 +231,7 @@ func newForeachValidator(foreach kyvernov1.ForEachValidation, rule *kyvernov1.Ru pattern: foreach.GetPattern(), anyPattern: foreach.GetAnyPattern(), deny: foreach.Deny, + rclient: rclient, } } @@ -256,7 +260,7 @@ func (v *validator) validate() *response.RuleResponse { ruleResponse := v.validateResourceWithRule() if isUpdateRequest(v.ctx) { - priorResp, err := validateOldObject(v.log, v.ctx, v.rule) + priorResp, err := validateOldObject(v.log, v.rclient, v.ctx, v.rule) if err != nil { return ruleError(v.rule, response.Validation, "failed to validate old object", err) } @@ -338,7 +342,7 @@ func (v *validator) validateElements(foreach kyvernov1.ForEachValidation, elemen return ruleError(v.rule, response.Validation, "failed to process foreach", err), applyCount } - foreachValidator := newForeachValidator(foreach, v.rule, ctx, v.log) + foreachValidator := newForeachValidator(v.log, v.rclient, foreach, v.rule, ctx) r := foreachValidator.validate() if r == nil { v.log.V(2).Info("skip rule due to empty result") @@ -398,7 +402,7 @@ func addElementToContext(ctx *PolicyContext, e interface{}, elementIndex int, el } func (v *validator) loadContext() error { - if err := LoadContext(v.log, v.contextEntries, v.ctx, v.rule.Name); err != nil { + if err := LoadContext(v.log, v.rclient, v.contextEntries, v.ctx, v.rule.Name); err != nil { if _, ok := err.(gojmespath.NotFoundError); ok { v.log.V(3).Info("failed to load context", "reason", err.Error()) } else { diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go index 0437e2576c..ea46946b11 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -11,6 +11,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/engine/utils" + "github.com/kyverno/kyverno/pkg/registryclient" utils2 "github.com/kyverno/kyverno/pkg/utils" "gotest.tools/assert" admissionv1 "k8s.io/api/admission/v1" @@ -131,7 +132,7 @@ func TestValidate_image_tag_fail(t *testing.T) { "validation error: imagePullPolicy 'Always' required with tag 'latest'. rule validate-latest failed at path /spec/containers/0/imagePullPolicy/", } - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message, msgs[index]) } @@ -231,7 +232,7 @@ func TestValidate_image_tag_pass(t *testing.T) { "validation rule 'validate-tag' passed.", "validation rule 'validate-latest' passed.", } - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message, msgs[index]) } @@ -305,7 +306,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) assert.Assert(t, !er.IsSuccessful()) msgs := []string{"validation error: A namespace is required. rule check-default-namespace[0] failed at path /metadata/namespace/ rule check-default-namespace[1] failed at path /metadata/namespace/"} @@ -388,7 +389,7 @@ func TestValidate_host_network_port(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation error: Host network and port are not allowed. rule validate-host-network-port failed at path /spec/containers/0/ports/0/hostPort/"} for index, r := range er.PolicyResponse.Rules { @@ -478,7 +479,7 @@ func TestValidate_anchor_arraymap_pass(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation rule 'validate-host-path' passed."} for index, r := range er.PolicyResponse.Rules { @@ -566,7 +567,7 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) { assert.NilError(t, err) resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation error: Host path '/var/lib/' is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/path/"} for index, r := range er.PolicyResponse.Rules { @@ -636,7 +637,7 @@ func TestValidate_anchor_map_notfound(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -709,7 +710,7 @@ func TestValidate_anchor_map_found_valid(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -783,7 +784,7 @@ func TestValidate_inequality_List_Processing(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -863,7 +864,7 @@ func TestValidate_inequality_List_ProcessingBrackets(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { @@ -937,7 +938,7 @@ func TestValidate_anchor_map_found_invalid(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation error: pod: validate run as non root user. rule pod rule 2 failed at path /spec/securityContext/runAsNonRoot/"} for index, r := range er.PolicyResponse.Rules { @@ -1012,7 +1013,7 @@ func TestValidate_AnchorList_pass(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation rule 'pod image rule' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1087,7 +1088,7 @@ func TestValidate_AnchorList_fail(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) assert.Assert(t, !er.IsSuccessful()) } @@ -1157,7 +1158,7 @@ func TestValidate_existenceAnchor_fail(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) assert.Assert(t, !er.IsSuccessful()) } @@ -1227,7 +1228,7 @@ func TestValidate_existenceAnchor_pass(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation rule 'pod image rule' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1315,7 +1316,7 @@ func TestValidate_negationAnchor_deny(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation error: Host path is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/"} for index, r := range er.PolicyResponse.Rules { @@ -1402,7 +1403,7 @@ func TestValidate_negationAnchor_pass(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) msgs := []string{"validation rule 'validate-host-path' passed."} for index, r := range er.PolicyResponse.Rules { @@ -1478,7 +1479,7 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) { policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - er := Validate(policyContext) + er := Validate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusError) @@ -1571,7 +1572,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - er := Validate(policyContext) + er := Validate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusError) @@ -1632,7 +1633,7 @@ func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) { policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - er := Validate(policyContext) + er := Validate(registryclient.NewOrDie(), policyContext) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail) assert.Equal(t, er.PolicyResponse.Rules[0].Message, "validation error: rule not-operator-with-variable-should-alway-fail-validation failed at path /spec/content/") } @@ -1723,7 +1724,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - er := Validate(policyContext) + er := Validate(registryclient.NewOrDie(), policyContext) assert.Equal(t, len(er.PolicyResponse.Rules), 1) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusError) @@ -1816,7 +1817,7 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - er := Validate(policyContext) + er := Validate(registryclient.NewOrDie(), policyContext) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail) assert.Equal(t, er.PolicyResponse.Rules[0].Message, @@ -1921,7 +1922,7 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing. policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - er := Validate(policyContext) + er := Validate(registryclient.NewOrDie(), policyContext) assert.Equal(t, er.PolicyResponse.Rules[0].Status, response.RuleStatusFail) assert.Equal(t, er.PolicyResponse.Rules[0].Message, "The animal cow is not in the allowed list of animals.") } @@ -1974,7 +1975,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) { policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - er := Validate(policyContext) + er := Validate(registryclient.NewOrDie(), policyContext) for i, rule := range er.PolicyResponse.Rules { assert.Equal(t, er.PolicyResponse.Rules[i].Status, test.expectedResults[i], "\ntest %s failed\nexpected: %s\nactual: %s", test.name, test.expectedResults[i].String(), er.PolicyResponse.Rules[i].Status.String()) @@ -2140,7 +2141,7 @@ func executeTest(t *testing.T, test testCase) { jsonContext: ctx, } - resp := Validate(pc) + resp := Validate(registryclient.NewOrDie(), pc) if resp.IsSuccessful() && test.requestDenied { t.Errorf("Testcase has failed, policy: %v", policy.Name) } @@ -2239,7 +2240,7 @@ func TestValidate_context_variable_substitution_CLI(t *testing.T) { msgs := []string{ "restrict pod counts to be no more than 10 on node minikube", } - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) for index, r := range er.PolicyResponse.Rules { assert.Equal(t, r.Message, msgs[index]) } @@ -2328,7 +2329,7 @@ func Test_EmptyStringInDenyCondition(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}) assert.Assert(t, !er.IsSuccessful()) } @@ -2417,7 +2418,7 @@ func Test_StringInDenyCondition(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: ctx}) assert.Assert(t, er.IsSuccessful()) } @@ -3004,7 +3005,7 @@ func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string, policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - er := Validate(policyContext) + er := Validate(registryclient.NewOrDie(), policyContext) assert.Equal(t, er.PolicyResponse.Rules[0].Status, status) if msg != "" { @@ -3068,7 +3069,7 @@ func Test_delete_ignore_pattern(t *testing.T) { policy: &policy, jsonContext: ctx, newResource: *resourceUnstructured} - engineResponseCreate := Validate(policyContextCreate) + engineResponseCreate := Validate(registryclient.NewOrDie(), policyContextCreate) assert.Equal(t, len(engineResponseCreate.PolicyResponse.Rules), 1) assert.Equal(t, engineResponseCreate.PolicyResponse.Rules[0].Status, response.RuleStatusFail) @@ -3076,7 +3077,7 @@ func Test_delete_ignore_pattern(t *testing.T) { policy: &policy, jsonContext: ctx, oldResource: *resourceUnstructured} - engineResponseDelete := Validate(policyContextDelete) + engineResponseDelete := Validate(registryclient.NewOrDie(), policyContextDelete) assert.Equal(t, len(engineResponseDelete.PolicyResponse.Rules), 0) } @@ -3135,7 +3136,7 @@ func Test_ValidatePattern_anyPattern(t *testing.T) { resourceUnstructured, err := utils.ConvertToUnstructured(tc.rawResource) assert.NilError(t, err) - er := Validate(&PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) + er := Validate(registryclient.NewOrDie(), &PolicyContext{policy: &policy, newResource: *resourceUnstructured, jsonContext: context.NewContext()}) if tc.expectedFailed { assert.Assert(t, er.IsFailed()) } else if tc.expectedSkipped { diff --git a/pkg/policy/apply.go b/pkg/policy/apply.go index 2e1e5db47a..346f173a9d 100644 --- a/pkg/policy/apply.go +++ b/pkg/policy/apply.go @@ -13,14 +13,20 @@ import ( "github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/response" + "github.com/kyverno/kyverno/pkg/registryclient" jsonutils "github.com/kyverno/kyverno/pkg/utils/json" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // applyPolicy applies policy on a resource -func applyPolicy(policy kyvernov1.PolicyInterface, resource unstructured.Unstructured, - logger logr.Logger, excludeGroupRole []string, - client dclient.Interface, namespaceLabels map[string]string, +func applyPolicy( + policy kyvernov1.PolicyInterface, + resource unstructured.Unstructured, + logger logr.Logger, + excludeGroupRole []string, + client dclient.Interface, + rclient registryclient.Client, + namespaceLabels map[string]string, ) (responses []*response.EngineResponse) { startTime := time.Now() defer func() { @@ -56,7 +62,7 @@ func applyPolicy(policy kyvernov1.PolicyInterface, resource unstructured.Unstruc logger.Error(err, "unable to set operation in context") } - engineResponseMutation, err = mutation(policy, resource, logger, ctx, namespaceLabels) + engineResponseMutation, err = mutation(policy, resource, logger, ctx, rclient, namespaceLabels) if err != nil { logger.Error(err, "failed to process mutation rule") } @@ -68,19 +74,26 @@ func applyPolicy(policy kyvernov1.PolicyInterface, resource unstructured.Unstruc WithClient(client). WithExcludeGroupRole(excludeGroupRole...) - engineResponseValidation = engine.Validate(policyCtx) + engineResponseValidation = engine.Validate(rclient, policyCtx) engineResponses = append(engineResponses, mergeRuleRespose(engineResponseMutation, engineResponseValidation)) return engineResponses } -func mutation(policy kyvernov1.PolicyInterface, resource unstructured.Unstructured, log logr.Logger, jsonContext context.Interface, namespaceLabels map[string]string) (*response.EngineResponse, error) { +func mutation( + policy kyvernov1.PolicyInterface, + resource unstructured.Unstructured, + log logr.Logger, + jsonContext context.Interface, + rclient registryclient.Client, + namespaceLabels map[string]string, +) (*response.EngineResponse, error) { policyContext := engine.NewPolicyContextWithJsonContext(jsonContext). WithPolicy(policy). WithNamespaceLabels(namespaceLabels). WithNewResource(resource) - engineResponse := engine.Mutate(policyContext) + engineResponse := engine.Mutate(rclient, policyContext) if !engineResponse.IsSuccessful() { log.V(4).Info("failed to apply mutation rules; reporting them") return engineResponse, nil diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index 4b67e55e3c..e59c2cc2f3 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -81,7 +81,7 @@ func (pc *PolicyController) applyPolicy(policy kyvernov1.PolicyInterface, resour } namespaceLabels := common.GetNamespaceSelectorsFromNamespaceLister(resource.GetKind(), resource.GetNamespace(), pc.nsLister, logger) - engineResponse := applyPolicy(policy, resource, logger, pc.configHandler.GetExcludeGroupRole(), pc.client, namespaceLabels) + engineResponse := applyPolicy(policy, resource, logger, pc.configHandler.GetExcludeGroupRole(), pc.client, pc.rclient, namespaceLabels) engineResponses = append(engineResponses, engineResponse...) // post-processing, register the resource as processed diff --git a/pkg/policy/policy_controller.go b/pkg/policy/policy_controller.go index 9aa37f8cb0..ac8c886102 100644 --- a/pkg/policy/policy_controller.go +++ b/pkg/policy/policy_controller.go @@ -24,6 +24,7 @@ import ( "github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" + "github.com/kyverno/kyverno/pkg/registryclient" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" "golang.org/x/exp/slices" corev1 "k8s.io/api/core/v1" @@ -53,8 +54,10 @@ const ( type PolicyController struct { client dclient.Interface kyvernoClient versioned.Interface - pInformer kyvernov1informers.ClusterPolicyInformer - npInformer kyvernov1informers.PolicyInformer + rclient registryclient.Client + + pInformer kyvernov1informers.ClusterPolicyInformer + npInformer kyvernov1informers.PolicyInformer eventGen event.Interface eventRecorder record.EventRecorder @@ -93,6 +96,7 @@ type PolicyController struct { func NewPolicyController( kyvernoClient versioned.Interface, client dclient.Interface, + rclient registryclient.Client, pInformer kyvernov1informers.ClusterPolicyInformer, npInformer kyvernov1informers.PolicyInformer, urInformer kyvernov1beta1informers.UpdateRequestInformer, @@ -112,6 +116,7 @@ func NewPolicyController( pc := PolicyController{ client: client, kyvernoClient: kyvernoClient, + rclient: rclient, pInformer: pInformer, npInformer: npInformer, eventGen: eventGen, diff --git a/pkg/policy/updaterequest.go b/pkg/policy/updaterequest.go index 5bc2ff6e8d..ab44c6ed4e 100644 --- a/pkg/policy/updaterequest.go +++ b/pkg/policy/updaterequest.go @@ -105,7 +105,7 @@ func (pc *PolicyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest return false, errors.Wrapf(err, "failed to build policy context for rule %s", rule.Name) } - engineResponse := engine.ApplyBackgroundChecks(policyContext) + engineResponse := engine.ApplyBackgroundChecks(pc.rclient, policyContext) if len(engineResponse.PolicyResponse.Rules) == 0 { return true, nil } diff --git a/pkg/registryclient/client.go b/pkg/registryclient/client.go index 7e0369cb96..207114c3ea 100644 --- a/pkg/registryclient/client.go +++ b/pkg/registryclient/client.go @@ -11,51 +11,55 @@ import ( "github.com/chrismellard/docker-credential-acr-env/pkg/credhelper" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/authn/github" - kauth "github.com/google/go-containerregistry/pkg/authn/kubernetes" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/google" gcrremote "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/pkg/errors" "github.com/sigstore/cosign/pkg/oci/remote" "k8s.io/client-go/kubernetes" ) -// DefaultClient is default registry client. -var DefaultClient, _ = InitClient() +var baseKeychain = authn.NewMultiKeychain( + authn.DefaultKeychain, + google.Keychain, + authn.NewKeychainFromHelper(ecr.NewECRHelper(ecr.WithLogger(io.Discard))), + authn.NewKeychainFromHelper(credhelper.NewACRCredentialsHelper()), + github.Keychain, +) // Client provides registry related objects. type Client interface { - // Keychain provides keychain object. - Keychain() authn.Keychain + // getKeychain provides keychain object. + getKeychain() authn.Keychain - // Transport provides transport object. - Transport() *http.Transport + // getTransport provides transport object. + getTransport() http.RoundTripper // FetchImageDescriptor fetches Descriptor from registry with given imageRef // and provides access to metadata about remote artifact. - FetchImageDescriptor(imageRef string) (*gcrremote.Descriptor, error) + FetchImageDescriptor(context.Context, string) (*gcrremote.Descriptor, error) - // UseLocalKeychain updates keychain with the default local keychain. - UseLocalKeychain() + // // RefreshKeychainPullSecrets loads fresh data from pull secrets and updates Keychain. + // // If pull secrets are empty - returns. + RefreshKeychainPullSecrets(context.Context) error - // RefreshKeychainPullSecrets loads fresh data from pull secrets and updates Keychain. - // If pull secrets are empty - returns. - RefreshKeychainPullSecrets() error + // BuildRemoteOption builds remote.Option based on client. + BuildRemoteOption() remote.Option } -// InitClient initialize registry client with given options. -func InitClient(options ...Option) (Client, error) { - baseKeychain := authn.NewMultiKeychain( - authn.DefaultKeychain, - google.Keychain, - authn.NewKeychainFromHelper(ecr.NewECRHelper(ecr.WithLogger(io.Discard))), - authn.NewKeychainFromHelper(credhelper.NewACRCredentialsHelper()), - github.Keychain, - ) +type client struct { + keychain authn.Keychain + transport *http.Transport + pullSecretRefresher func(context.Context, *client) error +} + +// Option is an option to initialize registry client. +type Option = func(*client) error + +// New creates a new Client with options +func New(options ...Option) (Client, error) { c := &client{ - keychain: baseKeychain, - baseKeychain: baseKeychain, - transport: gcrremote.DefaultTransport.(*http.Transport), + keychain: baseKeychain, + transport: gcrremote.DefaultTransport.(*http.Transport), } for _, opt := range options { if err := opt(c); err != nil { @@ -65,28 +69,30 @@ func InitClient(options ...Option) (Client, error) { return c, nil } -// Option is an option to initialize registry client. -type Option func(*client) error +// New creates a new Client with options +func NewOrDie(options ...Option) Client { + c, err := New(options...) + if err != nil { + panic(err) + } + return c +} // WithKeychainPullSecrets provides initialize registry client option that allows to use pull secrets. -func WithKeychainPullSecrets(kubClient kubernetes.Interface, namespace, serviceAccount string, imagePullSecrets []string) Option { +func WithKeychainPullSecrets(ctx context.Context, kubClient kubernetes.Interface, namespace, serviceAccount string, imagePullSecrets ...string) Option { return func(c *client) error { - refresher := func(c *client) error { - freshKeychain, err := generateKeychainForPullSecrets(kubClient, namespace, serviceAccount, imagePullSecrets) + c.pullSecretRefresher = func(ctx context.Context, c *client) error { + freshKeychain, err := generateKeychainForPullSecrets(ctx, kubClient, namespace, serviceAccount, imagePullSecrets...) if err != nil { return err } - c.keychain = authn.NewMultiKeychain( - c.baseKeychain, + baseKeychain, freshKeychain, ) - return nil } - - c.pullSecretRefresher = refresher - return refresher(c) + return c.pullSecretRefresher(ctx, c) } } @@ -98,79 +104,50 @@ func WithAllowInsecureRegistry() Option { } } -type client struct { - keychain authn.Keychain - transport *http.Transport - - baseKeychain authn.Keychain - pullSecretRefresher func(*client) error -} - -// Keychain provides keychain object. -func (c *client) Keychain() authn.Keychain { - return c.keychain -} - -// Transport provides transport object. -func (c *client) Transport() *http.Transport { - return c.transport -} - -// UseLocalKeychain updates keychain with the default local keychain. -func (c *client) UseLocalKeychain() { - c.keychain = authn.DefaultKeychain - c.baseKeychain = authn.DefaultKeychain -} - -// FetchImageDescriptor fetches Descriptor from registry with given imageRef -// and provides access to metadata about remote artifact. -func (c *client) FetchImageDescriptor(imageRef string) (*gcrremote.Descriptor, error) { - parsedRef, err := name.ParseReference(imageRef) - if err != nil { - return nil, fmt.Errorf("failed to parse image reference: %s, error: %v", imageRef, err) +// WithLocalKeychain provides initialize keychain with the default local keychain. +func WithLocalKeychain() Option { + return func(c *client) error { + c.pullSecretRefresher = nil + c.keychain = authn.DefaultKeychain + return nil } - - desc, err := gcrremote.Get(parsedRef, gcrremote.WithAuthFromKeychain(c.keychain)) - if err != nil { - return nil, fmt.Errorf("failed to fetch image reference: %s, error: %v", imageRef, err) - } - - return desc, nil } // RefreshKeychainPullSecrets loads fresh data from pull secrets and updates Keychain. // If pull secrets are empty - returns. -func (c *client) RefreshKeychainPullSecrets() error { +func (c *client) RefreshKeychainPullSecrets(ctx context.Context) error { if c.pullSecretRefresher == nil { return nil } - - return c.pullSecretRefresher(c) -} - -// generateKeychainForPullSecrets generates keychain by fetching secrets data from imagePullSecrets. -func generateKeychainForPullSecrets( - client kubernetes.Interface, - namespace, serviceAccount string, - imagePullSecrets []string, -) (authn.Keychain, error) { - kcOpts := kauth.Options{ - Namespace: namespace, - ServiceAccountName: serviceAccount, - ImagePullSecrets: imagePullSecrets, - } - - kc, err := kauth.New(context.Background(), client, kcOpts) // uses k8s client to fetch secrets data - if err != nil { - return nil, errors.Wrap(err, "failed to initialize registry keychain") - } - return kc, err + return c.pullSecretRefresher(ctx, c) } // BuildRemoteOption builds remote.Option based on client. -func BuildRemoteOption(c Client) remote.Option { +func (c *client) BuildRemoteOption() remote.Option { return remote.WithRemoteOptions( - gcrremote.WithAuthFromKeychain(c.Keychain()), - gcrremote.WithTransport(c.Transport()), + gcrremote.WithAuthFromKeychain(c.keychain), + gcrremote.WithTransport(c.transport), ) } + +// FetchImageDescriptor fetches Descriptor from registry with given imageRef +// and provides access to metadata about remote artifact. +func (c *client) FetchImageDescriptor(ctx context.Context, imageRef string) (*gcrremote.Descriptor, error) { + parsedRef, err := name.ParseReference(imageRef) + if err != nil { + return nil, fmt.Errorf("failed to parse image reference: %s, error: %v", imageRef, err) + } + desc, err := gcrremote.Get(parsedRef, gcrremote.WithAuthFromKeychain(c.keychain), gcrremote.WithContext(ctx)) + if err != nil { + return nil, fmt.Errorf("failed to fetch image reference: %s, error: %v", imageRef, err) + } + return desc, nil +} + +func (c *client) getKeychain() authn.Keychain { + return c.keychain +} + +func (c *client) getTransport() http.RoundTripper { + return c.transport +} diff --git a/pkg/registryclient/client_test.go b/pkg/registryclient/client_test.go index 194f2ae587..2b816277a3 100644 --- a/pkg/registryclient/client_test.go +++ b/pkg/registryclient/client_test.go @@ -16,22 +16,22 @@ func TestInitClientWithEmptyOptions(t *testing.T) { expClient := &client{ transport: remote.DefaultTransport.(*http.Transport), } - c, err := InitClient() + c, err := New() assert.NilError(t, err) - assert.Assert(t, expClient.transport == c.Transport()) - assert.Assert(t, c.Keychain() != nil) + assert.Assert(t, expClient.transport == c.getTransport()) + assert.Assert(t, c.getKeychain() != nil) } func TestInitClientWithInsecureRegistryOption(t *testing.T) { expClient := &client{ transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}, } - c, err := InitClient(WithAllowInsecureRegistry()) + c, err := New(WithAllowInsecureRegistry()) expInsecureSkipVerify := expClient.transport.TLSClientConfig.InsecureSkipVerify - gotInsecureSkipVerify := c.Transport().TLSClientConfig.InsecureSkipVerify + gotInsecureSkipVerify := c.getTransport().(*http.Transport).TLSClientConfig.InsecureSkipVerify assert.NilError(t, err) assert.Assert(t, expInsecureSkipVerify == gotInsecureSkipVerify) - assert.Assert(t, c.Keychain() != nil) + assert.Assert(t, c.getKeychain() != nil) } diff --git a/pkg/registryclient/utils.go b/pkg/registryclient/utils.go new file mode 100644 index 0000000000..46cc520295 --- /dev/null +++ b/pkg/registryclient/utils.go @@ -0,0 +1,24 @@ +package registryclient + +import ( + "context" + + "github.com/google/go-containerregistry/pkg/authn" + kauth "github.com/google/go-containerregistry/pkg/authn/kubernetes" + "github.com/pkg/errors" + "k8s.io/client-go/kubernetes" +) + +// generateKeychainForPullSecrets generates keychain by fetching secrets data from imagePullSecrets. +func generateKeychainForPullSecrets(ctx context.Context, client kubernetes.Interface, namespace, serviceAccount string, imagePullSecrets ...string) (authn.Keychain, error) { + kcOpts := kauth.Options{ + Namespace: namespace, + ServiceAccountName: serviceAccount, + ImagePullSecrets: imagePullSecrets, + } + kc, err := kauth.New(ctx, client, kcOpts) // uses k8s client to fetch secrets data + if err != nil { + return nil, errors.Wrap(err, "failed to initialize registry keychain") + } + return kc, err +} diff --git a/pkg/testrunner/scenario.go b/pkg/testrunner/scenario.go index 085ecc9eef..22e442b6e8 100644 --- a/pkg/testrunner/scenario.go +++ b/pkg/testrunner/scenario.go @@ -15,6 +15,7 @@ import ( "github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine/response" + "github.com/kyverno/kyverno/pkg/registryclient" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -145,7 +146,7 @@ func runTestCase(t *testing.T, tc TestCase) bool { policyContext := engine.NewPolicyContext().WithPolicy(policy).WithNewResource(*resource) - er := engine.Mutate(policyContext) + er := engine.Mutate(registryclient.NewOrDie(), policyContext) t.Log("---Mutation---") validateResource(t, er.PatchedResource, tc.Expected.Mutation.PatchedResource) validateResponse(t, er.PolicyResponse, tc.Expected.Mutation.PolicyResponse) @@ -157,7 +158,7 @@ func runTestCase(t *testing.T, tc TestCase) bool { policyContext = policyContext.WithNewResource(*resource) - er = engine.Validate(policyContext) + er = engine.Validate(registryclient.NewOrDie(), policyContext) t.Log("---Validation---") validateResponse(t, er.PolicyResponse, tc.Expected.Validation.PolicyResponse) @@ -173,7 +174,7 @@ func runTestCase(t *testing.T, tc TestCase) bool { } else { policyContext := policyContext.WithClient(client) - er = engine.ApplyBackgroundChecks(policyContext) + er = engine.ApplyBackgroundChecks(registryclient.NewOrDie(), policyContext) t.Log(("---Generation---")) validateResponse(t, er.PolicyResponse, tc.Expected.Generation.PolicyResponse) // Expected generate resource will be in same namespaces as resource diff --git a/pkg/webhooks/resource/generation/generation.go b/pkg/webhooks/resource/generation/generation.go index 1d0ad7660b..215f774ba0 100644 --- a/pkg/webhooks/resource/generation/generation.go +++ b/pkg/webhooks/resource/generation/generation.go @@ -21,6 +21,7 @@ import ( enginutils "github.com/kyverno/kyverno/pkg/engine/utils" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" + "github.com/kyverno/kyverno/pkg/registryclient" webhookgenerate "github.com/kyverno/kyverno/pkg/webhooks/updaterequest" webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils" admissionv1 "k8s.io/api/admission/v1" @@ -45,6 +46,7 @@ func NewGenerationHandler( log logr.Logger, client dclient.Interface, kyvernoClient versioned.Interface, + rclient registryclient.Client, nsLister corev1listers.NamespaceLister, urLister kyvernov1beta1listers.UpdateRequestNamespaceLister, urGenerator webhookgenerate.Generator, @@ -55,6 +57,7 @@ func NewGenerationHandler( log: log, client: client, kyvernoClient: kyvernoClient, + rclient: rclient, nsLister: nsLister, urLister: urLister, urGenerator: urGenerator, @@ -67,6 +70,7 @@ type generationHandler struct { log logr.Logger client dclient.Interface kyvernoClient versioned.Interface + rclient registryclient.Client nsLister corev1listers.NamespaceLister urLister kyvernov1beta1listers.UpdateRequestNamespaceLister urGenerator webhookgenerate.Generator @@ -92,7 +96,7 @@ func (h *generationHandler) Handle( if request.Kind.Kind != "Namespace" && request.Namespace != "" { policyContext = policyContext.WithNamespaceLabels(common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log)) } - engineResponse := engine.ApplyBackgroundChecks(policyContext) + engineResponse := engine.ApplyBackgroundChecks(h.rclient, policyContext) for _, rule := range engineResponse.PolicyResponse.Rules { if rule.Status != response.RuleStatusPass { h.deleteGR(engineResponse) diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go index c86853a7d5..1e10cc25d6 100644 --- a/pkg/webhooks/resource/handlers.go +++ b/pkg/webhooks/resource/handlers.go @@ -20,6 +20,7 @@ import ( "github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/openapi" "github.com/kyverno/kyverno/pkg/policycache" + "github.com/kyverno/kyverno/pkg/registryclient" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" jsonutils "github.com/kyverno/kyverno/pkg/utils/json" "github.com/kyverno/kyverno/pkg/webhooks" @@ -38,6 +39,7 @@ type handlers struct { // clients client dclient.Interface kyvernoClient versioned.Interface + rclient registryclient.Client // config configuration config.Configuration @@ -64,6 +66,7 @@ type handlers struct { func NewHandlers( client dclient.Interface, kyvernoClient versioned.Interface, + rclient registryclient.Client, configuration config.Configuration, metricsConfig metrics.MetricsConfigManager, pCache policycache.Cache, @@ -80,6 +83,7 @@ func NewHandlers( return &handlers{ client: client, kyvernoClient: kyvernoClient, + rclient: rclient, configuration: configuration, metricsConfig: metricsConfig, pCache: pCache, @@ -113,7 +117,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad } if len(generatePolicies) == 0 && request.Operation == admissionv1.Update { // handle generate source resource updates - gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen) + gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen) go gh.HandleUpdatesForGenerateRules(request, []kyvernov1.PolicyInterface{}) } @@ -129,7 +133,7 @@ func (h *handlers) Validate(ctx context.Context, logger logr.Logger, request *ad namespaceLabels = common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, logger) } - vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports) + vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.rclient, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports) ok, msg, warnings := vh.HandleValidation(h.metricsConfig, request, policies, policyContext, namespaceLabels, startTime) if !ok { @@ -163,7 +167,7 @@ func (h *handlers) Mutate(ctx context.Context, logger logr.Logger, request *admi if err := enginectx.MutateResourceWithImageInfo(request.Object.Raw, policyContext.JSONContext()); err != nil { logger.Error(err, "failed to patch images info to resource, policies that mutate images may be impacted") } - mh := mutation.NewMutationHandler(logger, h.eventGen, h.openApiManager, h.nsLister) + mh := mutation.NewMutationHandler(logger, h.rclient, h.eventGen, h.openApiManager, h.nsLister) mutatePatches, mutateWarnings, err := mh.HandleMutation(h.metricsConfig, request, mutatePolicies, policyContext, startTime) if err != nil { logger.Error(err, "mutation failed") diff --git a/pkg/webhooks/resource/imageverification/handler.go b/pkg/webhooks/resource/imageverification/handler.go index 21199dbd0f..a8f4e0e60e 100644 --- a/pkg/webhooks/resource/imageverification/handler.go +++ b/pkg/webhooks/resource/imageverification/handler.go @@ -12,6 +12,7 @@ import ( "github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" + "github.com/kyverno/kyverno/pkg/registryclient" controllerutils "github.com/kyverno/kyverno/pkg/utils/controller" jsonutils "github.com/kyverno/kyverno/pkg/utils/json" reportutils "github.com/kyverno/kyverno/pkg/utils/report" @@ -76,7 +77,7 @@ func (h *imageVerificationHandler) handleVerifyImages(logger logr.Logger, reques verifiedImageData := &engine.ImageVerificationMetadata{} for _, p := range policies { policyContext := policyContext.WithPolicy(p) - resp, ivm := engine.VerifyAndPatchImages(policyContext) + resp, ivm := engine.VerifyAndPatchImages(registryclient.NewOrDie(), policyContext) engineResponses = append(engineResponses, resp) patches = append(patches, resp.GetPatches()...) diff --git a/pkg/webhooks/resource/mutation/mutation.go b/pkg/webhooks/resource/mutation/mutation.go index 9c09e7ac3b..f4ffe65a99 100644 --- a/pkg/webhooks/resource/mutation/mutation.go +++ b/pkg/webhooks/resource/mutation/mutation.go @@ -14,6 +14,7 @@ import ( "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/openapi" + "github.com/kyverno/kyverno/pkg/registryclient" "github.com/kyverno/kyverno/pkg/utils" engineutils "github.com/kyverno/kyverno/pkg/utils/engine" jsonutils "github.com/kyverno/kyverno/pkg/utils/json" @@ -41,12 +42,14 @@ type MutationHandler interface { func NewMutationHandler( log logr.Logger, + rclient registryclient.Client, eventGen event.Interface, openApiManager openapi.ValidateInterface, nsLister corev1listers.NamespaceLister, ) MutationHandler { return &mutationHandler{ log: log, + rclient: rclient, eventGen: eventGen, openApiManager: openApiManager, nsLister: nsLister, @@ -55,6 +58,7 @@ func NewMutationHandler( type mutationHandler struct { log logr.Logger + rclient registryclient.Client eventGen event.Interface openApiManager openapi.ValidateInterface nsLister corev1listers.NamespaceLister @@ -144,7 +148,7 @@ func (h *mutationHandler) applyMutation(request *admissionv1.AdmissionRequest, p policyContext = policyContext.WithNamespaceLabels(common.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log)) } - engineResponse := engine.Mutate(policyContext) + engineResponse := engine.Mutate(h.rclient, policyContext) policyPatches := engineResponse.GetPatches() if !engineResponse.IsSuccessful() { diff --git a/pkg/webhooks/resource/updaterequest.go b/pkg/webhooks/resource/updaterequest.go index 418b254876..85d9154dd0 100644 --- a/pkg/webhooks/resource/updaterequest.go +++ b/pkg/webhooks/resource/updaterequest.go @@ -18,7 +18,7 @@ import ( // createUpdateRequests applies generate and mutateExisting policies, and creates update requests for background reconcile func (h *handlers) createUpdateRequests(logger logr.Logger, request *admissionv1.AdmissionRequest, policyContext *engine.PolicyContext, generatePolicies, mutatePolicies []kyvernov1.PolicyInterface, ts time.Time) { - gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen) + gh := generation.NewGenerationHandler(logger, h.client, h.kyvernoClient, h.rclient, h.nsLister, h.urLister, h.urGenerator, h.urUpdater, h.eventGen) go h.handleMutateExisting(logger, request, mutatePolicies, policyContext, ts) go gh.Handle(h.metricsConfig, request, generatePolicies, policyContext, ts) } @@ -43,7 +43,7 @@ func (h *handlers) handleMutateExisting(logger logr.Logger, request *admissionv1 var rules []response.RuleResponse policyContext := policyContext.WithPolicy(policy) - engineResponse := engine.ApplyBackgroundChecks(policyContext) + engineResponse := engine.ApplyBackgroundChecks(h.rclient, policyContext) for _, rule := range engineResponse.PolicyResponse.Rules { if rule.Status == response.RuleStatusPass { diff --git a/pkg/webhooks/resource/validation/validation.go b/pkg/webhooks/resource/validation/validation.go index 63e4cbfb90..6b42d6d0b9 100644 --- a/pkg/webhooks/resource/validation/validation.go +++ b/pkg/webhooks/resource/validation/validation.go @@ -13,6 +13,7 @@ import ( "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/policycache" + "github.com/kyverno/kyverno/pkg/registryclient" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" controllerutils "github.com/kyverno/kyverno/pkg/utils/controller" reportutils "github.com/kyverno/kyverno/pkg/utils/report" @@ -33,6 +34,7 @@ type ValidationHandler interface { func NewValidationHandler( log logr.Logger, kyvernoClient versioned.Interface, + rclient registryclient.Client, pCache policycache.Cache, pcBuilder webhookutils.PolicyContextBuilder, eventGen event.Interface, @@ -41,6 +43,7 @@ func NewValidationHandler( return &validationHandler{ log: log, kyvernoClient: kyvernoClient, + rclient: rclient, pCache: pCache, pcBuilder: pcBuilder, eventGen: eventGen, @@ -51,6 +54,7 @@ func NewValidationHandler( type validationHandler struct { log logr.Logger kyvernoClient versioned.Interface + rclient registryclient.Client pCache policycache.Cache pcBuilder webhookutils.PolicyContextBuilder eventGen event.Interface @@ -95,7 +99,7 @@ func (v *validationHandler) HandleValidation( failurePolicy = kyvernov1.Fail } - engineResponse := engine.Validate(policyContext) + engineResponse := engine.Validate(v.rclient, policyContext) if engineResponse.IsNil() { // we get an empty response if old and new resources created the same response // allow updates if resource update doesnt change the policy evaluation @@ -142,7 +146,7 @@ func (v *validationHandler) buildAuditResponses(resource unstructured.Unstructur var responses []*response.EngineResponse for _, policy := range policies { policyContext := policyContext.WithPolicy(policy).WithNamespaceLabels(namespaceLabels) - responses = append(responses, engine.Validate(policyContext)) + responses = append(responses, engine.Validate(v.rclient, policyContext)) } return responses, nil } diff --git a/pkg/webhooks/resource/validation_test.go b/pkg/webhooks/resource/validation_test.go index dd167dc71a..85cb946802 100644 --- a/pkg/webhooks/resource/validation_test.go +++ b/pkg/webhooks/resource/validation_test.go @@ -6,6 +6,7 @@ import ( "testing" log "github.com/kyverno/kyverno/pkg/logging" + "github.com/kyverno/kyverno/pkg/registryclient" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/pkg/engine" @@ -531,6 +532,7 @@ func TestValidate_failure_action_overrides(t *testing.T) { assert.NilError(t, err) er := engine.Validate( + registryclient.NewOrDie(), engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured), ) if tc.blocked && tc.messages != nil { @@ -592,7 +594,7 @@ func Test_RuleSelector(t *testing.T) { ctx := engine.NewPolicyContext().WithPolicy(&policy).WithNewResource(*resourceUnstructured) - resp := engine.Validate(ctx) + resp := engine.Validate(registryclient.NewOrDie(), ctx) assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 2) assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0) @@ -603,7 +605,7 @@ func Test_RuleSelector(t *testing.T) { applyOne := kyvernov1.ApplyOne policy.Spec.ApplyRules = &applyOne - resp = engine.Validate(ctx) + resp = engine.Validate(registryclient.NewOrDie(), ctx) assert.Assert(t, resp.PolicyResponse.RulesAppliedCount == 1) assert.Assert(t, resp.PolicyResponse.RulesErrorCount == 0)