1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

feat: add support for sigstore bundle verification (#10567)

* feat: add support for sigstore bundle verification

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* fix: missed change

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* fix: ci

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* fix: linter

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* fix: another linter

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* fix: add size check in layer

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

---------

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
Co-authored-by: shuting <shuting@nirmata.com>
Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Vishal Choudhary 2024-08-16 17:06:48 +05:30 committed by GitHub
parent f69ffe12ec
commit 06ffd1c961
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 710 additions and 46 deletions

View file

@ -9,7 +9,7 @@ import (
)
// ImageVerificationType selects the type of verification algorithm
// +kubebuilder:validation:Enum=Cosign;Notary
// +kubebuilder:validation:Enum=Cosign;SigstoreBundle;Notary
// +kubebuilder:default=Cosign
type ImageVerificationType string
@ -19,6 +19,7 @@ type ImageRegistryCredentialsProvidersType string
const (
Cosign ImageVerificationType = "Cosign"
SigstoreBundle ImageVerificationType = "SigstoreBundle"
Notary ImageVerificationType = "Notary"
DEFAULT ImageRegistryCredentialsProvidersType = "default"
@ -46,7 +47,7 @@ type ImageVerification struct {
ValidationFailureAction *ValidationFailureAction `json:"validationFailureAction,omitempty" yaml:"validationFailureAction,omitempty"`
// Type specifies the method of signature validation. The allowed options
// are Cosign and Notary. By default Cosign is used if a type is not specified.
// are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
// +kubebuilder:validation:Optional
Type ImageVerificationType `json:"type,omitempty" yaml:"type,omitempty"`

View file

@ -4295,9 +4295,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -8715,9 +8716,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -12879,6 +12881,7 @@ spec:
are Cosign and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -17296,9 +17299,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:

View file

@ -4296,9 +4296,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -8717,9 +8718,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -12882,6 +12884,7 @@ spec:
are Cosign and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -17299,9 +17302,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:

View file

@ -4289,9 +4289,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -8709,9 +8710,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -12873,6 +12875,7 @@ spec:
are Cosign and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -17290,9 +17293,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:

View file

@ -4290,9 +4290,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -8711,9 +8712,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -12876,6 +12878,7 @@ spec:
are Cosign and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -17293,9 +17296,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:

View file

@ -4289,9 +4289,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -8709,9 +8710,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -12873,6 +12875,7 @@ spec:
are Cosign and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -17290,9 +17293,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:

View file

@ -4290,9 +4290,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -8711,9 +8712,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -12876,6 +12878,7 @@ spec:
are Cosign and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -17293,9 +17296,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:

View file

@ -9487,9 +9487,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -13907,9 +13908,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -18071,6 +18073,7 @@ spec:
are Cosign and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -22488,9 +22491,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -27189,9 +27193,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -31610,9 +31615,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -35775,6 +35781,7 @@ spec:
are Cosign and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:
@ -40192,9 +40199,10 @@ spec:
type:
description: |-
Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.
enum:
- Cosign
- SigstoreBundle
- Notary
type: string
useCache:

View file

@ -2410,7 +2410,7 @@ ImageVerificationType
</td>
<td>
<p>Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.</p>
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.</p>
</td>
</tr>
<tr>

View file

@ -4743,7 +4743,7 @@ mutated to include the SHA digest retrieved during the registration.</p>
<p>Type specifies the method of signature validation. The allowed options
are Cosign and Notary. By default Cosign is used if a type is not specified.</p>
are Cosign, Sigstore Bundle and Notary. By default Cosign is used if a type is not specified.</p>

15
go.mod
View file

@ -23,7 +23,7 @@ require (
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.12.0
github.com/go-logr/logr v1.4.2
github.com/go-logr/zapr v1.3.0
github.com/go-logr/zerologr v1.2.3
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49
github.com/google/go-containerregistry v0.20.2
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20240530172801-3764db238e3e
@ -43,16 +43,20 @@ require (
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.19.0
github.com/robfig/cron v1.2.0
github.com/rs/zerolog v1.33.0
github.com/sigstore/cosign/v2 v2.2.4
github.com/sigstore/k8s-manifest-sigstore v0.5.4
github.com/sigstore/protobuf-specs v0.3.2 // indirect
github.com/sigstore/rekor v1.3.6
github.com/sigstore/sigstore v1.8.4
github.com/sigstore/sigstore-go v0.4.0
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.4
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.4
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.4
github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.4
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.9.0
github.com/theupdateframework/go-tuf/v2 v2.0.0-20240223092044-1e7978e83f63 // indirect
github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0
go.opentelemetry.io/otel v1.26.0
@ -66,7 +70,7 @@ require (
go.opentelemetry.io/otel/trace v1.26.0
go.uber.org/automaxprocs v1.5.3
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.26.0
golang.org/x/text v0.17.0
gomodules.xyz/jsonpatch/v2 v2.4.0
@ -94,11 +98,6 @@ require (
sigs.k8s.io/yaml v1.4.0
)
require (
github.com/go-logr/zerologr v1.2.3 // indirect
github.com/rs/zerolog v1.33.0 // indirect
)
require (
cloud.google.com/go v0.114.0 // indirect
cloud.google.com/go/auth v0.5.1 // indirect
@ -232,7 +231,7 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/cel-go v0.20.1 // indirect
github.com/google/certificate-transparency-go v1.1.7 // indirect
github.com/google/certificate-transparency-go v1.1.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-github/v55 v55.0.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect

22
go.sum
View file

@ -436,8 +436,8 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.17.8 h1:j9m730pMZt1Fc4oKhCLUHfjj6527LuhYcYw0Rl8gqto=
github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
github.com/google/certificate-transparency-go v1.1.7 h1:IASD+NtgSTJLPdzkthwvAG1ZVbF2WtFg4IvoA68XGSw=
github.com/google/certificate-transparency-go v1.1.7/go.mod h1:FSSBo8fyMVgqptbfF6j5p/XNdgQftAhSmXcIxV9iphE=
github.com/google/certificate-transparency-go v1.1.8 h1:LGYKkgZF7satzgTak9R4yzfJXEeYVAjV6/EAEJOf1to=
github.com/google/certificate-transparency-go v1.1.8/go.mod h1:bV/o8r0TBKRf1X//iiiSgWrvII4d7/8OiA+3vG26gI8=
github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM=
github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
@ -788,10 +788,14 @@ github.com/sigstore/fulcio v1.4.3 h1:9JcUCZjjVhRF9fmhVuz6i1RyhCc/EGCD7MOl+iqCJLQ
github.com/sigstore/fulcio v1.4.3/go.mod h1:BQPWo7cfxmJwgaHlphUHUpFkp5+YxeJes82oo39m5og=
github.com/sigstore/k8s-manifest-sigstore v0.5.4 h1:XPLs7Dz2OXkRdt4sgAoLxuyNSfkb/n5SwfnYb23YlRk=
github.com/sigstore/k8s-manifest-sigstore v0.5.4/go.mod h1:VN/nxjuAprSy1LrutFdhc5UsrQjij4DaCYV4Wklu5zo=
github.com/sigstore/protobuf-specs v0.3.2 h1:nCVARCN+fHjlNCk3ThNXwrZRqIommIeNKWwQvORuRQo=
github.com/sigstore/protobuf-specs v0.3.2/go.mod h1:RZ0uOdJR4OB3tLQeAyWoJFbNCBFrPQdcokntde4zRBA=
github.com/sigstore/rekor v1.3.6 h1:QvpMMJVWAp69a3CHzdrLelqEqpTM3ByQRt5B5Kspbi8=
github.com/sigstore/rekor v1.3.6/go.mod h1:JDTSNNMdQ/PxdsS49DJkJ+pRJCO/83nbR5p3aZQteXc=
github.com/sigstore/sigstore v1.8.4 h1:g4ICNpiENFnWxjmBzBDWUn62rNFeny/P77HUC8da32w=
github.com/sigstore/sigstore v1.8.4/go.mod h1:1jIKtkTFEeISen7en+ZPWdDHazqhxco/+v9CNjc7oNg=
github.com/sigstore/sigstore-go v0.4.0 h1:0BxofjPnd+1LzyiCgsFP0NviMg8l20ZMf4aitkvYEU8=
github.com/sigstore/sigstore-go v0.4.0/go.mod h1:KZQFwvDItf1sr5P8YhVIjjXBe1ZyeFuC4odn7/2Uie0=
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.4 h1:okxaVlaTrQowE1FA4UQ3rw54f7BUjdnzERIxbZTBZuc=
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.4/go.mod h1:jkcPErmnCECuSJajUaUq5pwCMOeBF19VzQo6bv4l1D0=
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.4 h1:1G6uLTZaqvu867DbgH7p75L6Y7Tu8LLnYJGZnWsTUu8=
@ -869,6 +873,8 @@ github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gt
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=
github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug=
github.com/theupdateframework/go-tuf/v2 v2.0.0-20240223092044-1e7978e83f63 h1:27XWhDZHPD+cufF6qSdYx6PgGQvD2jJ6pq9sDvR6VBk=
github.com/theupdateframework/go-tuf/v2 v2.0.0-20240223092044-1e7978e83f63/go.mod h1:+gWwqe1pk4nvGeOKosGJqPgD+N/kbD9M0QVLL9TGIYU=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
@ -928,12 +934,12 @@ go.etcd.io/etcd/client/v2 v2.305.12 h1:0m4ovXYo1CHaA/Mp3X/Fak5sRNIWf01wk/X1/G3sG
go.etcd.io/etcd/client/v2 v2.305.12/go.mod h1:aQ/yhsxMu+Oht1FOupSr60oBvcS9cKXHrzBpDsPTf9E=
go.etcd.io/etcd/client/v3 v3.5.13 h1:o0fHTNJLeO0MyVbc7I3fsCf6nrOqn5d+diSarKnB2js=
go.etcd.io/etcd/client/v3 v3.5.13/go.mod h1:cqiAeY8b5DEEcpxvgWKsbLIWNM/8Wy2xJSDMtioMcoI=
go.etcd.io/etcd/pkg/v3 v3.5.10 h1:WPR8K0e9kWl1gAhB5A7gEa5ZBTNkT9NdNWrR8Qpo1CM=
go.etcd.io/etcd/pkg/v3 v3.5.10/go.mod h1:TKTuCKKcF1zxmfKWDkfz5qqYaE3JncKKZPFf8c1nFUs=
go.etcd.io/etcd/raft/v3 v3.5.10 h1:cgNAYe7xrsrn/5kXMSaH8kM/Ky8mAdMqGOxyYwpP0LA=
go.etcd.io/etcd/raft/v3 v3.5.10/go.mod h1:odD6kr8XQXTy9oQnyMPBOr0TVe+gT0neQhElQ6jbGRc=
go.etcd.io/etcd/server/v3 v3.5.10 h1:4NOGyOwD5sUZ22PiWYKmfxqoeh72z6EhYjNosKGLmZg=
go.etcd.io/etcd/server/v3 v3.5.10/go.mod h1:gBplPHfs6YI0L+RpGkTQO7buDbHv5HJGG/Bst0/zIPo=
go.etcd.io/etcd/pkg/v3 v3.5.12 h1:OK2fZKI5hX/+BTK76gXSTyZMrbnARyX9S643GenNGb8=
go.etcd.io/etcd/pkg/v3 v3.5.12/go.mod h1:UVwg/QIMoJncyeb/YxvJBJCE/NEwtHWashqc8A1nj/M=
go.etcd.io/etcd/raft/v3 v3.5.12 h1:7r22RufdDsq2z3STjoR7Msz6fYH8tmbkdheGfwJNRmU=
go.etcd.io/etcd/raft/v3 v3.5.12/go.mod h1:ERQuZVe79PI6vcC3DlKBukDCLja/L7YMu29B74Iwj4U=
go.etcd.io/etcd/server/v3 v3.5.12 h1:EtMjsbfyfkwZuA2JlKOiBfuGkFCekv5H178qjXypbG8=
go.etcd.io/etcd/server/v3 v3.5.12/go.mod h1:axB0oCjMy+cemo5290/CutIjoxlfA6KVYKD1w0uue10=
go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc=
go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=

View file

@ -47,6 +47,19 @@ func NewVerifier() images.ImageVerifier {
type cosignVerifier struct{}
func (v *cosignVerifier) VerifySignature(ctx context.Context, opts images.Options) (*images.Response, error) {
if opts.SigstoreBundle {
results, err := verifyBundleAndFetchAttestations(ctx, opts)
if err != nil {
return nil, err
}
if len(results) == 0 {
return nil, fmt.Errorf("sigstore bundle verification failed: no matching signatures found")
}
return &images.Response{Digest: results[0].Desc.Digest.String()}, nil
}
ref, err := name.ParseReference(opts.ImageRef)
if err != nil {
return nil, fmt.Errorf("failed to parse image %s", opts.ImageRef)
@ -266,6 +279,22 @@ func loadCertChain(pem []byte) ([]*x509.Certificate, error) {
}
func (v *cosignVerifier) FetchAttestations(ctx context.Context, opts images.Options) (*images.Response, error) {
if opts.SigstoreBundle {
results, err := verifyBundleAndFetchAttestations(ctx, opts)
if err != nil {
return nil, err
}
if len(results) == 0 {
return nil, fmt.Errorf("sigstore bundle verification failed: no matching signatures found")
}
statements, err := decodeStatementsFromBundles(results)
if err != nil {
return nil, err
}
return &images.Response{Digest: results[0].Desc.Digest.String(), Statements: statements}, nil
}
cosignOpts, err := buildCosignOptions(ctx, opts)
if err != nil {
return nil, err

254
pkg/cosign/sigstore.go Normal file
View file

@ -0,0 +1,254 @@
package cosign
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"strings"
"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/in-toto/in-toto-golang/in_toto"
"github.com/kyverno/kyverno/pkg/images"
"github.com/kyverno/kyverno/pkg/utils/data"
"github.com/pkg/errors"
"github.com/sigstore/sigstore-go/pkg/bundle"
"github.com/sigstore/sigstore-go/pkg/root"
"github.com/sigstore/sigstore-go/pkg/verify"
"github.com/sigstore/sigstore/pkg/tuf"
)
var (
maxLayerSize = int64(10 * 1000 * 1000) // 10 MB
attestationlimit = 50
)
type VerificationResult struct {
Bundle *Bundle
Result *verify.VerificationResult
Desc *v1.Descriptor
}
type Bundle struct {
ProtoBundle *bundle.ProtobufBundle
DSSE_Envelope *in_toto.Statement
}
func verifyBundleAndFetchAttestations(ctx context.Context, opts images.Options) ([]*VerificationResult, error) {
ref, err := name.ParseReference(opts.ImageRef)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse image reference: %v", opts.ImageRef)
}
remoteOpts, err := opts.Client.Options(ctx)
if err != nil {
return nil, errors.Wrapf(err, "failed to create remote opts: %v", opts.ImageRef)
}
bundles, desc, err := fetchBundles(ref, attestationlimit, opts.Type, remoteOpts)
if err != nil {
return nil, errors.Wrapf(err, "failed to fetch bundles: %v", opts.ImageRef)
}
policy, err := buildPolicy(desc, opts)
if err != nil {
return nil, errors.Wrapf(err, "failed to build policy: %v", opts.ImageRef)
}
verifyOpts := buildVerifyOptions(opts)
trustedMaterial, err := getTrustedRoot(ctx)
if err != nil {
return nil, errors.Wrapf(err, "failed to get trusted root: %v", opts.ImageRef)
}
results, err := verifyBundles(bundles, desc, trustedMaterial, policy, verifyOpts)
if err != nil {
return nil, errors.Wrapf(err, "failed to get verify bundles: %v", opts.ImageRef)
}
return results, nil
}
func verifyBundles(bundles []*Bundle, desc *v1.Descriptor, trustedRoot *root.TrustedRoot, policy verify.PolicyBuilder, verifierOpts []verify.VerifierOption) ([]*VerificationResult, error) {
verifier, err := verify.NewSignedEntityVerifier(trustedRoot, verifierOpts...)
if err != nil {
return nil, err
}
verificationResults := make([]*VerificationResult, 0)
for _, bundle := range bundles {
result, err := verifier.Verify(bundle.ProtoBundle, policy)
if err == nil {
verificationResults = append(verificationResults, &VerificationResult{Bundle: bundle, Result: result, Desc: desc})
}
}
return verificationResults, nil
}
func fetchBundles(ref name.Reference, limit int, predicateType string, remoteOpts []remote.Option) ([]*Bundle, *v1.Descriptor, error) {
bundles := make([]*Bundle, 0)
desc, err := remote.Head(ref, remoteOpts...)
if err != nil {
return nil, nil, err
}
referrers, err := remote.Referrers(ref.Context().Digest(desc.Digest.String()), remoteOpts...)
if err != nil {
return nil, nil, err
}
referrersDescs, err := referrers.IndexManifest()
if err != nil {
return nil, nil, err
}
if len(referrersDescs.Manifests) > limit {
return nil, nil, fmt.Errorf("failed to fetch referrers: to many referrers found, max limit is %d", limit)
}
for _, manifestDesc := range referrersDescs.Manifests {
if !strings.HasPrefix(manifestDesc.ArtifactType, "application/vnd.dev.sigstore.bundle") {
continue
}
refImg, err := remote.Image(ref.Context().Digest(manifestDesc.Digest.String()), remoteOpts...)
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch referrer image: %w", err)
}
layers, err := refImg.Layers()
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch referrer layer: %w", err)
}
if len(layers) == 0 {
return nil, nil, fmt.Errorf("layers not found")
}
layer := layers[0]
layerSize, err := layer.Size()
if err != nil {
return nil, nil, err
}
if layerSize > maxLayerSize {
return nil, nil, fmt.Errorf("layer size %d exceeds %d", layerSize, maxLayerSize)
}
layerBytes, err := layer.Uncompressed()
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch referrer layer: %w", err)
}
bundleBytes, err := io.ReadAll(layerBytes)
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch referrer layer: %w", err)
}
b := &bundle.ProtobufBundle{}
err = b.UnmarshalJSON(bundleBytes)
if err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal bundle: %w", err)
}
bundles = append(bundles, &Bundle{ProtoBundle: b})
}
if predicateType != "" {
filteredBundles := make([]*Bundle, 0)
for _, b := range bundles {
dsseEnvelope := b.ProtoBundle.Bundle.GetDsseEnvelope()
if dsseEnvelope != nil {
if dsseEnvelope.PayloadType != "application/vnd.in-toto+json" {
continue
}
var intotoStatement in_toto.Statement
if err := json.Unmarshal(dsseEnvelope.Payload, &intotoStatement); err != nil {
continue
}
if intotoStatement.PredicateType == predicateType {
filteredBundles = append(filteredBundles, &Bundle{
ProtoBundle: b.ProtoBundle,
DSSE_Envelope: &intotoStatement,
})
}
}
}
return filteredBundles, desc, nil
}
return bundles, desc, nil
}
func buildPolicy(desc *v1.Descriptor, opts images.Options) (verify.PolicyBuilder, error) {
digest, err := hex.DecodeString(desc.Digest.Hex)
if err != nil {
return verify.PolicyBuilder{}, err
}
artifactDigestVerificationOption := verify.WithArtifactDigest(desc.Digest.Algorithm, digest)
// TODO: Add full regexp support to sigstore and cosign
// Verify images only has subject field, and no subject regexp, subject cannot be passed to subject regexp
// because then string containing the subjects will also work. We should just add an issuer regexp
// Solve this in a separate PR,
// See: https://github.com/sigstore/cosign/blob/7c20052077a81d667526af879ec40168899dde1f/pkg/cosign/verify.go#L339-L356
subjectRegexp := ""
if strings.Contains(opts.Subject, "*") {
subjectRegexp = opts.Subject
opts.Subject = ""
}
id, err := verify.NewShortCertificateIdentity(opts.Issuer, opts.Subject, "", subjectRegexp)
if err != nil {
return verify.PolicyBuilder{}, err
}
return verify.NewPolicy(artifactDigestVerificationOption, verify.WithCertificateIdentity(id)), nil
}
func buildVerifyOptions(opts images.Options) []verify.VerifierOption {
var verifierOptions []verify.VerifierOption
if !opts.IgnoreTlog {
verifierOptions = append(verifierOptions, verify.WithTransparencyLog(1))
}
if !opts.IgnoreSCT {
verifierOptions = append(verifierOptions, verify.WithObserverTimestamps(1))
}
return verifierOptions
}
func getTrustedRoot(ctx context.Context) (*root.TrustedRoot, error) {
tufClient, err := tuf.NewFromEnv(ctx)
if err != nil {
return nil, fmt.Errorf("initializing tuf: %w", err)
}
targetBytes, err := tufClient.GetTarget("trusted_root.json")
if err != nil {
return nil, fmt.Errorf("error getting targets: %w", err)
}
trustedRoot, err := root.NewTrustedRootFromJSON(targetBytes)
if err != nil {
return nil, fmt.Errorf("error creating trusted root: %w", err)
}
return trustedRoot, nil
}
func decodeStatementsFromBundles(bundles []*VerificationResult) ([]map[string]interface{}, error) {
if len(bundles) == 0 {
return []map[string]interface{}{}, nil
}
var err error
var statement map[string]interface{}
var intotostatement in_toto.Statement
decodedStatements := make([]map[string]interface{}, len(bundles))
for i, b := range bundles {
intotostatement = *b.Bundle.DSSE_Envelope
statement, err = data.ToMap(intotostatement)
if err != nil {
return nil, errors.Wrapf(err, "failed to decode statement: %v", intotostatement.Type)
}
statement["type"] = intotostatement.PredicateType
decodedStatements[i] = statement
}
return decodedStatements, nil
}

View file

@ -0,0 +1,89 @@
package cosign
import (
"context"
"testing"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/kyverno/kyverno/pkg/images"
"github.com/kyverno/kyverno/pkg/registryclient"
"gotest.tools/assert"
)
func TestSigstoreBundleSignatureVerification(t *testing.T) {
opts := images.Options{
SigstoreBundle: true,
ImageRef: "ghcr.io/vishal-chdhry/artifact-attestation-example:artifact-attestation",
Issuer: "https://token.actions.githubusercontent.com",
Subject: "https://github.com/vishal-chdhry/artifact-attestation-example/.github/workflows/build-attested-image.yaml@refs/heads/main",
}
rc, err := registryclient.New()
assert.NilError(t, err)
opts.Client = rc
verifier := &cosignVerifier{}
_, err = verifier.VerifySignature(context.TODO(), opts)
assert.NilError(t, err)
opts.Subject = "invalid"
_, err = verifier.VerifySignature(context.TODO(), opts)
assert.ErrorContains(t, err, "sigstore bundle verification failed: no matching signatures found")
}
func TestSigstoreBundleSignatureResponse(t *testing.T) {
opts := images.Options{
SigstoreBundle: true,
ImageRef: "ghcr.io/vishal-chdhry/artifact-attestation-example:artifact-attestation",
Issuer: "https://token.actions.githubusercontent.com",
Subject: "https://github.com/vishal-chdhry/artifact-attestation-example/.github/workflows/build-attested-image.yaml@refs/heads/main",
}
rc, err := registryclient.New()
assert.NilError(t, err)
opts.Client = rc
verifier := &cosignVerifier{}
response, err := verifier.VerifySignature(context.TODO(), opts)
assert.NilError(t, err)
ref, err := name.ParseReference(opts.ImageRef)
assert.NilError(t, err)
desc, err := remote.Head(ref)
assert.NilError(t, err)
assert.Equal(t, desc.Digest.String(), response.Digest)
assert.Equal(t, len(response.Statements), 0)
}
func TestSigstoreBundleAttestation(t *testing.T) {
opts := images.Options{
SigstoreBundle: true,
ImageRef: "ghcr.io/vishal-chdhry/artifact-attestation-example:artifact-attestation",
Issuer: "https://token.actions.githubusercontent.com",
Subject: "https://github.com/vishal-chdhry/artifact-attestation-example/.github/workflows/build-attested-image.yaml@refs/heads/main",
Type: "https://slsa.dev/provenance/v1",
}
rc, err := registryclient.New()
assert.NilError(t, err)
opts.Client = rc
verifier := &cosignVerifier{}
response, err := verifier.FetchAttestations(context.TODO(), opts)
assert.NilError(t, err)
ref, err := name.ParseReference(opts.ImageRef)
assert.NilError(t, err)
desc, err := remote.Head(ref)
assert.NilError(t, err)
assert.Equal(t, desc.Digest.String(), response.Digest)
assert.Assert(t, len(response.Statements) > 0)
buildType, ok := response.Statements[0]["predicate"].(map[string]interface{})["buildDefinition"].(map[string]interface{})["buildType"].(string)
assert.Assert(t, ok)
assert.Equal(t, buildType, "https://actions.github.io/buildtypes/workflow/v1")
}

View file

@ -556,6 +556,10 @@ func (iv *ImageVerifier) buildCosignVerifier(
Client: iv.rclient,
}
if imageVerify.Type == kyvernov1.SigstoreBundle {
opts.SigstoreBundle = true
}
if imageVerify.Roots != "" {
opts.Roots = imageVerify.Roots
}

View file

@ -21,6 +21,7 @@ type Client interface {
}
type Options struct {
SigstoreBundle bool
ImageRef string
Client Client
FetchAttestations bool

View file

@ -0,0 +1,4 @@
## Description
This test verifies the predicate in sigstore bundle attached to an image using regex issuer

View file

@ -0,0 +1,20 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
creationTimestamp: null
name: sigstore-attestation-verification-regexp
spec:
steps:
- name: step-01
try:
- apply:
file: policy.yaml
- assert:
file: policy-assert.yaml
- name: step-02
try:
- apply:
file: pod.yaml
- assert:
file: pod-assert.yaml

View file

@ -0,0 +1,6 @@
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: default

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: default
spec:
containers:
- image: ghcr.io/vishal-chdhry/artifact-attestation-example:artifact-attestation
name: test-container

View file

@ -0,0 +1,9 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: sigstore-attestation-verification-regexp
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,36 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
annotations:
pod-policies.kyverno.io/autogen-controllers: none
name: sigstore-attestation-verification-regexp
spec:
background: false
validationFailureAction: Enforce
webhookTimeoutSeconds: 30
rules:
- match:
any:
- resources:
kinds:
- Pod
name: sigstore-attestation-verification
verifyImages:
- imageReferences:
- "*"
type: SigstoreBundle
attestations:
- attestors:
- entries:
- keyless:
issuer: https://token.actions.githubusercontent.com
subject: https://github.com/vishal-chdhry/artifact-attestation-example/.github/workflows/*
rekor:
url: https://rekor.sigstore.dev
conditions:
- all:
- key: '{{ buildDefinition.buildType }}'
operator: Equals
value: https://actions.github.io/buildtypes/workflow/v1
type: https://slsa.dev/provenance/v1

View file

@ -0,0 +1,4 @@
## Description
This test verifies the predicate in sigstore bundle attached to an image

View file

@ -0,0 +1,20 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
creationTimestamp: null
name: sigstore-attestation-verification
spec:
steps:
- name: step-01
try:
- apply:
file: policy.yaml
- assert:
file: policy-assert.yaml
- name: step-02
try:
- apply:
file: pod.yaml
- assert:
file: pod-assert.yaml

View file

@ -0,0 +1,6 @@
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: default

View file

@ -0,0 +1,9 @@
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: default
spec:
containers:
- image: ghcr.io/vishal-chdhry/artifact-attestation-example:artifact-attestation
name: test-container

View file

@ -0,0 +1,10 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: sigstore-attestation-verification
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,36 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
annotations:
pod-policies.kyverno.io/autogen-controllers: none
name: sigstore-attestation-verification
spec:
background: false
validationFailureAction: Enforce
webhookTimeoutSeconds: 30
rules:
- match:
any:
- resources:
kinds:
- Pod
name: sigstore-attestation-verification
verifyImages:
- imageReferences:
- "*"
type: SigstoreBundle
attestations:
- attestors:
- entries:
- keyless:
issuer: https://token.actions.githubusercontent.com
subject: https://github.com/vishal-chdhry/artifact-attestation-example/.github/workflows/build-attested-image.yaml@refs/heads/main
rekor:
url: https://rekor.sigstore.dev
conditions:
- all:
- key: '{{ buildDefinition.buildType }}'
operator: Equals
value: https://actions.github.io/buildtypes/workflow/v1
type: https://slsa.dev/provenance/v1

View file

@ -0,0 +1,4 @@
## Description
This test verifies sigstore bundle attached to an image.

View file

@ -0,0 +1,20 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
creationTimestamp: null
name: sigstore-image-verification
spec:
steps:
- name: step-01
try:
- apply:
file: policy.yaml
- assert:
file: policy-assert.yaml
- name: step-02
try:
- apply:
file: pod.yaml
- assert:
file: pod-assert.yaml

View file

@ -0,0 +1,6 @@
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: default

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: default
spec:
containers:
- image: ghcr.io/vishal-chdhry/artifact-attestation-example:artifact-attestation
name: test-container

View file

@ -0,0 +1,10 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: sigstore-image-verification
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,29 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
annotations:
pod-policies.kyverno.io/autogen-controllers: none
name: sigstore-image-verification
spec:
background: false
validationFailureAction: Enforce
webhookTimeoutSeconds: 30
rules:
- match:
any:
- resources:
kinds:
- Pod
name: sigstore-image-verification
verifyImages:
- imageReferences:
- "*"
type: SigstoreBundle
attestors:
- entries:
- keyless:
issuer: https://token.actions.githubusercontent.com
subject: https://github.com/vishal-chdhry/artifact-attestation-example/.github/workflows/build-attested-image.yaml@refs/heads/main
rekor:
url: https://rekor.sigstore.dev