1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 02:18:15 +00:00

feat: add skipImageReferences in verify images (#8633)

* feat: add skipImageReferences in verify images

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

* fix: chainsaw tests

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

* fix: chainsaw-test.yaml

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

* fix: typo in assert

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

---------

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Vishal Choudhary 2024-01-23 17:57:39 +05:30 committed by GitHub
parent d47684c0d9
commit 87c7ce254a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 899 additions and 94 deletions

View file

@ -56,6 +56,13 @@ type ImageVerification struct {
// +kubebuilder:validation:Optional
ImageReferences []string `json:"imageReferences,omitempty" yaml:"imageReferences,omitempty"`
// SkipImageReferences is a list of matching image reference patterns that should be skipped.
// At least one pattern in the list must match the image for the rule to be skipped. Each image reference
// consists of a registry address (defaults to docker.io), repository, image, and tag (defaults to latest).
// Wildcards ('*' and '?') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.
// +kubebuilder:validation:Optional
SkipImageReferences []string `json:"skipImageReferences,omitempty" yaml:"skipImageReferences,omitempty"`
// Deprecated. Use StaticKeyAttestor instead.
Key string `json:"key,omitempty" yaml:"key,omitempty"`

View file

@ -758,6 +758,11 @@ func (in *ImageVerification) DeepCopyInto(out *ImageVerification) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.SkipImageReferences != nil {
in, out := &in.SkipImageReferences, &out.SkipImageReferences
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.AdditionalExtensions != nil {
in, out := &in.AdditionalExtensions, &out.AdditionalExtensions
*out = make(map[string]string, len(*in))

View file

@ -21,6 +21,13 @@ type ImageVerification struct {
// +kubebuilder:validation:Optional
ImageReferences []string `json:"imageReferences,omitempty" yaml:"imageReferences,omitempty"`
// SkipImageReferences is a list of matching image reference patterns that should be skipped.
// At least one pattern in the list must match the image for the rule to be skipped. Each image reference
// consists of a registry address (defaults to docker.io), repository, image, and tag (defaults to latest).
// Wildcards ('*' and '?') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.
// +kubebuilder:validation:Optional
SkipImageReferences []string `json:"skipImageReferences,omitempty" yaml:"skipImageReferences,omitempty"`
// Attestors specified the required attestors (i.e. authorities)
// +kubebuilder:validation:Optional
Attestors []kyvernov1.AttestorSet `json:"attestors,omitempty" yaml:"attestors,omitempty"`

View file

@ -376,6 +376,11 @@ func (in *ImageVerification) DeepCopyInto(out *ImageVerification) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.SkipImageReferences != nil {
in, out := &in.SkipImageReferences, &out.SkipImageReferences
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Attestors != nil {
in, out := &in.Attestors, &out.Attestors
*out = make([]v1.AttestorSet, len(*in))

View file

@ -14291,6 +14291,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -18849,6 +18860,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -23048,6 +23070,17 @@ spec:
i.e. have matched passed a signature or attestation
check.
type: boolean
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
type:
description: Type specifies the method of signature validation.
The allowed options are Cosign and Notary. By default
@ -27603,6 +27636,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -32099,6 +32143,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -36658,6 +36713,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -40858,6 +40924,17 @@ spec:
i.e. have matched passed a signature or attestation
check.
type: boolean
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
type:
description: Type specifies the method of signature validation.
The allowed options are Cosign and Notary. By default
@ -45413,6 +45490,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string

View file

@ -4340,6 +4340,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -8898,6 +8909,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -13097,6 +13119,17 @@ spec:
i.e. have matched passed a signature or attestation
check.
type: boolean
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
type:
description: Type specifies the method of signature validation.
The allowed options are Cosign and Notary. By default
@ -17652,6 +17685,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string

View file

@ -4341,6 +4341,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -8900,6 +8911,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -13100,6 +13122,17 @@ spec:
i.e. have matched passed a signature or attestation
check.
type: boolean
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
type:
description: Type specifies the method of signature validation.
The allowed options are Cosign and Notary. By default
@ -17655,6 +17688,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string

View file

@ -4340,6 +4340,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -8898,6 +8909,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -13097,6 +13119,17 @@ spec:
i.e. have matched passed a signature or attestation
check.
type: boolean
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
type:
description: Type specifies the method of signature validation.
The allowed options are Cosign and Notary. By default
@ -17652,6 +17685,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string

View file

@ -4341,6 +4341,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -8900,6 +8911,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -13100,6 +13122,17 @@ spec:
i.e. have matched passed a signature or attestation
check.
type: boolean
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
type:
description: Type specifies the method of signature validation.
The allowed options are Cosign and Notary. By default
@ -17655,6 +17688,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string

View file

@ -14510,6 +14510,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -19068,6 +19079,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -23267,6 +23289,17 @@ spec:
i.e. have matched passed a signature or attestation
check.
type: boolean
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
type:
description: Type specifies the method of signature validation.
The allowed options are Cosign and Notary. By default
@ -27822,6 +27855,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -32320,6 +32364,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -36879,6 +36934,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string
@ -41079,6 +41145,17 @@ spec:
i.e. have matched passed a signature or attestation
check.
type: boolean
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped. At
least one pattern in the list must match the image for
the rule to be skipped. Each image reference consists
of a registry address (defaults to docker.io), repository,
image, and tag (defaults to latest). Wildcards (''*''
and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
type:
description: Type specifies the method of signature validation.
The allowed options are Cosign and Notary. By default
@ -45634,6 +45711,17 @@ spec:
roots:
description: Deprecated. Use KeylessAttestor instead.
type: string
skipImageReferences:
description: 'SkipImageReferences is a list of matching
image reference patterns that should be skipped.
At least one pattern in the list must match the
image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io),
repository, image, and tag (defaults to latest).
Wildcards (''*'' and ''?'') are allowed. See: https://kubernetes.io/docs/concepts/containers/images.'
items:
type: string
type: array
subject:
description: Deprecated. Use KeylessAttestor instead.
type: string

View file

@ -2187,6 +2187,20 @@ Wildcards (&lsquo;*&rsquo; and &lsquo;?&rsquo;) are allowed. See: <a href="https
</tr>
<tr>
<td>
<code>skipImageReferences</code><br/>
<em>
[]string
</em>
</td>
<td>
<p>SkipImageReferences is a list of matching image reference patterns that should be skipped.
At least one pattern in the list must match the image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io), repository, image, and tag (defaults to latest).
Wildcards (&lsquo;*&rsquo; and &lsquo;?&rsquo;) are allowed. See: <a href="https://kubernetes.io/docs/concepts/containers/images">https://kubernetes.io/docs/concepts/containers/images</a>.</p>
</td>
</tr>
<tr>
<td>
<code>key</code><br/>
<em>
string
@ -9033,6 +9047,20 @@ Wildcards (&lsquo;*&rsquo; and &lsquo;?&rsquo;) are allowed. See: <a href="https
</tr>
<tr>
<td>
<code>skipImageReferences</code><br/>
<em>
[]string
</em>
</td>
<td>
<p>SkipImageReferences is a list of matching image reference patterns that should be skipped.
At least one pattern in the list must match the image for the rule to be skipped. Each image reference
consists of a registry address (defaults to docker.io), repository, image, and tag (defaults to latest).
Wildcards (&lsquo;*&rsquo; and &lsquo;?&rsquo;) are allowed. See: <a href="https://kubernetes.io/docs/concepts/containers/images">https://kubernetes.io/docs/concepts/containers/images</a>.</p>
</td>
</tr>
<tr>
<td>
<code>attestors</code><br/>
<em>
<a href="#kyverno.io/v1.AttestorSet">

View file

@ -28,6 +28,7 @@ type ImageVerificationApplyConfiguration struct {
Type *v1.ImageVerificationType `json:"type,omitempty"`
Image *string `json:"image,omitempty"`
ImageReferences []string `json:"imageReferences,omitempty"`
SkipImageReferences []string `json:"skipImageReferences,omitempty"`
Key *string `json:"key,omitempty"`
Roots *string `json:"roots,omitempty"`
Subject *string `json:"subject,omitempty"`
@ -76,6 +77,16 @@ func (b *ImageVerificationApplyConfiguration) WithImageReferences(values ...stri
return b
}
// WithSkipImageReferences adds the given value to the SkipImageReferences field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the SkipImageReferences field.
func (b *ImageVerificationApplyConfiguration) WithSkipImageReferences(values ...string) *ImageVerificationApplyConfiguration {
for i := range values {
b.SkipImageReferences = append(b.SkipImageReferences, values[i])
}
return b
}
// WithKey sets the Key field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Key field is set to the value of the last call.

View file

@ -28,6 +28,7 @@ import (
type ImageVerificationApplyConfiguration struct {
Type *v1.ImageVerificationType `json:"type,omitempty"`
ImageReferences []string `json:"imageReferences,omitempty"`
SkipImageReferences []string `json:"skipImageReferences,omitempty"`
Attestors []kyvernov1.AttestorSetApplyConfiguration `json:"attestors,omitempty"`
Attestations []kyvernov1.AttestationApplyConfiguration `json:"attestations,omitempty"`
Repository *string `json:"repository,omitempty"`
@ -62,6 +63,16 @@ func (b *ImageVerificationApplyConfiguration) WithImageReferences(values ...stri
return b
}
// WithSkipImageReferences adds the given value to the SkipImageReferences field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the SkipImageReferences field.
func (b *ImageVerificationApplyConfiguration) WithSkipImageReferences(values ...string) *ImageVerificationApplyConfiguration {
for i := range values {
b.SkipImageReferences = append(b.SkipImageReferences, values[i])
}
return b
}
// WithAttestors adds the given value to the Attestors field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Attestors field.

View file

@ -10,13 +10,21 @@ import (
"gomodules.xyz/jsonpatch/v2"
)
type ImageVerificationMetadataStatus string
const (
ImageVerificationPass ImageVerificationMetadataStatus = "pass"
ImageVerificationFail ImageVerificationMetadataStatus = "fail"
ImageVerificationSkip ImageVerificationMetadataStatus = "skip"
)
type ImageVerificationMetadata struct {
Data map[string]bool `json:"data"`
Data map[string]ImageVerificationMetadataStatus `json:"data"`
}
func (ivm *ImageVerificationMetadata) Add(image string, verified bool) {
func (ivm *ImageVerificationMetadata) Add(image string, verified ImageVerificationMetadataStatus) {
if ivm.Data == nil {
ivm.Data = make(map[string]bool)
ivm.Data = make(map[string]ImageVerificationMetadataStatus)
}
ivm.Data[image] = verified
}
@ -29,11 +37,22 @@ func (ivm *ImageVerificationMetadata) IsVerified(image string) bool {
if !ok {
return false
}
return verified == ImageVerificationPass || verified == ImageVerificationSkip
}
func (ivm *ImageVerificationMetadata) ImageVerificationStatus(image string) ImageVerificationMetadataStatus {
if ivm.Data == nil {
return ImageVerificationFail
}
verified, ok := ivm.Data[image]
if !ok {
return ImageVerificationFail
}
return verified
}
func ParseImageMetadata(jsonData string) (*ImageVerificationMetadata, error) {
var data map[string]bool
var data map[string]ImageVerificationMetadataStatus
if err := json.Unmarshal([]byte(jsonData), &data); err != nil {
return nil, err
}

View file

@ -10,7 +10,7 @@ import (
func TestImageVerificationMetadata_IsVerified(t *testing.T) {
type fields struct {
Data map[string]bool
Data map[string]ImageVerificationMetadataStatus
}
type args struct {
image string
@ -22,8 +22,8 @@ func TestImageVerificationMetadata_IsVerified(t *testing.T) {
want bool
}{{
fields: fields{
Data: map[string]bool{
"test": true,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
},
},
args: args{
@ -32,8 +32,8 @@ func TestImageVerificationMetadata_IsVerified(t *testing.T) {
want: true,
}, {
fields: fields{
Data: map[string]bool{
"test": true,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
},
},
args: args{
@ -42,8 +42,8 @@ func TestImageVerificationMetadata_IsVerified(t *testing.T) {
want: false,
}, {
fields: fields{
Data: map[string]bool{
"test2": false,
Data: map[string]ImageVerificationMetadataStatus{
"test2": ImageVerificationFail,
},
},
args: args{
@ -70,11 +70,11 @@ func TestImageVerificationMetadata_IsVerified(t *testing.T) {
func TestImageVerificationMetadata_Add(t *testing.T) {
type fields struct {
Data map[string]bool
Data map[string]ImageVerificationMetadataStatus
}
type args struct {
image string
verified bool
verified ImageVerificationMetadataStatus
}
tests := []struct {
name string
@ -83,43 +83,43 @@ func TestImageVerificationMetadata_Add(t *testing.T) {
want *ImageVerificationMetadata
}{{
fields: fields{
Data: map[string]bool{
"test": true,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
},
},
args: args{
image: "test",
verified: false,
verified: ImageVerificationFail,
},
want: &ImageVerificationMetadata{
Data: map[string]bool{
"test": false,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationFail,
},
},
}, {
args: args{
image: "test",
verified: false,
verified: ImageVerificationFail,
},
want: &ImageVerificationMetadata{
Data: map[string]bool{
"test": false,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationFail,
},
},
}, {
fields: fields{
Data: map[string]bool{
"test": true,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
},
},
args: args{
image: "test2",
verified: false,
verified: ImageVerificationFail,
},
want: &ImageVerificationMetadata{
Data: map[string]bool{
"test": true,
"test2": false,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
"test2": ImageVerificationFail,
},
},
}}
@ -152,21 +152,21 @@ func TestParseImageMetadata(t *testing.T) {
wantErr: true,
}, {
args: args{
jsonData: `{"test":true}`,
jsonData: `{"test":"pass"}`,
},
want: &ImageVerificationMetadata{
Data: map[string]bool{
"test": true,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
},
},
}, {
args: args{
jsonData: `{"test":true,"test2":false}`,
jsonData: `{"test":"pass","test2":"fail"}`,
},
want: &ImageVerificationMetadata{
Data: map[string]bool{
"test": true,
"test2": false,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
"test2": ImageVerificationFail,
},
},
}}
@ -186,7 +186,7 @@ func TestParseImageMetadata(t *testing.T) {
func TestImageVerificationMetadata_IsEmpty(t *testing.T) {
type fields struct {
Data map[string]bool
Data map[string]ImageVerificationMetadataStatus
}
tests := []struct {
name string
@ -194,8 +194,8 @@ func TestImageVerificationMetadata_IsEmpty(t *testing.T) {
want bool
}{{
fields: fields{
Data: map[string]bool{
"test": false,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationFail,
},
},
want: false,
@ -216,7 +216,7 @@ func TestImageVerificationMetadata_IsEmpty(t *testing.T) {
func TestImageVerificationMetadata_Merge(t *testing.T) {
type fields struct {
Data map[string]bool
Data map[string]ImageVerificationMetadataStatus
}
type args struct {
other ImageVerificationMetadata
@ -230,39 +230,39 @@ func TestImageVerificationMetadata_Merge(t *testing.T) {
want: &ImageVerificationMetadata{},
}, {
fields: fields{
Data: map[string]bool{
"test": true,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
},
},
args: args{
other: ImageVerificationMetadata{
Data: map[string]bool{
"test": false,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationFail,
},
},
},
want: &ImageVerificationMetadata{
Data: map[string]bool{
"test": false,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationFail,
},
},
}, {
fields: fields{
Data: map[string]bool{
"test": true,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
},
},
args: args{
other: ImageVerificationMetadata{
Data: map[string]bool{
"test2": false,
Data: map[string]ImageVerificationMetadataStatus{
"test2": ImageVerificationFail,
},
},
},
want: &ImageVerificationMetadata{
Data: map[string]bool{
"test": true,
"test2": false,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
"test2": ImageVerificationFail,
},
},
}}
@ -297,7 +297,7 @@ func Test_makeAnnotationKeyForJSONPatch(t *testing.T) {
func TestImageVerificationMetadata_Patches(t *testing.T) {
type fields struct {
Data map[string]bool
Data map[string]ImageVerificationMetadataStatus
}
type args struct {
hasAnnotations bool
@ -311,8 +311,8 @@ func TestImageVerificationMetadata_Patches(t *testing.T) {
wantErr bool
}{{
fields: fields{
Data: map[string]bool{
"test": true,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
},
},
args: args{
@ -321,12 +321,12 @@ func TestImageVerificationMetadata_Patches(t *testing.T) {
},
want: []string{
`{"op":"add","path":"/metadata/annotations","value":{}}`,
`{"op":"add","path":"/metadata/annotations/kyverno.io~1verify-images","value":"{\"test\":true}"}`,
`{"op":"add","path":"/metadata/annotations/kyverno.io~1verify-images","value":"{\"test\":\"pass\"}"}`,
},
}, {
fields: fields{
Data: map[string]bool{
"test": true,
Data: map[string]ImageVerificationMetadataStatus{
"test": ImageVerificationPass,
},
},
args: args{
@ -334,7 +334,7 @@ func TestImageVerificationMetadata_Patches(t *testing.T) {
log: logr.Discard(),
},
want: []string{
`{"op":"add","path":"/metadata/annotations/kyverno.io~1verify-images","value":"{\"test\":true}"}`,
`{"op":"add","path":"/metadata/annotations/kyverno.io~1verify-images","value":"{\"test\":\"pass\"}"}`,
},
}, {
args: args{

View file

@ -44,14 +44,21 @@ type RuleResponse struct {
podSecurityChecks *PodSecurityChecks
// exception is the exception applied (if any)
exception *kyvernov2beta1.PolicyException
// emitWarning enable passing rule message as warning to api server warning header
emitWarning bool
}
func NewRuleResponse(name string, ruleType RuleType, msg string, status RuleStatus) *RuleResponse {
emitWarn := false
if status == RuleStatusError || status == RuleStatusFail || status == RuleStatusWarn {
emitWarn = true
}
return &RuleResponse{
name: name,
ruleType: ruleType,
message: msg,
status: status,
name: name,
ruleType: ruleType,
message: msg,
status: status,
emitWarning: emitWarn,
}
}
@ -105,6 +112,11 @@ func (r RuleResponse) WithStats(stats ExecutionStats) RuleResponse {
return r
}
func (r RuleResponse) WithEmitWarning(emitWarning bool) *RuleResponse {
r.emitWarning = emitWarning
return &r
}
func (r *RuleResponse) Stats() ExecutionStats {
return r.stats
}
@ -145,6 +157,10 @@ func (r *RuleResponse) Status() RuleStatus {
return r.status
}
func (r *RuleResponse) EmitWarning() bool {
return r.emitWarning
}
// HasStatus checks if rule status is in a given list
func (r *RuleResponse) HasStatus(status ...RuleStatus) bool {
for _, s := range status {

View file

@ -3,6 +3,7 @@ package validation
import (
"context"
"fmt"
"strings"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
@ -61,6 +62,8 @@ func (h validateImageHandler) Process(
}
}
skippedImages := make([]string, 0)
passedImages := make([]string, 0)
for _, v := range rule.VerifyImages {
imageVerify := v.Convert()
for _, infoMap := range policyContext.JSONContext().ImageInfo() {
@ -73,31 +76,45 @@ func (h validateImageHandler) Process(
}
logger.V(4).Info("validating image", "image", image)
if err := validateImage(policyContext, imageVerify, name, imageInfo, logger); err != nil {
if v, err := validateImage(policyContext, imageVerify, name, imageInfo, logger); err != nil {
return resource, handlers.WithFail(rule, engineapi.ImageVerify, err.Error())
} else if v == engineapi.ImageVerificationSkip {
skippedImages = append(skippedImages, image)
} else if v == engineapi.ImageVerificationPass {
passedImages = append(passedImages, image)
}
}
}
}
logger.V(4).Info("validated image", "rule", rule.Name)
return resource, handlers.WithPass(rule, engineapi.Validation, "image verified")
if len(passedImages) > 0 || len(passedImages)+len(skippedImages) == 0 {
if len(skippedImages) > 0 {
return resource, handlers.WithPass(rule, engineapi.Validation, strings.Join(append([]string{"image verified, skipped images:"}, skippedImages...), " "))
}
return resource, handlers.WithPass(rule, engineapi.Validation, "image verified")
} else {
return resource, handlers.WithSkip(rule, engineapi.Validation, strings.Join(append([]string{"image skipped, skipped images:"}, skippedImages...), " "))
}
}
func validateImage(ctx engineapi.PolicyContext, imageVerify *kyvernov1.ImageVerification, name string, imageInfo apiutils.ImageInfo, log logr.Logger) error {
func validateImage(ctx engineapi.PolicyContext, imageVerify *kyvernov1.ImageVerification, name string, imageInfo apiutils.ImageInfo, log logr.Logger) (engineapi.ImageVerificationMetadataStatus, error) {
var verified engineapi.ImageVerificationMetadataStatus
var err error
image := imageInfo.String()
if imageVerify.VerifyDigest && imageInfo.Digest == "" {
log.V(2).Info("missing digest", "image", imageInfo.String())
return fmt.Errorf("missing digest for %s", image)
return engineapi.ImageVerificationFail, fmt.Errorf("missing digest for %s", image)
}
newResource := ctx.NewResource()
if imageVerify.Required && newResource.Object != nil {
verified, err := engineutils.IsImageVerified(newResource, image, log)
verified, err = engineutils.IsImageVerified(newResource, image, log)
if err != nil {
return err
return engineapi.ImageVerificationFail, err
}
if !verified {
return fmt.Errorf("unverified image %s", image)
if verified == engineapi.ImageVerificationFail {
return engineapi.ImageVerificationFail, fmt.Errorf("unverified image %s", image)
}
}
return nil
return verified, nil
}

View file

@ -972,7 +972,7 @@ func Test_MarkImageVerified(t *testing.T) {
verified, err := engineutils.IsImageVerified(resource, image, logr.Discard())
assert.NilError(t, err)
assert.Equal(t, verified, true)
assert.Equal(t, verified, engineapi.ImageVerificationPass)
}
func testApplyPatches(t *testing.T, patches []jsonpatch.JsonPatchOperation) unstructured.Unstructured {
@ -1360,3 +1360,126 @@ func Test_changePolicyCacheVerificationNotary(t *testing.T) {
errorAssertionUtil(t, image, ivm, er)
assert.Check(t, secondOperationTime > firstOperationTime/10 && secondOperationTime < firstOperationTime*10, "cache entry not found, so image verification should not be from cache.", firstOperationTime, secondOperationTime)
}
var excludeVerifyImageNotaryPolicy = `{
"apiVersion": "kyverno.io/v2beta1",
"kind": "ClusterPolicy",
"metadata": {
"name": "check-image-notary"
},
"spec": {
"validationFailureAction": "Enforce",
"webhookTimeoutSeconds": 30,
"failurePolicy": "Fail",
"rules": [
{
"name": "verify-signature-notary",
"match": {
"any": [
{
"resources": {
"kinds": [
"Pod"
]
}
}
]
},
"verifyImages": [
{
"type": "Notary",
"imageReferences": [
"ghcr.io/*"
],
"skipImageReferences" : [
"ghcr.io/invalid-user*"
],
"attestors": [
{
"count": 1,
"entries": [
{
"certificates": {
"cert": "-----BEGIN CERTIFICATE-----\nMIIDTTCCAjWgAwIBAgIJAPI+zAzn4s0xMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV\nBAYTAlVTMQswCQYDVQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEPMA0GA1UECgwG\nTm90YXJ5MQ0wCwYDVQQDDAR0ZXN0MB4XDTIzMDUyMjIxMTUxOFoXDTMzMDUxOTIx\nMTUxOFowTDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0\ndGxlMQ8wDQYDVQQKDAZOb3RhcnkxDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3\nDQEBAQUAA4IBDwAwggEKAoIBAQDNhTwv+QMk7jEHufFfIFlBjn2NiJaYPgL4eBS+\nb+o37ve5Zn9nzRppV6kGsa161r9s2KkLXmJrojNy6vo9a6g6RtZ3F6xKiWLUmbAL\nhVTCfYw/2n7xNlVMjyyUpE+7e193PF8HfQrfDFxe2JnX5LHtGe+X9vdvo2l41R6m\nIia04DvpMdG4+da2tKPzXIuLUz/FDb6IODO3+qsqQLwEKmmUee+KX+3yw8I6G1y0\nVp0mnHfsfutlHeG8gazCDlzEsuD4QJ9BKeRf2Vrb0ywqNLkGCbcCWF2H5Q80Iq/f\nETVO9z88R7WheVdEjUB8UrY7ZMLdADM14IPhY2Y+tLaSzEVZAgMBAAGjMjAwMAkG\nA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0G\nCSqGSIb3DQEBCwUAA4IBAQBX7x4Ucre8AIUmXZ5PUK/zUBVOrZZzR1YE8w86J4X9\nkYeTtlijf9i2LTZMfGuG0dEVFN4ae3CCpBst+ilhIndnoxTyzP+sNy4RCRQ2Y/k8\nZq235KIh7uucq96PL0qsF9s2RpTKXxyOGdtp9+HO0Ty5txJE2txtLDUIVPK5WNDF\nByCEQNhtHgN6V20b8KU2oLBZ9vyB8V010dQz0NRTDLhkcvJig00535/LUylECYAJ\n5/jn6XKt6UYCQJbVNzBg/YPGc1RF4xdsGVDBben/JXpeGEmkdmXPILTKd9tZ5TC0\nuOKpF5rWAruB5PCIrquamOejpXV9aQA/K2JQDuc0mcKz\n-----END CERTIFICATE-----"
}
}
]
}
]
}
]
}
]
}
}`
var excludeVerifyImageNotaryResourcePass = `{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"creationTimestamp": null,
"labels": {
"run": "test"
},
"name": "test",
"namespace": "default"
},
"spec": {
"containers": [
{
"image": "ghcr.io/kyverno/test-verify-image:signed",
"name": "test",
"resources": {}
}
],
"dnsPolicy": "ClusterFirst",
"restartPolicy": "Always"
},
"status": {}
}`
var excludeVerifyImageNotaryResourceSkip = `{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"creationTimestamp": null,
"labels": {
"run": "test"
},
"name": "testskip",
"namespace": "default"
},
"spec": {
"containers": [
{
"image": "ghcr.io/invalid-user/invalid-image:v1",
"name": "test",
"resources": {}
}
],
"dnsPolicy": "ClusterFirst",
"restartPolicy": "Always"
},
"status": {}
}`
func Test_SkipImageReferences(t *testing.T) {
policyContextPass := buildContext(t, excludeVerifyImageNotaryPolicy, excludeVerifyImageNotaryResourcePass, "")
// Passes as image is included and not excluded
erPass, ivm := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContextPass, cfg)
assert.Equal(t, len(erPass.PolicyResponse.Rules), 1)
assert.Equal(t, erPass.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass,
fmt.Sprintf("expected: %v, got: %v, failure: %v",
engineapi.RuleStatusPass, erPass.PolicyResponse.Rules[0].Status(), erPass.PolicyResponse.Rules[0].Message()))
assert.Equal(t, ivm.IsEmpty(), false)
policyContextSkip := buildContext(t, excludeVerifyImageNotaryPolicy, excludeVerifyImageNotaryResourceSkip, "")
// Skipped as image is excluded
erSkip, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContextSkip, cfg)
assert.Equal(t, len(erSkip.PolicyResponse.Rules), 1)
assert.Equal(t, erSkip.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusSkip,
fmt.Sprintf("expected: %v, got: %v, failure: %v",
engineapi.RuleStatusPass, erSkip.PolicyResponse.Rules[0].Status(), erSkip.PolicyResponse.Rules[0].Message()))
}

View file

@ -69,7 +69,7 @@ func HasImageVerifiedAnnotationChanged(ctx engineapi.PolicyContext, log logr.Log
if newValue == oldValue {
return false
}
var newValueObj, oldValueObj map[string]bool
var newValueObj, oldValueObj map[string]engineapi.ImageVerificationMetadataStatus
err := json.Unmarshal([]byte(newValue), &newValueObj)
if err != nil {
log.Error(err, "failed to parse new resource annotation.")
@ -93,7 +93,7 @@ func HasImageVerifiedAnnotationChanged(ctx engineapi.PolicyContext, log logr.Log
return false
}
func matchImageReferences(imageReferences []string, image string) bool {
func matchReferences(imageReferences []string, image string) bool {
for _, imageRef := range imageReferences {
if wildcard.Match(imageRef, image) {
return true
@ -102,6 +102,23 @@ func matchImageReferences(imageReferences []string, image string) bool {
return false
}
func ruleStatusToImageVerificationStatus(ruleStatus engineapi.RuleStatus) engineapi.ImageVerificationMetadataStatus {
var imageVerificationResult engineapi.ImageVerificationMetadataStatus
switch ruleStatus {
case engineapi.RuleStatusPass:
imageVerificationResult = engineapi.ImageVerificationPass
case engineapi.RuleStatusSkip:
imageVerificationResult = engineapi.ImageVerificationSkip
case engineapi.RuleStatusWarn:
imageVerificationResult = engineapi.ImageVerificationSkip
case engineapi.RuleStatusFail:
imageVerificationResult = engineapi.ImageVerificationFail
default:
imageVerificationResult = engineapi.ImageVerificationFail
}
return imageVerificationResult
}
func isImageVerified(resource unstructured.Unstructured, image string, log logr.Logger) (bool, error) {
if resource.Object == nil {
return false, fmt.Errorf("nil resource")
@ -234,14 +251,14 @@ func (iv *ImageVerifier) Verify(
changed, err := iv.policyContext.JSONContext().HasChanged(pointer)
if err == nil && !changed {
iv.logger.V(4).Info("no change in image, skipping check", "image", image)
iv.ivm.Add(image, true)
iv.ivm.Add(image, engineapi.ImageVerificationPass)
continue
}
verified, err := isImageVerified(iv.policyContext.NewResource(), image, iv.logger)
if err == nil && verified {
iv.logger.Info("image was previously verified, skipping check", "image", image)
iv.ivm.Add(image, true)
iv.ivm.Add(image, engineapi.ImageVerificationPass)
continue
}
start := time.Now()
@ -295,7 +312,7 @@ func (iv *ImageVerifier) Verify(
if ruleResp != nil {
if len(imageVerify.Attestors) > 0 || len(imageVerify.Attestations) > 0 {
iv.ivm.Add(image, ruleResp.Status() == engineapi.RuleStatusPass)
iv.ivm.Add(image, ruleStatusToImageVerificationStatus(ruleResp.Status()))
}
responses = append(responses, ruleResp)
}
@ -324,8 +341,14 @@ func (iv *ImageVerifier) verifyImage(
return engineapi.RuleError(iv.rule.Name, engineapi.ImageVerify, fmt.Sprintf("failed to add image to context %s", image), err), ""
}
if len(imageVerify.Attestors) > 0 {
if !matchImageReferences(imageVerify.ImageReferences, image) {
return nil, ""
if !matchReferences(imageVerify.ImageReferences, image) {
return engineapi.RuleSkip(iv.rule.Name, engineapi.ImageVerify, fmt.Sprintf("skipping image reference image %s, policy %s ruleName %s", image, iv.policyContext.Policy().GetName(), iv.rule.Name)), ""
}
if matchReferences(imageVerify.SkipImageReferences, image) {
iv.logger.Info("skipping image reference", "image", image, "policy", iv.policyContext.Policy().GetName(), "ruleName", iv.rule.Name)
iv.ivm.Add(image, engineapi.ImageVerificationSkip)
return engineapi.RuleSkip(iv.rule.Name, engineapi.ImageVerify, fmt.Sprintf("skipping image reference image %s, policy %s ruleName %s", image, iv.policyContext.Policy().GetName(), iv.rule.Name)).WithEmitWarning(true), ""
}
ruleResp, cosignResp := iv.verifyAttestors(ctx, imageVerify.Attestors, imageVerify, imageInfo, "")
if ruleResp.Status() != engineapi.RuleStatusPass {

View file

@ -21,7 +21,6 @@ func ImageMatches(image string, imagePatterns []string) bool {
return true
}
}
return false
}
@ -66,19 +65,19 @@ func ExtractMatchingImages(
return matchingImages, imageRefs, nil
}
func IsImageVerified(resource unstructured.Unstructured, image string, log logr.Logger) (bool, error) {
func IsImageVerified(resource unstructured.Unstructured, image string, log logr.Logger) (engineapi.ImageVerificationMetadataStatus, error) {
if resource.Object == nil {
return false, fmt.Errorf("nil resource")
return engineapi.ImageVerificationFail, fmt.Errorf("nil resource")
}
if annotations := resource.GetAnnotations(); len(annotations) == 0 {
return false, nil
return engineapi.ImageVerificationFail, nil
} else if data, ok := annotations[kyverno.AnnotationImageVerify]; !ok {
log.V(2).Info("missing image metadata in annotation", "key", kyverno.AnnotationImageVerify)
return false, fmt.Errorf("image is not verified")
return engineapi.ImageVerificationFail, fmt.Errorf("image is not verified")
} else if ivm, err := engineapi.ParseImageMetadata(data); err != nil {
log.Error(err, "failed to parse image verification metadata", "data", data)
return false, fmt.Errorf("failed to parse image metadata: %w", err)
return engineapi.ImageVerificationFail, fmt.Errorf("failed to parse image metadata: %w", err)
} else {
return ivm.IsVerified(image), nil
return ivm.ImageVerificationStatus(image), nil
}
}

View file

@ -10,7 +10,7 @@ func GetWarningMessages(engineResponses []engineapi.EngineResponse) []string {
var warnings []string
for _, er := range engineResponses {
for _, rule := range er.PolicyResponse.Rules {
if rule.Status() != engineapi.RuleStatusPass && rule.Status() != engineapi.RuleStatusSkip {
if rule.EmitWarning() {
msg := fmt.Sprintf("policy %s.%s: %s", er.Policy().GetName(), rule.Name(), rule.Message())
warnings = append(warnings, msg)
}

View file

@ -2,7 +2,7 @@ apiVersion: v1
kind: Pod
metadata:
annotations:
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db":true}'
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db":"pass"}'
name: zulu
namespace: default
spec:

View file

@ -2,7 +2,7 @@ apiVersion: v1
kind: Pod
metadata:
annotations:
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db":true}'
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db":"pass"}'
name: zulu
namespace: default
spec:

View file

@ -2,7 +2,7 @@ apiVersion: v1
kind: Pod
metadata:
annotations:
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db":true}'
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db":"pass"}'
name: zulu
namespace: default
spec:

View file

@ -2,7 +2,7 @@ apiVersion: v1
kind: Pod
metadata:
annotations:
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db":true}'
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db":"pass"}'
name: zulu
namespace: default
spec:

View file

@ -2,7 +2,7 @@ apiVersion: v1
kind: Pod
metadata:
annotations:
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db":true}'
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db":"pass"}'
name: zulu
namespace: default
spec:

View file

@ -2,7 +2,7 @@ apiVersion: v1
kind: Pod
metadata:
annotations:
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu:latest":true}'
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu:latest":"pass"}'
name: zulu
namespace: default
spec:

View file

@ -2,7 +2,7 @@ apiVersion: v1
kind: Pod
metadata:
annotations:
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu:latest":true}'
kyverno.io/verify-images: '{"ghcr.io/chipzoller/zulu:latest":"pass"}'
name: zulu
namespace: default
spec:

View file

@ -0,0 +1,12 @@
## Description
This test tests the exclude image references attribute.
## Expected Behavior
This test creates a cluster policy with exclude image references attribute.
The pod that is not excluded will be verified and the pod that is excluded will be skipped
## Reference Issue(s)
8592

View file

@ -0,0 +1,19 @@
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: test-bad
name: test-bad
namespace: exclude-refs
spec:
containers:
- image: ghcr.io/chipzoller/zulu:v0.0.14@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db
name: test
resources: {}
- image: ghcr.io/kyverno/kyverno:latest
name: test
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

View file

@ -0,0 +1,34 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
creationTimestamp: null
name: skip-image-reference
spec:
timeouts:
delete: 2m
steps:
- name: step-01
try:
- apply:
file: policy.yaml
- assert:
file: policy-ready.yaml
- name: step-02
try:
- apply:
file: pod.yaml
- assert:
file: pod-assert.yaml
- name: step-03
try:
- apply:
file: skipped.yaml
- assert:
file: skipped-assert.yaml
- name: step-04
try:
- apply:
expect:
- check:
($error != null): true
file: bad.yaml

View file

@ -0,0 +1,5 @@
apiVersion: v1
kind: Pod
metadata:
name: test
namespace: exclude-refs

View file

@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: test
name: test
namespace: exclude-refs
spec:
containers:
- image: ghcr.io/kyverno/test-verify-image:signed
name: test
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

View file

@ -0,0 +1,9 @@
apiVersion: kyverno.io/v2beta1
kind: ClusterPolicy
metadata:
name: verify-exclude-refs
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,66 @@
apiVersion: v1
kind: Namespace
metadata:
name: exclude-refs
---
apiVersion: v1
kind: ConfigMap
metadata:
name: keys
namespace: exclude-refs
data:
certificate: |-
-----BEGIN CERTIFICATE-----
MIIDTTCCAjWgAwIBAgIJAPI+zAzn4s0xMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
BAYTAlVTMQswCQYDVQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEPMA0GA1UECgwG
Tm90YXJ5MQ0wCwYDVQQDDAR0ZXN0MB4XDTIzMDUyMjIxMTUxOFoXDTMzMDUxOTIx
MTUxOFowTDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdTZWF0
dGxlMQ8wDQYDVQQKDAZOb3RhcnkxDTALBgNVBAMMBHRlc3QwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQDNhTwv+QMk7jEHufFfIFlBjn2NiJaYPgL4eBS+
b+o37ve5Zn9nzRppV6kGsa161r9s2KkLXmJrojNy6vo9a6g6RtZ3F6xKiWLUmbAL
hVTCfYw/2n7xNlVMjyyUpE+7e193PF8HfQrfDFxe2JnX5LHtGe+X9vdvo2l41R6m
Iia04DvpMdG4+da2tKPzXIuLUz/FDb6IODO3+qsqQLwEKmmUee+KX+3yw8I6G1y0
Vp0mnHfsfutlHeG8gazCDlzEsuD4QJ9BKeRf2Vrb0ywqNLkGCbcCWF2H5Q80Iq/f
ETVO9z88R7WheVdEjUB8UrY7ZMLdADM14IPhY2Y+tLaSzEVZAgMBAAGjMjAwMAkG
A1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0G
CSqGSIb3DQEBCwUAA4IBAQBX7x4Ucre8AIUmXZ5PUK/zUBVOrZZzR1YE8w86J4X9
kYeTtlijf9i2LTZMfGuG0dEVFN4ae3CCpBst+ilhIndnoxTyzP+sNy4RCRQ2Y/k8
Zq235KIh7uucq96PL0qsF9s2RpTKXxyOGdtp9+HO0Ty5txJE2txtLDUIVPK5WNDF
ByCEQNhtHgN6V20b8KU2oLBZ9vyB8V010dQz0NRTDLhkcvJig00535/LUylECYAJ
5/jn6XKt6UYCQJbVNzBg/YPGc1RF4xdsGVDBben/JXpeGEmkdmXPILTKd9tZ5TC0
uOKpF5rWAruB5PCIrquamOejpXV9aQA/K2JQDuc0mcKz
-----END CERTIFICATE-----
---
apiVersion: kyverno.io/v2beta1
kind: ClusterPolicy
metadata:
name: verify-exclude-refs
spec:
validationFailureAction: Enforce
webhookTimeoutSeconds: 30
failurePolicy: Fail
rules:
- name: verify-exclude-refs
context:
- name: keys
configMap:
name: keys
namespace: exclude-refs
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- type: Notary
imageReferences:
- "ghcr.io/*"
skipImageReferences:
- "ghcr.io/chipzoller*"
attestors:
- count: 1
entries:
- certificates:
cert: "{{ keys.data.certificate }}"
name: keys
namespace: test-verify-images

View file

@ -0,0 +1,5 @@
apiVersion: v1
kind: Pod
metadata:
name: test-skipped
namespace: exclude-refs

View file

@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: test-skipped
name: test-skipped
namespace: exclude-refs
spec:
containers:
- image: ghcr.io/chipzoller/zulu:v0.0.14@sha256:476b21f1a75dc90fac3579ee757f4607bb5546f476195cf645c54badf558c0db
name: test
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}