Skip to main content

Image Digests

Description

Requires container images to contain a digest. https://kubernetes.io/docs/concepts/containers/images/

Template

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8simagedigests
annotations:
metadata.gatekeeper.sh/title: "Image Digests"
metadata.gatekeeper.sh/version: 1.0.1
description: >-
Requires container images to contain a digest.

https://kubernetes.io/docs/concepts/containers/images/
spec:
crd:
spec:
names:
kind: K8sImageDigests
validation:
openAPIV3Schema:
type: object
description: >-
Requires container images to contain a digest.

https://kubernetes.io/docs/concepts/containers/images/
properties:
exemptImages:
description: >-
Any container that uses an image that matches an entry in this list will be excluded
from enforcement. Prefix-matching can be signified with `*`. For example: `my-image-*`.

It is recommended that users use the fully-qualified Docker image name (e.g. start with a domain name)
in order to avoid unexpectedly exempting images from an untrusted repository.
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8simagedigests

import data.lib.exempt_container.is_exempt

violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not is_exempt(container)
not regex.match("@[a-z0-9]+([+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+", container.image)
msg := sprintf("container <%v> uses an image without a digest <%v>", [container.name, container.image])
}

violation[{"msg": msg}] {
container := input.review.object.spec.initContainers[_]
not is_exempt(container)
not regex.match("@[a-z0-9]+([+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+", container.image)
msg := sprintf("initContainer <%v> uses an image without a digest <%v>", [container.name, container.image])
}

violation[{"msg": msg}] {
container := input.review.object.spec.ephemeralContainers[_]
not is_exempt(container)
not regex.match("@[a-z0-9]+([+._-][a-z0-9]+)*:[a-zA-Z0-9=_-]+", container.image)
msg := sprintf("ephemeralContainer <%v> uses an image without a digest <%v>", [container.name, container.image])
}
libs:
- |
package lib.exempt_container

is_exempt(container) {
exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", [])
img := container.image
exemption := exempt_images[_]
_matches_exemption(img, exemption)
}

_matches_exemption(img, exemption) {
not endswith(exemption, "*")
exemption == img
}

_matches_exemption(img, exemption) {
endswith(exemption, "*")
prefix := trim_suffix(exemption, "*")
startswith(img, prefix)
}

Usage

kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/imagedigests/template.yaml

Examples

container-image-must-have-digest
constraint
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sImageDigests
metadata:
name: container-image-must-have-digest
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces:
- "default"

Usage

kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/imagedigests/samples/container-image-must-have-digest/constraint.yaml
example-allowed
apiVersion: v1
kind: Pod
metadata:
name: opa-allowed
spec:
containers:
- name: opa
image: openpolicyagent/opa:0.9.2@sha256:04ff8fce2afd1a3bc26260348e5b290e8d945b1fad4b4c16d22834c2f3a1814a
args:
- "run"
- "--server"
- "--addr=localhost:8080"

Usage

kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/imagedigests/samples/container-image-must-have-digest/example_allowed.yaml
example-disallowed
apiVersion: v1
kind: Pod
metadata:
name: opa-disallowed
spec:
initContainers:
- name: opainit
image: openpolicyagent/opa:0.9.2
args:
- "run"
- "--server"
- "--addr=localhost:8080"
containers:
- name: opa
image: openpolicyagent/opa:0.9.2
args:
- "run"
- "--server"
- "--addr=localhost:8080"

Usage

kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/imagedigests/samples/container-image-must-have-digest/example_disallowed.yaml
disallowed-all
apiVersion: v1
kind: Pod
metadata:
name: opa-disallowed
spec:
initContainers:
- name: opainit
image: openpolicyagent/opa:0.9.2
args:
- "run"
- "--server"
- "--addr=localhost:8080"
containers:
- name: opa
image: openpolicyagent/opa:0.9.2
args:
- "run"
- "--server"
- "--addr=localhost:8080"
ephemeralContainers:
- name: opa
image: openpolicyagent/opa:0.9.2
args:
- "run"
- "--server"
- "--addr=localhost:8080"

Usage

kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/imagedigests/samples/container-image-must-have-digest/disallowed_all.yaml