diff --git a/pkg/engine/utils/match.go b/pkg/engine/utils/match.go index 432124a65d..073c888ec5 100644 --- a/pkg/engine/utils/match.go +++ b/pkg/engine/utils/match.go @@ -122,7 +122,7 @@ func doesResourceMatchConditionBlock( errs = append(errs, fmt.Errorf("failed to parse namespace selector: %v", err)) } else { if !hasPassed { - errs = append(errs, fmt.Errorf("namespace selector does not match")) + errs = append(errs, fmt.Errorf("namespace selector does not match labels")) } } } diff --git a/pkg/event/controller.go b/pkg/event/controller.go index 7a13208a43..be864f8a08 100644 --- a/pkg/event/controller.go +++ b/pkg/event/controller.go @@ -196,7 +196,7 @@ func (gen *generator) syncHandler(key Info) error { eventType = corev1.EventTypeNormal } - logger.V(2).Info("creating the event", "source", key.Source, "type", eventType, "resource", key.Resource()) + logger.V(3).Info("creating the event", "source", key.Source, "type", eventType, "resource", key.Resource()) // based on the source of event generation, use different event recorders switch key.Source { case AdmissionController: diff --git a/pkg/utils/image/infos.go b/pkg/utils/image/infos.go index a0759fd263..3886da99f5 100644 --- a/pkg/utils/image/infos.go +++ b/pkg/utils/image/infos.go @@ -51,8 +51,8 @@ func (i *ImageInfo) ReferenceWithTag() string { } func GetImageInfo(image string, cfg config.Configuration) (*ImageInfo, error) { - logger.V(2).Info( - "Getting the image info", + logger.V(3).Info( + "getting the image info", "image", image, "defaultRegistry", config.Configuration.GetDefaultRegistry(cfg), "enableDefaultRegistryMutation", config.Configuration.GetEnableDefaultRegistryMutation(cfg), @@ -86,8 +86,8 @@ func GetImageInfo(image string, cfg config.Configuration) (*ImageInfo, error) { registry = "" } - logger.V(2).Info( - "Getting the image info", + logger.V(3).Info( + "getting the image info", "image", image, "registry", registry, "name", name, diff --git a/pkg/webhooks/resource/handlers.go b/pkg/webhooks/resource/handlers.go index 50bac956a4..b5524decb5 100644 --- a/pkg/webhooks/resource/handlers.go +++ b/pkg/webhooks/resource/handlers.go @@ -176,7 +176,7 @@ func (h *resourceHandlers) Mutate(ctx context.Context, logger logr.Logger, reque logger.Error(err, "failed to build policy context") return admissionutils.Response(request.UID, err) } - ivh := imageverification.NewImageVerificationHandler(logger, h.kyvernoClient, h.engine, h.eventGen, h.admissionReports, h.configuration) + ivh := imageverification.NewImageVerificationHandler(logger, h.kyvernoClient, h.engine, h.eventGen, h.admissionReports, h.configuration, h.nsLister) imagePatches, imageVerifyWarnings, err := ivh.Handle(ctx, newRequest, verifyImagesPolicies, policyContext) if err != nil { logger.Error(err, "image verification failed") diff --git a/pkg/webhooks/resource/imageverification/handler.go b/pkg/webhooks/resource/imageverification/handler.go index 2522814b74..28426eb323 100644 --- a/pkg/webhooks/resource/imageverification/handler.go +++ b/pkg/webhooks/resource/imageverification/handler.go @@ -14,6 +14,7 @@ import ( "github.com/kyverno/kyverno/pkg/event" "github.com/kyverno/kyverno/pkg/tracing" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" + engineutils "github.com/kyverno/kyverno/pkg/utils/engine" jsonutils "github.com/kyverno/kyverno/pkg/utils/json" reportutils "github.com/kyverno/kyverno/pkg/utils/report" webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils" @@ -21,6 +22,7 @@ import ( admissionv1 "k8s.io/api/admission/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + corev1listers "k8s.io/client-go/listers/core/v1" ) type ImageVerificationHandler interface { @@ -34,6 +36,7 @@ type imageVerificationHandler struct { eventGen event.Interface admissionReports bool cfg config.Configuration + nsLister corev1listers.NamespaceLister } func NewImageVerificationHandler( @@ -43,6 +46,7 @@ func NewImageVerificationHandler( eventGen event.Interface, admissionReports bool, cfg config.Configuration, + nsLister corev1listers.NamespaceLister, ) ImageVerificationHandler { return &imageVerificationHandler{ kyvernoClient: kyvernoClient, @@ -51,6 +55,7 @@ func NewImageVerificationHandler( eventGen: eventGen, admissionReports: admissionReports, cfg: cfg, + nsLister: nsLister, } } @@ -81,24 +86,34 @@ func (h *imageVerificationHandler) handleVerifyImages( var engineResponses []engineapi.EngineResponse var patches [][]byte verifiedImageData := engineapi.ImageVerificationMetadata{} + failurePolicy := kyvernov1.Ignore + for _, policy := range policies { tracing.ChildSpan( ctx, "", fmt.Sprintf("POLICY %s/%s", policy.GetNamespace(), policy.GetName()), func(ctx context.Context, span trace.Span) { + if policy.GetSpec().GetFailurePolicy() == kyvernov1.Fail { + failurePolicy = kyvernov1.Fail + } + policyContext := policyContext.WithPolicy(policy) + if request.Kind.Kind != "Namespace" && request.Namespace != "" { + policyContext = policyContext.WithNamespaceLabels(engineutils.GetNamespaceSelectorsFromNamespaceLister(request.Kind.Kind, request.Namespace, h.nsLister, h.log)) + } + resp, ivm := h.engine.VerifyAndPatchImages(ctx, policyContext) if !resp.IsEmpty() { engineResponses = append(engineResponses, resp) } + patches = append(patches, resp.GetPatches()...) verifiedImageData.Merge(ivm) }, ) } - failurePolicy := policies[0].GetSpec().GetFailurePolicy() blocked := webhookutils.BlockRequest(engineResponses, failurePolicy, logger) events := webhookutils.GenerateEvents(engineResponses, blocked) h.eventGen.Add(events...) diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/01-assert.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/01-assert.yaml new file mode 100644 index 0000000000..fcd09f08dd --- /dev/null +++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/01-assert.yaml @@ -0,0 +1,9 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: keyed-basic-ns-selector-policy +status: + conditions: + - reason: Succeeded + status: "True" + type: Ready diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/01-manifests.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/01-manifests.yaml new file mode 100644 index 0000000000..fb71b09071 --- /dev/null +++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/01-manifests.yaml @@ -0,0 +1,47 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: test-verify-images + labels: + signed: "true" +--- +apiVersion: v1 +kind: Namespace +metadata: + name: test-verify-images-unprotected + labels: + signed: "false" +--- +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: keyed-basic-ns-selector-policy +spec: + validationFailureAction: Enforce + background: false + webhookTimeoutSeconds: 30 + failurePolicy: Fail + rules: + - name: keyed-basic-rule + match: + all: + - resources: + kinds: + - Pod + namespaceSelector: + matchExpressions: + - key: signed + operator: In + values: + - "true" + verifyImages: + - imageReferences: + - "ghcr.io/kyverno/test-verify-image:*" + attestors: + - entries: + - keys: + publicKeys: |- + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM + 5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA== + -----END PUBLIC KEY----- diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/02-assert.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/02-assert.yaml new file mode 100644 index 0000000000..639b8513a2 --- /dev/null +++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/02-assert.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-signed-pod + namespace: test-verify-images \ No newline at end of file diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/02-goodpod.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/02-goodpod.yaml new file mode 100644 index 0000000000..9caad98758 --- /dev/null +++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/02-goodpod.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-signed-pod + namespace: test-verify-images +spec: + containers: + - image: ghcr.io/kyverno/test-verify-image:signed + name: test-secret \ No newline at end of file diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/03-teststep.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/03-teststep.yaml new file mode 100644 index 0000000000..67bb2a7c8c --- /dev/null +++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/03-teststep.yaml @@ -0,0 +1,9 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +apply: + - file: pod-unsigned.yaml + shouldFail: true + - file: pod-signed.yaml + shouldFail: false + - file: pod-unprotected-ns.yaml + shouldFail: false \ No newline at end of file diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/README.md b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/README.md new file mode 100644 index 0000000000..8c84b5a79d --- /dev/null +++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/README.md @@ -0,0 +1,11 @@ +## Description + +This test performs a simple verification of an image using a public key specified directly in the policy. + +## Expected Behavior + +Pod creation should pass as the image has been signed by the public key specified in the policy. + +## Reference Issue(s) + +N/A \ No newline at end of file diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/pod-signed.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/pod-signed.yaml new file mode 100644 index 0000000000..e39d151826 --- /dev/null +++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/pod-signed.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-signed-pod2 + namespace: test-verify-images +spec: + containers: + - image: ghcr.io/kyverno/test-verify-image:signed + name: test-signed2 \ No newline at end of file diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/pod-unprotected-ns.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/pod-unprotected-ns.yaml new file mode 100644 index 0000000000..a9bf784f52 --- /dev/null +++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/pod-unprotected-ns.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-unsigned-pod + namespace: test-verify-images-unprotected +spec: + containers: + - image: ghcr.io/kyverno/test-verify-image:unsigned + name: test-unsigned \ No newline at end of file diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/pod-unsigned.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/pod-unsigned.yaml new file mode 100644 index 0000000000..09495cdfbf --- /dev/null +++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/keyed-basic-namespace-selector/pod-unsigned.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-unsigned-pod + namespace: test-verify-images +spec: + containers: + - image: ghcr.io/kyverno/test-verify-image:unsigned + name: test-unsigned \ No newline at end of file