1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-13 19:28:55 +00:00

#6055 Add JMESPath support to imageExtractors (#6183)

Signed-off-by: Brian Dunnigan <bdunnigan@clarityinnovates.com>
Co-authored-by: bdunnigan <bdunnigan@clarityinnovates.com>
Co-authored-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>
This commit is contained in:
Brian Dunnigan 2023-02-08 06:54:59 -05:00 committed by GitHub
parent cfd4501dcc
commit d33e616d69
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 487 additions and 16 deletions

View file

@ -32,6 +32,12 @@ type ImageExtractorConfig struct {
// Note - this field MUST be unique.
// +optional
Key string `json:"key,omitempty" yaml:"key,omitempty"`
// JMESPath is an optional JMESPath expression to apply to the image value.
// This is useful when the extracted image begins with a prefix like 'docker://'.
// The 'trim_prefix' function may be used to trim the prefix: trim_prefix(@, 'docker://').
// Note - Image digest mutation may not be used when applying a JMESPAth to an image.
// +optional
JMESPath string `json:"jmesPath,omitempty" yaml:"jmesPath,omitempty"`
}
// Rule defines a validation, mutation, or generation control for matching resources.

View file

@ -4397,6 +4397,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when the
extracted image begins with a prefix like ''docker://''.
The ''trim_prefix'' function may be used to trim the
prefix: trim_prefix(@, ''docker://''). Note - Image
digest mutation may not be used when applying a JMESPAth
to an image.'
type: string
key:
description: Key is an optional name of the field within
'path' that will be used to uniquely identify an image.
@ -7658,6 +7667,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when
the extracted image begins with a prefix like
''docker://''. The ''trim_prefix'' function may
be used to trim the prefix: trim_prefix(@, ''docker://'').
Note - Image digest mutation may not be used when
applying a JMESPAth to an image.'
type: string
key:
description: Key is an optional name of the field
within 'path' that will be used to uniquely identify
@ -10875,6 +10893,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when the
extracted image begins with a prefix like ''docker://''.
The ''trim_prefix'' function may be used to trim the
prefix: trim_prefix(@, ''docker://''). Note - Image
digest mutation may not be used when applying a JMESPAth
to an image.'
type: string
key:
description: Key is an optional name of the field within
'path' that will be used to uniquely identify an image.
@ -14066,6 +14093,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when
the extracted image begins with a prefix like
''docker://''. The ''trim_prefix'' function may
be used to trim the prefix: trim_prefix(@, ''docker://'').
Note - Image digest mutation may not be used when
applying a JMESPAth to an image.'
type: string
key:
description: Key is an optional name of the field
within 'path' that will be used to uniquely identify
@ -17508,6 +17544,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when the
extracted image begins with a prefix like ''docker://''.
The ''trim_prefix'' function may be used to trim the
prefix: trim_prefix(@, ''docker://''). Note - Image
digest mutation may not be used when applying a JMESPAth
to an image.'
type: string
key:
description: Key is an optional name of the field within
'path' that will be used to uniquely identify an image.
@ -20770,6 +20815,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when
the extracted image begins with a prefix like
''docker://''. The ''trim_prefix'' function may
be used to trim the prefix: trim_prefix(@, ''docker://'').
Note - Image digest mutation may not be used when
applying a JMESPAth to an image.'
type: string
key:
description: Key is an optional name of the field
within 'path' that will be used to uniquely identify
@ -23988,6 +24042,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when the
extracted image begins with a prefix like ''docker://''.
The ''trim_prefix'' function may be used to trim the
prefix: trim_prefix(@, ''docker://''). Note - Image
digest mutation may not be used when applying a JMESPAth
to an image.'
type: string
key:
description: Key is an optional name of the field within
'path' that will be used to uniquely identify an image.
@ -27179,6 +27242,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when
the extracted image begins with a prefix like
''docker://''. The ''trim_prefix'' function may
be used to trim the prefix: trim_prefix(@, ''docker://'').
Note - Image digest mutation may not be used when
applying a JMESPAth to an image.'
type: string
key:
description: Key is an optional name of the field
within 'path' that will be used to uniquely identify

View file

@ -995,6 +995,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when the
extracted image begins with a prefix like ''docker://''.
The ''trim_prefix'' function may be used to trim the
prefix: trim_prefix(@, ''docker://''). Note - Image
digest mutation may not be used when applying a JMESPAth
to an image.'
type: string
key:
description: Key is an optional name of the field within
'path' that will be used to uniquely identify an image.
@ -4256,6 +4265,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when
the extracted image begins with a prefix like
''docker://''. The ''trim_prefix'' function may
be used to trim the prefix: trim_prefix(@, ''docker://'').
Note - Image digest mutation may not be used when
applying a JMESPAth to an image.'
type: string
key:
description: Key is an optional name of the field
within 'path' that will be used to uniquely identify
@ -7473,6 +7491,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when the
extracted image begins with a prefix like ''docker://''.
The ''trim_prefix'' function may be used to trim the
prefix: trim_prefix(@, ''docker://''). Note - Image
digest mutation may not be used when applying a JMESPAth
to an image.'
type: string
key:
description: Key is an optional name of the field within
'path' that will be used to uniquely identify an image.
@ -10664,6 +10691,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when
the extracted image begins with a prefix like
''docker://''. The ''trim_prefix'' function may
be used to trim the prefix: trim_prefix(@, ''docker://'').
Note - Image digest mutation may not be used when
applying a JMESPAth to an image.'
type: string
key:
description: Key is an optional name of the field
within 'path' that will be used to uniquely identify

View file

@ -996,6 +996,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when the
extracted image begins with a prefix like ''docker://''.
The ''trim_prefix'' function may be used to trim the
prefix: trim_prefix(@, ''docker://''). Note - Image
digest mutation may not be used when applying a JMESPAth
to an image.'
type: string
key:
description: Key is an optional name of the field within
'path' that will be used to uniquely identify an image.
@ -4258,6 +4267,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when
the extracted image begins with a prefix like
''docker://''. The ''trim_prefix'' function may
be used to trim the prefix: trim_prefix(@, ''docker://'').
Note - Image digest mutation may not be used when
applying a JMESPAth to an image.'
type: string
key:
description: Key is an optional name of the field
within 'path' that will be used to uniquely identify
@ -7476,6 +7494,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when the
extracted image begins with a prefix like ''docker://''.
The ''trim_prefix'' function may be used to trim the
prefix: trim_prefix(@, ''docker://''). Note - Image
digest mutation may not be used when applying a JMESPAth
to an image.'
type: string
key:
description: Key is an optional name of the field within
'path' that will be used to uniquely identify an image.
@ -10667,6 +10694,15 @@ spec:
additionalProperties:
items:
properties:
jmesPath:
description: 'JMESPath is an optional JMESPath expression
to apply to the image value. This is useful when
the extracted image begins with a prefix like
''docker://''. The ''trim_prefix'' function may
be used to trim the prefix: trim_prefix(@, ''docker://'').
Note - Image digest mutation may not be used when
applying a JMESPAth to an image.'
type: string
key:
description: Key is an optional name of the field
within 'path' that will be used to uniquely identify

View file

@ -1689,6 +1689,21 @@ string
Note - this field MUST be unique.</p>
</td>
</tr>
<tr>
<td>
<code>jmesPath</code><br/>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>JMESPath is an optional JMESPath expression to apply to the image value.
This is useful when the extracted image begins with a prefix like &lsquo;docker://&rsquo;.
The &lsquo;trim_prefix&rsquo; function may be used to trim the prefix: trim_prefix(@, &lsquo;docker://&rsquo;).
Note - Image digest mutation may not be used when applying a JMESPAth to an image.</p>
</td>
</tr>
</tbody>
</table>
<hr />

View file

@ -42,6 +42,7 @@ var (
toUpper = "to_upper"
toLower = "to_lower"
trim = "trim"
trimPrefix = "trim_prefix"
split = "split"
regexReplaceAll = "regex_replace_all"
regexReplaceAllLiteral = "regex_replace_all_literal"
@ -145,6 +146,17 @@ func GetFunctions() []FunctionEntry {
},
ReturnType: []jpType{jpString},
Note: "trims both ends of the source string by characters appearing in the second string",
}, {
FunctionEntry: gojmespath.FunctionEntry{
Name: trimPrefix,
Arguments: []argSpec{
{Types: []jpType{jpString}},
{Types: []jpType{jpString}},
},
Handler: jpfTrimPrefix,
},
ReturnType: []jpType{jpString},
Note: "trims the second string prefix from the first string if the first string starts with the prefix",
}, {
FunctionEntry: gojmespath.FunctionEntry{
Name: split,
@ -588,6 +600,21 @@ func jpfTrim(arguments []interface{}) (interface{}, error) {
return strings.Trim(str.String(), cutset.String()), nil
}
func jpfTrimPrefix(arguments []interface{}) (interface{}, error) {
var err error
str, err := validateArg(trimPrefix, arguments, 0, reflect.String)
if err != nil {
return nil, err
}
prefix, err := validateArg(trimPrefix, arguments, 1, reflect.String)
if err != nil {
return nil, err
}
return strings.TrimPrefix(str.String(), prefix.String()), nil
}
func jpfSplit(arguments []interface{}) (interface{}, error) {
var err error
str, err := validateArg(split, arguments, 0, reflect.String)

View file

@ -334,6 +334,65 @@ func Test_Trim(t *testing.T) {
assert.Equal(t, trim, "Hello, Gophers")
}
func Test_TrimPrefix(t *testing.T) {
type args struct {
arguments []interface{}
}
tests := []struct {
name string
args args
want interface{}
wantErr bool
}{
{
name: "trims prefix",
args: args{
arguments: []interface{}{"¡¡¡Hello, Gophers!!!", "¡¡¡Hello, "},
},
want: "Gophers!!!",
wantErr: false,
},
{
name: "does not trim prefix",
args: args{
arguments: []interface{}{"¡¡¡Hello, Gophers!!!", "¡¡¡Hola, "},
},
want: "¡¡¡Hello, Gophers!!!",
wantErr: false,
},
{
name: "invalid first argument",
args: args{
arguments: []interface{}{1, "¡¡¡Hello, "},
},
want: nil,
wantErr: true,
},
{
name: "invalid first argument",
args: args{
arguments: []interface{}{"¡¡¡Hello, Gophers!!!", 1},
},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := jpfTrimPrefix(tt.args.arguments)
if (err != nil) != tt.wantErr {
t.Errorf("jpfTrimPrefix() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("jpfTrimPrefix() = %v, want %v", got, tt.want)
}
})
}
}
func Test_Split(t *testing.T) {
jp, err := New("split('Hello, Gophers', ', ')")
assert.NilError(t, err)

View file

@ -373,6 +373,7 @@ func TestNotAllowedVars_VariableFormats(t *testing.T) {
{"custom_func_to_upper", "to_upper(string)", true},
{"custom_func_to_lower", "to_lower(string)", true},
{"custom_func_trim", "trim(str string, cutset string)", true},
{"custom_func_trim_prefix", "trim_prefix(str string, prefix string)", true},
{"custom_func_split", "split(str string, sep string)", true},
{"custom_func_regex_replace_all", "regex_replace_all(regex string, src string|number, replace string|number)", true},
{"custom_func_regex_replace_all_literal", "regex_replace_all_literal(regex string, src string|number, replace string|number)", true},

View file

@ -219,6 +219,10 @@ func Validate(policy kyvernov1.PolicyInterface, client dclient.Interface, mock b
return warnings, fmt.Errorf("path: spec.rules[%d]: %v", i, err)
}
if err := validateRuleImageExtractorsJMESPath(rule); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d]: %v", i, err)
}
// If a rule's match block does not match any kind,
// we should only allow it to have metadata in its overlay
if len(rule.MatchResources.Any) > 0 {
@ -1014,6 +1018,44 @@ func validateRuleContext(rule kyvernov1.Rule) error {
return nil
}
// validateRuleImageExtractorsJMESPath ensures that the rule does not
// mutate image digests if it has an image extractor that uses a JMESPath.
func validateRuleImageExtractorsJMESPath(rule kyvernov1.Rule) error {
imageExtractorConfigs := rule.ImageExtractors
imageVerifications := rule.VerifyImages
if imageExtractorConfigs == nil || imageVerifications == nil {
return nil
}
anyMutateDigest := false
for _, imageVerification := range imageVerifications {
if imageVerification.MutateDigest {
anyMutateDigest = true
break
}
}
if !anyMutateDigest {
return nil
}
anyJMESPath := false
for _, imageExtractors := range imageExtractorConfigs {
for _, imageExtractor := range imageExtractors {
if imageExtractor.JMESPath != "" {
anyJMESPath = true
break
}
}
}
if anyJMESPath {
return fmt.Errorf("jmespath may not be used in an image extractor when mutating digests with verify images")
}
return nil
}
func validateVariable(entry kyvernov1.ContextEntry) error {
// If JMESPath contains variables, the validation will fail because it's not possible to infer which value
// will be inserted by the variable

View file

@ -3,6 +3,7 @@ package policy
import (
"encoding/json"
"errors"
"fmt"
"testing"
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
@ -2239,3 +2240,61 @@ func Test_Any_wildcard_policy(t *testing.T) {
_, err = Validate(policy, nil, true, openApiManager)
assert.Assert(t, err != nil)
}
func Test_Validate_RuleImageExtractorsJMESPath(t *testing.T) {
rawPolicy := []byte(`{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "jmes-path-and-mutate-digest"
},
"spec": {
"rules": [
{
"match": {
"resources": {
"kinds": [
"CRD"
]
}
},
"imageExtractors": {
"CRD": [
{
"path": "/path/to/image/prefixed/with/scheme",
"jmesPath": "trim_prefix(@, 'docker://')"
}
]
},
"verifyImages": [
{
"mutateDigest": true,
"attestors": [
{
"count": 1,
"entries": [
{
"keys": {
"publicKeys": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM\n5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==\n-----END PUBLIC KEY-----"
}
}
]
}
]
}
]
}
]
}
}`)
var policy *kyverno.ClusterPolicy
err := json.Unmarshal(rawPolicy, &policy)
assert.NilError(t, err)
expectedErr := fmt.Errorf("path: spec.rules[0]: jmespath may not be used in an image extractor when mutating digests with verify images")
openApiManager, _ := openapi.NewManager()
_, actualErr := Validate(policy, nil, true, openApiManager)
assert.Equal(t, expectedErr.Error(), actualErr.Error())
}

View file

@ -7,6 +7,7 @@ import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/engine/jmespath"
imageutils "github.com/kyverno/kyverno/pkg/utils/image"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -34,21 +35,22 @@ var (
)
type imageExtractor struct {
Fields []string
Key string
Value string
Name string
Fields []string
Key string
Value string
Name string
JMESPath string
}
func (i *imageExtractor) ExtractFromResource(resource interface{}, cfg config.Configuration) (map[string]ImageInfo, error) {
imageInfo := map[string]ImageInfo{}
if err := extract(resource, []string{}, i.Key, i.Value, i.Fields, &imageInfo, cfg); err != nil {
if err := extract(resource, []string{}, i.Key, i.Value, i.Fields, i.JMESPath, &imageInfo, cfg); err != nil {
return nil, err
}
return imageInfo, nil
}
func extract(obj interface{}, path []string, keyPath, valuePath string, fields []string, imageInfos *map[string]ImageInfo, cfg config.Configuration) error {
func extract(obj interface{}, path []string, keyPath, valuePath string, fields []string, jmesPath string, imageInfos *map[string]ImageInfo, cfg config.Configuration) error {
if obj == nil {
return nil
}
@ -56,13 +58,13 @@ func extract(obj interface{}, path []string, keyPath, valuePath string, fields [
switch typedObj := obj.(type) {
case []interface{}:
for i, v := range typedObj {
if err := extract(v, append(path, strconv.Itoa(i)), keyPath, valuePath, fields[1:], imageInfos, cfg); err != nil {
if err := extract(v, append(path, strconv.Itoa(i)), keyPath, valuePath, fields[1:], jmesPath, imageInfos, cfg); err != nil {
return err
}
}
case map[string]interface{}:
for i, v := range typedObj {
if err := extract(v, append(path, i), keyPath, valuePath, fields[1:], imageInfos, cfg); err != nil {
if err := extract(v, append(path, i), keyPath, valuePath, fields[1:], jmesPath, imageInfos, cfg); err != nil {
return err
}
}
@ -90,6 +92,26 @@ func extract(obj interface{}, path []string, keyPath, valuePath string, fields [
if !ok {
return fmt.Errorf("invalid value")
}
if jmesPath != "" {
jp, err := jmespath.New(jmesPath)
if err != nil {
return fmt.Errorf("invalid jmespath %s: %v", jmesPath, err)
}
result, err := jp.Search(value)
if err != nil {
return fmt.Errorf("failed to apply jmespath %s: %v", jmesPath, err)
}
resultStr, ok := result.(string)
if !ok {
return fmt.Errorf("jmespath %s must produce a string, but produced %v", jmesPath, result)
}
value = resultStr
}
if imageInfo, err := imageutils.GetImageInfo(value, cfg); err != nil {
return fmt.Errorf("invalid image %s", value)
} else {
@ -99,7 +121,7 @@ func extract(obj interface{}, path []string, keyPath, valuePath string, fields [
}
currentPath := fields[0]
return extract(output[currentPath], append(path, currentPath), keyPath, valuePath, fields[1:], imageInfos, cfg)
return extract(output[currentPath], append(path, currentPath), keyPath, valuePath, fields[1:], jmesPath, imageInfos, cfg)
}
func BuildStandardExtractors(tags ...string) []imageExtractor {
@ -139,10 +161,11 @@ func lookupImageExtractor(kind string, configs kyvernov1.ImageExtractorConfigs)
fields = fields[:len(fields)-1]
}
extractors = append(extractors, imageExtractor{
Fields: fields,
Key: c.Key,
Name: name,
Value: value,
Fields: fields,
Key: c.Key,
Name: name,
Value: value,
JMESPath: c.JMESPath,
})
}
return extractors

View file

@ -217,6 +217,27 @@ func Test_extractImageInfo(t *testing.T) {
},
},
},
{
extractionConfig: kyvernov1.ImageExtractorConfigs{
"DataVolume": []kyvernov1.ImageExtractorConfig{
{Path: "/spec/source/registry/url", JMESPath: "trim_prefix(@, 'docker://')"},
},
},
raw: []byte(`{"apiVersion":"cdi.kubevirt.io/v1beta1","kind":"DataVolume","metadata":{"name":"registry-image-datavolume"},"spec":{"source":{"registry":{"url":"docker://kubevirt/fedora-cloud-registry-disk-demo"}},"pvc":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"5Gi"}}}}}`),
images: map[string]map[string]ImageInfo{
"custom": {
"/spec/source/registry/url": {
imageutils.ImageInfo{
Registry: "docker.io",
Name: "fedora-cloud-registry-disk-demo",
Path: "kubevirt/fedora-cloud-registry-disk-demo",
Tag: "latest",
},
"/spec/source/registry/url",
},
},
},
},
}
for _, test := range tests {

View file

@ -9,8 +9,18 @@ results:
resource: signed
kind: Pod
status: pass
- policy: check-image
- policy: check-image
rule: verify-signature
resource: unsigned
kind: Pod
status: fail
- policy: check-data-volume-image
rule: verify-signature
resource: signed-registry-image-datavolume
kind: DataVolume
status: pass
- policy: check-data-volume-image
rule: verify-signature
resource: unsigned-registry-image-datavolume
kind: DataVolume
status: fail

View file

@ -24,4 +24,38 @@ spec:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM
5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
-----END PUBLIC KEY-----
-----END PUBLIC KEY-----
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: check-data-volume-image
annotations:
pod-policies.kyverno.io/autogen-controllers: none
spec:
validationFailureAction: enforce
background: false
rules:
- name: verify-signature
match:
any:
- resources:
kinds:
- DataVolume
imageExtractors:
DataVolume:
- path: /spec/source/registry/url
jmesPath: "trim_prefix(@, 'docker://')"
verifyImages:
- imageReferences:
- "*"
mutateDigest: false
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM
5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
-----END PUBLIC KEY-----

View file

@ -15,4 +15,34 @@ metadata:
spec:
containers:
- name: signed
image: ghcr.io/kyverno/test-verify-image:unsigned
image: ghcr.io/kyverno/test-verify-image:unsigned
---
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: signed-registry-image-datavolume
spec:
source:
registry:
url: "docker://ghcr.io/kyverno/test-verify-image:signed"
pvc:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
apiVersion: cdi.kubevirt.io/v1beta1
kind: DataVolume
metadata:
name: unsigned-registry-image-datavolume
spec:
source:
registry:
url: "docker://ghcr.io/kyverno/test-verify-image:unsigned"
pvc:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi