mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
feat: Add Manifest Index to ImageRegistry context (#9883)
* feat: Add Manifest Index to ImageRegistry context Signed-off-by: Netanel Kadosh <kadoshnetanel@gmail.com> * test: adding manifest list tests Signed-off-by: Netanel Kadosh <kadoshnetanel@gmail.com> --------- Signed-off-by: Netanel Kadosh <kadoshnetanel@gmail.com> Co-authored-by: shuting <shuting@nirmata.com>
This commit is contained in:
parent
244dbe19cf
commit
cef7be1fdc
7 changed files with 95 additions and 8 deletions
|
@ -34,6 +34,8 @@ func (a *rclientAdapter) ForRef(ctx context.Context, ref string) (*engineapi.Ima
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to resolve image reference: %s, error: %v", ref, err)
|
return nil, fmt.Errorf("failed to resolve image reference: %s, error: %v", ref, err)
|
||||||
}
|
}
|
||||||
|
// we ignore image index errors as it might be unavailable
|
||||||
|
manifestList, _ := desc.ImageIndex()
|
||||||
// We need to use the raw config and manifest to avoid dropping unknown keys
|
// We need to use the raw config and manifest to avoid dropping unknown keys
|
||||||
// which are not defined in GGCR structs.
|
// which are not defined in GGCR structs.
|
||||||
rawManifest, err := image.RawManifest()
|
rawManifest, err := image.RawManifest()
|
||||||
|
@ -44,12 +46,21 @@ func (a *rclientAdapter) ForRef(ctx context.Context, ref string) (*engineapi.Ima
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch config for image reference: %s, error: %v", ref, err)
|
return nil, fmt.Errorf("failed to fetch config for image reference: %s, error: %v", ref, err)
|
||||||
}
|
}
|
||||||
|
var rawManifestList []byte
|
||||||
|
if manifestList != nil {
|
||||||
|
rawManifestList, err = manifestList.RawManifest()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to fetch image index for image reference: %s, error: %v", ref, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data := engineapi.ImageData{
|
data := engineapi.ImageData{
|
||||||
Image: ref,
|
Image: ref,
|
||||||
ResolvedImage: fmt.Sprintf("%s@%s", parsedRef.Context().Name(), desc.Digest.String()),
|
ResolvedImage: fmt.Sprintf("%s@%s", parsedRef.Context().Name(), desc.Digest.String()),
|
||||||
Registry: parsedRef.Context().RegistryStr(),
|
Registry: parsedRef.Context().RegistryStr(),
|
||||||
Repository: parsedRef.Context().RepositoryStr(),
|
Repository: parsedRef.Context().RepositoryStr(),
|
||||||
Identifier: parsedRef.Identifier(),
|
Identifier: parsedRef.Identifier(),
|
||||||
|
ManifestList: rawManifestList,
|
||||||
Manifest: rawManifest,
|
Manifest: rawManifest,
|
||||||
Config: rawConfig,
|
Config: rawConfig,
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ type ImageData struct {
|
||||||
Registry string
|
Registry string
|
||||||
Repository string
|
Repository string
|
||||||
Identifier string
|
Identifier string
|
||||||
|
ManifestList []byte
|
||||||
Manifest []byte
|
Manifest []byte
|
||||||
Config []byte
|
Config []byte
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,12 +123,20 @@ func (idl *imageDataLoader) fetchImageDataMap(client engineapi.ImageDataClient,
|
||||||
return nil, fmt.Errorf("failed to decode config for image reference: %s, error: %v", ref, err)
|
return nil, fmt.Errorf("failed to decode config for image reference: %s, error: %v", ref, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var manifestList interface{}
|
||||||
|
if desc.ManifestList != nil {
|
||||||
|
if err := json.Unmarshal(desc.ManifestList, &manifestList); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode image index for image reference: %s, error: %v", ref, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"image": desc.Image,
|
"image": desc.Image,
|
||||||
"resolvedImage": desc.ResolvedImage,
|
"resolvedImage": desc.ResolvedImage,
|
||||||
"registry": desc.Registry,
|
"registry": desc.Registry,
|
||||||
"repository": desc.Repository,
|
"repository": desc.Repository,
|
||||||
"identifier": desc.Identifier,
|
"identifier": desc.Identifier,
|
||||||
|
"manifestList": manifestList,
|
||||||
"manifest": manifest,
|
"manifest": manifest,
|
||||||
"configData": configData,
|
"configData": configData,
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,3 +77,40 @@ spec:
|
||||||
list: request.object.spec.containers
|
list: request.object.spec.containers
|
||||||
message: Images must specify a source/base image from which they are built to
|
message: Images must specify a source/base image from which they are built to
|
||||||
be valid.
|
be valid.
|
||||||
|
---
|
||||||
|
apiVersion: kyverno.io/v1
|
||||||
|
kind: ClusterPolicy
|
||||||
|
metadata:
|
||||||
|
name: check-manifest-list
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- match:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
kinds:
|
||||||
|
- Pod
|
||||||
|
name: check-manifest-list-rule
|
||||||
|
preconditions:
|
||||||
|
all:
|
||||||
|
- key: '{{request.operation}}'
|
||||||
|
operator: NotEquals
|
||||||
|
value: DELETE
|
||||||
|
validate:
|
||||||
|
foreach:
|
||||||
|
- context:
|
||||||
|
- imageRegistry:
|
||||||
|
reference: '{{ element.image }}'
|
||||||
|
name: imageData
|
||||||
|
- name: manifests
|
||||||
|
variable:
|
||||||
|
default: 0
|
||||||
|
jmesPath: 'imageData.manifestList.manifests | length(@)'
|
||||||
|
deny:
|
||||||
|
conditions:
|
||||||
|
all:
|
||||||
|
- key: '{{ manifests }}'
|
||||||
|
operator: Equals
|
||||||
|
value: 0
|
||||||
|
list: request.object.spec.containers
|
||||||
|
message: Images must specify a manifest list to be valid.
|
||||||
|
validationFailureAction: Enforce
|
||||||
|
|
|
@ -13,6 +13,19 @@ results:
|
||||||
- test-pod-with-trusted-registry
|
- test-pod-with-trusted-registry
|
||||||
result: pass
|
result: pass
|
||||||
rule: check-image-base-rule
|
rule: check-image-base-rule
|
||||||
|
- kind: Pod
|
||||||
|
policy: check-manifest-list
|
||||||
|
resources:
|
||||||
|
- test-pod-with-single-arch-no-index
|
||||||
|
result: fail
|
||||||
|
rule: check-manifest-list-rule
|
||||||
|
- kind: Pod
|
||||||
|
policy: check-manifest-list
|
||||||
|
resources:
|
||||||
|
- test-pod-with-trusted-registry
|
||||||
|
- test-pod-with-single-arch-index
|
||||||
|
result: pass
|
||||||
|
rule: check-manifest-list-rule
|
||||||
- kind: Pod
|
- kind: Pod
|
||||||
policy: images
|
policy: images
|
||||||
resources:
|
resources:
|
||||||
|
|
|
@ -16,4 +16,21 @@ spec:
|
||||||
containers:
|
containers:
|
||||||
- name: kyverno
|
- name: kyverno
|
||||||
image: ghcr.io/kyverno/kyverno:v1.7.3
|
image: ghcr.io/kyverno/kyverno:v1.7.3
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test-pod-with-single-arch-index
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: solr-single-arch
|
||||||
|
image: solr:6
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: test-pod-with-single-arch-no-index
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: no-index
|
||||||
|
image: ghcr.io/kyverno/test-verify-image:signed
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
apiVersion: cli.kyverno.io/v1alpha1
|
apiVersion: cli.kyverno.io/v1alpha1
|
||||||
|
globalValues:
|
||||||
|
request.operation: CREATE
|
||||||
kind: Value
|
kind: Value
|
||||||
metadata:
|
metadata:
|
||||||
name: values
|
name: values
|
||||||
globalValues:
|
|
||||||
request.operation: CREATE
|
|
||||||
policies:
|
policies:
|
||||||
- name: gctx
|
- name: gctx
|
||||||
rules:
|
rules:
|
||||||
- name: main-deployment-exists
|
- name: main-deployment-exists
|
||||||
values:
|
values:
|
||||||
deploymentCount: 1
|
deploymentCount: 1
|
||||||
|
|
Loading…
Add table
Reference in a new issue