From 632bd99612a9de488ace2d6d8d0f9a787ee631a8 Mon Sep 17 00:00:00 2001 From: Pratik Shah Date: Tue, 18 Oct 2022 14:08:28 +0530 Subject: [PATCH] Fixed issue-4655: verifyImages is executed before mutate (#4996) Signed-off-by: Pratik Shah --- pkg/webhooks/resource/handlers.go | 6 ++ pkg/webhooks/resource/handlers_test.go | 136 +++++++++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go index abf2b51c51..7f05171e9d 100644 --- a/pkg/webhooks/resource/handlers.go +++ b/pkg/webhooks/resource/handlers.go @@ -181,6 +181,12 @@ func (h *handlers) Mutate(logger logr.Logger, request *admissionv1.AdmissionRequ return admissionutils.ResponseFailure(err.Error()) } newRequest := patchRequest(mutatePatches, request, logger) + // rebuild context to process images updated via mutate policies + policyContext, err = h.pcBuilder.Build(newRequest, mutatePolicies...) + if err != nil { + logger.Error(err, "failed to build policy context") + return admissionutils.ResponseFailure(err.Error()) + } ivh := imageverification.NewImageVerificationHandler(logger, h.eventGen) imagePatches, imageVerifyWarnings, err := ivh.Handle(h.metricsConfig, newRequest, verifyImagesPolicies, policyContext) if err != nil { diff --git a/pkg/webhooks/resource/handlers_test.go b/pkg/webhooks/resource/handlers_test.go index 19995a4c16..dc6330dadc 100644 --- a/pkg/webhooks/resource/handlers_test.go +++ b/pkg/webhooks/resource/handlers_test.go @@ -133,6 +133,112 @@ var policyVerifySignature = ` } ` +var policyMutateAndVerify = ` +{ + "apiVersion": "kyverno.io/v1", + "kind": "ClusterPolicy", + "metadata": { + "name": "disallow-unsigned-images" + }, + "spec": { + "validationFailureAction": "enforce", + "background": false, + "rules": [ + { + "name": "replace-image-registry", + "match": { + "any": [ + { + "resources": { + "kinds": [ + "Pod" + ] + } + } + ] + }, + "mutate": { + "foreach": [ + { + "list": "request.object.spec.containers", + "patchStrategicMerge": { + "spec": { + "containers": [ + { + "name": "{{ element.name }}", + "image": "{{ regex_replace_all_literal('.*(.*)/', '{{element.image}}', 'ghcr.io/kyverno/' )}}" + } + ] + } + } + } + ] + } + }, + { + "name": "disallow-unsigned-images-rule", + "match": { + "any": [ + { + "resources": { + "kinds": [ + "Pod" + ] + } + } + ] + }, + "verifyImages": [ + { + "imageReferences": [ + "*" + ], + "verifyDigest": false, + "required": null, + "mutateDigest": false, + "attestors": [ + { + "count": 1, + "entries": [ + { + "keys": { + "publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----" + } + } + ] + } + ] + } + ] + } + ] + } +} +` + +var resourceMutateAndVerify = `{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "labels": { + "run": "rewrite" + }, + "name": "rewrite" + }, + "spec": { + "containers": [ + { + "image": "test-verify-image:signed", + "name": "rewrite", + "resources": {} + } + ], + "dnsPolicy": "ClusterFirst", + "restartPolicy": "OnFailure" + } +} +` + var pod = `{ "apiVersion": "v1", "kind": "Pod", @@ -274,6 +380,36 @@ func Test_ImageVerify(t *testing.T) { assert.Equal(t, len(response.Warnings), 0) } +func Test_MutateAndVerify(t *testing.T) { + policyCache := policycache.NewCache() + logger := log.WithName("Test_MutateAndVerify") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + handlers := NewFakeHandlers(ctx, policyCache) + + var policy kyverno.ClusterPolicy + err := json.Unmarshal([]byte(policyMutateAndVerify), &policy) + assert.NilError(t, err) + + key := makeKey(&policy) + policyCache.Set(key, &policy) + + request := &v1.AdmissionRequest{ + Operation: v1.Create, + Kind: metav1.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, + Resource: metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "Pod"}, + Object: runtime.RawExtension{ + Raw: []byte(resourceMutateAndVerify), + }, + } + + response := handlers.Mutate(logger, request, "", time.Now()) + assert.Equal(t, response.Allowed, true) + assert.Equal(t, len(response.Warnings), 0) +} + func makeKey(policy kyverno.PolicyInterface) string { name := policy.GetName() namespace := policy.GetNamespace()