diff --git a/api/kyverno/v1/image_verification_types.go b/api/kyverno/v1/image_verification_types.go
index 4f281acdee..b30d0dfe76 100644
--- a/api/kyverno/v1/image_verification_types.go
+++ b/api/kyverno/v1/image_verification_types.go
@@ -9,13 +9,13 @@ import (
)
// ImageVerificationType selects the type of verification algorithm
-// +kubebuilder:validation:Enum=Cosign;NotaryV2
+// +kubebuilder:validation:Enum=Cosign;Notary
// +kubebuilder:default=Cosign
type ImageVerificationType string
const (
- Cosign ImageVerificationType = "Cosign"
- NotaryV2 ImageVerificationType = "NotaryV2"
+ Cosign ImageVerificationType = "Cosign"
+ Notary ImageVerificationType = "Notary"
)
// ImageVerification validates that images that match the specified pattern
@@ -23,7 +23,7 @@ const (
// mutated to include the SHA digest retrieved during the registration.
type ImageVerification struct {
// Type specifies the method of signature validation. The allowed options
- // are Cosign and NotaryV2. By default Cosign is used if a type is not specified.
+ // are Cosign and Notary. By default Cosign is used if a type is not specified.
// +kubebuilder:validation:Optional
Type ImageVerificationType `json:"type,omitempty" yaml:"type,omitempty"`
@@ -236,9 +236,14 @@ type CTLog struct {
// OCI registry and decodes them into a list of Statements.
type Attestation struct {
// PredicateType defines the type of Predicate contained within the Statement.
- // +kubebuilder:validation:Required
+ // Deprecated in favour of 'Type', to be removed soon
+ // +kubebuilder:validation:Optional
PredicateType string `json:"predicateType" yaml:"predicateType"`
+ // Type defines the type of attestation contained within the Statement.
+ // +kubebuilder:validation:Optional
+ Type string `json:"type" yaml:"type"`
+
// Attestors specify the required attestors (i.e. authorities)
// +kubebuilder:validation:Optional
Attestors []AttestorSet `json:"attestors" yaml:"attestors"`
@@ -281,6 +286,19 @@ func (iv *ImageVerification) Validate(isAuditFailureAction bool, path *field.Pat
errs = append(errs, attestorErrors...)
}
+ if iv.Type == Notary {
+ for _, attestorSet := range iv.Attestors {
+ for _, attestor := range attestorSet.Entries {
+ if attestor.Keyless != nil {
+ errs = append(errs, field.Invalid(attestorsPath, iv, "Keyless field is not allowed for type notary"))
+ }
+ if attestor.Keys != nil {
+ errs = append(errs, field.Invalid(attestorsPath, iv, "Keys field is not allowed for type notary"))
+ }
+ }
+ }
+ }
+
return errs
}
diff --git a/api/kyverno/v2beta1/image_verification_types.go b/api/kyverno/v2beta1/image_verification_types.go
index 8485ccf15d..a2577ac234 100644
--- a/api/kyverno/v2beta1/image_verification_types.go
+++ b/api/kyverno/v2beta1/image_verification_types.go
@@ -6,13 +6,13 @@ import (
)
// ImageVerificationType selects the type of verification algorithm
-// +kubebuilder:validation:Enum=Cosign;NotaryV2
+// +kubebuilder:validation:Enum=Cosign;Notary
// +kubebuilder:default=Cosign
type ImageVerificationType string
const (
- Cosign ImageVerificationType = "Cosign"
- NotaryV2 ImageVerificationType = "NotaryV2"
+ Cosign ImageVerificationType = "Cosign"
+ Notary ImageVerificationType = "Notary"
)
// ImageVerification validates that images that match the specified pattern
@@ -20,7 +20,7 @@ const (
// mutated to include the SHA digest retrieved during the registration.
type ImageVerification struct {
// Type specifies the method of signature validation. The allowed options
- // are Cosign and NotaryV2. By default Cosign is used if a type is not specified.
+ // are Cosign and Notary. By default Cosign is used if a type is not specified.
// +kubebuilder:validation:Optional
Type ImageVerificationType `json:"type,omitempty" yaml:"type,omitempty"`
@@ -86,5 +86,18 @@ func (iv *ImageVerification) Validate(isAuditFailureAction bool, path *field.Pat
errs = append(errs, attestorErrors...)
}
+ if iv.Type == Notary {
+ for _, attestorSet := range iv.Attestors {
+ for _, attestor := range attestorSet.Entries {
+ if attestor.Keyless != nil {
+ errs = append(errs, field.Invalid(attestorsPath, iv, "Keyless field is not allowed for type notary"))
+ }
+ if attestor.Keys != nil {
+ errs = append(errs, field.Invalid(attestorsPath, iv, "Keys field is not allowed for type notary"))
+ }
+ }
+ }
+ }
+
return errs
}
diff --git a/charts/kyverno/templates/crds/crds.yaml b/charts/kyverno/templates/crds/crds.yaml
index 0ba7ecab21..05ca055a60 100644
--- a/charts/kyverno/templates/crds/crds.yaml
+++ b/charts/kyverno/templates/crds/crds.yaml
@@ -7248,10 +7248,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -7499,11 +7502,11 @@ spec:
type: string
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -11153,9 +11156,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -11412,11 +11419,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -14748,10 +14755,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -14974,11 +14984,11 @@ spec:
type: boolean
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -18628,9 +18638,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -18887,11 +18901,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -22509,10 +22523,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -22760,11 +22777,11 @@ spec:
type: string
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -26415,9 +26432,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -26674,11 +26695,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -30011,10 +30032,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -30237,11 +30261,11 @@ spec:
type: boolean
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -33891,9 +33915,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -34150,11 +34178,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
diff --git a/config/crds/kyverno.io_clusterpolicies.yaml b/config/crds/kyverno.io_clusterpolicies.yaml
index 999640c451..b374e3b355 100644
--- a/config/crds/kyverno.io_clusterpolicies.yaml
+++ b/config/crds/kyverno.io_clusterpolicies.yaml
@@ -3494,10 +3494,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -3745,11 +3748,11 @@ spec:
type: string
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -7399,9 +7402,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -7658,11 +7665,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -10994,10 +11001,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -11220,11 +11230,11 @@ spec:
type: boolean
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -14874,9 +14884,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -15133,11 +15147,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
diff --git a/config/crds/kyverno.io_policies.yaml b/config/crds/kyverno.io_policies.yaml
index 764c0e3bcf..4b0446f5d5 100644
--- a/config/crds/kyverno.io_policies.yaml
+++ b/config/crds/kyverno.io_policies.yaml
@@ -3495,10 +3495,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -3746,11 +3749,11 @@ spec:
type: string
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -7401,9 +7404,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -7660,11 +7667,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -10997,10 +11004,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -11223,11 +11233,11 @@ spec:
type: boolean
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -14877,9 +14887,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -15136,11 +15150,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
diff --git a/config/install-latest-testing.yaml b/config/install-latest-testing.yaml
index 88b47ddc32..b95ed23734 100644
--- a/config/install-latest-testing.yaml
+++ b/config/install-latest-testing.yaml
@@ -7451,10 +7451,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -7702,11 +7705,11 @@ spec:
type: string
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -11356,9 +11359,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -11615,11 +11622,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -14951,10 +14958,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -15177,11 +15187,11 @@ spec:
type: boolean
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -18831,9 +18841,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -19090,11 +19104,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -22712,10 +22726,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -22963,11 +22980,11 @@ spec:
type: string
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -26618,9 +26635,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -26877,11 +26898,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -30214,10 +30235,13 @@ spec:
type: array
predicateType:
description: PredicateType defines the type of Predicate
+ contained within the Statement. Deprecated in
+ favour of 'Type', to be removed soon
+ type: string
+ type:
+ description: Type defines the type of attestation
contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -30440,11 +30464,11 @@ spec:
type: boolean
type:
description: Type specifies the method of signature validation.
- The allowed options are Cosign and NotaryV2. By default
+ The allowed options are Cosign and Notary. By default
Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
@@ -34094,9 +34118,13 @@ spec:
predicateType:
description: PredicateType defines the type
of Predicate contained within the Statement.
+ Deprecated in favour of 'Type', to be removed
+ soon
+ type: string
+ type:
+ description: Type defines the type of attestation
+ contained within the Statement.
type: string
- required:
- - predicateType
type: object
type: array
attestors:
@@ -34353,11 +34381,11 @@ spec:
type: string
type:
description: Type specifies the method of signature
- validation. The allowed options are Cosign and NotaryV2.
+ validation. The allowed options are Cosign and Notary.
By default Cosign is used if a type is not specified.
enum:
- Cosign
- - NotaryV2
+ - Notary
type: string
verifyDigest:
default: true
diff --git a/docs/user/crd/index.html b/docs/user/crd/index.html
index 220aee3cd8..5060ed34fc 100644
--- a/docs/user/crd/index.html
+++ b/docs/user/crd/index.html
@@ -715,7 +715,19 @@ string
- PredicateType defines the type of Predicate contained within the Statement.
+PredicateType defines the type of Predicate contained within the Statement.
+Deprecated in favour of ‘Type’, to be removed soon
+ |
+
+
+
+type
+
+string
+
+ |
+
+ Type defines the type of attestation contained within the Statement.
|
@@ -1972,7 +1984,7 @@ ImageVerificationType
Type specifies the method of signature validation. The allowed options
-are Cosign and NotaryV2. By default Cosign is used if a type is not specified.
+are Cosign and Notary. By default Cosign is used if a type is not specified.
|
@@ -6372,7 +6384,7 @@ ImageVerificationType
Type specifies the method of signature validation. The allowed options
-are Cosign and NotaryV2. By default Cosign is used if a type is not specified.
+are Cosign and Notary. By default Cosign is used if a type is not specified.
|
diff --git a/go.mod b/go.mod
index cfecfba775..3bf9a76f60 100644
--- a/go.mod
+++ b/go.mod
@@ -20,7 +20,7 @@ require (
github.com/go-logr/logr v1.2.4
github.com/go-logr/zapr v1.2.4
github.com/google/gnostic v0.6.9
- github.com/google/go-containerregistry v0.14.0
+ github.com/google/go-containerregistry v0.14.1-0.20230425172351-b7c6e9dc3944
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20230403180904-b8d1c0a1df12
github.com/in-toto/in-toto-golang v0.6.0
github.com/jmespath/go-jmespath v0.4.0
@@ -33,6 +33,7 @@ require (
github.com/notaryproject/notation-go v1.0.0-rc.3
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.27.7
+ github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0-rc2
github.com/orcaman/concurrent-map/v2 v2.0.1
github.com/pkg/errors v0.9.1
@@ -76,7 +77,6 @@ require (
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f
k8s.io/pod-security-admission v0.27.2
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2
- oras.land/oras-go/v2 v2.2.0
sigs.k8s.io/controller-runtime v0.15.0
sigs.k8s.io/kustomize/api v0.13.4
sigs.k8s.io/kustomize/kyaml v0.14.2
@@ -84,13 +84,13 @@ require (
)
require (
+ cloud.google.com/go/compute v1.19.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/google/cel-go v0.12.6 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
)
require (
- cloud.google.com/go/compute v1.19.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.0.0 // indirect
cloud.google.com/go/kms v1.10.1 // indirect
@@ -108,7 +108,7 @@ require (
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
- github.com/Microsoft/go-winio v0.6.0 // indirect
+ github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
@@ -156,9 +156,9 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/djherbis/times v1.5.0 // indirect
- github.com/docker/cli v23.0.2+incompatible // indirect
+ github.com/docker/cli v23.0.4+incompatible // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
- github.com/docker/docker v23.0.3+incompatible // indirect
+ github.com/docker/docker v23.0.4+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
@@ -229,7 +229,7 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
- github.com/klauspost/compress v1.16.3 // indirect
+ github.com/klauspost/compress v1.16.5 // indirect
github.com/leodido/go-urn v1.2.2 // indirect
github.com/letsencrypt/boulder v0.0.0-20230331213904-8c67769be400 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
@@ -254,7 +254,6 @@ require (
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 // indirect
github.com/open-policy-agent/gatekeeper v0.0.0-20210824170141-dd97b8a7e966 // indirect
github.com/open-policy-agent/opa v0.51.0 // indirect
- github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
@@ -313,7 +312,7 @@ require (
go.uber.org/atomic v1.10.0 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.10.0 // indirect
- golang.org/x/oauth2 v0.6.0 // indirect
+ golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sync v0.2.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/term v0.8.0 // indirect
@@ -330,6 +329,7 @@ require (
gopkg.in/warnings.v0 v0.1.2 // indirect
k8s.io/component-base v0.27.2 // indirect
k8s.io/kubectl v0.26.3 // indirect
+ oras.land/oras-go/v2 v2.1.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/release-utils v0.7.3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
diff --git a/go.sum b/go.sum
index c324ad23b7..f1c0fff693 100644
--- a/go.sum
+++ b/go.sum
@@ -27,8 +27,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ=
-cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
+cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
+cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
@@ -109,8 +109,8 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0
github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
-github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
-github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
+github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
+github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.7/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
@@ -355,12 +355,12 @@ github.com/distribution/distribution v2.8.2+incompatible h1:k9+4DKdOG+quPFZXT/mU
github.com/distribution/distribution v2.8.2+incompatible/go.mod h1:EgLm2NgWtdKgzF9NpMzUKgzmR7AMmb0VQi2B+ZzDRjc=
github.com/djherbis/times v1.5.0 h1:79myA211VwPhFTqUk8xehWrsEO+zcIZj0zT8mXPVARU=
github.com/djherbis/times v1.5.0/go.mod h1:5q7FDLvbNg1L/KaBmPcWlVR9NmoKo3+ucqUA3ijQhA0=
-github.com/docker/cli v23.0.2+incompatible h1:Yj4wkrNtyCNLCMobKDYzEUIsbtMbfAulkHMH75/ecik=
-github.com/docker/cli v23.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v23.0.4+incompatible h1:xClB7PsiATttDHj8ce5qvJcikiApNy7teRR1XkoBZGs=
+github.com/docker/cli v23.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
-github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v23.0.4+incompatible h1:Kd3Bh9V/rO+XpTP/BLqM+gx8z7+Yb0AA2Ibj+nNo4ek=
+github.com/docker/docker v23.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
@@ -669,8 +669,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-containerregistry v0.14.0 h1:z58vMqHxuwvAsVwvKEkmVBz2TlgBgH5k6koEXBtlYkw=
-github.com/google/go-containerregistry v0.14.0/go.mod h1:aiJ2fp/SXvkWgmYHioXnbMdlgB8eXiiYOY55gfN91Wk=
+github.com/google/go-containerregistry v0.14.1-0.20230425172351-b7c6e9dc3944 h1:7c5khUnWebZDFMUQ7rf2vynmmnKI1VvBACrTZKKpoD4=
+github.com/google/go-containerregistry v0.14.1-0.20230425172351-b7c6e9dc3944/go.mod h1:0JopT7wiZeP5/ATNgx85oApuNAiNnfn4mr8+WOssYNQ=
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20230403180904-b8d1c0a1df12 h1:LLLVB/7zCZVKI27rqA7bbZHZJxH1lL2jbLxdomX1Eew=
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20230403180904-b8d1c0a1df12/go.mod h1:CSeefFZsOfyNrYGXDafpWNkf3tUz17nKReR5INPRaMI=
github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FCD+K3FI=
@@ -880,8 +880,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
-github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
+github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -1672,8 +1672,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
-golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
-golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
+golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
+golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -2176,8 +2176,8 @@ mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE=
-oras.land/oras-go/v2 v2.2.0 h1:E1fqITD56Eg5neZbxBtAdZVgDHD6wBabJo6xESTcQyo=
-oras.land/oras-go/v2 v2.2.0/go.mod h1:pXjn0+KfarspMHHNR3A56j3tgvr+mxArHuI8qVn59v8=
+oras.land/oras-go/v2 v2.1.0 h1:1nS8BIeEP6CBVQifwxrsth2bkuD+cYfjp7Hf7smUcS8=
+oras.land/oras-go/v2 v2.1.0/go.mod h1:v5ZSAPIMEJYnZjZ6rTGPAyaonH+rCFmbE95IAzCTeGU=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/pkg/cosign/cosign.go b/pkg/cosign/cosign.go
index 37c9b2af8c..20a745fd1c 100644
--- a/pkg/cosign/cosign.go
+++ b/pkg/cosign/cosign.go
@@ -79,7 +79,7 @@ func (v *cosignVerifier) VerifySignature(ctx context.Context, opts images.Option
}
var digest string
- if opts.PredicateType == "" {
+ if opts.Type == "" {
digest, err = extractDigest(opts.ImageRef, payload)
if err != nil {
return nil, err
@@ -265,13 +265,13 @@ func (v *cosignVerifier) FetchAttestations(ctx context.Context, opts images.Opti
}
for _, signature := range signatures {
- match, predicateType, err := matchPredicateType(signature, opts.PredicateType)
+ match, predicateType, err := matchType(signature, opts.Type)
if err != nil {
return nil, err
}
if !match {
- logger.V(4).Info("predicateType doesn't match, continue", "expected", opts.PredicateType, "received", predicateType)
+ logger.V(4).Info("type doesn't match, continue", "expected", opts.Type, "received", predicateType)
continue
}
@@ -294,15 +294,15 @@ func (v *cosignVerifier) FetchAttestations(ctx context.Context, opts images.Opti
return &images.Response{Digest: digest, Statements: inTotoStatements}, nil
}
-func matchPredicateType(sig oci.Signature, expectedPredicateType string) (bool, string, error) {
- if expectedPredicateType != "" {
+func matchType(sig oci.Signature, expectedType string) (bool, string, error) {
+ if expectedType != "" {
statement, _, err := decodeStatement(sig)
if err != nil {
- return false, "", fmt.Errorf("failed to decode predicateType: %w", err)
+ return false, "", fmt.Errorf("failed to decode type: %w", err)
}
- if pType, ok := statement["predicateType"]; ok {
- if pType.(string) == expectedPredicateType {
+ if pType, ok := statement["type"]; ok {
+ if pType.(string) == expectedType {
return true, pType.(string), nil
}
}
@@ -360,6 +360,7 @@ func decodeStatement(sig oci.Signature) (map[string]interface{}, string, error)
if err != nil {
return nil, "", fmt.Errorf("failed to decode statement %s: %w", string(pld), err)
}
+ decodedStatement["type"] = decodedStatement["predicateType"]
return decodedStatement, digest, nil
}
@@ -376,7 +377,7 @@ func decodePayload(payloadBase64 string) (map[string]interface{}, error) {
return nil, err
}
- if statement.PredicateType != attestation.CosignCustomProvenanceV01 {
+ if statement.Type != attestation.CosignCustomProvenanceV01 {
// This assumes that the following statements are JSON objects:
// - in_toto.PredicateSLSAProvenanceV01
// - in_toto.PredicateLinkV1
@@ -389,7 +390,7 @@ func decodePayload(payloadBase64 string) (map[string]interface{}, error) {
}
func decodeCosignCustomProvenanceV01(statement in_toto.Statement) (map[string]interface{}, error) {
- if statement.PredicateType != attestation.CosignCustomProvenanceV01 {
+ if statement.Type != attestation.CosignCustomProvenanceV01 {
return nil, fmt.Errorf("invalid statement type %s", attestation.CosignCustomProvenanceV01)
}
diff --git a/pkg/engine/api/context.go b/pkg/engine/api/context.go
index d1b80b63d7..0091ad8c8c 100644
--- a/pkg/engine/api/context.go
+++ b/pkg/engine/api/context.go
@@ -6,6 +6,7 @@ import (
"fmt"
"github.com/go-logr/logr"
+ "github.com/google/go-containerregistry/pkg/name"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/engine/apicall"
@@ -141,6 +142,13 @@ func fetchImageData(ctx context.Context, jp jmespath.Interface, rclient registry
// FetchImageDataMap fetches image information from the remote registry.
func fetchImageDataMap(ctx context.Context, rclient registryclient.Client, ref string) (interface{}, error) {
desc, err := rclient.FetchImageDescriptor(ctx, ref)
+ if err != nil {
+ return nil, fmt.Errorf("failed to fetch image descriptor: %s, error: %v", ref, err)
+ }
+ parsedRef, err := name.ParseReference(ref)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse image reference: %s, error: %v", ref, err)
+ }
if err != nil {
return nil, err
}
@@ -169,10 +177,10 @@ func fetchImageDataMap(ctx context.Context, rclient registryclient.Client, ref s
data := map[string]interface{}{
"image": ref,
- "resolvedImage": fmt.Sprintf("%s@%s", desc.Ref.Context().Name(), desc.Digest.String()),
- "registry": desc.Ref.Context().RegistryStr(),
- "repository": desc.Ref.Context().RepositoryStr(),
- "identifier": desc.Ref.Identifier(),
+ "resolvedImage": fmt.Sprintf("%s@%s", parsedRef.Context().Name(), desc.Digest.String()),
+ "registry": parsedRef.Context().RegistryStr(),
+ "repository": parsedRef.Context().RepositoryStr(),
+ "identifier": parsedRef.Identifier(),
"manifest": manifest,
"configData": configData,
}
diff --git a/pkg/engine/handlers/mutation/mutate_image.go b/pkg/engine/handlers/mutation/mutate_image.go
index 85b187ce22..c48ca38c21 100644
--- a/pkg/engine/handlers/mutation/mutate_image.go
+++ b/pkg/engine/handlers/mutation/mutate_image.go
@@ -112,7 +112,9 @@ func substituteVariables(rule kyvernov1.Rule, ctx enginecontext.EvalInterface, l
// remove attestations as variables are not substituted in them
ruleCopy := *rule.DeepCopy()
for i := range ruleCopy.VerifyImages {
- ruleCopy.VerifyImages[i].Attestations = nil
+ for j := range ruleCopy.VerifyImages[i].Attestations {
+ ruleCopy.VerifyImages[i].Attestations[j].Conditions = nil
+ }
}
var err error
ruleCopy, err = variables.SubstituteAllInRule(logger, ctx, ruleCopy)
@@ -120,8 +122,10 @@ func substituteVariables(rule kyvernov1.Rule, ctx enginecontext.EvalInterface, l
return nil, err
}
// replace attestations
- for i := range rule.VerifyImages {
- ruleCopy.VerifyImages[i].Attestations = rule.VerifyImages[i].Attestations
+ for i := range ruleCopy.VerifyImages {
+ for j := range ruleCopy.VerifyImages[i].Attestations {
+ ruleCopy.VerifyImages[i].Attestations[j].Conditions = rule.VerifyImages[i].Attestations[j].Conditions
+ }
}
return &ruleCopy, nil
}
diff --git a/pkg/engine/internal/imageverifier.go b/pkg/engine/internal/imageverifier.go
index f8a9bb21d8..ffd5601c6d 100644
--- a/pkg/engine/internal/imageverifier.go
+++ b/pkg/engine/internal/imageverifier.go
@@ -15,7 +15,7 @@ import (
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/variables"
"github.com/kyverno/kyverno/pkg/images"
- "github.com/kyverno/kyverno/pkg/notaryv2"
+ "github.com/kyverno/kyverno/pkg/notary"
"github.com/kyverno/kyverno/pkg/registryclient"
apiutils "github.com/kyverno/kyverno/pkg/utils/api"
"github.com/kyverno/kyverno/pkg/utils/jsonpointer"
@@ -138,7 +138,7 @@ func buildStatementMap(statements []map[string]interface{}) (map[string][]map[st
results := map[string][]map[string]interface{}{}
var predicateTypes []string
for _, s := range statements {
- predicateType := s["predicateType"].(string)
+ predicateType := s["type"].(string)
if results[predicateType] != nil {
results[predicateType] = append(results[predicateType], s)
} else {
@@ -249,6 +249,11 @@ func (iv *ImageVerifier) verifyImage(
return nil, ""
}
image := imageInfo.String()
+ for _, att := range imageVerify.Attestations {
+ if att.Type == "" && att.PredicateType != "" {
+ att.Type = att.PredicateType
+ }
+ }
iv.logger.V(2).Info("verifying image signatures", "image", image, "attestors", len(imageVerify.Attestors), "attestations", len(imageVerify.Attestations))
if err := iv.policyContext.JSONContext().AddImageInfo(imageInfo, cfg); err != nil {
iv.logger.Error(err, "failed to add image to context")
@@ -319,8 +324,13 @@ func (iv *ImageVerifier) verifyAttestations(
var attestationError error
path := fmt.Sprintf(".attestations[%d]", i)
- if attestation.PredicateType == "" {
- return engineapi.RuleFail(iv.rule.Name, engineapi.ImageVerify, path+": missing predicateType"), ""
+ iv.logger.V(2).Info(fmt.Sprintf("attestation %+v", attestation))
+ if attestation.Type == "" && attestation.PredicateType == "" {
+ return engineapi.RuleFail(iv.rule.Name, engineapi.ImageVerify, path+": missing type"), ""
+ }
+
+ if attestation.Type == "" && attestation.PredicateType != "" {
+ attestation.Type = attestation.PredicateType
}
if len(attestation.Attestors) == 0 {
@@ -366,7 +376,7 @@ func (iv *ImageVerifier) verifyAttestations(
}
}
- iv.logger.V(4).Info("attestation checks passed", "path", path, "image", imageInfo.String(), "predicateType", attestation.PredicateType)
+ iv.logger.V(4).Info("attestation checks passed", "path", path, "image", imageInfo.String(), "type", attestation.Type)
}
msg := fmt.Sprintf("verified image attestations for %s", image)
@@ -432,8 +442,8 @@ func (iv *ImageVerifier) buildVerifier(
attestation *kyvernov1.Attestation,
) (images.ImageVerifier, *images.Options, string) {
switch imageVerify.Type {
- case kyvernov1.NotaryV2:
- return iv.buildNotaryV2Verifier(attestor, imageVerify, image)
+ case kyvernov1.Notary:
+ return iv.buildNotaryVerifier(attestor, imageVerify, image, attestation)
default:
return iv.buildCosignVerifier(attestor, imageVerify, image, attestation)
}
@@ -463,6 +473,11 @@ func (iv *ImageVerifier) buildCosignVerifier(
if attestation != nil {
opts.PredicateType = attestation.PredicateType
+ opts.Type = attestation.Type
+ if attestation.PredicateType != "" && attestation.Type == "" {
+ iv.logger.Info("predicate type has been deprecated, please use type instead")
+ opts.Type = attestation.PredicateType
+ }
opts.FetchAttestations = true
}
@@ -509,10 +524,11 @@ func (iv *ImageVerifier) buildCosignVerifier(
return cosign.NewVerifier(), opts, path
}
-func (iv *ImageVerifier) buildNotaryV2Verifier(
+func (iv *ImageVerifier) buildNotaryVerifier(
attestor kyvernov1.Attestor,
imageVerify kyvernov1.ImageVerification,
image string,
+ attestation *kyvernov1.Attestation,
) (images.ImageVerifier, *images.Options, string) {
path := ""
opts := &images.Options{
@@ -522,20 +538,38 @@ func (iv *ImageVerifier) buildNotaryV2Verifier(
RegistryClient: iv.rclient,
}
- return notaryv2.NewVerifier(), opts, path
+ if attestation != nil {
+ opts.Type = attestation.Type
+ opts.PredicateType = attestation.PredicateType
+ if attestation.PredicateType != "" && attestation.Type == "" {
+ iv.logger.Info("predicate type has been deprecated, please use type instead")
+ opts.Type = attestation.PredicateType
+ }
+ opts.FetchAttestations = true
+ }
+
+ if attestor.Repository != "" {
+ opts.Repository = attestor.Repository
+ }
+
+ if attestor.Annotations != nil {
+ opts.Annotations = attestor.Annotations
+ }
+
+ return notary.NewVerifier(), opts, path
}
func (iv *ImageVerifier) verifyAttestation(statements []map[string]interface{}, attestation kyvernov1.Attestation, imageInfo apiutils.ImageInfo) error {
- if attestation.PredicateType == "" {
- return fmt.Errorf("a predicateType is required")
+ if attestation.Type == "" && attestation.PredicateType == "" {
+ return fmt.Errorf("a type is required")
}
image := imageInfo.String()
statementsByPredicate, types := buildStatementMap(statements)
iv.logger.V(4).Info("checking attestations", "predicates", types, "image", image)
- statements = statementsByPredicate[attestation.PredicateType]
+ statements = statementsByPredicate[attestation.Type]
if statements == nil {
- iv.logger.Info("no attestations found for predicate", "type", attestation.PredicateType, "predicates", types, "image", imageInfo.String())
- return fmt.Errorf("attestions not found for predicate type %s", attestation.PredicateType)
+ iv.logger.Info("no attestations found for predicate", "type", attestation.Type, "predicates", types, "image", imageInfo.String())
+ return fmt.Errorf("attestions not found for predicate type %s", attestation.Type)
}
for _, s := range statements {
iv.logger.Info("checking attestation", "predicates", types, "image", imageInfo.String())
@@ -544,7 +578,7 @@ func (iv *ImageVerifier) verifyAttestation(statements []map[string]interface{},
return fmt.Errorf("failed to check attestations: %w", err)
}
if !val {
- return fmt.Errorf("attestation checks failed for %s and predicate %s: %s", imageInfo.String(), attestation.PredicateType, msg)
+ return fmt.Errorf("attestation checks failed for %s and predicate %s: %s", imageInfo.String(), attestation.Type, msg)
}
}
return nil
diff --git a/pkg/images/verifier.go b/pkg/images/verifier.go
index def5d9ecc1..51874e1c36 100644
--- a/pkg/images/verifier.go
+++ b/pkg/images/verifier.go
@@ -30,6 +30,7 @@ type Options struct {
RekorURL string
SignatureAlgorithm string
PredicateType string
+ Type string
Identities string
}
diff --git a/pkg/notary/notary.go b/pkg/notary/notary.go
new file mode 100644
index 0000000000..993434ef75
--- /dev/null
+++ b/pkg/notary/notary.go
@@ -0,0 +1,348 @@
+package notary
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "github.com/go-logr/logr"
+ "github.com/google/go-containerregistry/pkg/crane"
+ "github.com/google/go-containerregistry/pkg/name"
+ v1 "github.com/google/go-containerregistry/pkg/v1"
+ "github.com/google/go-containerregistry/pkg/v1/remote"
+ "github.com/kyverno/kyverno/pkg/images"
+ "github.com/kyverno/kyverno/pkg/logging"
+ _ "github.com/notaryproject/notation-core-go/signature/cose"
+ _ "github.com/notaryproject/notation-core-go/signature/jws"
+ "github.com/notaryproject/notation-go"
+ "github.com/notaryproject/notation-go/verifier"
+ "github.com/notaryproject/notation-go/verifier/trustpolicy"
+ "github.com/opencontainers/go-digest"
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+ "github.com/sigstore/sigstore/pkg/cryptoutils"
+ "go.uber.org/multierr"
+)
+
+func NewVerifier() images.ImageVerifier {
+ return ¬aryVerifier{
+ log: logging.WithName("Notary"),
+ }
+}
+
+type notaryVerifier struct {
+ log logr.Logger
+}
+
+func (v *notaryVerifier) VerifySignature(ctx context.Context, opts images.Options) (*images.Response, error) {
+ v.log.V(2).Info("verifying image", "reference", opts.ImageRef)
+
+ certsPEM := combineCerts(opts)
+ certs, err := cryptoutils.LoadCertificatesFromPEM(bytes.NewReader([]byte(certsPEM)))
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to parse certificates")
+ }
+
+ trustStore := NewTrustStore("kyverno", certs)
+ policyDoc := v.buildPolicy()
+ notationVerifier, err := verifier.New(policyDoc, trustStore, nil)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to created verifier")
+ }
+
+ v.log.V(4).Info("creating notation repo", "reference", opts.ImageRef)
+ parsedRef, err := parseReferenceCrane(ctx, opts.ImageRef, opts.RegistryClient)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to parse image reference: %s", opts.ImageRef)
+ }
+ v.log.V(4).Info("created parsedRef", "reference", opts.ImageRef)
+
+ ref := parsedRef.Ref.Name()
+ remoteVerifyOptions := notation.RemoteVerifyOptions{
+ ArtifactReference: ref,
+ MaxSignatureAttempts: 10,
+ }
+
+ targetDesc, outcomes, err := notation.Verify(context.TODO(), notationVerifier, parsedRef.Repo, remoteVerifyOptions)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to verify %s", ref)
+ }
+
+ if err := v.verifyOutcomes(outcomes); err != nil {
+ return nil, err
+ }
+
+ v.log.V(2).Info("verified image", "type", targetDesc.MediaType, "digest", targetDesc.Digest, "size", targetDesc.Size)
+
+ resp := &images.Response{
+ Digest: targetDesc.Digest.String(),
+ Statements: nil,
+ }
+
+ return resp, nil
+}
+
+func combineCerts(opts images.Options) string {
+ certs := opts.Cert
+ if opts.CertChain != "" {
+ if certs != "" {
+ certs = certs + "\n"
+ }
+
+ certs = certs + opts.CertChain
+ }
+
+ return certs
+}
+
+func (v *notaryVerifier) buildPolicy() *trustpolicy.Document {
+ return &trustpolicy.Document{
+ Version: "1.0",
+ TrustPolicies: []trustpolicy.TrustPolicy{
+ {
+ Name: "kyverno",
+ RegistryScopes: []string{"*"},
+ SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: trustpolicy.LevelStrict.Name},
+ TrustStores: []string{"ca:kyverno"},
+ TrustedIdentities: []string{"*"},
+ },
+ },
+ }
+}
+
+func (v *notaryVerifier) verifyOutcomes(outcomes []*notation.VerificationOutcome) error {
+ var errs []error
+ for _, outcome := range outcomes {
+ if outcome.Error != nil {
+ errs = append(errs, outcome.Error)
+ continue
+ }
+
+ content := outcome.EnvelopeContent.Payload.Content
+ contentType := outcome.EnvelopeContent.Payload.ContentType
+
+ v.log.V(2).Info("content", "type", contentType, "data", content)
+ }
+
+ return multierr.Combine(errs...)
+}
+
+func (v *notaryVerifier) FetchAttestations(ctx context.Context, opts images.Options) (*images.Response, error) {
+ v.log.V(2).Info("fetching attestations", "reference", opts.ImageRef, "opts", opts)
+
+ ref, err := name.ParseReference(opts.ImageRef)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to parse image reference: %s", opts.ImageRef)
+ }
+ authenticator, err := getAuthenticator(ctx, opts.ImageRef, opts.RegistryClient)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to parse authenticator: %s", opts.ImageRef)
+ }
+ craneOpts := crane.WithAuth(*authenticator)
+
+ remoteOpts, err := getRemoteOpts(*authenticator)
+ if err != nil {
+ return nil, err
+ }
+
+ v.log.V(4).Info("client setup done", "repo", ref)
+
+ repoDesc, err := crane.Head(opts.ImageRef, craneOpts)
+ if err != nil {
+ return nil, err
+ }
+ v.log.V(4).Info("fetched repository", "repoDesc", repoDesc)
+
+ referrers, err := remote.Referrers(ref.Context().Digest(repoDesc.Digest.String()), remoteOpts...)
+ if err != nil {
+ return nil, err
+ }
+ referrersDescs, err := referrers.IndexManifest()
+ if err != nil {
+ return nil, err
+ }
+
+ v.log.V(4).Info("fetched referrers", "referrers", referrersDescs)
+
+ var statements []map[string]interface{}
+
+ for _, referrer := range referrersDescs.Manifests {
+ match, _, err := matchArtifactType(referrer, opts.Type)
+ if err != nil {
+ return nil, err
+ }
+
+ if !match {
+ v.log.V(6).Info("type doesn't match, continue", "expected", opts.Type, "received", referrer.ArtifactType)
+ continue
+ }
+
+ targetDesc, err := verifyAttestators(ctx, v, ref, opts, referrer)
+ if err != nil {
+ msg := err.Error()
+ v.log.V(4).Info(msg, "failed to verify referrer %s", targetDesc.Digest.String())
+ return nil, err
+ }
+
+ v.log.V(4).Info("extracting statements", "desc", referrer, "repo", ref)
+ statements, err = extractStatements(ctx, ref, referrer, craneOpts)
+ if err != nil {
+ msg := err.Error()
+ v.log.V(4).Info("failed to extract statements %s", "err", msg)
+ return nil, err
+ }
+
+ v.log.V(4).Info("verified attestators", "digest", targetDesc.Digest.String())
+
+ if len(statements) == 0 {
+ return nil, fmt.Errorf("failed to fetch attestations")
+ }
+ v.log.V(6).Info("sending response")
+ return &images.Response{Digest: repoDesc.Digest.String(), Statements: statements}, nil
+ }
+
+ return nil, fmt.Errorf("failed to fetch attestations %s", err)
+}
+
+func verifyAttestators(ctx context.Context, v *notaryVerifier, ref name.Reference, opts images.Options, desc v1.Descriptor) (ocispec.Descriptor, error) {
+ v.log.V(2).Info("verifying attestations", "reference", opts.ImageRef, "opts", opts)
+ if opts.Cert == "" && opts.CertChain == "" {
+ // skips the checks when no attestor is provided
+ v1Desc := ocispec.Descriptor{
+ MediaType: string(desc.MediaType),
+ Size: desc.Size,
+ Digest: digest.Digest(desc.Digest.String()),
+ URLs: desc.URLs,
+ Annotations: desc.Annotations,
+ Data: desc.Data,
+ }
+ return v1Desc, nil
+ }
+ certsPEM := combineCerts(opts)
+ certs, err := cryptoutils.LoadCertificatesFromPEM(bytes.NewReader([]byte(certsPEM)))
+ if err != nil {
+ v.log.V(4).Info("failed to parse certificates", "err", err)
+ return ocispec.Descriptor{}, errors.Wrapf(err, "failed to parse certificates")
+ }
+
+ v.log.V(4).Info("parsed certificates")
+ trustStore := NewTrustStore("kyverno", certs)
+ policyDoc := v.buildPolicy()
+ notationVerifier, err := verifier.New(policyDoc, trustStore, nil)
+ if err != nil {
+ v.log.V(4).Info("failed to created verifier", "err", err)
+ return ocispec.Descriptor{}, errors.Wrapf(err, "failed to created verifier")
+ }
+
+ v.log.V(4).Info("created verifier")
+ reference := ref.Context().RegistryStr() + "/" + ref.Context().RepositoryStr() + "@" + desc.Digest.String()
+ parsedRef, err := parseReferenceCrane(ctx, reference, opts.RegistryClient)
+ if err != nil {
+ return ocispec.Descriptor{}, errors.Wrapf(err, "failed to parse image reference: %s", opts.ImageRef)
+ }
+ v.log.V(4).Info("created notation repo", "reference", opts.ImageRef)
+
+ remoteVerifyOptions := notation.RemoteVerifyOptions{
+ ArtifactReference: reference,
+ MaxSignatureAttempts: 10,
+ }
+
+ v.log.V(4).Info("verification started")
+ targetDesc, outcomes, err := notation.Verify(context.TODO(), notationVerifier, parsedRef.Repo, remoteVerifyOptions)
+ if err != nil {
+ v.log.V(4).Info("failed to vefify attestator", "remoteVerifyOptions", remoteVerifyOptions, "repo", parsedRef.Repo)
+ return targetDesc, err
+ }
+ if err := v.verifyOutcomes(outcomes); err != nil {
+ return targetDesc, err
+ }
+
+ if targetDesc.Digest.String() != desc.Digest.String() {
+ v.log.V(4).Info("digest mismatch", "expected", desc.Digest.String(), "found", targetDesc.Digest.String())
+ return targetDesc, errors.Errorf("digest mismatch")
+ }
+ v.log.V(2).Info("attestator verified", "desc", targetDesc.Digest.String())
+
+ return targetDesc, nil
+}
+
+func extractStatements(ctx context.Context, repoRef name.Reference, desc v1.Descriptor, craneOpts ...crane.Option) ([]map[string]interface{}, error) {
+ statements := make([]map[string]interface{}, 0)
+ data, err := extractStatement(ctx, repoRef, desc, craneOpts...)
+ if err != nil {
+ return nil, err
+ }
+ statements = append(statements, data)
+
+ if len(statements) == 0 {
+ return nil, fmt.Errorf("no statements found")
+ }
+ return statements, nil
+}
+
+func extractStatement(ctx context.Context, repoRef name.Reference, desc v1.Descriptor, craneOpts ...crane.Option) (map[string]interface{}, error) {
+ refStr := repoRef.Context().RegistryStr() + "/" + repoRef.Context().RepositoryStr() + "@" + desc.Digest.String()
+ ref, err := name.ParseReference(refStr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to parse image reference: %s", refStr)
+ }
+
+ manifestBytes, err := crane.Manifest(refStr, craneOpts...)
+ if err != nil {
+ return nil, fmt.Errorf("error in fetching statement: %w", err)
+ }
+ var manifest ocispec.Manifest
+ if err := json.Unmarshal(manifestBytes, &manifest); err != nil {
+ return nil, err
+ }
+
+ if len(manifest.Layers) == 0 {
+ return nil, fmt.Errorf("no predicate found: %+v", manifest)
+ }
+ if len(manifest.Layers) > 1 {
+ return nil, fmt.Errorf("multiple layers in predicate not supported: %+v", manifest)
+ }
+ predicateDesc := manifest.Layers[0]
+ predicateRef := ref.Context().RegistryStr() + "/" + ref.Context().RepositoryStr() + "@" + predicateDesc.Digest.String()
+
+ layer, err := crane.PullLayer(predicateRef, craneOpts...)
+ if err != nil {
+ return nil, err
+ }
+ ioPredicate, err := layer.Uncompressed()
+ if err != nil {
+ return nil, err
+ }
+ predicateBytes := new(bytes.Buffer)
+ _, err = predicateBytes.ReadFrom(ioPredicate)
+ if err != nil {
+ return nil, err
+ }
+
+ predicate := make(map[string]interface{})
+ if err := json.Unmarshal(predicateBytes.Bytes(), &predicate); err != nil {
+ return nil, err
+ }
+ data := make(map[string]interface{})
+ if err := json.Unmarshal(manifestBytes, &data); err != nil {
+ return nil, err
+ }
+
+ if data["type"] == nil {
+ data["type"] = desc.ArtifactType
+ }
+ if data["predicate"] == nil {
+ data["predicate"] = predicate
+ }
+ return data, nil
+}
+
+func matchArtifactType(ref v1.Descriptor, expectedArtifactType string) (bool, string, error) {
+ if expectedArtifactType != "" {
+ if ref.ArtifactType == expectedArtifactType {
+ return true, ref.ArtifactType, nil
+ }
+ }
+ return false, "", nil
+}
diff --git a/pkg/notary/notary_test.go b/pkg/notary/notary_test.go
new file mode 100644
index 0000000000..dc16eea92e
--- /dev/null
+++ b/pkg/notary/notary_test.go
@@ -0,0 +1,33 @@
+package notary
+
+import (
+ "context"
+ "testing"
+
+ "github.com/google/go-containerregistry/pkg/crane"
+ "github.com/google/go-containerregistry/pkg/name"
+ "github.com/google/go-containerregistry/pkg/v1/remote"
+ "gotest.tools/assert"
+)
+
+func TestExtractStatements(t *testing.T) {
+ imageRef := "jimnotarytest.azurecr.io/jim/net-monitor:v1"
+ ref, err := name.ParseReference(imageRef)
+ assert.NilError(t, err)
+ repoDesc, err := crane.Head(imageRef)
+ assert.NilError(t, err)
+ referrers, err := remote.Referrers(ref.Context().Digest(repoDesc.Digest.String()))
+ assert.NilError(t, err)
+ referrersDescs, err := referrers.IndexManifest()
+ assert.NilError(t, err)
+
+ for _, referrer := range referrersDescs.Manifests {
+ if referrer.ArtifactType == "application/vnd.cncf.notary.signature" {
+ statements, err := extractStatements(context.Background(), ref, referrer)
+ assert.NilError(t, err)
+ assert.Assert(t, len(statements) == 1)
+ assert.Assert(t, statements[0]["type"] == referrer.ArtifactType)
+ assert.Assert(t, statements[0]["mediaType"] == string(referrer.MediaType))
+ }
+ }
+}
diff --git a/pkg/notary/registry.go b/pkg/notary/registry.go
new file mode 100644
index 0000000000..6c0227dc10
--- /dev/null
+++ b/pkg/notary/registry.go
@@ -0,0 +1,133 @@
+package notary
+
+import (
+ "context"
+ "strings"
+
+ "github.com/google/go-containerregistry/pkg/authn"
+ "github.com/google/go-containerregistry/pkg/crane"
+ "github.com/google/go-containerregistry/pkg/name"
+ gcrremote "github.com/google/go-containerregistry/pkg/v1/remote"
+ "github.com/kyverno/kyverno/pkg/registryclient"
+ notationregistry "github.com/notaryproject/notation-go/registry"
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+type parsedReference struct {
+ Repo notationregistry.Repository
+ CraneOpts crane.Option
+ RemoteOpts []gcrremote.Option
+ Ref name.Reference
+ Desc ocispec.Descriptor
+}
+
+func parseReferenceCrane(ctx context.Context, ref string, registryClient registryclient.Client) (*parsedReference, error) {
+ nameRef, err := name.ParseReference(ref)
+ if err != nil {
+ return nil, err
+ }
+
+ authenticator, err := getAuthenticator(ctx, ref, registryClient)
+ if err != nil {
+ return nil, err
+ }
+
+ craneOpts := crane.WithAuth(*authenticator)
+ remoteOpts, err := getRemoteOpts(*authenticator)
+ if err != nil {
+ return nil, err
+ }
+
+ desc, err := crane.Head(ref, craneOpts)
+ if err != nil {
+ return nil, err
+ }
+
+ if !isDigestReference(ref) {
+ nameRef, err = name.ParseReference(GetReferenceFromDescriptor(v1ToOciSpecDescriptor(*desc), nameRef))
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ repository := NewRepository(craneOpts, remoteOpts, nameRef)
+ err = resolveDigestCrane(repository, craneOpts, remoteOpts, nameRef)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to resolve digest")
+ }
+
+ return &parsedReference{
+ Repo: repository,
+ CraneOpts: craneOpts,
+ RemoteOpts: remoteOpts,
+ Ref: nameRef,
+ Desc: v1ToOciSpecDescriptor(*desc),
+ }, nil
+}
+
+type imageResource struct {
+ ref name.Reference
+}
+
+func (ir *imageResource) String() string {
+ return ir.ref.Name()
+}
+
+func (ir *imageResource) RegistryStr() string {
+ return ir.ref.Context().RegistryStr()
+}
+
+func getAuthenticator(ctx context.Context, ref string, registryClient registryclient.Client) (*authn.Authenticator, error) {
+ parsedRef, err := name.ParseReference(ref)
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to parse registry reference %s", ref)
+ }
+
+ if err := registryClient.RefreshKeychainPullSecrets(ctx); err != nil {
+ return nil, errors.Wrapf(err, "failed to refresh image pull secrets")
+ }
+
+ authn, err := registryClient.Keychain().Resolve(&imageResource{parsedRef})
+ if err != nil {
+ return nil, errors.Wrapf(err, "failed to resolve auth for %s", parsedRef.String())
+ }
+ return &authn, nil
+}
+
+func isDigestReference(reference string) bool {
+ parts := strings.SplitN(reference, "/", 2)
+ if len(parts) == 1 {
+ return false
+ }
+
+ index := strings.Index(parts[1], "@")
+ return index != -1
+}
+
+func getRemoteOpts(authenticator authn.Authenticator) ([]gcrremote.Option, error) {
+ remoteOpts := []gcrremote.Option{}
+ remoteOpts = append(remoteOpts, gcrremote.WithAuth(authenticator))
+
+ pusher, err := gcrremote.NewPusher(remoteOpts...)
+ if err != nil {
+ return nil, err
+ }
+ remoteOpts = append(remoteOpts, gcrremote.Reuse(pusher))
+
+ puller, err := gcrremote.NewPuller(remoteOpts...)
+ if err != nil {
+ return nil, err
+ }
+ remoteOpts = append(remoteOpts, gcrremote.Reuse(puller))
+
+ return remoteOpts, nil
+}
+
+func resolveDigestCrane(repo notationregistry.Repository, craneOpts crane.Option, remoteOpts []gcrremote.Option, ref name.Reference) error {
+ _, err := repo.Resolve(context.Background(), ref.Name())
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/pkg/notary/repository.go b/pkg/notary/repository.go
new file mode 100644
index 0000000000..f8115f29d1
--- /dev/null
+++ b/pkg/notary/repository.go
@@ -0,0 +1,127 @@
+package notary
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "github.com/google/go-containerregistry/pkg/crane"
+ "github.com/google/go-containerregistry/pkg/name"
+ v1 "github.com/google/go-containerregistry/pkg/v1"
+ "github.com/google/go-containerregistry/pkg/v1/remote"
+ notationregistry "github.com/notaryproject/notation-go/registry"
+ "github.com/opencontainers/go-digest"
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+type repositoryClient struct {
+ ref name.Reference
+ craneOpts crane.Option
+ remoteOpts []remote.Option
+}
+
+func NewRepository(craneOpts crane.Option, remoteOpts []remote.Option, ref name.Reference) notationregistry.Repository {
+ return &repositoryClient{
+ craneOpts: craneOpts,
+ remoteOpts: remoteOpts,
+ ref: ref,
+ }
+}
+
+func (c *repositoryClient) Resolve(ctx context.Context, reference string) (ocispec.Descriptor, error) {
+ head, err := crane.Head(reference)
+ if err != nil {
+ return ocispec.Descriptor{}, nil
+ }
+ descriptor := v1ToOciSpecDescriptor(*head)
+ return descriptor, nil
+}
+
+func (c *repositoryClient) ListSignatures(ctx context.Context, desc ocispec.Descriptor, fn func(signatureManifests []ocispec.Descriptor) error) error {
+ referrers, err := remote.Referrers(c.ref.Context().Digest(desc.Digest.String()), c.remoteOpts...)
+ if err != nil {
+ return err
+ }
+
+ referrersDescs, err := referrers.IndexManifest()
+ if err != nil {
+ return err
+ }
+
+ descList := []ocispec.Descriptor{}
+ for _, d := range referrersDescs.Manifests {
+ if d.ArtifactType == notationregistry.ArtifactTypeNotation {
+ descList = append(descList, v1ToOciSpecDescriptor(d))
+ }
+ }
+
+ return fn(descList)
+}
+
+func (c *repositoryClient) FetchSignatureBlob(ctx context.Context, desc ocispec.Descriptor) ([]byte, ocispec.Descriptor, error) {
+ manifestRef := c.getReferenceFromDescriptor(desc)
+
+ manifestBytes, err := crane.Manifest(manifestRef)
+ if err != nil {
+ return nil, ocispec.Descriptor{}, err
+ }
+
+ var manifest ocispec.Manifest
+ if err := json.Unmarshal(manifestBytes, &manifest); err != nil {
+ return nil, ocispec.Descriptor{}, err
+ }
+ manifestDesc := manifest.Layers[0]
+
+ signatureBlobRef := c.getReferenceFromDescriptor(manifestDesc)
+
+ signatureBlobLayer, err := crane.PullLayer(signatureBlobRef)
+ if err != nil {
+ panic(err)
+ }
+
+ io, err := signatureBlobLayer.Uncompressed()
+ if err != nil {
+ panic(err)
+ }
+ SigBlobBuf := new(bytes.Buffer)
+
+ _, err = SigBlobBuf.ReadFrom(io)
+ if err != nil {
+ panic(err)
+ }
+ return SigBlobBuf.Bytes(), manifestDesc, nil
+}
+
+func (c *repositoryClient) PushSignature(ctx context.Context, mediaType string, blob []byte, subject ocispec.Descriptor, annotations map[string]string) (blobDesc, manifestDesc ocispec.Descriptor, err error) {
+ return ocispec.Descriptor{}, ocispec.Descriptor{}, fmt.Errorf("push signature is not implemented")
+}
+
+func v1ToOciSpecDescriptor(v1desc v1.Descriptor) ocispec.Descriptor {
+ ociDesc := ocispec.Descriptor{
+ MediaType: string(v1desc.MediaType),
+ Digest: digest.Digest(v1desc.Digest.String()),
+ Size: v1desc.Size,
+ URLs: v1desc.URLs,
+ Annotations: v1desc.Annotations,
+ Data: v1desc.Data,
+
+ ArtifactType: v1desc.ArtifactType,
+ }
+ if v1desc.Platform != nil {
+ ociDesc.Platform = &ocispec.Platform{
+ Architecture: v1desc.Platform.Architecture,
+ OS: v1desc.Platform.OS,
+ OSVersion: v1desc.Platform.OSVersion,
+ }
+ }
+ return ociDesc
+}
+
+func (c *repositoryClient) getReferenceFromDescriptor(desc ocispec.Descriptor) string {
+ return GetReferenceFromDescriptor(desc, c.ref)
+}
+
+func GetReferenceFromDescriptor(desc ocispec.Descriptor, ref name.Reference) string {
+ return ref.Context().RegistryStr() + "/" + ref.Context().RepositoryStr() + "@" + desc.Digest.String()
+}
diff --git a/pkg/notaryv2/truststore.go b/pkg/notary/truststore.go
similarity index 97%
rename from pkg/notaryv2/truststore.go
rename to pkg/notary/truststore.go
index 899d70574e..a5c561822a 100644
--- a/pkg/notaryv2/truststore.go
+++ b/pkg/notary/truststore.go
@@ -1,4 +1,4 @@
-package notaryv2
+package notary
import (
"context"
diff --git a/pkg/notaryv2/notaryv2.go b/pkg/notaryv2/notaryv2.go
deleted file mode 100644
index 414efdd172..0000000000
--- a/pkg/notaryv2/notaryv2.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package notaryv2
-
-import (
- "bytes"
- "context"
-
- "github.com/go-logr/logr"
- "github.com/kyverno/kyverno/pkg/images"
- "github.com/kyverno/kyverno/pkg/logging"
- _ "github.com/notaryproject/notation-core-go/signature/cose"
- _ "github.com/notaryproject/notation-core-go/signature/jws"
- "github.com/notaryproject/notation-go"
- "github.com/notaryproject/notation-go/verifier"
- "github.com/notaryproject/notation-go/verifier/trustpolicy"
- "github.com/pkg/errors"
- "github.com/sigstore/sigstore/pkg/cryptoutils"
- "go.uber.org/multierr"
-)
-
-func NewVerifier() images.ImageVerifier {
- return ¬aryV2Verifier{
- log: logging.WithName("NotaryV2"),
- }
-}
-
-type notaryV2Verifier struct {
- log logr.Logger
-}
-
-func (v *notaryV2Verifier) VerifySignature(ctx context.Context, opts images.Options) (*images.Response, error) {
- v.log.V(2).Info("verifying image", "reference", opts.ImageRef)
-
- certsPEM := combineCerts(opts)
- certs, err := cryptoutils.LoadCertificatesFromPEM(bytes.NewReader([]byte(certsPEM)))
- if err != nil {
- return nil, errors.Wrapf(err, "failed to parse certificates")
- }
-
- trustStore := NewTrustStore("kyverno", certs)
- policyDoc := v.buildPolicy()
- notationVerifier, err := verifier.New(policyDoc, trustStore, nil)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to created verifier")
- }
-
- repo, parsedRef, err := parseReference(ctx, opts.ImageRef, opts.RegistryClient)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to parse image reference: %s", opts.ImageRef)
- }
-
- digest, err := parsedRef.Digest()
- if err != nil {
- return nil, errors.Wrapf(err, "failed to fetch digest")
- }
-
- ref := parsedRef.String()
- remoteVerifyOptions := notation.RemoteVerifyOptions{
- ArtifactReference: ref,
- MaxSignatureAttempts: 10,
- }
-
- targetDesc, outcomes, err := notation.Verify(context.TODO(), notationVerifier, repo, remoteVerifyOptions)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to verify %s", ref)
- }
-
- if err := v.verifyOutcomes(outcomes); err != nil {
- return nil, err
- }
-
- if targetDesc.Digest != digest {
- return nil, errors.Errorf("digest mismatch")
- }
-
- v.log.V(2).Info("verified image", "type", targetDesc.MediaType, "digest", targetDesc.Digest, "size", targetDesc.Size)
-
- resp := &images.Response{
- Digest: targetDesc.Digest.String(),
- Statements: nil,
- }
-
- return resp, nil
-}
-
-func combineCerts(opts images.Options) string {
- certs := opts.Cert
- if opts.CertChain != "" {
- if certs != "" {
- certs = certs + "\n"
- }
-
- certs = certs + opts.CertChain
- }
-
- return certs
-}
-
-func (v *notaryV2Verifier) buildPolicy() *trustpolicy.Document {
- return &trustpolicy.Document{
- Version: "1.0",
- TrustPolicies: []trustpolicy.TrustPolicy{
- {
- Name: "kyverno",
- RegistryScopes: []string{"*"},
- SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: trustpolicy.LevelStrict.Name},
- TrustStores: []string{"ca:kyverno"},
- TrustedIdentities: []string{"*"},
- },
- },
- }
-}
-
-func (v *notaryV2Verifier) verifyOutcomes(outcomes []*notation.VerificationOutcome) error {
- var errs []error
- for _, outcome := range outcomes {
- if outcome.Error != nil {
- errs = append(errs, outcome.Error)
- continue
- }
-
- content := outcome.EnvelopeContent.Payload.Content
- contentType := outcome.EnvelopeContent.Payload.ContentType
-
- v.log.Info("content", "type", contentType, "data", content)
- }
-
- return multierr.Combine(errs...)
-}
-
-func (v *notaryV2Verifier) FetchAttestations(ctx context.Context, opts images.Options) (*images.Response, error) {
- return nil, errors.Errorf("not implemented")
-}
diff --git a/pkg/notaryv2/registry.go b/pkg/notaryv2/registry.go
deleted file mode 100644
index 71e63b94ac..0000000000
--- a/pkg/notaryv2/registry.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package notaryv2
-
-import (
- "context"
- "strings"
-
- "github.com/kyverno/kyverno/pkg/registryclient"
- notationregistry "github.com/notaryproject/notation-go/registry"
- ocispec "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
- "oras.land/oras-go/v2/registry"
- "oras.land/oras-go/v2/registry/remote"
- "oras.land/oras-go/v2/registry/remote/auth"
-)
-
-func parseReference(ctx context.Context, ref string, registryClient registryclient.Client) (notationregistry.Repository, registry.Reference, error) {
- parsedRef, err := registry.ParseReference(ref)
- if err != nil {
- return nil, registry.Reference{}, errors.Wrapf(err, "failed to parse registry reference %s", ref)
- }
-
- authClient, plainHTTP, err := getAuthClient(ctx, parsedRef, registryClient)
- if err != nil {
- return nil, registry.Reference{}, err
- }
-
- repo, err := remote.NewRepository(ref)
- if err != nil {
- return nil, registry.Reference{}, errors.Wrapf(err, "failed to initialize repository")
- }
-
- repo.PlainHTTP = plainHTTP
- repo.Client = authClient
- repository := notationregistry.NewRepository(repo)
-
- parsedRef, err = resolveDigest(repository, parsedRef)
- if err != nil {
- return nil, registry.Reference{}, errors.Wrapf(err, "failed to resolve digest")
- }
-
- return repository, parsedRef, nil
-}
-
-type imageResource struct {
- ref registry.Reference
-}
-
-func (ir *imageResource) String() string {
- return ir.ref.String()
-}
-
-func (ir *imageResource) RegistryStr() string {
- return ir.ref.Registry
-}
-
-func getAuthClient(ctx context.Context, ref registry.Reference, rc registryclient.Client) (*auth.Client, bool, error) {
- if err := rc.RefreshKeychainPullSecrets(ctx); err != nil {
- return nil, false, errors.Wrapf(err, "failed to refresh image pull secrets")
- }
-
- authn, err := rc.Keychain().Resolve(&imageResource{ref})
- if err != nil {
- return nil, false, errors.Wrapf(err, "failed to resolve auth for %s", ref.String())
- }
-
- authConfig, err := authn.Authorization()
- if err != nil {
- return nil, false, errors.Wrapf(err, "failed to get auth config for %s", ref.String())
- }
-
- credentials := auth.Credential{
- Username: authConfig.Username,
- Password: authConfig.Password,
- AccessToken: authConfig.IdentityToken,
- RefreshToken: authConfig.RegistryToken,
- }
-
- authClient := &auth.Client{
- Credential: func(ctx context.Context, registry string) (auth.Credential, error) {
- switch registry {
- default:
- return credentials, nil
- }
- },
- Cache: auth.NewCache(),
- ClientID: "notation",
- }
-
- authClient.SetUserAgent("kyverno.io")
- return authClient, false, nil
-}
-
-func resolveDigest(repo notationregistry.Repository, ref registry.Reference) (registry.Reference, error) {
- if isDigestReference(ref.String()) {
- return ref, nil
- }
-
- // Resolve tag reference to digest reference.
- manifestDesc, err := getManifestDescriptorFromReference(repo, ref.String())
- if err != nil {
- return registry.Reference{}, err
- }
-
- ref.Reference = manifestDesc.Digest.String()
- return ref, nil
-}
-
-func isDigestReference(reference string) bool {
- parts := strings.SplitN(reference, "/", 2)
- if len(parts) == 1 {
- return false
- }
-
- index := strings.Index(parts[1], "@")
- return index != -1
-}
-
-func getManifestDescriptorFromReference(repo notationregistry.Repository, reference string) (ocispec.Descriptor, error) {
- ref, err := registry.ParseReference(reference)
- if err != nil {
- return ocispec.Descriptor{}, err
- }
-
- return repo.Resolve(context.Background(), ref.ReferenceOrDefault())
-}
diff --git a/pkg/validation/policy/validate.go b/pkg/validation/policy/validate.go
index f7504198d7..0159c1290f 100644
--- a/pkg/validation/policy/validate.go
+++ b/pkg/validation/policy/validate.go
@@ -364,6 +364,10 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
checkForScaleSubresource(mutationJson, allKinds, &warnings)
checkForStatusSubresource(mutationJson, allKinds, &warnings)
}
+
+ if rule.HasVerifyImages() {
+ checkForDeprecatedFieldsInVerifyImages(rule, &warnings)
+ }
}
if !mock && (spec.SchemaValidation == nil || *spec.SchemaValidation) {
if err := openApiManager.ValidatePolicyMutation(policy); err != nil {
@@ -1301,3 +1305,14 @@ func checkForStatusSubresource(ruleTypeJson []byte, allKinds []string, warnings
*warnings = append(*warnings, msg)
}
}
+
+func checkForDeprecatedFieldsInVerifyImages(rule kyvernov1.Rule, warnings *[]string) {
+ for _, imageVerify := range rule.VerifyImages {
+ for _, attestation := range imageVerify.Attestations {
+ if attestation.PredicateType != "" {
+ msg := fmt.Sprintf("predicateType has been deprecated use 'type: %s' instead of 'prediacteType: %s'", attestation.PredicateType, attestation.PredicateType)
+ *warnings = append(*warnings, msg)
+ }
+ }
+ }
+}
diff --git a/test/conformance/kuttl/flags/standard/emit-events/admission-controller-assert.yaml b/test/conformance/kuttl/flags/standard/emit-events/admission-controller-assert.yaml
index 4ed694ab26..c12a6220a3 100644
--- a/test/conformance/kuttl/flags/standard/emit-events/admission-controller-assert.yaml
+++ b/test/conformance/kuttl/flags/standard/emit-events/admission-controller-assert.yaml
@@ -1,9 +1,8 @@
-apiVersion: kyverno.io/v1
-kind: Policy
+apiVersion: apps/v1
+kind: Deployment
metadata:
name: kyverno-admission-controller
+ namespace: kyverno
status:
- conditions:
- - reason: Succeeded
- status: "True"
- type: Ready
+ readyReplicas: 1
+ updatedReplicas: 1
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/01-policy.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/01-policy.yaml
new file mode 100644
index 0000000000..f3857739b0
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/01-policy.yaml
@@ -0,0 +1,6 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+apply:
+- policy.yaml
+assert:
+- policy-ready.yaml
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/02-resource.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/02-resource.yaml
new file mode 100644
index 0000000000..52ffd92005
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/02-resource.yaml
@@ -0,0 +1,6 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+apply:
+- pod.yaml
+assert:
+- pod-assert.yaml
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/README.md b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/README.md
new file mode 100644
index 0000000000..b246475eda
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/README.md
@@ -0,0 +1,12 @@
+## Description
+
+This test verifies image attestations using notary signatures
+
+## Expected Behavior
+
+This test creates a cluster policy.
+When a pod is created with the image reference and the signature on attestations matches, the pod creation is successful
+
+## Reference Issue(s)
+
+6142
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/pod-assert.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/pod-assert.yaml
new file mode 100644
index 0000000000..d18a0a10e9
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/pod-assert.yaml
@@ -0,0 +1,5 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: test
+ namespace: notary-verify-attestation
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/pod.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/pod.yaml
new file mode 100644
index 0000000000..e16637872d
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/pod.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ creationTimestamp: null
+ labels:
+ run: test
+ name: test
+ namespace: notary-verify-attestation
+spec:
+ containers:
+ - image: ghcr.io/kyverno/test-verify-image:signed
+ name: test
+ resources: {}
+ dnsPolicy: ClusterFirst
+ restartPolicy: Always
+status: {}
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/policy-ready.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/policy-ready.yaml
new file mode 100644
index 0000000000..83c51e7057
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/policy-ready.yaml
@@ -0,0 +1,9 @@
+apiVersion: kyverno.io/v2beta1
+kind: ClusterPolicy
+metadata:
+ name: check-image-attestation
+status:
+ conditions:
+ - reason: Succeeded
+ status: "True"
+ type: Ready
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/policy.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/policy.yaml
new file mode 100644
index 0000000000..25245849de
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-attestation-verification/policy.yaml
@@ -0,0 +1,63 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: notary-verify-attestation
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: keys
+ namespace: notary-verify-attestation
+data:
+ certificate: |-
+ -----BEGIN CERTIFICATE-----
+ MIIDTTCCAjWgAwIBAgIJAPI+zAzn4s0xMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
+ BAYTAlVTMQswCQYDVQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEPMA0GA1UECgwG
+ Tm90YXJ5MQ0wCwYDVQQDDAR0ZXN0MB4XDTIzMDUyMjIxMTUxOFoXDTMzMDUxOTIx
+ MTUxOFowTDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0
+ dGxlMQ8wDQYDVQQKDAZOb3RhcnkxDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3
+ DQEBAQUAA4IBDwAwggEKAoIBAQDNhTwv+QMk7jEHufFfIFlBjn2NiJaYPgL4eBS+
+ b+o37ve5Zn9nzRppV6kGsa161r9s2KkLXmJrojNy6vo9a6g6RtZ3F6xKiWLUmbAL
+ hVTCfYw/2n7xNlVMjyyUpE+7e193PF8HfQrfDFxe2JnX5LHtGe+X9vdvo2l41R6m
+ Iia04DvpMdG4+da2tKPzXIuLUz/FDb6IODO3+qsqQLwEKmmUee+KX+3yw8I6G1y0
+ Vp0mnHfsfutlHeG8gazCDlzEsuD4QJ9BKeRf2Vrb0ywqNLkGCbcCWF2H5Q80Iq/f
+ ETVO9z88R7WheVdEjUB8UrY7ZMLdADM14IPhY2Y+tLaSzEVZAgMBAAGjMjAwMAkG
+ A1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0G
+ CSqGSIb3DQEBCwUAA4IBAQBX7x4Ucre8AIUmXZ5PUK/zUBVOrZZzR1YE8w86J4X9
+ kYeTtlijf9i2LTZMfGuG0dEVFN4ae3CCpBst+ilhIndnoxTyzP+sNy4RCRQ2Y/k8
+ Zq235KIh7uucq96PL0qsF9s2RpTKXxyOGdtp9+HO0Ty5txJE2txtLDUIVPK5WNDF
+ ByCEQNhtHgN6V20b8KU2oLBZ9vyB8V010dQz0NRTDLhkcvJig00535/LUylECYAJ
+ 5/jn6XKt6UYCQJbVNzBg/YPGc1RF4xdsGVDBben/JXpeGEmkdmXPILTKd9tZ5TC0
+ uOKpF5rWAruB5PCIrquamOejpXV9aQA/K2JQDuc0mcKz
+ -----END CERTIFICATE-----
+---
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+ name: check-image-attestation
+spec:
+ validationFailureAction: Enforce
+ webhookTimeoutSeconds: 30
+ failurePolicy: Fail
+ rules:
+ - name: verify-attestation-notary
+ match:
+ any:
+ - resources:
+ kinds:
+ - Pod
+ context:
+ - name: keys
+ configMap:
+ name: keys
+ namespace: notary-verify-attestation
+ verifyImages:
+ - type: Notary
+ imageReferences:
+ - "ghcr.io/kyverno/test-verify-image*"
+ attestations:
+ - type: sbom/cyclone-dx
+ attestors:
+ - entries:
+ - certificates:
+ cert: "{{ keys.data.certificate }}"
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/01-policy.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/01-policy.yaml
new file mode 100644
index 0000000000..f3857739b0
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/01-policy.yaml
@@ -0,0 +1,6 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+apply:
+- policy.yaml
+assert:
+- policy-ready.yaml
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/02-resource.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/02-resource.yaml
new file mode 100644
index 0000000000..52ffd92005
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/02-resource.yaml
@@ -0,0 +1,6 @@
+apiVersion: kuttl.dev/v1beta1
+kind: TestStep
+apply:
+- pod.yaml
+assert:
+- pod-assert.yaml
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/README.md b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/README.md
new file mode 100644
index 0000000000..a87ff91a4a
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/README.md
@@ -0,0 +1,12 @@
+## Description
+
+This test verifies images using notary signatures
+
+## Expected Behavior
+
+This test creates a cluster policy.
+When a pod is created with the image reference and the signature matches, the pod creation is successful
+
+## Reference Issue(s)
+
+6142
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/pod-assert.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/pod-assert.yaml
new file mode 100644
index 0000000000..4bf9852a3c
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/pod-assert.yaml
@@ -0,0 +1,5 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: test
+ namespace: notary-verify-images
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/pod.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/pod.yaml
new file mode 100644
index 0000000000..b5ab8f7959
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/pod.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ creationTimestamp: null
+ labels:
+ run: test
+ name: test
+ namespace: notary-verify-images
+spec:
+ containers:
+ - image: ghcr.io/kyverno/test-verify-image:signed
+ name: test
+ resources: {}
+ dnsPolicy: ClusterFirst
+ restartPolicy: Always
+status: {}
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/policy-ready.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/policy-ready.yaml
new file mode 100644
index 0000000000..b3ad396d26
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/policy-ready.yaml
@@ -0,0 +1,9 @@
+apiVersion: kyverno.io/v2beta1
+kind: ClusterPolicy
+metadata:
+ name: check-image-notary
+status:
+ conditions:
+ - reason: Succeeded
+ status: "True"
+ type: Ready
\ No newline at end of file
diff --git a/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/policy.yaml b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/policy.yaml
new file mode 100644
index 0000000000..05d6d6311c
--- /dev/null
+++ b/test/conformance/kuttl/verifyImages/clusterpolicy/standard/notary-image-verification/policy.yaml
@@ -0,0 +1,62 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: notary-verify-images
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: keys
+ namespace: notary-verify-images
+data:
+ certificate: |-
+ -----BEGIN CERTIFICATE-----
+ MIIDTTCCAjWgAwIBAgIJAPI+zAzn4s0xMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
+ BAYTAlVTMQswCQYDVQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEPMA0GA1UECgwG
+ Tm90YXJ5MQ0wCwYDVQQDDAR0ZXN0MB4XDTIzMDUyMjIxMTUxOFoXDTMzMDUxOTIx
+ MTUxOFowTDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0
+ dGxlMQ8wDQYDVQQKDAZOb3RhcnkxDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3
+ DQEBAQUAA4IBDwAwggEKAoIBAQDNhTwv+QMk7jEHufFfIFlBjn2NiJaYPgL4eBS+
+ b+o37ve5Zn9nzRppV6kGsa161r9s2KkLXmJrojNy6vo9a6g6RtZ3F6xKiWLUmbAL
+ hVTCfYw/2n7xNlVMjyyUpE+7e193PF8HfQrfDFxe2JnX5LHtGe+X9vdvo2l41R6m
+ Iia04DvpMdG4+da2tKPzXIuLUz/FDb6IODO3+qsqQLwEKmmUee+KX+3yw8I6G1y0
+ Vp0mnHfsfutlHeG8gazCDlzEsuD4QJ9BKeRf2Vrb0ywqNLkGCbcCWF2H5Q80Iq/f
+ ETVO9z88R7WheVdEjUB8UrY7ZMLdADM14IPhY2Y+tLaSzEVZAgMBAAGjMjAwMAkG
+ A1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0G
+ CSqGSIb3DQEBCwUAA4IBAQBX7x4Ucre8AIUmXZ5PUK/zUBVOrZZzR1YE8w86J4X9
+ kYeTtlijf9i2LTZMfGuG0dEVFN4ae3CCpBst+ilhIndnoxTyzP+sNy4RCRQ2Y/k8
+ Zq235KIh7uucq96PL0qsF9s2RpTKXxyOGdtp9+HO0Ty5txJE2txtLDUIVPK5WNDF
+ ByCEQNhtHgN6V20b8KU2oLBZ9vyB8V010dQz0NRTDLhkcvJig00535/LUylECYAJ
+ 5/jn6XKt6UYCQJbVNzBg/YPGc1RF4xdsGVDBben/JXpeGEmkdmXPILTKd9tZ5TC0
+ uOKpF5rWAruB5PCIrquamOejpXV9aQA/K2JQDuc0mcKz
+ -----END CERTIFICATE-----
+---
+apiVersion: kyverno.io/v2beta1
+kind: ClusterPolicy
+metadata:
+ name: check-image-notary
+spec:
+ validationFailureAction: Enforce
+ webhookTimeoutSeconds: 30
+ failurePolicy: Fail
+ rules:
+ - name: verify-signature-notary
+ context:
+ - name: keys
+ configMap:
+ name: keys
+ namespace: notary-verify-images
+ match:
+ any:
+ - resources:
+ kinds:
+ - Pod
+ verifyImages:
+ - type: Notary
+ imageReferences:
+ - "ghcr.io/kyverno/test-verify-image*"
+ attestors:
+ - count: 1
+ entries:
+ - certificates:
+ cert: "{{ keys.data.certificate }}"
\ No newline at end of file