diff --git a/artifacthub/library/general/disallowedtags/1.0.2/artifacthub-pkg.yml b/artifacthub/library/general/disallowedtags/1.0.2/artifacthub-pkg.yml new file mode 100644 index 000000000..ba52d6b68 --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/artifacthub-pkg.yml @@ -0,0 +1,25 @@ +version: 1.0.2 +name: k8sdisallowedtags +displayName: Disallow tags +createdAt: "2025-06-26T13:09:27Z" +description: |- + Requires container images to have an image tag different from the ones in the specified list. + https://kubernetes.io/docs/concepts/containers/images/#image-names +digest: 49570f47aaa2367dc7496f0cb73a7f19456ed5f130f0fab6ff73ff24987751a3 +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/disallowedtags +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # Disallow tags + Requires container images to have an image tag different from the ones in the specified list. + https://kubernetes.io/docs/concepts/containers/images/#image-names +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/disallowedtags/1.0.2/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/general/disallowedtags/1.0.2/kustomization.yaml b/artifacthub/library/general/disallowedtags/1.0.2/kustomization.yaml new file mode 100644 index 000000000..a2469fa0a --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - template.yaml diff --git a/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/constraint.yaml b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/constraint.yaml new file mode 100644 index 000000000..cceb50565 --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/constraint.yaml @@ -0,0 +1,14 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sDisallowedTags +metadata: + name: container-image-must-not-have-latest-tag +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + namespaces: + - "default" + parameters: + tags: ["latest"] + exemptImages: ["openpolicyagent/opa-exp:latest", "openpolicyagent/opa-exp2:latest"] diff --git a/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/disallowed_tag_ephemeral.yaml b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/disallowed_tag_ephemeral.yaml new file mode 100644 index 000000000..66db83c15 --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/disallowed_tag_ephemeral.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed-ephemeral +spec: + containers: + - name: opa + image: openpolicyagent/opa:0.9.2 + args: + - "run" + - "--server" + - "--addr=localhost:8080" + ephemeralContainers: + - name: opa + image: openpolicyagent/opa:latest + args: + - "run" + - "--server" + - "--addr=localhost:8080" diff --git a/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_allowed.yaml b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_allowed.yaml new file mode 100644 index 000000000..cc6407401 --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_allowed.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-allowed +spec: + containers: + - name: opa + image: openpolicyagent/opa:0.9.2 + args: + - "run" + - "--server" + - "--addr=localhost:8080" diff --git a/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_disallowed_tag.yaml b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_disallowed_tag.yaml new file mode 100644 index 000000000..1c689315f --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_disallowed_tag.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed-2 +spec: + containers: + - name: opa + image: openpolicyagent/opa:latest + args: + - "run" + - "--server" + - "--addr=localhost:8080" diff --git a/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_exempt_image_w_disallowed_tag.yaml b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_exempt_image_w_disallowed_tag.yaml new file mode 100644 index 000000000..2bd249fec --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_exempt_image_w_disallowed_tag.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-exempt-allowed +spec: + containers: + - name: opa-exp + image: openpolicyagent/opa-exp:latest + args: + - "run" + - "--server" + - "--addr=localhost:8080" + - name: opa-init + image: openpolicyagent/init:v1 + args: + - "run" + - "--server" + - "--addr=localhost:8080" + - name: opa-exp2 + image: openpolicyagent/opa-exp2:latest + args: + - "run" + - "--server" + - "--addr=localhost:8080" diff --git a/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_no_tag.yaml b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_no_tag.yaml new file mode 100644 index 000000000..3da276bf8 --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_no_tag.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed +spec: + containers: + - name: opa + image: openpolicyagent/opa + args: + - "run" + - "--server" + - "--addr=localhost:8080" diff --git a/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_no_tag_w_port.yaml b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_no_tag_w_port.yaml new file mode 100644 index 000000000..f567b845d --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_no_tag_w_port.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed-4 +spec: + containers: + - name: opa + image: openpolicyagent:443/opa + args: + - "run" + - "--server" + - "--addr=localhost:8080" diff --git a/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_some_disallowed_tags.yaml b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_some_disallowed_tags.yaml new file mode 100644 index 000000000..3596fc0c4 --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/samples/container-image-must-not-have-latest-tag/example_some_disallowed_tags.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed-3 +spec: + containers: + - name: opa + image: openpolicyagent/opa-exp:latest + args: + - "run" + - "--server" + - "--addr=localhost:8080" + - name: opa-init + image: openpolicyagent/init:latest + args: + - "run" + - "--server" + - "--addr=localhost:8080" + - name: opa-exp2 + image: openpolicyagent/opa-exp2:latest + args: + - "run" + - "--server" + - "--addr=localhost:8080" + - name: opa-monitor + image: openpolicyagent/monitor:latest + args: + - "run" + - "--server" + - "--addr=localhost:8080" diff --git a/artifacthub/library/general/disallowedtags/1.0.2/suite.yaml b/artifacthub/library/general/disallowedtags/1.0.2/suite.yaml new file mode 100644 index 000000000..7c80f37b8 --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/suite.yaml @@ -0,0 +1,37 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: disallowedtags +tests: +- name: disallowed-tags + template: template.yaml + constraint: samples/container-image-must-not-have-latest-tag/constraint.yaml + cases: + - name: allowed + object: samples/container-image-must-not-have-latest-tag/example_allowed.yaml + assertions: + - violations: no + - name: exempt-images-with-disallowed-tags + object: samples/container-image-must-not-have-latest-tag/example_exempt_image_w_disallowed_tag.yaml + assertions: + - violations: no + - name: no-tag + object: samples/container-image-must-not-have-latest-tag/example_no_tag.yaml + assertions: + - violations: yes + - name: no-tag-with-port + object: samples/container-image-must-not-have-latest-tag/example_no_tag_w_port.yaml + assertions: + - violations: yes + - name: single-disallowed-tag + object: samples/container-image-must-not-have-latest-tag/example_disallowed_tag.yaml + assertions: + - violations: yes + - name: single-disallowed-tag-ephemeral + object: samples/container-image-must-not-have-latest-tag/disallowed_tag_ephemeral.yaml + assertions: + - violations: yes + - name: some-disallow-tags + object: samples/container-image-must-not-have-latest-tag/example_some_disallowed_tags.yaml + assertions: + - violations: yes diff --git a/artifacthub/library/general/disallowedtags/1.0.2/template.yaml b/artifacthub/library/general/disallowedtags/1.0.2/template.yaml new file mode 100644 index 000000000..ba2c41bf2 --- /dev/null +++ b/artifacthub/library/general/disallowedtags/1.0.2/template.yaml @@ -0,0 +1,90 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8sdisallowedtags + annotations: + metadata.gatekeeper.sh/title: "Disallow tags" + metadata.gatekeeper.sh/version: 1.0.2 + description: >- + Requires container images to have an image tag different from the ones in + the specified list. + + https://kubernetes.io/docs/concepts/containers/images/#image-names +spec: + crd: + spec: + names: + kind: K8sDisallowedTags + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + 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 + tags: + type: array + description: Disallowed container image tags. + items: + type: string + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package k8sdisallowedtags + + import data.lib.exempt_container.is_exempt + + violation[{"msg": msg}] { + container := input_containers[_] + not is_exempt(container) + tags := [tag_with_prefix | tag := input.parameters.tags[_]; tag_with_prefix := concat(":", ["", tag])] + strings.any_suffix_match(container.image, tags) + msg := sprintf("container <%v> uses a disallowed tag <%v>; disallowed tags are %v", [container.name, container.image, input.parameters.tags]) + } + + violation[{"msg": msg}] { + container := input_containers[_] + not is_exempt(container) + parts := split(container.image, "/") + not contains(parts[count(parts) - 1], ":") + msg := sprintf("container <%v> didn't specify an image tag <%v>", [container.name, container.image]) + } + + input_containers[c] { + c := input.review.object.spec.containers[_] + } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + 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) + } + diff --git a/library/general/disallowedtags/samples/container-image-must-not-have-latest-tag/example_no_tag_w_port.yaml b/library/general/disallowedtags/samples/container-image-must-not-have-latest-tag/example_no_tag_w_port.yaml new file mode 100644 index 000000000..f567b845d --- /dev/null +++ b/library/general/disallowedtags/samples/container-image-must-not-have-latest-tag/example_no_tag_w_port.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed-4 +spec: + containers: + - name: opa + image: openpolicyagent:443/opa + args: + - "run" + - "--server" + - "--addr=localhost:8080" diff --git a/library/general/disallowedtags/suite.yaml b/library/general/disallowedtags/suite.yaml index 3f67b403c..7c80f37b8 100644 --- a/library/general/disallowedtags/suite.yaml +++ b/library/general/disallowedtags/suite.yaml @@ -19,6 +19,10 @@ tests: object: samples/container-image-must-not-have-latest-tag/example_no_tag.yaml assertions: - violations: yes + - name: no-tag-with-port + object: samples/container-image-must-not-have-latest-tag/example_no_tag_w_port.yaml + assertions: + - violations: yes - name: single-disallowed-tag object: samples/container-image-must-not-have-latest-tag/example_disallowed_tag.yaml assertions: diff --git a/library/general/disallowedtags/template.yaml b/library/general/disallowedtags/template.yaml index 43651dd0c..ba2c41bf2 100644 --- a/library/general/disallowedtags/template.yaml +++ b/library/general/disallowedtags/template.yaml @@ -4,7 +4,7 @@ metadata: name: k8sdisallowedtags annotations: metadata.gatekeeper.sh/title: "Disallow tags" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.0.2 description: >- Requires container images to have an image tag different from the ones in the specified list. @@ -52,7 +52,8 @@ spec: violation[{"msg": msg}] { container := input_containers[_] not is_exempt(container) - not contains(container.image, ":") + parts := split(container.image, "/") + not contains(parts[count(parts) - 1], ":") msg := sprintf("container <%v> didn't specify an image tag <%v>", [container.name, container.image]) } diff --git a/src/general/disallowedtags/constraint.tmpl b/src/general/disallowedtags/constraint.tmpl index 561d55206..483e894a3 100644 --- a/src/general/disallowedtags/constraint.tmpl +++ b/src/general/disallowedtags/constraint.tmpl @@ -4,7 +4,7 @@ metadata: name: k8sdisallowedtags annotations: metadata.gatekeeper.sh/title: "Disallow tags" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.0.2 description: >- Requires container images to have an image tag different from the ones in the specified list. diff --git a/src/general/disallowedtags/src.rego b/src/general/disallowedtags/src.rego index de10aeb7e..c9ee26779 100644 --- a/src/general/disallowedtags/src.rego +++ b/src/general/disallowedtags/src.rego @@ -13,7 +13,8 @@ violation[{"msg": msg}] { violation[{"msg": msg}] { container := input_containers[_] not is_exempt(container) - not contains(container.image, ":") + parts := split(container.image, "/") + not contains(parts[count(parts) - 1], ":") msg := sprintf("container <%v> didn't specify an image tag <%v>", [container.name, container.image]) } diff --git a/src/general/disallowedtags/src_test.rego b/src/general/disallowedtags/src_test.rego index 7bddec15f..cc0e583fd 100644 --- a/src/general/disallowedtags/src_test.rego +++ b/src/general/disallowedtags/src_test.rego @@ -15,6 +15,11 @@ test_input_denied_container_emtpy { results := violation with input as inp count(results) == 1 } +test_input_denied_container_empty_with_port { + inp := { "review": input_review(input_container_denied_empty_with_port), "parameters": {"tags": ["latest", "testing"]}} + results := violation with input as inp + count(results) == 1 +} test_input_denied_container_latest { inp := { "review": input_review(input_container_denied_latest), "parameters": {"tags": ["latest", "testing"]}} results := violation with input as inp @@ -146,6 +151,12 @@ input_container_denied_empty = [ "image": "nginx", }] +input_container_denied_empty_with_port = [ +{ + "name": "nginx", + "image": "nginx:443/nginx", +}] + input_container_denied_latest = [ { "name": "nginx", diff --git a/website/docs/validation/disallowedtags.md b/website/docs/validation/disallowedtags.md index fa5482c90..e9553fc39 100644 --- a/website/docs/validation/disallowedtags.md +++ b/website/docs/validation/disallowedtags.md @@ -17,7 +17,7 @@ metadata: name: k8sdisallowedtags annotations: metadata.gatekeeper.sh/title: "Disallow tags" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.0.2 description: >- Requires container images to have an image tag different from the ones in the specified list. @@ -65,7 +65,8 @@ spec: violation[{"msg": msg}] { container := input_containers[_] not is_exempt(container) - not contains(container.image, ":") + parts := split(container.image, "/") + not contains(parts[count(parts) - 1], ":") msg := sprintf("container <%v> didn't specify an image tag <%v>", [container.name, container.image]) } @@ -229,6 +230,32 @@ Usage kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowedtags/samples/container-image-must-not-have-latest-tag/example_no_tag.yaml ``` + +
+no-tag-with-port + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed-4 +spec: + containers: + - name: opa + image: openpolicyagent:443/opa + args: + - "run" + - "--server" + - "--addr=localhost:8080" + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowedtags/samples/container-image-must-not-have-latest-tag/example_no_tag_w_port.yaml +``` +
single-disallowed-tag