From fc89cf3c9acdfbd9c97dd0901a94445b6b4d7eef Mon Sep 17 00:00:00 2001 From: Xinhe Li Date: Thu, 5 Dec 2024 08:31:49 +0000 Subject: [PATCH 1/4] containerlimits support to skip cpu limit validation Signed-off-by: Xinhe Li --- .../container-ignore-cpu-limits/constraint.yaml | 12 ++++++++++++ .../example_allowed.yaml | 15 +++++++++++++++ .../example_disallowed.yaml | 15 +++++++++++++++ library/general/containerlimits/suite.yaml | 12 ++++++++++++ library/general/containerlimits/template.yaml | 6 ++++-- src/general/containerlimits/constraint.tmpl | 4 ++-- src/general/containerlimits/src.rego | 2 ++ src/general/containerlimits/src_test.rego | 7 ++++++- 8 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 library/general/containerlimits/samples/container-ignore-cpu-limits/constraint.yaml create mode 100644 library/general/containerlimits/samples/container-ignore-cpu-limits/example_allowed.yaml create mode 100644 library/general/containerlimits/samples/container-ignore-cpu-limits/example_disallowed.yaml diff --git a/library/general/containerlimits/samples/container-ignore-cpu-limits/constraint.yaml b/library/general/containerlimits/samples/container-ignore-cpu-limits/constraint.yaml new file mode 100644 index 000000000..edeea3e41 --- /dev/null +++ b/library/general/containerlimits/samples/container-ignore-cpu-limits/constraint.yaml @@ -0,0 +1,12 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sContainerLimits +metadata: + name: container-must-have-limits +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + cpu: "-1" + memory: "1Gi" diff --git a/library/general/containerlimits/samples/container-ignore-cpu-limits/example_allowed.yaml b/library/general/containerlimits/samples/container-ignore-cpu-limits/example_allowed.yaml new file mode 100644 index 000000000..0b8285731 --- /dev/null +++ b/library/general/containerlimits/samples/container-ignore-cpu-limits/example_allowed.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-allowed +spec: + containers: + - name: opa + image: openpolicyagent/opa:0.9.2 + args: + - "run" + - "--server" + - "--addr=localhost:8080" + resources: + limits: + memory: "1Gi" diff --git a/library/general/containerlimits/samples/container-ignore-cpu-limits/example_disallowed.yaml b/library/general/containerlimits/samples/container-ignore-cpu-limits/example_disallowed.yaml new file mode 100644 index 000000000..31218680a --- /dev/null +++ b/library/general/containerlimits/samples/container-ignore-cpu-limits/example_disallowed.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed +spec: + containers: + - name: opa + image: openpolicyagent/opa:0.9.2 + args: + - "run" + - "--server" + - "--addr=localhost:8080" + resources: + limits: + memory: "2Gi" \ No newline at end of file diff --git a/library/general/containerlimits/suite.yaml b/library/general/containerlimits/suite.yaml index c22bd5b77..e7bda65cd 100644 --- a/library/general/containerlimits/suite.yaml +++ b/library/general/containerlimits/suite.yaml @@ -15,3 +15,15 @@ tests: object: samples/container-must-have-limits/example_disallowed.yaml assertions: - violations: yes +- name: container-limits-ignore-cpu + template: template.yaml + constraint: samples/container-ignore-cpu-limits/constraint.yaml + cases: + - name: example-allowed + object: samples/container-ignore-cpu-limits/example_allowed.yaml + assertions: + - violations: no + - name: example-disallowed + object: samples/container-ignore-cpu-limits/example_disallowed.yaml + assertions: + - violations: yes diff --git a/library/general/containerlimits/template.yaml b/library/general/containerlimits/template.yaml index 4b254c663..40a58293c 100644 --- a/library/general/containerlimits/template.yaml +++ b/library/general/containerlimits/template.yaml @@ -4,7 +4,7 @@ metadata: name: k8scontainerlimits annotations: metadata.gatekeeper.sh/title: "Container Limits" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.1.0 description: >- Requires containers to have memory and CPU limits set and constrains limits to be within the specified maximum values. @@ -31,7 +31,7 @@ spec: items: type: string cpu: - description: "The maximum allowed cpu limit on a Pod, exclusive." + description: "The maximum allowed cpu limit on a Pod, exclusive. Set to -1 to disable." type: string memory: description: "The maximum allowed memory limit on a Pod, exclusive." @@ -207,6 +207,7 @@ spec: } general_violation[{"msg": msg, "field": field}] { + input.parameters.cpu != "-1" container := input.review.object.spec[field][_] not is_exempt(container) missing(container.resources.limits, "cpu") @@ -226,6 +227,7 @@ spec: cpu_orig := container.resources.limits.cpu cpu := canonify_cpu(cpu_orig) max_cpu_orig := input.parameters.cpu + max_cpu_orig != "-1" max_cpu := canonify_cpu(max_cpu_orig) cpu > max_cpu msg := sprintf("container <%v> cpu limit <%v> is higher than the maximum allowed of <%v>", [container.name, cpu_orig, max_cpu_orig]) diff --git a/src/general/containerlimits/constraint.tmpl b/src/general/containerlimits/constraint.tmpl index 33b5b791d..5337a4505 100644 --- a/src/general/containerlimits/constraint.tmpl +++ b/src/general/containerlimits/constraint.tmpl @@ -4,7 +4,7 @@ metadata: name: k8scontainerlimits annotations: metadata.gatekeeper.sh/title: "Container Limits" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.1.0 description: >- Requires containers to have memory and CPU limits set and constrains limits to be within the specified maximum values. @@ -31,7 +31,7 @@ spec: items: type: string cpu: - description: "The maximum allowed cpu limit on a Pod, exclusive." + description: "The maximum allowed cpu limit on a Pod, exclusive. Set to -1 to disable." type: string memory: description: "The maximum allowed memory limit on a Pod, exclusive." diff --git a/src/general/containerlimits/src.rego b/src/general/containerlimits/src.rego index 53dcaa0c6..5a81f1db9 100644 --- a/src/general/containerlimits/src.rego +++ b/src/general/containerlimits/src.rego @@ -166,6 +166,7 @@ general_violation[{"msg": msg, "field": field}] { } general_violation[{"msg": msg, "field": field}] { + input.parameters.cpu != "-1" container := input.review.object.spec[field][_] not is_exempt(container) missing(container.resources.limits, "cpu") @@ -185,6 +186,7 @@ general_violation[{"msg": msg, "field": field}] { cpu_orig := container.resources.limits.cpu cpu := canonify_cpu(cpu_orig) max_cpu_orig := input.parameters.cpu + max_cpu_orig != "-1" max_cpu := canonify_cpu(max_cpu_orig) cpu > max_cpu msg := sprintf("container <%v> cpu limit <%v> is higher than the maximum allowed of <%v>", [container.name, cpu_orig, max_cpu_orig]) diff --git a/src/general/containerlimits/src_test.rego b/src/general/containerlimits/src_test.rego index d3f983870..f719e90b6 100644 --- a/src/general/containerlimits/src_test.rego +++ b/src/general/containerlimits/src_test.rego @@ -52,10 +52,15 @@ test_no_parse_cpu { results := violation with input as inp count(results) == 1 } +test_no_parse_cpu_skip { + inp := {"review": review([ctr("a", "1", "212asdf")]), "parameters": {"memory": "2", "cpu": "-1"}} + results := violation with input as inp + count(results) == 0 +} test_no_parse_ram { inp := {"review": review([ctr("a", "1asdf", "2")]), "parameters": {"memory": "2", "cpu": "4"}} results := violation with input as inp - count(results) == 1 + count(results) == 0 } test_1_bad_cpu { inp := {"review": review([ctr("a", "1", "2"), ctr("b", "1", "8")]), "parameters": {"memory": "2", "cpu": "4"}} From 49878ededccbebed880c5daa7a883f60785eb4f4 Mon Sep 17 00:00:00 2001 From: Xinhe Li Date: Thu, 5 Dec 2024 08:37:19 +0000 Subject: [PATCH 2/4] f Signed-off-by: Xinhe Li --- src/general/containerlimits/src_test.rego | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/general/containerlimits/src_test.rego b/src/general/containerlimits/src_test.rego index f719e90b6..0f8fdfd2e 100644 --- a/src/general/containerlimits/src_test.rego +++ b/src/general/containerlimits/src_test.rego @@ -60,7 +60,7 @@ test_no_parse_cpu_skip { test_no_parse_ram { inp := {"review": review([ctr("a", "1asdf", "2")]), "parameters": {"memory": "2", "cpu": "4"}} results := violation with input as inp - count(results) == 0 + count(results) == 1 } test_1_bad_cpu { inp := {"review": review([ctr("a", "1", "2"), ctr("b", "1", "8")]), "parameters": {"memory": "2", "cpu": "4"}} From 7b18fae20b2c33121b9a8efdac29be7141d23a1e Mon Sep 17 00:00:00 2001 From: Xinhe Li Date: Thu, 5 Dec 2024 08:42:55 +0000 Subject: [PATCH 3/4] f Signed-off-by: Xinhe Li --- .../containerlimits/1.1.0/artifacthub-pkg.yml | 25 ++ .../containerlimits/1.1.0/kustomization.yaml | 2 + .../constraint.yaml | 12 + .../example_allowed.yaml | 15 + .../example_disallowed.yaml | 15 + .../constraint.yaml | 12 + .../example_allowed.yaml | 18 ++ .../example_disallowed.yaml | 18 ++ .../general/containerlimits/1.1.0/suite.yaml | 29 ++ .../containerlimits/1.1.0/template.yaml | 266 ++++++++++++++++++ website/docs/validation/containerlimits.md | 95 ++++++- 11 files changed, 505 insertions(+), 2 deletions(-) create mode 100644 artifacthub/library/general/containerlimits/1.1.0/artifacthub-pkg.yml create mode 100644 artifacthub/library/general/containerlimits/1.1.0/kustomization.yaml create mode 100644 artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/constraint.yaml create mode 100644 artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/example_allowed.yaml create mode 100644 artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/example_disallowed.yaml create mode 100644 artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/constraint.yaml create mode 100644 artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/example_allowed.yaml create mode 100644 artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/example_disallowed.yaml create mode 100644 artifacthub/library/general/containerlimits/1.1.0/suite.yaml create mode 100644 artifacthub/library/general/containerlimits/1.1.0/template.yaml diff --git a/artifacthub/library/general/containerlimits/1.1.0/artifacthub-pkg.yml b/artifacthub/library/general/containerlimits/1.1.0/artifacthub-pkg.yml new file mode 100644 index 000000000..91540d21c --- /dev/null +++ b/artifacthub/library/general/containerlimits/1.1.0/artifacthub-pkg.yml @@ -0,0 +1,25 @@ +version: 1.1.0 +name: k8scontainerlimits +displayName: Container Limits +createdAt: "2024-12-05T08:42:14Z" +description: |- + Requires containers to have memory and CPU limits set and constrains limits to be within the specified maximum values. + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ +digest: 4e4c84cd3532a7a0a30b3a823fb6a5f572d8b63f847f5fa5c98e8697353cb5d4 +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/containerlimits +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # Container Limits + Requires containers to have memory and CPU limits set and constrains limits to be within the specified maximum values. + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/containerlimits/1.1.0/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/general/containerlimits/1.1.0/kustomization.yaml b/artifacthub/library/general/containerlimits/1.1.0/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/artifacthub/library/general/containerlimits/1.1.0/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/constraint.yaml b/artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/constraint.yaml new file mode 100644 index 000000000..edeea3e41 --- /dev/null +++ b/artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/constraint.yaml @@ -0,0 +1,12 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sContainerLimits +metadata: + name: container-must-have-limits +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + cpu: "-1" + memory: "1Gi" diff --git a/artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/example_allowed.yaml b/artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/example_allowed.yaml new file mode 100644 index 000000000..0b8285731 --- /dev/null +++ b/artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/example_allowed.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-allowed +spec: + containers: + - name: opa + image: openpolicyagent/opa:0.9.2 + args: + - "run" + - "--server" + - "--addr=localhost:8080" + resources: + limits: + memory: "1Gi" diff --git a/artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/example_disallowed.yaml b/artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/example_disallowed.yaml new file mode 100644 index 000000000..31218680a --- /dev/null +++ b/artifacthub/library/general/containerlimits/1.1.0/samples/container-ignore-cpu-limits/example_disallowed.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed +spec: + containers: + - name: opa + image: openpolicyagent/opa:0.9.2 + args: + - "run" + - "--server" + - "--addr=localhost:8080" + resources: + limits: + memory: "2Gi" \ No newline at end of file diff --git a/artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/constraint.yaml b/artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/constraint.yaml new file mode 100644 index 000000000..2b06fd8d1 --- /dev/null +++ b/artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/constraint.yaml @@ -0,0 +1,12 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sContainerLimits +metadata: + name: container-must-have-limits +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + cpu: "200m" + memory: "1Gi" diff --git a/artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/example_allowed.yaml b/artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/example_allowed.yaml new file mode 100644 index 000000000..c77c3668f --- /dev/null +++ b/artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/example_allowed.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-allowed + labels: + owner: me.agilebank.demo +spec: + containers: + - name: opa + image: openpolicyagent/opa:0.9.2 + args: + - "run" + - "--server" + - "--addr=localhost:8080" + resources: + limits: + cpu: "100m" + memory: "1Gi" diff --git a/artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/example_disallowed.yaml b/artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/example_disallowed.yaml new file mode 100644 index 000000000..a5bd508a7 --- /dev/null +++ b/artifacthub/library/general/containerlimits/1.1.0/samples/container-must-have-limits/example_disallowed.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed + labels: + owner: me.agilebank.demo +spec: + containers: + - name: opa + image: openpolicyagent/opa:0.9.2 + args: + - "run" + - "--server" + - "--addr=localhost:8080" + resources: + limits: + cpu: "100m" + memory: "2Gi" \ No newline at end of file diff --git a/artifacthub/library/general/containerlimits/1.1.0/suite.yaml b/artifacthub/library/general/containerlimits/1.1.0/suite.yaml new file mode 100644 index 000000000..e7bda65cd --- /dev/null +++ b/artifacthub/library/general/containerlimits/1.1.0/suite.yaml @@ -0,0 +1,29 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: containerlimits +tests: +- name: container-limits + template: template.yaml + constraint: samples/container-must-have-limits/constraint.yaml + cases: + - name: example-allowed + object: samples/container-must-have-limits/example_allowed.yaml + assertions: + - violations: no + - name: example-disallowed + object: samples/container-must-have-limits/example_disallowed.yaml + assertions: + - violations: yes +- name: container-limits-ignore-cpu + template: template.yaml + constraint: samples/container-ignore-cpu-limits/constraint.yaml + cases: + - name: example-allowed + object: samples/container-ignore-cpu-limits/example_allowed.yaml + assertions: + - violations: no + - name: example-disallowed + object: samples/container-ignore-cpu-limits/example_disallowed.yaml + assertions: + - violations: yes diff --git a/artifacthub/library/general/containerlimits/1.1.0/template.yaml b/artifacthub/library/general/containerlimits/1.1.0/template.yaml new file mode 100644 index 000000000..40a58293c --- /dev/null +++ b/artifacthub/library/general/containerlimits/1.1.0/template.yaml @@ -0,0 +1,266 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8scontainerlimits + annotations: + metadata.gatekeeper.sh/title: "Container Limits" + metadata.gatekeeper.sh/version: 1.1.0 + description: >- + Requires containers to have memory and CPU limits set and constrains + limits to be within the specified maximum values. + + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ +spec: + crd: + spec: + names: + kind: K8sContainerLimits + 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 + cpu: + description: "The maximum allowed cpu limit on a Pod, exclusive. Set to -1 to disable." + type: string + memory: + description: "The maximum allowed memory limit on a Pod, exclusive." + type: string + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package k8scontainerlimits + + import data.lib.exempt_container.is_exempt + + missing(obj, field) = true { + not obj[field] + } + + missing(obj, field) = true { + obj[field] == "" + } + + canonify_cpu(orig) = new { + is_number(orig) + new := orig * 1000 + } + + canonify_cpu(orig) = new { + not is_number(orig) + endswith(orig, "m") + new := to_number(replace(orig, "m", "")) + } + + canonify_cpu(orig) = new { + not is_number(orig) + not endswith(orig, "m") + regex.match("^[0-9]+(\\.[0-9]+)?$", orig) + new := to_number(orig) * 1000 + } + + # 10 ** 21 + mem_multiple("E") = 1000000000000000000000 { true } + + # 10 ** 18 + mem_multiple("P") = 1000000000000000000 { true } + + # 10 ** 15 + mem_multiple("T") = 1000000000000000 { true } + + # 10 ** 12 + mem_multiple("G") = 1000000000000 { true } + + # 10 ** 9 + mem_multiple("M") = 1000000000 { true } + + # 10 ** 6 + mem_multiple("k") = 1000000 { true } + + # 10 ** 3 + mem_multiple("") = 1000 { true } + + # Kubernetes accepts millibyte precision when it probably shouldn't. + # https://github.com/kubernetes/kubernetes/issues/28741 + # 10 ** 0 + mem_multiple("m") = 1 { true } + + # 1000 * 2 ** 10 + mem_multiple("Ki") = 1024000 { true } + + # 1000 * 2 ** 20 + mem_multiple("Mi") = 1048576000 { true } + + # 1000 * 2 ** 30 + mem_multiple("Gi") = 1073741824000 { true } + + # 1000 * 2 ** 40 + mem_multiple("Ti") = 1099511627776000 { true } + + # 1000 * 2 ** 50 + mem_multiple("Pi") = 1125899906842624000 { true } + + # 1000 * 2 ** 60 + mem_multiple("Ei") = 1152921504606846976000 { true } + + get_suffix(mem) = suffix { + not is_string(mem) + suffix := "" + } + + get_suffix(mem) = suffix { + is_string(mem) + count(mem) > 0 + suffix := substring(mem, count(mem) - 1, -1) + mem_multiple(suffix) + } + + get_suffix(mem) = suffix { + is_string(mem) + count(mem) > 1 + suffix := substring(mem, count(mem) - 2, -1) + mem_multiple(suffix) + } + + get_suffix(mem) = suffix { + is_string(mem) + count(mem) > 1 + not mem_multiple(substring(mem, count(mem) - 1, -1)) + not mem_multiple(substring(mem, count(mem) - 2, -1)) + suffix := "" + } + + get_suffix(mem) = suffix { + is_string(mem) + count(mem) == 1 + not mem_multiple(substring(mem, count(mem) - 1, -1)) + suffix := "" + } + + get_suffix(mem) = suffix { + is_string(mem) + count(mem) == 0 + suffix := "" + } + + canonify_mem(orig) = new { + is_number(orig) + new := orig * 1000 + } + + canonify_mem(orig) = new { + not is_number(orig) + suffix := get_suffix(orig) + raw := replace(orig, suffix, "") + regex.match("^[0-9]+(\\.[0-9]+)?$", raw) + new := to_number(raw) * mem_multiple(suffix) + } + + violation[{"msg": msg}] { + general_violation[{"msg": msg, "field": "containers"}] + } + + violation[{"msg": msg}] { + general_violation[{"msg": msg, "field": "initContainers"}] + } + + # Ephemeral containers not checked as it is not possible to set field. + + general_violation[{"msg": msg, "field": field}] { + container := input.review.object.spec[field][_] + not is_exempt(container) + cpu_orig := container.resources.limits.cpu + not canonify_cpu(cpu_orig) + msg := sprintf("container <%v> cpu limit <%v> could not be parsed", [container.name, cpu_orig]) + } + + general_violation[{"msg": msg, "field": field}] { + container := input.review.object.spec[field][_] + not is_exempt(container) + mem_orig := container.resources.limits.memory + not canonify_mem(mem_orig) + msg := sprintf("container <%v> memory limit <%v> could not be parsed", [container.name, mem_orig]) + } + + general_violation[{"msg": msg, "field": field}] { + container := input.review.object.spec[field][_] + not is_exempt(container) + not container.resources + msg := sprintf("container <%v> has no resource limits", [container.name]) + } + + general_violation[{"msg": msg, "field": field}] { + container := input.review.object.spec[field][_] + not is_exempt(container) + not container.resources.limits + msg := sprintf("container <%v> has no resource limits", [container.name]) + } + + general_violation[{"msg": msg, "field": field}] { + input.parameters.cpu != "-1" + container := input.review.object.spec[field][_] + not is_exempt(container) + missing(container.resources.limits, "cpu") + msg := sprintf("container <%v> has no cpu limit", [container.name]) + } + + general_violation[{"msg": msg, "field": field}] { + container := input.review.object.spec[field][_] + not is_exempt(container) + missing(container.resources.limits, "memory") + msg := sprintf("container <%v> has no memory limit", [container.name]) + } + + general_violation[{"msg": msg, "field": field}] { + container := input.review.object.spec[field][_] + not is_exempt(container) + cpu_orig := container.resources.limits.cpu + cpu := canonify_cpu(cpu_orig) + max_cpu_orig := input.parameters.cpu + max_cpu_orig != "-1" + max_cpu := canonify_cpu(max_cpu_orig) + cpu > max_cpu + msg := sprintf("container <%v> cpu limit <%v> is higher than the maximum allowed of <%v>", [container.name, cpu_orig, max_cpu_orig]) + } + + general_violation[{"msg": msg, "field": field}] { + container := input.review.object.spec[field][_] + not is_exempt(container) + mem_orig := container.resources.limits.memory + mem := canonify_mem(mem_orig) + max_mem_orig := input.parameters.memory + max_mem := canonify_mem(max_mem_orig) + mem > max_mem + msg := sprintf("container <%v> memory limit <%v> is higher than the maximum allowed of <%v>", [container.name, mem_orig, max_mem_orig]) + } + 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/website/docs/validation/containerlimits.md b/website/docs/validation/containerlimits.md index 3ba618443..90882dd46 100644 --- a/website/docs/validation/containerlimits.md +++ b/website/docs/validation/containerlimits.md @@ -17,7 +17,7 @@ metadata: name: k8scontainerlimits annotations: metadata.gatekeeper.sh/title: "Container Limits" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.1.0 description: >- Requires containers to have memory and CPU limits set and constrains limits to be within the specified maximum values. @@ -44,7 +44,7 @@ spec: items: type: string cpu: - description: "The maximum allowed cpu limit on a Pod, exclusive." + description: "The maximum allowed cpu limit on a Pod, exclusive. Set to -1 to disable." type: string memory: description: "The maximum allowed memory limit on a Pod, exclusive." @@ -220,6 +220,7 @@ spec: } general_violation[{"msg": msg, "field": field}] { + input.parameters.cpu != "-1" container := input.review.object.spec[field][_] not is_exempt(container) missing(container.resources.limits, "cpu") @@ -239,6 +240,7 @@ spec: cpu_orig := container.resources.limits.cpu cpu := canonify_cpu(cpu_orig) max_cpu_orig := input.parameters.cpu + max_cpu_orig != "-1" max_cpu := canonify_cpu(max_cpu_orig) cpu > max_cpu msg := sprintf("container <%v> cpu limit <%v> is higher than the maximum allowed of <%v>", [container.name, cpu_orig, max_cpu_orig]) @@ -378,4 +380,93 @@ kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper- +
+container-limits-ignore-cpu + +
+constraint + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sContainerLimits +metadata: + name: container-must-have-limits +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + cpu: "-1" + memory: "1Gi" + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/containerlimits/samples/container-ignore-cpu-limits/constraint.yaml +``` + +
+ +
+example-allowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: opa-allowed +spec: + containers: + - name: opa + image: openpolicyagent/opa:0.9.2 + args: + - "run" + - "--server" + - "--addr=localhost:8080" + resources: + limits: + memory: "1Gi" + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/containerlimits/samples/container-ignore-cpu-limits/example_allowed.yaml +``` + +
+
+example-disallowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: opa-disallowed +spec: + containers: + - name: opa + image: openpolicyagent/opa:0.9.2 + args: + - "run" + - "--server" + - "--addr=localhost:8080" + resources: + limits: + memory: "2Gi" +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/containerlimits/samples/container-ignore-cpu-limits/example_disallowed.yaml +``` + +
+ +
\ No newline at end of file From ba9d3cb206bf72d914635dcd99dcdf89d3ed213e Mon Sep 17 00:00:00 2001 From: Xinhe Li Date: Thu, 5 Dec 2024 08:56:56 +0000 Subject: [PATCH 4/4] f Signed-off-by: Xinhe Li --- .../library/general/containerlimits/1.1.0/artifacthub-pkg.yml | 2 +- artifacthub/library/general/containerlimits/1.1.0/template.yaml | 1 + library/general/containerlimits/template.yaml | 1 + src/general/containerlimits/src.rego | 1 + website/docs/validation/containerlimits.md | 1 + 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/artifacthub/library/general/containerlimits/1.1.0/artifacthub-pkg.yml b/artifacthub/library/general/containerlimits/1.1.0/artifacthub-pkg.yml index 91540d21c..28132704f 100644 --- a/artifacthub/library/general/containerlimits/1.1.0/artifacthub-pkg.yml +++ b/artifacthub/library/general/containerlimits/1.1.0/artifacthub-pkg.yml @@ -5,7 +5,7 @@ createdAt: "2024-12-05T08:42:14Z" description: |- Requires containers to have memory and CPU limits set and constrains limits to be within the specified maximum values. https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ -digest: 4e4c84cd3532a7a0a30b3a823fb6a5f572d8b63f847f5fa5c98e8697353cb5d4 +digest: 80cdb57a413ec205ca940639fb78b7cb4b400fb820557654909578d0ddd0b5ef license: Apache-2.0 homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/containerlimits keywords: diff --git a/artifacthub/library/general/containerlimits/1.1.0/template.yaml b/artifacthub/library/general/containerlimits/1.1.0/template.yaml index 40a58293c..c9b2838af 100644 --- a/artifacthub/library/general/containerlimits/1.1.0/template.yaml +++ b/artifacthub/library/general/containerlimits/1.1.0/template.yaml @@ -177,6 +177,7 @@ spec: # Ephemeral containers not checked as it is not possible to set field. general_violation[{"msg": msg, "field": field}] { + input.parameters.cpu != "-1" container := input.review.object.spec[field][_] not is_exempt(container) cpu_orig := container.resources.limits.cpu diff --git a/library/general/containerlimits/template.yaml b/library/general/containerlimits/template.yaml index 40a58293c..c9b2838af 100644 --- a/library/general/containerlimits/template.yaml +++ b/library/general/containerlimits/template.yaml @@ -177,6 +177,7 @@ spec: # Ephemeral containers not checked as it is not possible to set field. general_violation[{"msg": msg, "field": field}] { + input.parameters.cpu != "-1" container := input.review.object.spec[field][_] not is_exempt(container) cpu_orig := container.resources.limits.cpu diff --git a/src/general/containerlimits/src.rego b/src/general/containerlimits/src.rego index 5a81f1db9..c800ead84 100644 --- a/src/general/containerlimits/src.rego +++ b/src/general/containerlimits/src.rego @@ -136,6 +136,7 @@ violation[{"msg": msg}] { # Ephemeral containers not checked as it is not possible to set field. general_violation[{"msg": msg, "field": field}] { + input.parameters.cpu != "-1" container := input.review.object.spec[field][_] not is_exempt(container) cpu_orig := container.resources.limits.cpu diff --git a/website/docs/validation/containerlimits.md b/website/docs/validation/containerlimits.md index 90882dd46..24633ee75 100644 --- a/website/docs/validation/containerlimits.md +++ b/website/docs/validation/containerlimits.md @@ -190,6 +190,7 @@ spec: # Ephemeral containers not checked as it is not possible to set field. general_violation[{"msg": msg, "field": field}] { + input.parameters.cpu != "-1" container := input.review.object.spec[field][_] not is_exempt(container) cpu_orig := container.resources.limits.cpu