mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 02:18:15 +00:00
Notary v2 (#6011)
* fix make debug-deploy Signed-off-by: Jim Bugwadia <jim@nirmata.com> * improve log messages Signed-off-by: Jim Bugwadia <jim@nirmata.com> * initial update Signed-off-by: Jim Bugwadia <jim@nirmata.com> * initial update Signed-off-by: Jim Bugwadia <jim@nirmata.com> * update registry credentials handling order Signed-off-by: Jim Bugwadia <jim@nirmata.com> * comment out ACR helper - breaks anonymous image pull Signed-off-by: Jim Bugwadia <jim@nirmata.com> * merge main and refactor verifiers Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix opt init Signed-off-by: Jim Bugwadia <jim@nirmata.com> * remove local address Signed-off-by: Jim Bugwadia <jim@nirmata.com> * update to NotaryV2 RC Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix fmt Signed-off-by: Jim Bugwadia <jim@nirmata.com> * update deps Signed-off-by: Jim Bugwadia <jim@nirmata.com> * format imports Signed-off-by: Jim Bugwadia <jim@nirmata.com> * remove env and no-op statement Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix merge issues Signed-off-by: Jim Bugwadia <jim@nirmata.com> * fix linter issue Signed-off-by: Jim Bugwadia <jim@nirmata.com> * remove unused field Signed-off-by: Jim Bugwadia <jim@nirmata.com> * make fmt Signed-off-by: Jim Bugwadia <jim@nirmata.com> * renable ACR credential helper Signed-off-by: Jim Bugwadia <jim@nirmata.com> * Update .vscode/launch.json Signed-off-by: shuting <shutting06@gmail.com> --------- Signed-off-by: Jim Bugwadia <jim@nirmata.com> Signed-off-by: shuting <shutting06@gmail.com> Co-authored-by: shuting <shuting@nirmata.com> Co-authored-by: shuting <shutting06@gmail.com>
This commit is contained in:
parent
b76a73e7b6
commit
29997fe446
21 changed files with 755 additions and 147 deletions
5
.vscode/launch.json
vendored
5
.vscode/launch.json
vendored
|
@ -9,7 +9,8 @@
|
|||
"program": "${workspaceFolder}/cmd/kyverno",
|
||||
"args": [
|
||||
"--kubeconfig=${userHome}/.kube/config",
|
||||
"--serverIP=<local ip>:9443",
|
||||
"--serverIP=<SERVER-IP>:9443",
|
||||
"-v=2",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -24,4 +25,4 @@
|
|||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,25 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// ImageVerificationType selects the type of verification algorithm
|
||||
// +kubebuilder:validation:Enum=Cosign;NotaryV2
|
||||
// +kubebuilder:default=Cosign
|
||||
type ImageVerificationType string
|
||||
|
||||
const (
|
||||
Cosign ImageVerificationType = "Cosign"
|
||||
NotaryV2 ImageVerificationType = "NotaryV2"
|
||||
)
|
||||
|
||||
// ImageVerification validates that images that match the specified pattern
|
||||
// are signed with the supplied public key. Once the image is verified it is
|
||||
// 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.
|
||||
// +kubebuilder:validation:Optional
|
||||
Type ImageVerificationType `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
|
||||
// Image is the image name consisting of the registry address, repository, image, and tag.
|
||||
// Wildcards ('*' and '?') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.
|
||||
// Deprecated. Use ImageReferences instead.
|
||||
|
@ -234,6 +249,14 @@ type Attestation struct {
|
|||
Conditions []AnyAllConditions `json:"conditions,omitempty" yaml:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
func (iv *ImageVerification) GetType() ImageVerificationType {
|
||||
if iv.Type != "" {
|
||||
return iv.Type
|
||||
}
|
||||
|
||||
return Cosign
|
||||
}
|
||||
|
||||
// Validate implements programmatic validation
|
||||
func (iv *ImageVerification) Validate(path *field.Path) (errs field.ErrorList) {
|
||||
copy := iv.Convert()
|
||||
|
|
|
@ -5,10 +5,25 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// ImageVerificationType selects the type of verification algorithm
|
||||
// +kubebuilder:validation:Enum=Cosign;NotaryV2
|
||||
// +kubebuilder:default=Cosign
|
||||
type ImageVerificationType string
|
||||
|
||||
const (
|
||||
Cosign ImageVerificationType = "Cosign"
|
||||
NotaryV2 ImageVerificationType = "NotaryV2"
|
||||
)
|
||||
|
||||
// ImageVerification validates that images that match the specified pattern
|
||||
// are signed with the supplied public key. Once the image is verified it is
|
||||
// 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.
|
||||
// +kubebuilder:validation:Optional
|
||||
Type ImageVerificationType `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
|
||||
// ImageReferences is a list of matching image reference patterns. At least one pattern in the
|
||||
// list must match the image for the rule to apply. Each image reference consists of a registry
|
||||
// address (defaults to docker.io), repository, image, and tag (defaults to latest).
|
||||
|
|
|
@ -6632,6 +6632,14 @@ spec:
|
|||
signing, for example an email address Deprecated. Use
|
||||
KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have a
|
||||
|
@ -10001,6 +10009,14 @@ spec:
|
|||
signing, for example an email address Deprecated.
|
||||
Use KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have
|
||||
|
@ -13058,6 +13074,14 @@ spec:
|
|||
i.e. have matched passed a signature or attestation
|
||||
check.
|
||||
type: boolean
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have a
|
||||
|
@ -16427,6 +16451,14 @@ spec:
|
|||
signing, for example an email address Deprecated.
|
||||
Use KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have
|
||||
|
@ -19779,6 +19811,14 @@ spec:
|
|||
signing, for example an email address Deprecated. Use
|
||||
KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have a
|
||||
|
@ -23149,6 +23189,14 @@ spec:
|
|||
signing, for example an email address Deprecated.
|
||||
Use KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have
|
||||
|
@ -26207,6 +26255,14 @@ spec:
|
|||
i.e. have matched passed a signature or attestation
|
||||
check.
|
||||
type: boolean
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have a
|
||||
|
@ -29576,6 +29632,14 @@ spec:
|
|||
signing, for example an email address Deprecated.
|
||||
Use KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have
|
||||
|
|
|
@ -3230,6 +3230,14 @@ spec:
|
|||
signing, for example an email address Deprecated. Use
|
||||
KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have a
|
||||
|
@ -6599,6 +6607,14 @@ spec:
|
|||
signing, for example an email address Deprecated.
|
||||
Use KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have
|
||||
|
@ -9656,6 +9672,14 @@ spec:
|
|||
i.e. have matched passed a signature or attestation
|
||||
check.
|
||||
type: boolean
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have a
|
||||
|
@ -13025,6 +13049,14 @@ spec:
|
|||
signing, for example an email address Deprecated.
|
||||
Use KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have
|
||||
|
|
|
@ -3231,6 +3231,14 @@ spec:
|
|||
signing, for example an email address Deprecated. Use
|
||||
KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have a
|
||||
|
@ -6601,6 +6609,14 @@ spec:
|
|||
signing, for example an email address Deprecated.
|
||||
Use KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have
|
||||
|
@ -9659,6 +9675,14 @@ spec:
|
|||
i.e. have matched passed a signature or attestation
|
||||
check.
|
||||
type: boolean
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have a
|
||||
|
@ -13028,6 +13052,14 @@ spec:
|
|||
signing, for example an email address Deprecated.
|
||||
Use KeylessAttestor instead.
|
||||
type: string
|
||||
type:
|
||||
description: 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.
|
||||
enum:
|
||||
- Cosign
|
||||
- NotaryV2
|
||||
type: string
|
||||
verifyDigest:
|
||||
default: true
|
||||
description: VerifyDigest validates that images have
|
||||
|
|
|
@ -1784,6 +1784,20 @@ mutated to include the SHA digest retrieved during the registration.</p>
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>type</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v1.ImageVerificationType">
|
||||
ImageVerificationType
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>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.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>image</code><br/>
|
||||
<em>
|
||||
string
|
||||
|
@ -1961,6 +1975,15 @@ bool
|
|||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<h3 id="kyverno.io/v1.ImageVerificationType">ImageVerificationType
|
||||
(<code>string</code> alias)</p></h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v1.ImageVerification">ImageVerification</a>)
|
||||
</p>
|
||||
<p>
|
||||
<p>ImageVerificationType selects the type of verification algorithm</p>
|
||||
</p>
|
||||
<h3 id="kyverno.io/v1.KeylessAttestor">KeylessAttestor
|
||||
</h3>
|
||||
<p>
|
||||
|
@ -6029,6 +6052,20 @@ mutated to include the SHA digest retrieved during the registration.</p>
|
|||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code>type</code><br/>
|
||||
<em>
|
||||
<a href="#kyverno.io/v2beta1.ImageVerificationType">
|
||||
ImageVerificationType
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<p>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.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>imageReferences</code><br/>
|
||||
<em>
|
||||
[]string
|
||||
|
@ -6119,6 +6156,15 @@ bool
|
|||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<h3 id="kyverno.io/v2beta1.ImageVerificationType">ImageVerificationType
|
||||
(<code>string</code> alias)</p></h3>
|
||||
<p>
|
||||
(<em>Appears on:</em>
|
||||
<a href="#kyverno.io/v2beta1.ImageVerification">ImageVerification</a>)
|
||||
</p>
|
||||
<p>
|
||||
<p>ImageVerificationType selects the type of verification algorithm</p>
|
||||
</p>
|
||||
<h3 id="kyverno.io/v2beta1.MatchResources">MatchResources
|
||||
</h3>
|
||||
<p>
|
||||
|
|
14
go.mod
14
go.mod
|
@ -30,9 +30,13 @@ require (
|
|||
github.com/kataras/tablewriter v0.0.0-20180708051242-e063d29b7c23
|
||||
github.com/lensesio/tableprinter v0.0.0-20201125135848-89e81fc956e7
|
||||
github.com/mattbaird/jsonpatch v0.0.0-20200820163806-098863c1fc24
|
||||
github.com/notaryproject/notation-core-go v1.0.0-rc.1
|
||||
github.com/notaryproject/notation-go v1.0.0-rc.1
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.27.1
|
||||
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
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/robfig/cron v1.2.0
|
||||
github.com/sigstore/cosign v1.13.1
|
||||
|
@ -71,6 +75,7 @@ require (
|
|||
k8s.io/kube-openapi v0.0.0-20230118215034-64b6bb138190
|
||||
k8s.io/pod-security-admission v0.26.1
|
||||
k8s.io/utils v0.0.0-20230115233650-391b47cb4029
|
||||
oras.land/oras-go/v2 v2.0.0
|
||||
sigs.k8s.io/controller-runtime v0.14.4
|
||||
sigs.k8s.io/kustomize/api v0.12.1
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.9
|
||||
|
@ -95,6 +100,7 @@ require (
|
|||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||
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-20220621081337-cb9428e4ac1e // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230117203413-a47887b8f098 // indirect
|
||||
|
@ -155,10 +161,13 @@ require (
|
|||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-chi/chi v4.1.2+incompatible // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.4.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
|
@ -253,13 +262,11 @@ require (
|
|||
github.com/open-policy-agent/gatekeeper v0.0.0-20210824170141-dd97b8a7e966 // indirect
|
||||
github.com/open-policy-agent/opa v0.48.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
github.com/pjbgf/sha1cd v0.2.3 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.39.0 // indirect
|
||||
|
@ -296,6 +303,8 @@ require (
|
|||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/transparency-dev/merkle v0.0.1 // indirect
|
||||
github.com/vbatts/tar-split v0.11.2 // indirect
|
||||
github.com/veraison/go-cose v1.0.0-rc.2 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/go-gitlab v0.78.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
|
@ -341,5 +350,4 @@ replace (
|
|||
github.com/docker/cli => github.com/docker/cli v20.10.9+incompatible
|
||||
github.com/evanphx/json-patch/v5 => github.com/kyverno/json-patch/v5 v5.5.1-0.20210915204938-7578f4ee9c77
|
||||
github.com/jmespath/go-jmespath => github.com/kyverno/go-jmespath v0.4.1-0.20230204162932-3ee946b9433d
|
||||
github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.0.2
|
||||
)
|
||||
|
|
22
go.sum
22
go.sum
|
@ -93,6 +93,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z
|
|||
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
|
@ -426,6 +428,8 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV
|
|||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM=
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
||||
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
|
@ -434,6 +438,8 @@ github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ER
|
|||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
|
||||
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-critic/go-critic v0.5.6/go.mod h1:cVjj0DfqewQVIlIAGexPCaGaZDAqGE29PYDDADIVNEo=
|
||||
|
@ -458,6 +464,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
|||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
|
||||
github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
|
@ -1072,6 +1080,10 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
|||
github.com/nishanths/exhaustive v0.1.0/go.mod h1:S1j9110vxV1ECdCudXRkeMnFQ/DQk9ajLT0Uf2MYZQQ=
|
||||
github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ=
|
||||
github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE=
|
||||
github.com/notaryproject/notation-core-go v1.0.0-rc.1 h1:ACi0gr6mD1bzp9+gu3P0meJ/N6iWHlyM9zgtdnooNAA=
|
||||
github.com/notaryproject/notation-core-go v1.0.0-rc.1/go.mod h1:n8Gbvl9sKa00KptkKEL5XKUyMTIALe74QipKauE2rj4=
|
||||
github.com/notaryproject/notation-go v1.0.0-rc.1 h1:WobIGCUPcPUDCD2qGMCccTfLm2J5y1bsh4SFVsyMIaA=
|
||||
github.com/notaryproject/notation-go v1.0.0-rc.1/go.mod h1:xk4q0GXqGfEiy7WmyHi3Om3OM2dgHk0OHL6NIiJv5vA=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
|
@ -1122,8 +1134,8 @@ github.com/open-policy-agent/opa v0.48.0 h1:s2K823yohAUu/HB4MOPWDhBh88JMKQv7uTr6
|
|||
github.com/open-policy-agent/opa v0.48.0/go.mod h1:CsQcksP+qGBxO9oEBj1NnZqKcjgjmTJbRNTzjZB/DXQ=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
|
@ -1427,9 +1439,13 @@ github.com/valyala/quicktemplate v1.6.3/go.mod h1:fwPzK2fHuYEODzJ9pkw0ipCPNHZ2tD
|
|||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
|
||||
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
|
||||
github.com/veraison/go-cose v1.0.0-rc.2 h1:zH3QmP4N5kwpdGauceIT3aJm8iUyV9OqpUOb+7CF7rQ=
|
||||
github.com/veraison/go-cose v1.0.0-rc.2/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4=
|
||||
github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/go-gitlab v0.78.0 h1:8jUHfQVAprG04Av5g0PxVd3CNsZ5hCbojIax7Hba1mE=
|
||||
github.com/xanzy/go-gitlab v0.78.0/go.mod h1:DlByVTSXhPsJMYL6+cm8e8fTJjeBmhrXdC/yvkKKt6M=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
|
@ -2209,6 +2225,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.0.0 h1:+LRAz92WF7AvYQsQjPEAIw3Xb2zPPhuydjpi4pIHmc0=
|
||||
oras.land/oras-go/v2 v2.0.0/go.mod h1:iVExH1NxrccIxjsiq17L91WCZ4KIw6jVQyCLsZsu1gc=
|
||||
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=
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/kyverno/kyverno/pkg/registryclient"
|
||||
"github.com/kyverno/kyverno/pkg/images"
|
||||
"github.com/kyverno/kyverno/pkg/tracing"
|
||||
datautils "github.com/kyverno/kyverno/pkg/utils/data"
|
||||
wildcard "github.com/kyverno/kyverno/pkg/utils/wildcard"
|
||||
|
@ -34,32 +34,13 @@ import (
|
|||
// ImageSignatureRepository is an alternate signature repository
|
||||
var ImageSignatureRepository string
|
||||
|
||||
type Options struct {
|
||||
ImageRef string
|
||||
FetchAttestations bool
|
||||
Key string
|
||||
Cert string
|
||||
CertChain string
|
||||
Roots string
|
||||
Subject string
|
||||
Issuer string
|
||||
AdditionalExtensions map[string]string
|
||||
Annotations map[string]string
|
||||
Repository string
|
||||
RekorURL string
|
||||
SignatureAlgorithm string
|
||||
PredicateType string
|
||||
func NewVerifier() images.ImageVerifier {
|
||||
return &cosignVerifier{}
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Digest string
|
||||
Statements []map[string]interface{}
|
||||
}
|
||||
type cosignVerifier struct{}
|
||||
|
||||
type CosignError struct{}
|
||||
|
||||
// VerifySignature verifies that the image has the expected signatures
|
||||
func VerifySignature(ctx context.Context, rclient registryclient.Client, opts Options) (*Response, error) {
|
||||
func (v *cosignVerifier) VerifySignature(ctx context.Context, opts images.Options) (*images.Response, error) {
|
||||
ref, err := name.ParseReference(opts.ImageRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse image %s", opts.ImageRef)
|
||||
|
@ -70,7 +51,7 @@ func VerifySignature(ctx context.Context, rclient registryclient.Client, opts Op
|
|||
"",
|
||||
"VERIFY IMG SIGS",
|
||||
func(ctx context.Context, span trace.Span) ([]oci.Signature, bool, error) {
|
||||
cosignOpts, err := buildCosignOptions(ctx, rclient, opts)
|
||||
cosignOpts, err := buildCosignOptions(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
@ -105,10 +86,10 @@ func VerifySignature(ctx context.Context, rclient registryclient.Client, opts Op
|
|||
}
|
||||
}
|
||||
|
||||
return &Response{Digest: digest}, nil
|
||||
return &images.Response{Digest: digest}, nil
|
||||
}
|
||||
|
||||
func buildCosignOptions(ctx context.Context, rclient registryclient.Client, opts Options) (*cosign.CheckOpts, error) {
|
||||
func buildCosignOptions(ctx context.Context, opts images.Options) (*cosign.CheckOpts, error) {
|
||||
var remoteOpts []remote.Option
|
||||
var err error
|
||||
signatureAlgorithmMap := map[string]crypto.Hash{
|
||||
|
@ -121,7 +102,7 @@ func buildCosignOptions(ctx context.Context, rclient registryclient.Client, opts
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("constructing client options: %w", err)
|
||||
}
|
||||
remoteOpts = append(remoteOpts, rclient.BuildRemoteOption(ctx))
|
||||
remoteOpts = append(remoteOpts, opts.RegistryClient.BuildRemoteOption(ctx))
|
||||
cosignOpts := &cosign.CheckOpts{
|
||||
Annotations: map[string]interface{}{},
|
||||
RegistryClientOpts: remoteOpts,
|
||||
|
@ -250,10 +231,8 @@ func loadCertChain(pem []byte) ([]*x509.Certificate, error) {
|
|||
return cryptoutils.LoadCertificatesFromPEM(bytes.NewReader(pem))
|
||||
}
|
||||
|
||||
// FetchAttestations retrieves signed attestations and decodes them into in-toto statements
|
||||
// https://github.com/in-toto/attestation/blob/main/spec/README.md#statement
|
||||
func FetchAttestations(ctx context.Context, rclient registryclient.Client, opts Options) (*Response, error) {
|
||||
cosignOpts, err := buildCosignOptions(ctx, rclient, opts)
|
||||
func (v *cosignVerifier) FetchAttestations(ctx context.Context, opts images.Options) (*images.Response, error) {
|
||||
cosignOpts, err := buildCosignOptions(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -312,7 +291,7 @@ func FetchAttestations(ctx context.Context, rclient registryclient.Client, opts
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &Response{Digest: digest, Statements: inTotoStatements}, nil
|
||||
return &images.Response{Digest: digest, Statements: inTotoStatements}, nil
|
||||
}
|
||||
|
||||
func matchPredicateType(sig oci.Signature, expectedPredicateType string) (bool, string, error) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
"github.com/kyverno/kyverno/pkg/images"
|
||||
"github.com/kyverno/kyverno/pkg/registryclient"
|
||||
"github.com/sigstore/cosign/pkg/cosign"
|
||||
"github.com/sigstore/cosign/pkg/cosign/bundle"
|
||||
|
@ -72,24 +73,26 @@ func TestCosignPayload(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCosignKeyless(t *testing.T) {
|
||||
opts := Options{
|
||||
opts := images.Options{
|
||||
ImageRef: "ghcr.io/jimbugwadia/pause2",
|
||||
Issuer: "https://github.com/",
|
||||
Subject: "jim",
|
||||
}
|
||||
|
||||
client, err := registryclient.New()
|
||||
rc, err := registryclient.New()
|
||||
assert.NilError(t, err)
|
||||
opts.RegistryClient = rc
|
||||
|
||||
_, err = VerifySignature(context.TODO(), client, opts)
|
||||
verifier := &cosignVerifier{}
|
||||
_, err = verifier.VerifySignature(context.TODO(), opts)
|
||||
assert.ErrorContains(t, err, "subject mismatch: expected jim, received jim@nirmata.com")
|
||||
|
||||
opts.Subject = "jim@nirmata.com"
|
||||
_, err = VerifySignature(context.TODO(), client, opts)
|
||||
_, err = verifier.VerifySignature(context.TODO(), opts)
|
||||
assert.ErrorContains(t, err, "issuer mismatch: expected https://github.com/, received https://github.com/login/oauth")
|
||||
|
||||
opts.Issuer = "https://github.com/login/oauth"
|
||||
_, err = VerifySignature(context.TODO(), client, opts)
|
||||
_, err = verifier.VerifySignature(context.TODO(), opts)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -49,78 +49,7 @@ func (e *engine) verifyAndPatchImages(
|
|||
"pkg/engine",
|
||||
fmt.Sprintf("RULE %s", rule.Name),
|
||||
func(ctx context.Context, span trace.Span) {
|
||||
if len(rule.VerifyImages) == 0 {
|
||||
return
|
||||
}
|
||||
startTime := time.Now()
|
||||
logger := internal.LoggerWithRule(logger, rules[i])
|
||||
kindsInPolicy := append(rule.MatchResources.GetKinds(), rule.ExcludeResources.GetKinds()...)
|
||||
subresourceGVKToAPIResource := GetSubresourceGVKToAPIResourceMap(e.client, kindsInPolicy, policyContext)
|
||||
|
||||
if !matches(logger, rule, policyContext, subresourceGVKToAPIResource, e.configuration) {
|
||||
return
|
||||
}
|
||||
|
||||
// check if there is a corresponding policy exception
|
||||
ruleResp := hasPolicyExceptions(logger, engineapi.ImageVerify, e.exceptionSelector, policyContext, rule, subresourceGVKToAPIResource, e.configuration)
|
||||
if ruleResp != nil {
|
||||
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
|
||||
return
|
||||
}
|
||||
|
||||
logger.V(3).Info("processing image verification rule")
|
||||
|
||||
ruleImages, imageRefs, err := e.extractMatchingImages(policyContext, rule)
|
||||
if err != nil {
|
||||
internal.AddRuleResponse(
|
||||
&resp.PolicyResponse,
|
||||
internal.RuleError(rule, engineapi.ImageVerify, "failed to extract images", err),
|
||||
startTime,
|
||||
)
|
||||
return
|
||||
}
|
||||
if len(ruleImages) == 0 {
|
||||
internal.AddRuleResponse(
|
||||
&resp.PolicyResponse,
|
||||
internal.RuleSkip(
|
||||
rule,
|
||||
engineapi.ImageVerify,
|
||||
fmt.Sprintf("skip run verification as image in resource not found in imageRefs '%s'", imageRefs),
|
||||
),
|
||||
startTime,
|
||||
)
|
||||
return
|
||||
}
|
||||
policyContext.JSONContext().Restore()
|
||||
if err := internal.LoadContext(ctx, e, policyContext, *rule); err != nil {
|
||||
internal.AddRuleResponse(
|
||||
&resp.PolicyResponse,
|
||||
internal.RuleError(rule, engineapi.ImageVerify, "failed to load context", err),
|
||||
startTime,
|
||||
)
|
||||
return
|
||||
}
|
||||
ruleCopy, err := substituteVariables(rule, policyContext.JSONContext(), logger)
|
||||
if err != nil {
|
||||
internal.AddRuleResponse(
|
||||
&resp.PolicyResponse,
|
||||
internal.RuleError(rule, engineapi.ImageVerify, "failed to substitute variables", err),
|
||||
startTime,
|
||||
)
|
||||
return
|
||||
}
|
||||
iv := internal.NewImageVerifier(
|
||||
logger,
|
||||
e.rclient,
|
||||
policyContext,
|
||||
ruleCopy,
|
||||
ivm,
|
||||
)
|
||||
for _, imageVerify := range ruleCopy.VerifyImages {
|
||||
for _, r := range iv.Verify(ctx, imageVerify, ruleImages, e.configuration) {
|
||||
internal.AddRuleResponse(&resp.PolicyResponse, r, startTime)
|
||||
}
|
||||
}
|
||||
e.doVerifyAndPatch(ctx, logger, policyContext, rule, resp, ivm)
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -132,6 +61,88 @@ func (e *engine) verifyAndPatchImages(
|
|||
return resp, ivm
|
||||
}
|
||||
|
||||
func (e *engine) doVerifyAndPatch(
|
||||
ctx context.Context,
|
||||
logger logr.Logger,
|
||||
policyContext engineapi.PolicyContext,
|
||||
rule *kyvernov1.Rule,
|
||||
resp *engineapi.EngineResponse,
|
||||
ivm *engineapi.ImageVerificationMetadata,
|
||||
) {
|
||||
if len(rule.VerifyImages) == 0 {
|
||||
return
|
||||
}
|
||||
startTime := time.Now()
|
||||
logger = internal.LoggerWithRule(logger, *rule)
|
||||
kindsInPolicy := append(rule.MatchResources.GetKinds(), rule.ExcludeResources.GetKinds()...)
|
||||
subresourceGVKToAPIResource := GetSubresourceGVKToAPIResourceMap(e.client, kindsInPolicy, policyContext)
|
||||
|
||||
if !matches(logger, rule, policyContext, subresourceGVKToAPIResource, e.configuration) {
|
||||
return
|
||||
}
|
||||
|
||||
// check if there is a corresponding policy exception
|
||||
ruleResp := hasPolicyExceptions(logger, engineapi.ImageVerify, e.exceptionSelector, policyContext, rule, subresourceGVKToAPIResource, e.configuration)
|
||||
if ruleResp != nil {
|
||||
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, *ruleResp)
|
||||
return
|
||||
}
|
||||
|
||||
logger.V(3).Info("processing image verification rule")
|
||||
|
||||
ruleImages, imageRefs, err := e.extractMatchingImages(policyContext, rule)
|
||||
if err != nil {
|
||||
internal.AddRuleResponse(
|
||||
&resp.PolicyResponse,
|
||||
internal.RuleError(rule, engineapi.ImageVerify, "failed to extract images", err),
|
||||
startTime,
|
||||
)
|
||||
return
|
||||
}
|
||||
if len(ruleImages) == 0 {
|
||||
internal.AddRuleResponse(
|
||||
&resp.PolicyResponse,
|
||||
internal.RuleSkip(
|
||||
rule,
|
||||
engineapi.ImageVerify,
|
||||
fmt.Sprintf("skip run verification as image in resource not found in imageRefs '%s'", imageRefs),
|
||||
),
|
||||
startTime,
|
||||
)
|
||||
return
|
||||
}
|
||||
policyContext.JSONContext().Restore()
|
||||
if err := internal.LoadContext(ctx, e, policyContext, *rule); err != nil {
|
||||
internal.AddRuleResponse(
|
||||
&resp.PolicyResponse,
|
||||
internal.RuleError(rule, engineapi.ImageVerify, "failed to load context", err),
|
||||
startTime,
|
||||
)
|
||||
return
|
||||
}
|
||||
ruleCopy, err := substituteVariables(rule, policyContext.JSONContext(), logger)
|
||||
if err != nil {
|
||||
internal.AddRuleResponse(
|
||||
&resp.PolicyResponse,
|
||||
internal.RuleError(rule, engineapi.ImageVerify, "failed to substitute variables", err),
|
||||
startTime,
|
||||
)
|
||||
return
|
||||
}
|
||||
iv := internal.NewImageVerifier(
|
||||
logger,
|
||||
e.rclient,
|
||||
policyContext,
|
||||
ruleCopy,
|
||||
ivm,
|
||||
)
|
||||
for _, imageVerify := range ruleCopy.VerifyImages {
|
||||
for _, r := range iv.Verify(ctx, imageVerify, ruleImages, e.configuration) {
|
||||
internal.AddRuleResponse(&resp.PolicyResponse, r, startTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getMatchingImages(images map[string]map[string]apiutils.ImageInfo, rule *kyvernov1.Rule) ([]apiutils.ImageInfo, string) {
|
||||
imageInfos := []apiutils.ImageInfo{}
|
||||
imageRefs := []string{}
|
||||
|
|
|
@ -16,6 +16,8 @@ import (
|
|||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
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/registryclient"
|
||||
apiutils "github.com/kyverno/kyverno/pkg/utils/api"
|
||||
"github.com/kyverno/kyverno/pkg/utils/jsonpointer"
|
||||
|
@ -285,8 +287,8 @@ func (iv *ImageVerifier) verifyAttestors(
|
|||
imageVerify kyvernov1.ImageVerification,
|
||||
imageInfo apiutils.ImageInfo,
|
||||
predicateType string,
|
||||
) (*engineapi.RuleResponse, *cosign.Response) {
|
||||
var cosignResponse *cosign.Response
|
||||
) (*engineapi.RuleResponse, *images.Response) {
|
||||
var cosignResponse *images.Response
|
||||
image := imageInfo.String()
|
||||
for i, attestorSet := range attestors {
|
||||
var err error
|
||||
|
@ -341,8 +343,8 @@ func (iv *ImageVerifier) verifyAttestations(
|
|||
|
||||
for _, a := range attestor.Entries {
|
||||
entryPath := fmt.Sprintf("%s.entries[%d]", attestorPath, i)
|
||||
opts, subPath := iv.buildOptionsAndPath(a, imageVerify, image, &imageVerify.Attestations[i])
|
||||
cosignResp, err := cosign.FetchAttestations(ctx, iv.rclient, *opts)
|
||||
v, opts, subPath := iv.buildVerifier(a, imageVerify, image, &imageVerify.Attestations[i])
|
||||
cosignResp, err := v.FetchAttestations(ctx, *opts)
|
||||
if err != nil {
|
||||
iv.logger.Error(err, "failed to fetch attestations")
|
||||
return iv.handleRegistryErrors(image, err), ""
|
||||
|
@ -386,7 +388,7 @@ func (iv *ImageVerifier) verifyAttestorSet(
|
|||
imageVerify kyvernov1.ImageVerification,
|
||||
imageInfo apiutils.ImageInfo,
|
||||
path string,
|
||||
) (*cosign.Response, error) {
|
||||
) (*images.Response, error) {
|
||||
var errorList []error
|
||||
verifiedCount := 0
|
||||
attestorSet = ExpandStaticKeys(attestorSet)
|
||||
|
@ -395,7 +397,7 @@ func (iv *ImageVerifier) verifyAttestorSet(
|
|||
|
||||
for i, a := range attestorSet.Entries {
|
||||
var entryError error
|
||||
var cosignResp *cosign.Response
|
||||
var cosignResp *images.Response
|
||||
attestorPath := fmt.Sprintf("%s.entries[%d]", path, i)
|
||||
iv.logger.V(4).Info("verifying attestorSet", "path", attestorPath)
|
||||
|
||||
|
@ -408,8 +410,8 @@ func (iv *ImageVerifier) verifyAttestorSet(
|
|||
cosignResp, entryError = iv.verifyAttestorSet(ctx, *nestedAttestorSet, imageVerify, imageInfo, attestorPath)
|
||||
}
|
||||
} else {
|
||||
opts, subPath := iv.buildOptionsAndPath(a, imageVerify, image, nil)
|
||||
cosignResp, entryError = cosign.VerifySignature(ctx, iv.rclient, *opts)
|
||||
v, opts, subPath := iv.buildVerifier(a, imageVerify, image, nil)
|
||||
cosignResp, entryError = v.VerifySignature(ctx, *opts)
|
||||
if entryError != nil {
|
||||
entryError = fmt.Errorf("%s: %w", attestorPath+subPath, entryError)
|
||||
}
|
||||
|
@ -431,27 +433,49 @@ func (iv *ImageVerifier) verifyAttestorSet(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
func (iv *ImageVerifier) buildOptionsAndPath(attestor kyvernov1.Attestor, imageVerify kyvernov1.ImageVerification, image string, attestation *kyvernov1.Attestation) (*cosign.Options, string) {
|
||||
path := ""
|
||||
opts := &cosign.Options{
|
||||
ImageRef: image,
|
||||
Repository: imageVerify.Repository,
|
||||
Annotations: imageVerify.Annotations,
|
||||
func (iv *ImageVerifier) buildVerifier(
|
||||
attestor kyvernov1.Attestor,
|
||||
imageVerify kyvernov1.ImageVerification,
|
||||
image string,
|
||||
attestation *kyvernov1.Attestation,
|
||||
) (images.ImageVerifier, *images.Options, string) {
|
||||
switch imageVerify.Type {
|
||||
case kyvernov1.NotaryV2:
|
||||
return iv.buildNotaryV2Verifier(attestor, imageVerify, image)
|
||||
default:
|
||||
return iv.buildCosignVerifier(attestor, imageVerify, image, attestation)
|
||||
}
|
||||
}
|
||||
|
||||
func (iv *ImageVerifier) buildCosignVerifier(
|
||||
attestor kyvernov1.Attestor,
|
||||
imageVerify kyvernov1.ImageVerification,
|
||||
image string,
|
||||
attestation *kyvernov1.Attestation,
|
||||
) (images.ImageVerifier, *images.Options, string) {
|
||||
path := ""
|
||||
opts := &images.Options{
|
||||
ImageRef: image,
|
||||
Repository: imageVerify.Repository,
|
||||
Annotations: imageVerify.Annotations,
|
||||
RegistryClient: iv.rclient,
|
||||
}
|
||||
|
||||
if imageVerify.Roots != "" {
|
||||
opts.Roots = imageVerify.Roots
|
||||
}
|
||||
|
||||
if attestation != nil {
|
||||
opts.PredicateType = attestation.PredicateType
|
||||
opts.FetchAttestations = true
|
||||
}
|
||||
|
||||
if attestor.Keys != nil {
|
||||
path = path + ".keys"
|
||||
if attestor.Keys.PublicKeys != "" {
|
||||
opts.Key = attestor.Keys.PublicKeys
|
||||
} else if attestor.Keys.Secret != nil {
|
||||
opts.Key = fmt.Sprintf("k8s://%s/%s", attestor.Keys.Secret.Namespace,
|
||||
attestor.Keys.Secret.Name)
|
||||
opts.Key = fmt.Sprintf("k8s://%s/%s", attestor.Keys.Secret.Namespace, attestor.Keys.Secret.Name)
|
||||
} else if attestor.Keys.KMS != "" {
|
||||
opts.Key = attestor.Keys.KMS
|
||||
}
|
||||
|
@ -471,18 +495,38 @@ func (iv *ImageVerifier) buildOptionsAndPath(attestor kyvernov1.Attestor, imageV
|
|||
if attestor.Keyless.Rekor != nil {
|
||||
opts.RekorURL = attestor.Keyless.Rekor.URL
|
||||
}
|
||||
|
||||
opts.Roots = attestor.Keyless.Roots
|
||||
opts.Issuer = attestor.Keyless.Issuer
|
||||
opts.Subject = attestor.Keyless.Subject
|
||||
opts.AdditionalExtensions = attestor.Keyless.AdditionalExtensions
|
||||
}
|
||||
|
||||
if attestor.Repository != "" {
|
||||
opts.Repository = attestor.Repository
|
||||
}
|
||||
|
||||
if attestor.Annotations != nil {
|
||||
opts.Annotations = attestor.Annotations
|
||||
}
|
||||
return opts, path
|
||||
|
||||
return cosign.NewVerifier(), opts, path
|
||||
}
|
||||
|
||||
func (iv *ImageVerifier) buildNotaryV2Verifier(
|
||||
attestor kyvernov1.Attestor,
|
||||
imageVerify kyvernov1.ImageVerification,
|
||||
image string,
|
||||
) (images.ImageVerifier, *images.Options, string) {
|
||||
path := ""
|
||||
opts := &images.Options{
|
||||
ImageRef: image,
|
||||
Cert: attestor.Certificates.Certificate,
|
||||
CertChain: attestor.Certificates.CertificateChain,
|
||||
RegistryClient: iv.rclient,
|
||||
}
|
||||
|
||||
return notaryv2.NewVerifier(), opts, path
|
||||
}
|
||||
|
||||
func (iv *ImageVerifier) verifyAttestation(statements []map[string]interface{}, attestation kyvernov1.Attestation, imageInfo apiutils.ImageInfo) error {
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"testing"
|
||||
|
||||
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
client "github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
||||
|
@ -23,7 +22,7 @@ import (
|
|||
|
||||
func testMutate(
|
||||
ctx context.Context,
|
||||
client dclient.Interface,
|
||||
client client.Interface,
|
||||
rclient registryclient.Client,
|
||||
pContext *PolicyContext,
|
||||
contextLoader engineapi.ContextLoaderFactory,
|
||||
|
|
|
@ -157,7 +157,7 @@ func MatchesResourceDescription(subresourceGVKToAPIResource map[string]*metav1.A
|
|||
|
||||
var reasonsForFailure []error
|
||||
if policyNamespace != "" && policyNamespace != resourceRef.GetNamespace() {
|
||||
return fmt.Errorf(" The policy and resource namespace are different. Therefore, policy skip this resource.")
|
||||
return fmt.Errorf("policy and resource namespaces mismatch")
|
||||
}
|
||||
|
||||
if len(rule.MatchResources.Any) > 0 {
|
||||
|
|
39
pkg/images/verifier.go
Normal file
39
pkg/images/verifier.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package images
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/kyverno/kyverno/pkg/registryclient"
|
||||
)
|
||||
|
||||
type ImageVerifier interface {
|
||||
// VerifySignature verifies that the image has the expected signatures
|
||||
VerifySignature(ctx context.Context, opts Options) (*Response, error)
|
||||
// FetchAttestations retrieves signed attestations and decodes them into in-toto statements
|
||||
// https://github.com/in-toto/attestation/blob/main/spec/README.md#statement
|
||||
FetchAttestations(ctx context.Context, opts Options) (*Response, error)
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
ImageRef string
|
||||
RegistryClient registryclient.Client
|
||||
FetchAttestations bool
|
||||
Key string
|
||||
Cert string
|
||||
CertChain string
|
||||
Roots string
|
||||
Subject string
|
||||
Issuer string
|
||||
AdditionalExtensions map[string]string
|
||||
Annotations map[string]string
|
||||
Repository string
|
||||
RekorURL string
|
||||
SignatureAlgorithm string
|
||||
PredicateType string
|
||||
Identities string
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Digest string
|
||||
Statements []map[string]interface{}
|
||||
}
|
132
pkg/notaryv2/notaryv2.go
Normal file
132
pkg/notaryv2/notaryv2.go
Normal file
|
@ -0,0 +1,132 @@
|
|||
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")
|
||||
}
|
125
pkg/notaryv2/registry.go
Normal file
125
pkg/notaryv2/registry.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
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())
|
||||
}
|
35
pkg/notaryv2/truststore.go
Normal file
35
pkg/notaryv2/truststore.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package notaryv2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
|
||||
"github.com/notaryproject/notation-go/verifier/truststore"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type simpleTrustStore struct {
|
||||
name string
|
||||
storeType truststore.Type
|
||||
certs []*x509.Certificate
|
||||
}
|
||||
|
||||
func NewTrustStore(name string, certs []*x509.Certificate) truststore.X509TrustStore {
|
||||
return &simpleTrustStore{
|
||||
name: name,
|
||||
storeType: truststore.TypeCA,
|
||||
certs: certs,
|
||||
}
|
||||
}
|
||||
|
||||
func (ts *simpleTrustStore) GetCertificates(ctx context.Context, storeType truststore.Type, name string) ([]*x509.Certificate, error) {
|
||||
if storeType != ts.storeType {
|
||||
return nil, errors.Errorf("invalid truststore type")
|
||||
}
|
||||
|
||||
if name != ts.name {
|
||||
return nil, errors.Errorf("invalid truststore name")
|
||||
}
|
||||
|
||||
return ts.certs, nil
|
||||
}
|
|
@ -9,7 +9,7 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
|
||||
"github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
|
||||
"github.com/chrismellard/docker-credential-acr-env/pkg/credhelper"
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/authn/github"
|
||||
|
@ -49,8 +49,8 @@ var (
|
|||
|
||||
// Client provides registry related objects.
|
||||
type Client interface {
|
||||
// getKeychain provides keychain object.
|
||||
getKeychain() authn.Keychain
|
||||
// Keychain provides the configured credentials
|
||||
Keychain() authn.Keychain
|
||||
|
||||
// getTransport provides transport object.
|
||||
getTransport() http.RoundTripper
|
||||
|
@ -61,6 +61,9 @@ type Client interface {
|
|||
|
||||
// BuildRemoteOption builds remote.Option based on client.
|
||||
BuildRemoteOption(context.Context) remote.Option
|
||||
|
||||
// RefreshKeychainPullSecrets loads fresh data from pull secrets (if non-empty) and updates Keychain.
|
||||
RefreshKeychainPullSecrets(ctx context.Context) error
|
||||
}
|
||||
|
||||
type client struct {
|
||||
|
@ -165,7 +168,7 @@ func (c *client) BuildRemoteOption(ctx context.Context) remote.Option {
|
|||
// FetchImageDescriptor fetches Descriptor from registry with given imageRef
|
||||
// and provides access to metadata about remote artifact.
|
||||
func (c *client) FetchImageDescriptor(ctx context.Context, imageRef string) (*gcrremote.Descriptor, error) {
|
||||
if err := c.refreshKeychainPullSecrets(ctx); err != nil {
|
||||
if err := c.RefreshKeychainPullSecrets(ctx); err != nil {
|
||||
return nil, fmt.Errorf("failed to refresh image pull secrets, error: %v", err)
|
||||
}
|
||||
parsedRef, err := name.ParseReference(imageRef)
|
||||
|
@ -179,16 +182,15 @@ func (c *client) FetchImageDescriptor(ctx context.Context, imageRef string) (*gc
|
|||
return desc, nil
|
||||
}
|
||||
|
||||
// refreshKeychainPullSecrets loads fresh data from pull secrets and updates Keychain.
|
||||
// If pull secrets are empty - returns.
|
||||
func (c *client) refreshKeychainPullSecrets(ctx context.Context) error {
|
||||
// refreshKeychainPullSecrets loads fresh data from pull secrets (if non-empty) and updates Keychain.
|
||||
func (c *client) RefreshKeychainPullSecrets(ctx context.Context) error {
|
||||
if c.pullSecretRefresher == nil {
|
||||
return nil
|
||||
}
|
||||
return c.pullSecretRefresher(ctx, c)
|
||||
}
|
||||
|
||||
func (c *client) getKeychain() authn.Keychain {
|
||||
func (c *client) Keychain() authn.Keychain {
|
||||
return c.keychain
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ func TestInitClientWithEmptyOptions(t *testing.T) {
|
|||
c, err := New()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, defaultTransport == c.getTransport())
|
||||
assert.Assert(t, c.getKeychain() != nil)
|
||||
assert.Assert(t, c.Keychain() != nil)
|
||||
}
|
||||
|
||||
func TestInitClientWithInsecureRegistryOption(t *testing.T) {
|
||||
|
@ -27,5 +27,5 @@ func TestInitClientWithInsecureRegistryOption(t *testing.T) {
|
|||
gotInsecureSkipVerify := c.getTransport().(*http.Transport).TLSClientConfig.InsecureSkipVerify
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, expInsecureSkipVerify == gotInsecureSkipVerify)
|
||||
assert.Assert(t, c.getKeychain() != nil)
|
||||
assert.Assert(t, c.Keychain() != nil)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue