From 4fb9e232d3222cbe5741054c4d3daed42dfe7d35 Mon Sep 17 00:00:00 2001 From: Thomas Spear Date: Mon, 20 Mar 2023 14:29:58 -0500 Subject: [PATCH 01/14] Add a disallow interactive TTY constraint Signed-off-by: Thomas Spear --- .../disallowinteractive/kustomization.yaml | 2 + .../no-interactive-containers/constraint.yaml | 9 ++ .../example_allowed.yaml | 12 +++ .../example_disallowed.yaml | 12 +++ .../general/disallowinteractive/suite.yaml | 17 ++++ .../general/disallowinteractive/template.yaml | 87 +++++++++++++++++++ library/general/kustomization.yaml | 7 +- 7 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 library/general/disallowinteractive/kustomization.yaml create mode 100644 library/general/disallowinteractive/samples/no-interactive-containers/constraint.yaml create mode 100644 library/general/disallowinteractive/samples/no-interactive-containers/example_allowed.yaml create mode 100644 library/general/disallowinteractive/samples/no-interactive-containers/example_disallowed.yaml create mode 100644 library/general/disallowinteractive/suite.yaml create mode 100644 library/general/disallowinteractive/template.yaml diff --git a/library/general/disallowinteractive/kustomization.yaml b/library/general/disallowinteractive/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/library/general/disallowinteractive/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/library/general/disallowinteractive/samples/no-interactive-containers/constraint.yaml b/library/general/disallowinteractive/samples/no-interactive-containers/constraint.yaml new file mode 100644 index 000000000..cf5eac82b --- /dev/null +++ b/library/general/disallowinteractive/samples/no-interactive-containers/constraint.yaml @@ -0,0 +1,9 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sDisallowInteractiveTTY +metadata: + name: no-interactive-tty-containers +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] diff --git a/library/general/disallowinteractive/samples/no-interactive-containers/example_allowed.yaml b/library/general/disallowinteractive/samples/no-interactive-containers/example_allowed.yaml new file mode 100644 index 000000000..bed6b5954 --- /dev/null +++ b/library/general/disallowinteractive/samples/no-interactive-containers/example_allowed.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-interactive-tty-allowed + labels: + app: nginx-interactive-tty +spec: + containers: + - name: nginx + image: nginx + stdin: false + tty: false diff --git a/library/general/disallowinteractive/samples/no-interactive-containers/example_disallowed.yaml b/library/general/disallowinteractive/samples/no-interactive-containers/example_disallowed.yaml new file mode 100644 index 000000000..aa4949323 --- /dev/null +++ b/library/general/disallowinteractive/samples/no-interactive-containers/example_disallowed.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-privilege-escalation-disallowed + labels: + app: nginx-privilege-escalation +spec: + containers: + - name: nginx + image: nginx + stdin: true + tty: true diff --git a/library/general/disallowinteractive/suite.yaml b/library/general/disallowinteractive/suite.yaml new file mode 100644 index 000000000..ed9acb08f --- /dev/null +++ b/library/general/disallowinteractive/suite.yaml @@ -0,0 +1,17 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: disallowinteractive +tests: +- name: disallow-interactive + template: template.yaml + constraint: samples/no-interactive-containers/constraint.yaml + cases: + - name: example-allowed + object: samples/no-interactive-containers/example_allowed.yaml + assertions: + - violations: no + - name: example-disallowed + object: samples/no-interactive-containers/example_disallowed.yaml + assertions: + - violations: yes diff --git a/library/general/disallowinteractive/template.yaml b/library/general/disallowinteractive/template.yaml new file mode 100644 index 000000000..8a82bc49b --- /dev/null +++ b/library/general/disallowinteractive/template.yaml @@ -0,0 +1,87 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8sdisallowinteractivetty + annotations: + metadata.gatekeeper.sh/title: "Disallow Interactive TTY Containers" + metadata.gatekeeper.sh/version: 1.0.0 + description: >- + Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. +spec: + crd: + spec: + names: + kind: K8sDisallowInteractiveTTY + validation: + openAPIV3Schema: + type: object + description: >- + Controls use of fields related to gaining an interactive session. Corresponds to the `tty` and + `stdin` fields in the Pod `spec.containers`, `spec.ephemeralContainers`, and `spec.initContainers`. + 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: + - rego: | + package k8sdisallowinteractivetty + + import data.lib.exempt_container.is_exempt + + violation[{"msg": msg, "details": {}}] { + c := input_containers[_] + not is_exempt(c) + input_allow_interactive_fields(c) + msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) + } + + input_allow_interactive_fields(c) { + has_field(c, "stdin") + not c.stdin == false + } + input_allow_interactive_fields(c) { + has_field(c, "tty") + not c.tty == false + } + input_containers[c] { + c := input.review.object.spec.containers[_] + } + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + # has_field returns whether an object has a field + has_field(object, field) = true { + object[field] + } + 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) + } + target: admission.k8s.gatekeeper.sh diff --git a/library/general/kustomization.yaml b/library/general/kustomization.yaml index b36358f6e..e93b562c4 100644 --- a/library/general/kustomization.yaml +++ b/library/general/kustomization.yaml @@ -10,9 +10,12 @@ resources: - containerlimits - containerrequests - containerresourceratios -- disallowedrepos +- containerresources - disallowanonymous +- disallowedrepos - disallowedtags +- disallowinteractive +- ephemeralstoragelimit - externalip - httpsonly - imagedigests @@ -25,6 +28,4 @@ resources: - uniqueingresshost - uniqueserviceselector - verifydeprecatedapi -- containerresources - storageclass -- ephemeralstoragelimit From f0dd4cdfed46f1f5da5f59819a308aeac42f4e27 Mon Sep 17 00:00:00 2001 From: Thomas Spear Date: Mon, 20 Mar 2023 15:14:12 -0500 Subject: [PATCH 02/14] Add source for disallow interactive Signed-off-by: Thomas Spear --- .../disallowinteractive/constraint.tmpl | 36 ++++ .../lib_exempt_container.rego | 19 ++ src/general/disallowinteractive/src.rego | 32 ++++ src/general/disallowinteractive/src_test.rego | 165 ++++++++++++++++++ 4 files changed, 252 insertions(+) create mode 100644 src/general/disallowinteractive/constraint.tmpl create mode 100644 src/general/disallowinteractive/lib_exempt_container.rego create mode 100644 src/general/disallowinteractive/src.rego create mode 100644 src/general/disallowinteractive/src_test.rego diff --git a/src/general/disallowinteractive/constraint.tmpl b/src/general/disallowinteractive/constraint.tmpl new file mode 100644 index 000000000..80b89e606 --- /dev/null +++ b/src/general/disallowinteractive/constraint.tmpl @@ -0,0 +1,36 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8sdisallowinteractivetty + annotations: + metadata.gatekeeper.sh/title: "Disallow Interactive TTY Containers" + metadata.gatekeeper.sh/version: 1.0.0 + description: >- + Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. +spec: + crd: + spec: + names: + kind: K8sDisallowInteractiveTTY + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + description: >- + Controls use of fields related to gaining an interactive session. Corresponds to the `tty` and + `stdin` fields in the Pod `spec.containers`, `spec.ephemeralContainers`, and `spec.initContainers`. + 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: | +{{ file.Read "src/general/disallowinteractive/src.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} diff --git a/src/general/disallowinteractive/lib_exempt_container.rego b/src/general/disallowinteractive/lib_exempt_container.rego new file mode 100644 index 000000000..c483416be --- /dev/null +++ b/src/general/disallowinteractive/lib_exempt_container.rego @@ -0,0 +1,19 @@ +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/src/general/disallowinteractive/src.rego b/src/general/disallowinteractive/src.rego new file mode 100644 index 000000000..355220695 --- /dev/null +++ b/src/general/disallowinteractive/src.rego @@ -0,0 +1,32 @@ +package k8sdisallowinteractivetty + +import data.lib.exempt_container.is_exempt + +violation[{"msg": msg, "details": {}}] { + c := input_containers[_] + not is_exempt(c) + input_allow_interactive_fields(c) + msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) +} + +input_allow_interactive_fields(c) { + has_field(c, "stdin") + not c.stdin == false +} +input_allow_interactive_fields(c) { + has_field(c, "tty") + not c.tty == false +} +input_containers[c] { + c := input.review.object.spec.containers[_] +} +input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] +} +input_containers[c] { + c := input.review.object.spec.initContainers[_] +} +# has_field returns whether an object has a field +has_field(object, field) = true { + object[field] +} diff --git a/src/general/disallowinteractive/src_test.rego b/src/general/disallowinteractive/src_test.rego new file mode 100644 index 000000000..78af2ca2d --- /dev/null +++ b/src/general/disallowinteractive/src_test.rego @@ -0,0 +1,165 @@ +package k8sdisallowinteractivetty + +test_input_container_not_tty_allowed { + input := { "review": input_review} + results := violation with input as input + count(results) == 0 +} +test_input_container_stdin_not_allowed { + input := { "review": input_review_stdin} + results := violation with input as input + count(results) == 1 +} +test_input_container_tty_not_allowed { + input := { "review": input_review_tty} + results := violation with input as input + count(results) == 1 +} +test_input_one_container_with_exemption { + input := { "review": input_review_stdin, "parameters": {"exemptImages": ["one/*"]}} + results := violation with input as input + count(results) == 0 +} +test_input_container_many_not_stdin_allowed { + input := { "review": input_review_many} + results := violation with input as input + count(results) == 1 +} +test_input_container_many_mixed_stdin_not_allowed { + input := { "review": input_review_many_mixed} + results := violation with input as input + count(results) == 2 +} +test_input_container_many_mixed_stdin_not_allowed_one_exempted { + input := { "review": input_review_many_mixed, "parameters": {"exemptImages": ["one/*"]}} + results := violation with input as input + count(results) == 1 +} +test_input_container_many_mixed_stdin_not_allowed_all_exempted { + input := { "review": input_review_many_mixed, "parameters": {"exemptImages": ["one/*", "two/*", "three/*"]}} + results := violation with input as input + count(results) == 0 +} +test_input_container_many_mixed_stdin_not_allowed_two { + input := { "review": input_review_many_mixed_two} + results := violation with input as input + count(results) == 2 +} + +input_review = { + "object": { + "metadata": { + "name": "nginx" + }, + "spec": { + "containers": input_containers_one + } + } +} + +input_review_stdin = { + "object": { + "metadata": { + "name": "nginx" + }, + "spec": { + "containers": input_containers_one_stdin, + } + } +} + +input_review_tty = { + "object": { + "metadata": { + "name": "nginx" + }, + "spec": { + "containers": input_containers_one_tty, + } + } +} + +input_review_many = { + "object": { + "metadata": { + "name": "nginx" + }, + "spec": { + "containers": input_containers_many, + "initContainers": input_containers_one + } + } +} + +input_review_many_mixed = { + "object": { + "metadata": { + "name": "nginx" + }, + "spec": { + "containers": input_containers_many, + "initContainers": input_containers_one_stdin + } + } +} + +input_review_many_mixed_two = { + "object": { + "metadata": { + "name": "nginx" + }, + "spec": { + "containers": input_containers_many_mixed, + "initContainers": input_containers_one_stdin + } + } +} + +input_containers_one = [ +{ + "name": "nginx", + "image": "one/nginx", +}] + +input_containers_one_stdin = [ +{ + "name": "nginx", + "image": "one/nginx", + "stdin": true +}] + +input_containers_one_tty = [ +{ + "name": "nginx", + "image": "one/nginx", + "tty": true +}] + +input_containers_many = [ +{ + "name": "nginx", + "image": "one/nginx", + "stdin": false +}, +{ + "name": "nginx1", + "image": "two/nginx" +}, +{ + "name": "nginx2", + "image": "three/nginx", + "stdin": true + +}] + +input_containers_many_mixed = [ +{ + "name": "nginx", + "image": "one/nginx", + "stdin": false +}, +{ + "name": "nginx1", + "image": "two/nginx", + "tty": true +}] From c8728c9204769529d3ae7d9b796689df1b3c37d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serta=C3=A7=20=C3=96zercan?= <852750+sozercan@users.noreply.github.com> Date: Sun, 26 Mar 2023 09:39:31 -0700 Subject: [PATCH 03/14] ci: remove skip for storage class (#300) * remove skip for storage class Signed-off-by: Sertac Ozercan * revert timestamp Signed-off-by: Sertac Ozercan --------- Signed-off-by: Sertac Ozercan Signed-off-by: Thomas Spear --- artifacthub/library/general/storageclass/1.0.0/README.md | 8 +++----- artifacthub/library/general/storageclass/1.0.1/README.md | 8 +++----- library/general/storageclass/README.md | 8 +++----- test/bats/test.bats | 2 -- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/artifacthub/library/general/storageclass/1.0.0/README.md b/artifacthub/library/general/storageclass/1.0.0/README.md index af119a56c..47aa0f477 100644 --- a/artifacthub/library/general/storageclass/1.0.0/README.md +++ b/artifacthub/library/general/storageclass/1.0.0/README.md @@ -1,16 +1,14 @@ # StorageClass -The `StorageClass` constraint blocks the creation of PVCs or StatefulSets +The `StorageClass` constraint blocks the creation of PVCs or StatefulSets where the specified storage class doesn't exist on the cluster, or that no storage class at all is specified. This policy helps prevent workloads from getting stuck indefinitely waiting -for a storage class to provision the persistent storage that will never +for a storage class to provision the persistent storage that will never happen. This often causes users to get confused as to why their pods are stuck pending, and requires deleting the StatefulSet and any PVCs it has created along with redeploying the workload in order to fix. Blocking it up front makes it much easier to fix before there is a mess to clean up. -**WARNING** This constraint only functions properly -on gatekeeper version 3.9 or above. - +> Please note that this policy requires Gatekeeper v3.9.0 or later. \ No newline at end of file diff --git a/artifacthub/library/general/storageclass/1.0.1/README.md b/artifacthub/library/general/storageclass/1.0.1/README.md index af119a56c..89e4178d7 100644 --- a/artifacthub/library/general/storageclass/1.0.1/README.md +++ b/artifacthub/library/general/storageclass/1.0.1/README.md @@ -1,16 +1,14 @@ # StorageClass -The `StorageClass` constraint blocks the creation of PVCs or StatefulSets +The `StorageClass` constraint blocks the creation of PVCs or StatefulSets where the specified storage class doesn't exist on the cluster, or that no storage class at all is specified. This policy helps prevent workloads from getting stuck indefinitely waiting -for a storage class to provision the persistent storage that will never +for a storage class to provision the persistent storage that will never happen. This often causes users to get confused as to why their pods are stuck pending, and requires deleting the StatefulSet and any PVCs it has created along with redeploying the workload in order to fix. Blocking it up front makes it much easier to fix before there is a mess to clean up. -**WARNING** This constraint only functions properly -on gatekeeper version 3.9 or above. - +> Please note that this policy requires Gatekeeper v3.9.0 or later. diff --git a/library/general/storageclass/README.md b/library/general/storageclass/README.md index af119a56c..89e4178d7 100644 --- a/library/general/storageclass/README.md +++ b/library/general/storageclass/README.md @@ -1,16 +1,14 @@ # StorageClass -The `StorageClass` constraint blocks the creation of PVCs or StatefulSets +The `StorageClass` constraint blocks the creation of PVCs or StatefulSets where the specified storage class doesn't exist on the cluster, or that no storage class at all is specified. This policy helps prevent workloads from getting stuck indefinitely waiting -for a storage class to provision the persistent storage that will never +for a storage class to provision the persistent storage that will never happen. This often causes users to get confused as to why their pods are stuck pending, and requires deleting the StatefulSet and any PVCs it has created along with redeploying the workload in order to fix. Blocking it up front makes it much easier to fix before there is a mess to clean up. -**WARNING** This constraint only functions properly -on gatekeeper version 3.9 or above. - +> Please note that this policy requires Gatekeeper v3.9.0 or later. diff --git a/test/bats/test.bats b/test/bats/test.bats index eb8a017af..460c6bfab 100755 --- a/test/bats/test.bats +++ b/test/bats/test.bats @@ -85,8 +85,6 @@ setup() { if [ -d "$policy" ]; then local policy_group=$(basename "$(dirname "$policy")") local template_name=$(basename "$policy") - # Skip storageclass test until gatekeeper 3.8 is unsupported - [ "$template_name" == "storageclass" ] && continue echo "running integration test against policy group: $policy_group, constraint template: $template_name" # apply template wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl apply -k $policy" From 3e52793f9d32e141c53bdde688ebc1bea7231bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serta=C3=A7=20=C3=96zercan?= <852750+sozercan@users.noreply.github.com> Date: Sun, 26 Mar 2023 09:40:18 -0700 Subject: [PATCH 04/14] fix: disallowed repos sample test name (#301) * fix: disallowed repos sample test name Signed-off-by: Sertac Ozercan * fix tests Signed-off-by: Sertac Ozercan --------- Signed-off-by: Sertac Ozercan Signed-off-by: Thomas Spear --- .../general/disallowedrepos/1.0.0/suite.yaml | 2 +- library/general/disallowedrepos/suite.yaml | 2 +- src/general/disallowedrepos/src_test.rego | 24 +++++++++---------- website/docs/validation/disallowedrepos.md | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/artifacthub/library/general/disallowedrepos/1.0.0/suite.yaml b/artifacthub/library/general/disallowedrepos/1.0.0/suite.yaml index 68714252d..bcc77de96 100644 --- a/artifacthub/library/general/disallowedrepos/1.0.0/suite.yaml +++ b/artifacthub/library/general/disallowedrepos/1.0.0/suite.yaml @@ -3,7 +3,7 @@ apiVersion: test.gatekeeper.sh/v1alpha1 metadata: name: disallowedrepos tests: -- name: allowed-repos +- name: repo-must-not-be-k8s-gcr-io template: template.yaml constraint: samples/repo-must-not-be-k8s-gcr-io/constraint.yaml cases: diff --git a/library/general/disallowedrepos/suite.yaml b/library/general/disallowedrepos/suite.yaml index 68714252d..bcc77de96 100644 --- a/library/general/disallowedrepos/suite.yaml +++ b/library/general/disallowedrepos/suite.yaml @@ -3,7 +3,7 @@ apiVersion: test.gatekeeper.sh/v1alpha1 metadata: name: disallowedrepos tests: -- name: allowed-repos +- name: repo-must-not-be-k8s-gcr-io template: template.yaml constraint: samples/repo-must-not-be-k8s-gcr-io/constraint.yaml cases: diff --git a/src/general/disallowedrepos/src_test.rego b/src/general/disallowedrepos/src_test.rego index ed4f7eea1..d13458af8 100644 --- a/src/general/disallowedrepos/src_test.rego +++ b/src/general/disallowedrepos/src_test.rego @@ -1,17 +1,17 @@ package k8sdisallowedrepos test_input_allowed_container { - input := { "review": input_review(input_container_disallowed), "parameters": {"repos": ["disallowed"]}} + input := { "review": input_review(input_container_allowed), "parameters": {"repos": ["disallowed"]}} results := violation with input as input count(results) == 0 } test_input_allowed_container_x2 { - input := { "review": input_review(input_container_disallowed), "parameters": {"repos": ["other", "disallowed"]}} + input := { "review": input_review(input_container_allowed), "parameters": {"repos": ["other", "disallowed"]}} results := violation with input as input count(results) == 0 } test_input_allowed_dual_container { - input := { "review": input_review(input_container_dual_disallowed), "parameters": {"repos": ["allowed"]}} + input := { "review": input_review(input_container_dual_allowed), "parameters": {"repos": ["disallowed"]}} results := violation with input as input count(results) == 0 } @@ -31,24 +31,24 @@ test_input_denied_dual_container { count(results) == 2 } test_input_denied_mixed_container { - input := { "review": input_review(array.concat(input_container_disallowed, input_container_denied)), "parameters": {"repos": ["allowed"]}} + input := { "review": input_review(array.concat(input_container_allowed, input_container_denied)), "parameters": {"repos": ["disallowed"]}} results := violation with input as input count(results) == 1 } # init containers test_input_allowed_initcontainer { - input := { "review": input_init_review(input_container_disallowed), "parameters": {"repos": ["disallowed"]}} + input := { "review": input_init_review(input_container_allowed), "parameters": {"repos": ["disallowed"]}} results := violation with input as input count(results) == 0 } test_input_allowed_initcontainer_x2 { - input := { "review": input_init_review(input_container_disallowed), "parameters": {"repos": ["other", "disallowed"]}} + input := { "review": input_init_review(input_container_allowed), "parameters": {"repos": ["other", "disallowed"]}} results := violation with input as input count(results) == 0 } test_input_allowed_dual_initcontainer { - input := { "review": input_init_review(input_container_dual_disallowed), "parameters": {"repos": ["allowed"]}} + input := { "review": input_init_review(input_container_dual_allowed), "parameters": {"repos": ["disallowed"]}} results := violation with input as input count(results) == 0 } @@ -68,7 +68,7 @@ test_input_denied_dual_initcontainer { count(results) == 2 } test_input_denied_mixed_initcontainer { - input := { "review": input_init_review(array.concat(input_container_disallowed, input_container_denied)), "parameters": {"repos": ["allowed"]}} + input := { "review": input_init_review(array.concat(input_container_allowed, input_container_denied)), "parameters": {"repos": ["allowed"]}} results := violation with input as input count(results) == 1 } @@ -99,7 +99,7 @@ input_init_review(containers) = output { } } -input_container_disallowed = [ +input_container_allowed = [ { "name": "nginx", "image": "allowed/nginx", @@ -111,14 +111,14 @@ input_container_denied = [ "image": "disallowed/nginx", }] -input_container_dual_disallowed = [ +input_container_dual_allowed = [ { "name": "nginx", - "image": "disallowed/nginx", + "image": "allowed/nginx", }, { "name": "other", - "image": "disallowed/other", + "image": "allowed/other", }] input_container_dual_denied = [ diff --git a/website/docs/validation/disallowedrepos.md b/website/docs/validation/disallowedrepos.md index 6b35709d0..2a761d5e1 100644 --- a/website/docs/validation/disallowedrepos.md +++ b/website/docs/validation/disallowedrepos.md @@ -68,7 +68,7 @@ kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper- ``` ## Examples
-allowed-repos
+repo-must-not-be-k8s-gcr-io
constraint From 05408fb897d499ba7e5022fe80c76b597a055df2 Mon Sep 17 00:00:00 2001 From: Craig Trought Date: Fri, 31 Mar 2023 18:42:55 -0400 Subject: [PATCH 05/14] feat: add HorizontalPodAutoscaler policy (#235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add HorizontalPodAutoscaler policy Signed-off-by: Craig Trought * chore: add metadata for artifacts Signed-off-by: Craig Trought * chore: generate artifacts Signed-off-by: Craig Trought * fix: remove sample constraint Signed-off-by: Craig Trought * core: add requiresSyncData metadata Signed-off-by: Craig Trought * add hpa policy to kustomize Signed-off-by: Craig Trought --------- Signed-off-by: Craig Trought Co-authored-by: Sertaç Özercan <852750+sozercan@users.noreply.github.com> Co-authored-by: Max Smythe Signed-off-by: Thomas Spear --- .../1.0.0/artifacthub-pkg.yml | 22 ++ .../1.0.0/kustomization.yaml | 2 + .../horizontalpodautoscaler/constraint.yaml | 16 + .../example_allowed_hpa.yaml | 19 ++ .../example_disallowed_hpa_replicas.yaml | 19 ++ .../example_disallowed_hpa_replicaspread.yaml | 19 ++ .../example_disallowed_hpa_scaletarget.yaml | 19 ++ .../example_inventory.yaml | 24 ++ .../horizontalpodautoscaler/1.0.0/suite.yaml | 33 ++ .../horizontalpodautoscaler/1.0.0/sync.yaml | 14 + .../1.0.0/template.yaml | 102 ++++++ .../kustomization.yaml | 2 + .../horizontalpodautoscaler/constraint.yaml | 16 + .../example_allowed_hpa.yaml | 19 ++ .../example_disallowed_hpa_replicas.yaml | 19 ++ .../example_disallowed_hpa_replicaspread.yaml | 19 ++ .../example_disallowed_hpa_scaletarget.yaml | 19 ++ .../example_inventory.yaml | 24 ++ .../horizontalpodautoscaler/suite.yaml | 33 ++ .../general/horizontalpodautoscaler/sync.yaml | 14 + .../horizontalpodautoscaler/template.yaml | 102 ++++++ library/general/kustomization.yaml | 1 + .../horizontalpodautoscaler/constraint.tmpl | 60 ++++ src/general/horizontalpodautoscaler/src.rego | 43 +++ .../horizontalpodautoscaler/src_test.rego | 130 ++++++++ website/docs/horizontalpodautoscaler.md | 291 ++++++++++++++++++ 26 files changed, 1081 insertions(+) create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/artifacthub-pkg.yml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/kustomization.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/constraint.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_allowed_hpa.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_inventory.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/suite.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/sync.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.0/template.yaml create mode 100644 library/general/horizontalpodautoscaler/kustomization.yaml create mode 100644 library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/constraint.yaml create mode 100644 library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_allowed_hpa.yaml create mode 100644 library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml create mode 100644 library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml create mode 100644 library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml create mode 100644 library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_inventory.yaml create mode 100644 library/general/horizontalpodautoscaler/suite.yaml create mode 100644 library/general/horizontalpodautoscaler/sync.yaml create mode 100644 library/general/horizontalpodautoscaler/template.yaml create mode 100644 src/general/horizontalpodautoscaler/constraint.tmpl create mode 100644 src/general/horizontalpodautoscaler/src.rego create mode 100644 src/general/horizontalpodautoscaler/src_test.rego create mode 100644 website/docs/horizontalpodautoscaler.md diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/artifacthub-pkg.yml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/artifacthub-pkg.yml new file mode 100644 index 000000000..bde593a8f --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/artifacthub-pkg.yml @@ -0,0 +1,22 @@ +version: 1.0.0 +name: k8shorizontalpodautoscaler +displayName: Horizontal Pod Autoscaler +createdAt: "2022-10-19T05:10:16Z" +description: Disallow the following scenarios when deploying `HorizontalPodAutoscalers` 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +digest: 7d4d8e0768cf5cc626e0ee27138d8fa48fbf6a016de3a1af618b5d14f18bef3b +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/horizontalpodautoscaler +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # Horizontal Pod Autoscaler + Disallow the following scenarios when deploying `HorizontalPodAutoscalers` 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/horizontalpodautoscaler/1.0.0/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/kustomization.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/constraint.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/constraint.yaml new file mode 100644 index 000000000..80d2c9a93 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/constraint.yaml @@ -0,0 +1,16 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sHorizontalPodAutoscaler +metadata: + name: horizontal-pod-autoscaler +spec: + enforcementAction: deny + match: + kinds: + - apiGroups: ["autoscaling"] + kinds: ["HorizontalPodAutoscaler"] + parameters: + minimumReplicaSpread: 1 + enforceScaleTargetRef: true + ranges: + - min_replicas: 3 + max_replicas: 6 diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_allowed_hpa.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_allowed_hpa.yaml new file mode 100644 index 000000000..5c0f0dc42 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_allowed_hpa.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-allowed + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml new file mode 100644 index 000000000..e7b8a984b --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicas + namespace: default +spec: + minReplicas: 2 + maxReplicas: 7 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml new file mode 100644 index 000000000..ee915d1ba --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicaspread + namespace: default +spec: + minReplicas: 4 + maxReplicas: 4 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml new file mode 100644 index 000000000..4c390f674 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-scaletarget + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment-missing diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_inventory.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_inventory.yaml new file mode 100644 index 000000000..507103931 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/samples/horizontalpodautoscaler/example_inventory.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + namespace: default + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + example: allowed-deployment + template: + metadata: + labels: + app: nginx + example: allowed-deployment + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/suite.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/suite.yaml new file mode 100644 index 000000000..1500708b0 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/suite.yaml @@ -0,0 +1,33 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: horizontalpodautoscaler +tests: +- name: horizontal-pod-autoscaler + template: template.yaml + constraint: samples/horizontalpodautoscaler/constraint.yaml + cases: + - name: example-allowed-hpa + object: samples/horizontalpodautoscaler/example_allowed_hpa.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: no + - name: example-disallowed-hpa-replicas + object: samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: yes + - name: example-disallowed-hpa-replicaspread + object: samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: yes + - name: example-disallowed-scaletarget + object: samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: yes diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/sync.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/sync.yaml new file mode 100644 index 000000000..abfbe2169 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/sync.yaml @@ -0,0 +1,14 @@ +apiVersion: config.gatekeeper.sh/v1alpha1 +kind: Config +metadata: + name: config + namespace: "gatekeeper-system" +spec: + sync: + syncOnly: + - group: "apps" + version: "v1" + kind: "Deployment" + - group: "apps" + version: "v1" + kind: "StatefulSet" diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.0/template.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/template.yaml new file mode 100644 index 000000000..1e15438db --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.0/template.yaml @@ -0,0 +1,102 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8shorizontalpodautoscaler + annotations: + metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" + metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/requiresSyncData: | + "[ + [ + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["Deployment"] + }, + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["StatefulSet"] + } + ] + ]" + description: >- + Disallow the following scenarios when deploying `HorizontalPodAutoscalers` + 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint + 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` + 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +spec: + crd: + spec: + names: + kind: K8sHorizontalPodAutoscaler + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + properties: + enforceScaleTargetRef: + description: If set to true it validates the HPA scaleTargetRef exists + type: boolean + minimumReplicaSpread: + description: If configured it enforces the minReplicas and maxReplicas in an HPA must have a spread of at least this many replicas + type: integer + ranges: + type: array + description: Allowed ranges for numbers of replicas. Values are inclusive. + items: + type: object + description: A range of allowed replicas. Values are inclusive. + properties: + min_replicas: + description: The minimum number of replicas allowed, inclusive. + type: integer + max_replicas: + description: The maximum number of replicas allowed, inclusive. + type: integer + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package k8shorizontalpodautoscaler + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_limit(hpa) + msg := sprintf("The %v <%v> minReplicas %v or maxReplicas %v is not allowed: %v. Allowed ranges: %v", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, input.parameters.ranges]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_spread(hpa) + + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + input.parameters.enforceScaleTargetRef + + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] + msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) + } + + input_replica_limit(hpa) { + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, hpa.spec.minReplicas, hpa.spec.maxReplicas) + } + + value_within_range(range, min_provided, max_provided) { + range.min_replicas <= min_provided + range.max_replicas >= max_provided + } + + input_replica_spread(hpa) { + input.parameters.minimumReplicaSpread + (hpa.spec.maxReplicas - hpa.spec.minReplicas) >= input.parameters.minimumReplicaSpread + } diff --git a/library/general/horizontalpodautoscaler/kustomization.yaml b/library/general/horizontalpodautoscaler/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/library/general/horizontalpodautoscaler/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/constraint.yaml b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/constraint.yaml new file mode 100644 index 000000000..80d2c9a93 --- /dev/null +++ b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/constraint.yaml @@ -0,0 +1,16 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sHorizontalPodAutoscaler +metadata: + name: horizontal-pod-autoscaler +spec: + enforcementAction: deny + match: + kinds: + - apiGroups: ["autoscaling"] + kinds: ["HorizontalPodAutoscaler"] + parameters: + minimumReplicaSpread: 1 + enforceScaleTargetRef: true + ranges: + - min_replicas: 3 + max_replicas: 6 diff --git a/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_allowed_hpa.yaml b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_allowed_hpa.yaml new file mode 100644 index 000000000..5c0f0dc42 --- /dev/null +++ b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_allowed_hpa.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-allowed + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment diff --git a/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml new file mode 100644 index 000000000..e7b8a984b --- /dev/null +++ b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicas + namespace: default +spec: + minReplicas: 2 + maxReplicas: 7 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment diff --git a/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml new file mode 100644 index 000000000..ee915d1ba --- /dev/null +++ b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicaspread + namespace: default +spec: + minReplicas: 4 + maxReplicas: 4 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment diff --git a/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml new file mode 100644 index 000000000..4c390f674 --- /dev/null +++ b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-scaletarget + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment-missing diff --git a/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_inventory.yaml b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_inventory.yaml new file mode 100644 index 000000000..507103931 --- /dev/null +++ b/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_inventory.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + namespace: default + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + example: allowed-deployment + template: + metadata: + labels: + app: nginx + example: allowed-deployment + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 diff --git a/library/general/horizontalpodautoscaler/suite.yaml b/library/general/horizontalpodautoscaler/suite.yaml new file mode 100644 index 000000000..1500708b0 --- /dev/null +++ b/library/general/horizontalpodautoscaler/suite.yaml @@ -0,0 +1,33 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: horizontalpodautoscaler +tests: +- name: horizontal-pod-autoscaler + template: template.yaml + constraint: samples/horizontalpodautoscaler/constraint.yaml + cases: + - name: example-allowed-hpa + object: samples/horizontalpodautoscaler/example_allowed_hpa.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: no + - name: example-disallowed-hpa-replicas + object: samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: yes + - name: example-disallowed-hpa-replicaspread + object: samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: yes + - name: example-disallowed-scaletarget + object: samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: yes diff --git a/library/general/horizontalpodautoscaler/sync.yaml b/library/general/horizontalpodautoscaler/sync.yaml new file mode 100644 index 000000000..abfbe2169 --- /dev/null +++ b/library/general/horizontalpodautoscaler/sync.yaml @@ -0,0 +1,14 @@ +apiVersion: config.gatekeeper.sh/v1alpha1 +kind: Config +metadata: + name: config + namespace: "gatekeeper-system" +spec: + sync: + syncOnly: + - group: "apps" + version: "v1" + kind: "Deployment" + - group: "apps" + version: "v1" + kind: "StatefulSet" diff --git a/library/general/horizontalpodautoscaler/template.yaml b/library/general/horizontalpodautoscaler/template.yaml new file mode 100644 index 000000000..1e15438db --- /dev/null +++ b/library/general/horizontalpodautoscaler/template.yaml @@ -0,0 +1,102 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8shorizontalpodautoscaler + annotations: + metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" + metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/requiresSyncData: | + "[ + [ + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["Deployment"] + }, + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["StatefulSet"] + } + ] + ]" + description: >- + Disallow the following scenarios when deploying `HorizontalPodAutoscalers` + 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint + 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` + 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +spec: + crd: + spec: + names: + kind: K8sHorizontalPodAutoscaler + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + properties: + enforceScaleTargetRef: + description: If set to true it validates the HPA scaleTargetRef exists + type: boolean + minimumReplicaSpread: + description: If configured it enforces the minReplicas and maxReplicas in an HPA must have a spread of at least this many replicas + type: integer + ranges: + type: array + description: Allowed ranges for numbers of replicas. Values are inclusive. + items: + type: object + description: A range of allowed replicas. Values are inclusive. + properties: + min_replicas: + description: The minimum number of replicas allowed, inclusive. + type: integer + max_replicas: + description: The maximum number of replicas allowed, inclusive. + type: integer + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package k8shorizontalpodautoscaler + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_limit(hpa) + msg := sprintf("The %v <%v> minReplicas %v or maxReplicas %v is not allowed: %v. Allowed ranges: %v", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, input.parameters.ranges]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_spread(hpa) + + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + input.parameters.enforceScaleTargetRef + + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] + msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) + } + + input_replica_limit(hpa) { + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, hpa.spec.minReplicas, hpa.spec.maxReplicas) + } + + value_within_range(range, min_provided, max_provided) { + range.min_replicas <= min_provided + range.max_replicas >= max_provided + } + + input_replica_spread(hpa) { + input.parameters.minimumReplicaSpread + (hpa.spec.maxReplicas - hpa.spec.minReplicas) >= input.parameters.minimumReplicaSpread + } diff --git a/library/general/kustomization.yaml b/library/general/kustomization.yaml index e93b562c4..5e7cf5c01 100644 --- a/library/general/kustomization.yaml +++ b/library/general/kustomization.yaml @@ -17,6 +17,7 @@ resources: - disallowinteractive - ephemeralstoragelimit - externalip +- horizontalpodautoscaler - httpsonly - imagedigests - noupdateserviceaccount diff --git a/src/general/horizontalpodautoscaler/constraint.tmpl b/src/general/horizontalpodautoscaler/constraint.tmpl new file mode 100644 index 000000000..db32145fb --- /dev/null +++ b/src/general/horizontalpodautoscaler/constraint.tmpl @@ -0,0 +1,60 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8shorizontalpodautoscaler + annotations: + metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" + metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/requiresSyncData: | + "[ + [ + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["Deployment"] + }, + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["StatefulSet"] + } + ] + ]" + description: >- + Disallow the following scenarios when deploying `HorizontalPodAutoscalers` + 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint + 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` + 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +spec: + crd: + spec: + names: + kind: K8sHorizontalPodAutoscaler + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + properties: + enforceScaleTargetRef: + description: If set to true it validates the HPA scaleTargetRef exists + type: boolean + minimumReplicaSpread: + description: If configured it enforces the minReplicas and maxReplicas in an HPA must have a spread of at least this many replicas + type: integer + ranges: + type: array + description: Allowed ranges for numbers of replicas. Values are inclusive. + items: + type: object + description: A range of allowed replicas. Values are inclusive. + properties: + min_replicas: + description: The minimum number of replicas allowed, inclusive. + type: integer + max_replicas: + description: The maximum number of replicas allowed, inclusive. + type: integer + targets: + - target: admission.k8s.gatekeeper.sh + rego: | +{{ file.Read "src/general/horizontalpodautoscaler/src.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} diff --git a/src/general/horizontalpodautoscaler/src.rego b/src/general/horizontalpodautoscaler/src.rego new file mode 100644 index 000000000..536393b55 --- /dev/null +++ b/src/general/horizontalpodautoscaler/src.rego @@ -0,0 +1,43 @@ +package k8shorizontalpodautoscaler + +violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_limit(hpa) + msg := sprintf("The %v <%v> minReplicas %v or maxReplicas %v is not allowed: %v. Allowed ranges: %v", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, input.parameters.ranges]) +} + +violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_spread(hpa) + + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) +} + +violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + input.parameters.enforceScaleTargetRef + + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] + msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) +} + +input_replica_limit(hpa) { + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, hpa.spec.minReplicas, hpa.spec.maxReplicas) +} + +value_within_range(range, min_provided, max_provided) { + range.min_replicas <= min_provided + range.max_replicas >= max_provided +} + +input_replica_spread(hpa) { + input.parameters.minimumReplicaSpread + (hpa.spec.maxReplicas - hpa.spec.minReplicas) >= input.parameters.minimumReplicaSpread +} diff --git a/src/general/horizontalpodautoscaler/src_test.rego b/src/general/horizontalpodautoscaler/src_test.rego new file mode 100644 index 000000000..4100ab8db --- /dev/null +++ b/src/general/horizontalpodautoscaler/src_test.rego @@ -0,0 +1,130 @@ +package k8shorizontalpodautoscaler + +namespace := "namespace-1" + +valid_scale_target_ref := { + "apiVersion": "apps/v1", + "kind": "Deployment", + "name": "deployment-1" +} + +invalid_scale_target_ref := { + "apiVersion": "apps/v1", + "kind": "Deployment", + "name": "deployment-invalid" +} + +deployment := { + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "name": "deployment-1", + "namespace": "namespace-1", + }, + "spec": { + "replicas": 1, + } +} + +test_input_hpa_min_replicas_outside_range { + input := {"review": input_hpa(2,5,valid_scale_target_ref), "parameters": input_parameters_valid_range} + results := violation with input as input + count(results) == 1 +} + +test_input_hpa_max_replicas_outside_range { + input := {"review": input_hpa(4,7,valid_scale_target_ref), "parameters": input_parameters_valid_range} + results := violation with input as input + count(results) == 1 +} + +test_input_hpa_replicas_within_range { + input := {"review": input_hpa(4,5,valid_scale_target_ref), "parameters": input_parameters_valid_range} + results := violation with input as input + count(results) == 0 +} + +test_input_hpa_replicas_equal_range { + input := {"review": input_hpa(3,6,valid_scale_target_ref), "parameters": input_parameters_valid_range} + results := violation with input as input + count(results) == 0 +} + +test_input_hpa_replicas_below_min_spread { + input := {"review": input_hpa(3,4,valid_scale_target_ref), "parameters": input_parameters_min_spread} + results := violation with input as input + count(results) == 1 +} + +test_input_hpa_replicas_above_min_spread { + input := {"review": input_hpa(3,6,valid_scale_target_ref), "parameters": input_parameters_min_spread} + results := violation with input as input + count(results) == 0 +} + +test_input_hpa_replicas_equal_min_spread { + input := {"review": input_hpa(4,6,valid_scale_target_ref), "parameters": input_parameters_min_spread} + results := violation with input as input + count(results) == 0 +} + +test_input_hpa_invalid_scale_target{ + input := {"review": input_hpa(3,6,invalid_scale_target_ref), "parameters": input_parameters_enforce_scale_target_ref} + inv := inv_deployment(deployment) + results := violation with input as input with data.inventory as inv + count(results) == 1 +} + +test_input_hpa_valid_scale_target{ + input := {"review": input_hpa(3,6,valid_scale_target_ref), "parameters": input_parameters_enforce_scale_target_ref} + inv := inv_deployment(deployment) + results := violation with input as input with data.inventory as inv + count(results) == 0 +} + +hpa(min_replicas, max_replicas, scale_target_ref) = output { + output := { + "apiVersion": "autoscaling/v1", + "kind": "HorizontalPodAutoscaler", + "metadata": { + "name": "hpa-1", + "namespace": "namespace-1", + }, + "spec": { + "scaleTargetRef": scale_target_ref, + "minReplicas": min_replicas, + "maxReplicas": max_replicas, + }, + } +} + +input_hpa(min_replicas, max_replicas, scale_target_ref) = output { + output := { + "kind": {"kind": "HorizontalPodAutoscaler"}, + "object": hpa(min_replicas, max_replicas, scale_target_ref), + } +} + +inventory(obj) = output { + output := {"namespace": {namespace: {obj.apiVersion: {obj.kind: {obj.metadata.name: obj}}}}} +} + +inv_deployment(deploy) = output { + output := inventory(deploy) +} + +input_parameters_valid_range = { + "ranges": [ + { + "min_replicas": 3, + "max_replicas": 6 + }] +} + +input_parameters_min_spread = { + "minimumReplicaSpread": 2 +} + +input_parameters_enforce_scale_target_ref = { + "enforceScaleTargetRef": true +} diff --git a/website/docs/horizontalpodautoscaler.md b/website/docs/horizontalpodautoscaler.md new file mode 100644 index 000000000..3431a1c7e --- /dev/null +++ b/website/docs/horizontalpodautoscaler.md @@ -0,0 +1,291 @@ +--- +id: horizontalpodautoscaler +title: Horizontal Pod Autoscaler +--- + +# Horizontal Pod Autoscaler + +## Description +Disallow the following scenarios when deploying `HorizontalPodAutoscalers` 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). + +## Template +```yaml +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8shorizontalpodautoscaler + annotations: + metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" + metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/requiresSyncData: | + "[ + [ + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["Deployment"] + }, + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["StatefulSet"] + } + ] + ]" + description: >- + Disallow the following scenarios when deploying `HorizontalPodAutoscalers` + 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint + 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` + 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +spec: + crd: + spec: + names: + kind: K8sHorizontalPodAutoscaler + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + properties: + enforceScaleTargetRef: + description: If set to true it validates the HPA scaleTargetRef exists + type: boolean + minimumReplicaSpread: + description: If configured it enforces the minReplicas and maxReplicas in an HPA must have a spread of at least this many replicas + type: integer + ranges: + type: array + description: Allowed ranges for numbers of replicas. Values are inclusive. + items: + type: object + description: A range of allowed replicas. Values are inclusive. + properties: + min_replicas: + description: The minimum number of replicas allowed, inclusive. + type: integer + max_replicas: + description: The maximum number of replicas allowed, inclusive. + type: integer + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package k8shorizontalpodautoscaler + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_limit(hpa) + msg := sprintf("The %v <%v> minReplicas %v or maxReplicas %v is not allowed: %v. Allowed ranges: %v", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, input.parameters.ranges]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_spread(hpa) + + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + input.parameters.enforceScaleTargetRef + + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] + msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) + } + + input_replica_limit(hpa) { + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, hpa.spec.minReplicas, hpa.spec.maxReplicas) + } + + value_within_range(range, min_provided, max_provided) { + range.min_replicas <= min_provided + range.max_replicas >= max_provided + } + + input_replica_spread(hpa) { + input.parameters.minimumReplicaSpread + (hpa.spec.maxReplicas - hpa.spec.minReplicas) >= input.parameters.minimumReplicaSpread + } + +``` + +### Usage +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/template.yaml +``` +## Examples +
+horizontal-pod-autoscaler
+ +
+constraint + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sHorizontalPodAutoscaler +metadata: + name: horizontal-pod-autoscaler +spec: + enforcementAction: deny + match: + kinds: + - apiGroups: ["autoscaling"] + kinds: ["HorizontalPodAutoscaler"] + parameters: + minimumReplicaSpread: 1 + enforceScaleTargetRef: true + ranges: + - min_replicas: 3 + max_replicas: 6 + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/constraint.yaml +``` + +
+ +
+example-allowed-hpa + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-allowed + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_allowed_hpa.yaml +``` + +
+
+example-disallowed-hpa-replicas + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicas + namespace: default +spec: + minReplicas: 2 + maxReplicas: 7 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml +``` + +
+
+example-disallowed-hpa-replicaspread + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicaspread + namespace: default +spec: + minReplicas: 4 + maxReplicas: 4 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml +``` + +
+
+example-disallowed-scaletarget + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-scaletarget + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment-missing + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml +``` + +
+ + +
\ No newline at end of file From 52fe3c3c306861d590a67cdcd2690f60f90d6147 Mon Sep 17 00:00:00 2001 From: Andrew Peabody Date: Wed, 5 Apr 2023 20:44:38 -0700 Subject: [PATCH 06/14] fix: remove hardcoded kind from replicalimits msg (#309) Signed-off-by: Thomas Spear --- .../replicalimits/1.0.1/artifacthub-pkg.yml | 22 +++++++ .../replicalimits/1.0.1/kustomization.yaml | 2 + .../samples/replicalimits/constraint.yaml | 13 +++++ .../replicalimits/example_allowed.yaml | 19 ++++++ .../replicalimits/example_disallowed.yaml | 19 ++++++ .../general/replicalimits/1.0.1/suite.yaml | 17 ++++++ .../general/replicalimits/1.0.1/template.yaml | 58 +++++++++++++++++++ library/general/replicalimits/template.yaml | 7 ++- src/general/replicalimits/constraint.tmpl | 2 +- src/general/replicalimits/src.rego | 5 +- src/general/replicalimits/src_test.rego | 7 ++- website/docs/validation/replicalimits.md | 7 ++- 12 files changed, 168 insertions(+), 10 deletions(-) create mode 100644 artifacthub/library/general/replicalimits/1.0.1/artifacthub-pkg.yml create mode 100644 artifacthub/library/general/replicalimits/1.0.1/kustomization.yaml create mode 100644 artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/constraint.yaml create mode 100644 artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/example_allowed.yaml create mode 100644 artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/example_disallowed.yaml create mode 100644 artifacthub/library/general/replicalimits/1.0.1/suite.yaml create mode 100644 artifacthub/library/general/replicalimits/1.0.1/template.yaml diff --git a/artifacthub/library/general/replicalimits/1.0.1/artifacthub-pkg.yml b/artifacthub/library/general/replicalimits/1.0.1/artifacthub-pkg.yml new file mode 100644 index 000000000..91893619a --- /dev/null +++ b/artifacthub/library/general/replicalimits/1.0.1/artifacthub-pkg.yml @@ -0,0 +1,22 @@ +version: 1.0.1 +name: k8sreplicalimits +displayName: Replica Limits +createdAt: "2023-03-31T20:34:16Z" +description: Requires that objects with the field `spec.replicas` (Deployments, ReplicaSets, etc.) specify a number of replicas within defined ranges. +digest: a8dc0e94f30cdf1f4aff2e69551744b6eaee3f4e60dba3393705f81363a249e5 +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/replicalimits +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # Replica Limits + Requires that objects with the field `spec.replicas` (Deployments, ReplicaSets, etc.) specify a number of replicas within defined ranges. +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/replicalimits/1.0.1/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/general/replicalimits/1.0.1/kustomization.yaml b/artifacthub/library/general/replicalimits/1.0.1/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/artifacthub/library/general/replicalimits/1.0.1/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/constraint.yaml b/artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/constraint.yaml new file mode 100644 index 000000000..b496235f3 --- /dev/null +++ b/artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/constraint.yaml @@ -0,0 +1,13 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sReplicaLimits +metadata: + name: replica-limits +spec: + match: + kinds: + - apiGroups: ["apps"] + kinds: ["Deployment"] + parameters: + ranges: + - min_replicas: 3 + max_replicas: 50 diff --git a/artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/example_allowed.yaml b/artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/example_allowed.yaml new file mode 100644 index 000000000..f5a2b1d8c --- /dev/null +++ b/artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/example_allowed.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: allowed-deployment +spec: + selector: + matchLabels: + app: nginx + replicas: 3 + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 diff --git a/artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/example_disallowed.yaml b/artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/example_disallowed.yaml new file mode 100644 index 000000000..1c4899d20 --- /dev/null +++ b/artifacthub/library/general/replicalimits/1.0.1/samples/replicalimits/example_disallowed.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: disallowed-deployment +spec: + selector: + matchLabels: + app: nginx + replicas: 100 + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 diff --git a/artifacthub/library/general/replicalimits/1.0.1/suite.yaml b/artifacthub/library/general/replicalimits/1.0.1/suite.yaml new file mode 100644 index 000000000..598efb814 --- /dev/null +++ b/artifacthub/library/general/replicalimits/1.0.1/suite.yaml @@ -0,0 +1,17 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: replicalimits +tests: +- name: replica-limit + template: template.yaml + constraint: samples/replicalimits/constraint.yaml + cases: + - name: example-allowed + object: samples/replicalimits/example_allowed.yaml + assertions: + - violations: no + - name: example-disallowed + object: samples/replicalimits/example_disallowed.yaml + assertions: + - violations: yes diff --git a/artifacthub/library/general/replicalimits/1.0.1/template.yaml b/artifacthub/library/general/replicalimits/1.0.1/template.yaml new file mode 100644 index 000000000..540abc67f --- /dev/null +++ b/artifacthub/library/general/replicalimits/1.0.1/template.yaml @@ -0,0 +1,58 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8sreplicalimits + annotations: + metadata.gatekeeper.sh/title: "Replica Limits" + metadata.gatekeeper.sh/version: 1.0.1 + description: >- + Requires that objects with the field `spec.replicas` (Deployments, + ReplicaSets, etc.) specify a number of replicas within defined ranges. +spec: + crd: + spec: + names: + kind: K8sReplicaLimits + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + properties: + ranges: + type: array + description: Allowed ranges for numbers of replicas. Values are inclusive. + items: + type: object + description: A range of allowed replicas. Values are inclusive. + properties: + min_replicas: + description: The minimum number of replicas allowed, inclusive. + type: integer + max_replicas: + description: The maximum number of replicas allowed, inclusive. + type: integer + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package k8sreplicalimits + + object_name = input.review.object.metadata.name + object_kind = input.review.kind.kind + + violation[{"msg": msg}] { + spec := input.review.object.spec + not input_replica_limit(spec) + msg := sprintf("The provided number of replicas is not allowed for %v: %v. Allowed ranges: %v", [object_kind, object_name, input.parameters]) + } + + input_replica_limit(spec) { + provided := input.review.object.spec.replicas + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, provided) + } + + value_within_range(range, value) { + range.min_replicas <= value + range.max_replicas >= value + } diff --git a/library/general/replicalimits/template.yaml b/library/general/replicalimits/template.yaml index b0e763d1c..540abc67f 100644 --- a/library/general/replicalimits/template.yaml +++ b/library/general/replicalimits/template.yaml @@ -4,7 +4,7 @@ metadata: name: k8sreplicalimits annotations: metadata.gatekeeper.sh/title: "Replica Limits" - metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/version: 1.0.1 description: >- Requires that objects with the field `spec.replicas` (Deployments, ReplicaSets, etc.) specify a number of replicas within defined ranges. @@ -36,12 +36,13 @@ spec: rego: | package k8sreplicalimits - deployment_name = input.review.object.metadata.name + object_name = input.review.object.metadata.name + object_kind = input.review.kind.kind violation[{"msg": msg}] { spec := input.review.object.spec not input_replica_limit(spec) - msg := sprintf("The provided number of replicas is not allowed for deployment: %v. Allowed ranges: %v", [deployment_name, input.parameters]) + msg := sprintf("The provided number of replicas is not allowed for %v: %v. Allowed ranges: %v", [object_kind, object_name, input.parameters]) } input_replica_limit(spec) { diff --git a/src/general/replicalimits/constraint.tmpl b/src/general/replicalimits/constraint.tmpl index 9bbd7c9df..2b4dc9183 100644 --- a/src/general/replicalimits/constraint.tmpl +++ b/src/general/replicalimits/constraint.tmpl @@ -4,7 +4,7 @@ metadata: name: k8sreplicalimits annotations: metadata.gatekeeper.sh/title: "Replica Limits" - metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/version: 1.0.1 description: >- Requires that objects with the field `spec.replicas` (Deployments, ReplicaSets, etc.) specify a number of replicas within defined ranges. diff --git a/src/general/replicalimits/src.rego b/src/general/replicalimits/src.rego index cae495691..796000da2 100644 --- a/src/general/replicalimits/src.rego +++ b/src/general/replicalimits/src.rego @@ -1,11 +1,12 @@ package k8sreplicalimits -deployment_name = input.review.object.metadata.name +object_name = input.review.object.metadata.name +object_kind = input.review.kind.kind violation[{"msg": msg}] { spec := input.review.object.spec not input_replica_limit(spec) - msg := sprintf("The provided number of replicas is not allowed for deployment: %v. Allowed ranges: %v", [deployment_name, input.parameters]) + msg := sprintf("The provided number of replicas is not allowed for %v: %v. Allowed ranges: %v", [object_kind, object_name, input.parameters]) } input_replica_limit(spec) { diff --git a/src/general/replicalimits/src_test.rego b/src/general/replicalimits/src_test.rego index e4c1f09a1..26a9a201b 100644 --- a/src/general/replicalimits/src_test.rego +++ b/src/general/replicalimits/src_test.rego @@ -52,6 +52,11 @@ empty = { review(replicas) = output { output = { + "kind": { + "kind": "Deployment", + "version": "v1", + "group": "apps", + }, "object": { "metadata": { "name": "nginx" @@ -61,7 +66,7 @@ review(replicas) = output { }, } } -} +} input_parameters_valid_range = { "ranges": [ diff --git a/website/docs/validation/replicalimits.md b/website/docs/validation/replicalimits.md index 8cab3e9a6..74e16a4dd 100644 --- a/website/docs/validation/replicalimits.md +++ b/website/docs/validation/replicalimits.md @@ -16,7 +16,7 @@ metadata: name: k8sreplicalimits annotations: metadata.gatekeeper.sh/title: "Replica Limits" - metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/version: 1.0.1 description: >- Requires that objects with the field `spec.replicas` (Deployments, ReplicaSets, etc.) specify a number of replicas within defined ranges. @@ -48,12 +48,13 @@ spec: rego: | package k8sreplicalimits - deployment_name = input.review.object.metadata.name + object_name = input.review.object.metadata.name + object_kind = input.review.kind.kind violation[{"msg": msg}] { spec := input.review.object.spec not input_replica_limit(spec) - msg := sprintf("The provided number of replicas is not allowed for deployment: %v. Allowed ranges: %v", [deployment_name, input.parameters]) + msg := sprintf("The provided number of replicas is not allowed for %v: %v. Allowed ranges: %v", [object_kind, object_name, input.parameters]) } input_replica_limit(spec) { From 651c7c40a3b5e9f10c783faff61fed14f46e4136 Mon Sep 17 00:00:00 2001 From: Thomas Spear Date: Thu, 6 Apr 2023 18:52:09 -0500 Subject: [PATCH 07/14] Run 'make generate generate-website-docs generate-artifacthub-artifacts' Signed-off-by: Thomas Spear --- .../1.0.0/artifacthub-pkg.yml | 22 ++ .../1.0.0/kustomization.yaml | 2 + .../no-interactive-containers/constraint.yaml | 9 + .../example_allowed.yaml | 12 + .../example_disallowed.yaml | 12 + .../disallowinteractive/1.0.0/suite.yaml | 17 + .../disallowinteractive/1.0.0/template.yaml | 67 ++++ .../general/disallowinteractive/template.yaml | 84 ++--- .../horizontalpodautoscaler/template.yaml | 4 +- .../horizontalpodautoscaler/constraint.tmpl | 2 +- src/general/horizontalpodautoscaler/src.rego | 4 +- .../docs/validation/disallowinteractive.md | 169 ++++++++++ .../validation/horizontalpodautoscaler.md | 291 ++++++++++++++++++ 13 files changed, 638 insertions(+), 57 deletions(-) create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/template.yaml create mode 100644 website/docs/validation/disallowinteractive.md create mode 100644 website/docs/validation/horizontalpodautoscaler.md diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml b/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml new file mode 100644 index 000000000..314750a7d --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml @@ -0,0 +1,22 @@ +version: 1.0.0 +name: k8sdisallowinteractivetty +displayName: Disallow Interactive TTY Containers +createdAt: "2023-04-06T23:43:38Z" +description: Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. +digest: 485078cadf4c4a849eb3a89071ee058c9d4bf7aec9cf5e6e448bd356eda80c92 +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/disallowinteractive +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # Disallow Interactive TTY Containers + Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml new file mode 100644 index 000000000..cf5eac82b --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml @@ -0,0 +1,9 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sDisallowInteractiveTTY +metadata: + name: no-interactive-tty-containers +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml new file mode 100644 index 000000000..bed6b5954 --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-interactive-tty-allowed + labels: + app: nginx-interactive-tty +spec: + containers: + - name: nginx + image: nginx + stdin: false + tty: false diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml new file mode 100644 index 000000000..aa4949323 --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-privilege-escalation-disallowed + labels: + app: nginx-privilege-escalation +spec: + containers: + - name: nginx + image: nginx + stdin: true + tty: true diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml new file mode 100644 index 000000000..ed9acb08f --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml @@ -0,0 +1,17 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: disallowinteractive +tests: +- name: disallow-interactive + template: template.yaml + constraint: samples/no-interactive-containers/constraint.yaml + cases: + - name: example-allowed + object: samples/no-interactive-containers/example_allowed.yaml + assertions: + - violations: no + - name: example-disallowed + object: samples/no-interactive-containers/example_disallowed.yaml + assertions: + - violations: yes diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml new file mode 100644 index 000000000..6cc1ab39d --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml @@ -0,0 +1,67 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8sdisallowinteractivetty + annotations: + metadata.gatekeeper.sh/title: "Disallow Interactive TTY Containers" + metadata.gatekeeper.sh/version: 1.0.0 + description: >- + Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. +spec: + crd: + spec: + names: + kind: K8sDisallowInteractiveTTY + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + description: >- + Controls use of fields related to gaining an interactive session. Corresponds to the `tty` and + `stdin` fields in the Pod `spec.containers`, `spec.ephemeralContainers`, and `spec.initContainers`. + 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 k8sdisallowinteractivetty + + import data.lib.exempt_container.is_exempt + + violation[{"msg": msg, "details": {}}] { + c := input_containers[_] + not is_exempt(c) + input_allow_interactive_fields(c) + msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) + } + + input_allow_interactive_fields(c) { + has_field(c, "stdin") + not c.stdin == false + } + input_allow_interactive_fields(c) { + has_field(c, "tty") + not c.tty == false + } + input_containers[c] { + c := input.review.object.spec.containers[_] + } + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + # has_field returns whether an object has a field + has_field(object, field) = true { + object[field] + } diff --git a/library/general/disallowinteractive/template.yaml b/library/general/disallowinteractive/template.yaml index 8a82bc49b..6cc1ab39d 100644 --- a/library/general/disallowinteractive/template.yaml +++ b/library/general/disallowinteractive/template.yaml @@ -13,6 +13,7 @@ spec: names: kind: K8sDisallowInteractiveTTY validation: + # Schema for the `parameters` field openAPIV3Schema: type: object description: >- @@ -30,58 +31,37 @@ spec: items: type: string targets: - - rego: | - package k8sdisallowinteractivetty + - target: admission.k8s.gatekeeper.sh + rego: | + package k8sdisallowinteractivetty - import data.lib.exempt_container.is_exempt + import data.lib.exempt_container.is_exempt - violation[{"msg": msg, "details": {}}] { - c := input_containers[_] - not is_exempt(c) - input_allow_interactive_fields(c) - msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) - } + violation[{"msg": msg, "details": {}}] { + c := input_containers[_] + not is_exempt(c) + input_allow_interactive_fields(c) + msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) + } - input_allow_interactive_fields(c) { - has_field(c, "stdin") - not c.stdin == false - } - input_allow_interactive_fields(c) { - has_field(c, "tty") - not c.tty == false - } - input_containers[c] { - c := input.review.object.spec.containers[_] - } - input_containers[c] { - c := input.review.object.spec.ephemeralContainers[_] - } - input_containers[c] { - c := input.review.object.spec.initContainers[_] - } - # has_field returns whether an object has a field - has_field(object, field) = true { - object[field] - } - 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) - } - target: admission.k8s.gatekeeper.sh + input_allow_interactive_fields(c) { + has_field(c, "stdin") + not c.stdin == false + } + input_allow_interactive_fields(c) { + has_field(c, "tty") + not c.tty == false + } + input_containers[c] { + c := input.review.object.spec.containers[_] + } + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + # has_field returns whether an object has a field + has_field(object, field) = true { + object[field] + } diff --git a/library/general/horizontalpodautoscaler/template.yaml b/library/general/horizontalpodautoscaler/template.yaml index 1e15438db..34cc720de 100644 --- a/library/general/horizontalpodautoscaler/template.yaml +++ b/library/general/horizontalpodautoscaler/template.yaml @@ -72,7 +72,7 @@ spec: hpa := input.review.object not input_replica_spread(hpa) - + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) } @@ -80,7 +80,7 @@ spec: input.review.kind.kind == "HorizontalPodAutoscaler" hpa := input.review.object input.parameters.enforceScaleTargetRef - + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) } diff --git a/src/general/horizontalpodautoscaler/constraint.tmpl b/src/general/horizontalpodautoscaler/constraint.tmpl index db32145fb..914496b9f 100644 --- a/src/general/horizontalpodautoscaler/constraint.tmpl +++ b/src/general/horizontalpodautoscaler/constraint.tmpl @@ -4,7 +4,7 @@ metadata: name: k8shorizontalpodautoscaler annotations: metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" - metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/version: 1.0.1 metadata.gatekeeper.sh/requiresSyncData: | "[ [ diff --git a/src/general/horizontalpodautoscaler/src.rego b/src/general/horizontalpodautoscaler/src.rego index 536393b55..45cc10bc6 100644 --- a/src/general/horizontalpodautoscaler/src.rego +++ b/src/general/horizontalpodautoscaler/src.rego @@ -13,7 +13,7 @@ violation[{"msg": msg}] { hpa := input.review.object not input_replica_spread(hpa) - + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) } @@ -21,7 +21,7 @@ violation[{"msg": msg}] { input.review.kind.kind == "HorizontalPodAutoscaler" hpa := input.review.object input.parameters.enforceScaleTargetRef - + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) } diff --git a/website/docs/validation/disallowinteractive.md b/website/docs/validation/disallowinteractive.md new file mode 100644 index 000000000..39cba15d0 --- /dev/null +++ b/website/docs/validation/disallowinteractive.md @@ -0,0 +1,169 @@ +--- +id: disallowinteractive +title: Disallow Interactive TTY Containers +--- + +# Disallow Interactive TTY Containers + +## Description +Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. + +## Template +```yaml +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8sdisallowinteractivetty + annotations: + metadata.gatekeeper.sh/title: "Disallow Interactive TTY Containers" + metadata.gatekeeper.sh/version: 1.0.0 + description: >- + Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. +spec: + crd: + spec: + names: + kind: K8sDisallowInteractiveTTY + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + description: >- + Controls use of fields related to gaining an interactive session. Corresponds to the `tty` and + `stdin` fields in the Pod `spec.containers`, `spec.ephemeralContainers`, and `spec.initContainers`. + 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 k8sdisallowinteractivetty + + import data.lib.exempt_container.is_exempt + + violation[{"msg": msg, "details": {}}] { + c := input_containers[_] + not is_exempt(c) + input_allow_interactive_fields(c) + msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) + } + + input_allow_interactive_fields(c) { + has_field(c, "stdin") + not c.stdin == false + } + input_allow_interactive_fields(c) { + has_field(c, "tty") + not c.tty == false + } + input_containers[c] { + c := input.review.object.spec.containers[_] + } + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + # has_field returns whether an object has a field + has_field(object, field) = true { + object[field] + } + +``` + +### Usage +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/template.yaml +``` +## Examples +
+disallow-interactive
+ +
+constraint + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sDisallowInteractiveTTY +metadata: + name: no-interactive-tty-containers +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/samples/no-interactive-containers/constraint.yaml +``` + +
+ +
+example-allowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-interactive-tty-allowed + labels: + app: nginx-interactive-tty +spec: + containers: + - name: nginx + image: nginx + stdin: false + tty: false + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/samples/no-interactive-containers/example_allowed.yaml +``` + +
+
+example-disallowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-privilege-escalation-disallowed + labels: + app: nginx-privilege-escalation +spec: + containers: + - name: nginx + image: nginx + stdin: true + tty: true + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/samples/no-interactive-containers/example_disallowed.yaml +``` + +
+ + +
\ No newline at end of file diff --git a/website/docs/validation/horizontalpodautoscaler.md b/website/docs/validation/horizontalpodautoscaler.md new file mode 100644 index 000000000..8c6938ce4 --- /dev/null +++ b/website/docs/validation/horizontalpodautoscaler.md @@ -0,0 +1,291 @@ +--- +id: horizontalpodautoscaler +title: Horizontal Pod Autoscaler +--- + +# Horizontal Pod Autoscaler + +## Description +Disallow the following scenarios when deploying `HorizontalPodAutoscalers` 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). + +## Template +```yaml +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8shorizontalpodautoscaler + annotations: + metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" + metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/requiresSyncData: | + "[ + [ + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["Deployment"] + }, + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["StatefulSet"] + } + ] + ]" + description: >- + Disallow the following scenarios when deploying `HorizontalPodAutoscalers` + 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint + 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` + 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +spec: + crd: + spec: + names: + kind: K8sHorizontalPodAutoscaler + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + properties: + enforceScaleTargetRef: + description: If set to true it validates the HPA scaleTargetRef exists + type: boolean + minimumReplicaSpread: + description: If configured it enforces the minReplicas and maxReplicas in an HPA must have a spread of at least this many replicas + type: integer + ranges: + type: array + description: Allowed ranges for numbers of replicas. Values are inclusive. + items: + type: object + description: A range of allowed replicas. Values are inclusive. + properties: + min_replicas: + description: The minimum number of replicas allowed, inclusive. + type: integer + max_replicas: + description: The maximum number of replicas allowed, inclusive. + type: integer + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package k8shorizontalpodautoscaler + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_limit(hpa) + msg := sprintf("The %v <%v> minReplicas %v or maxReplicas %v is not allowed: %v. Allowed ranges: %v", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, input.parameters.ranges]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_spread(hpa) + + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + input.parameters.enforceScaleTargetRef + + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] + msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) + } + + input_replica_limit(hpa) { + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, hpa.spec.minReplicas, hpa.spec.maxReplicas) + } + + value_within_range(range, min_provided, max_provided) { + range.min_replicas <= min_provided + range.max_replicas >= max_provided + } + + input_replica_spread(hpa) { + input.parameters.minimumReplicaSpread + (hpa.spec.maxReplicas - hpa.spec.minReplicas) >= input.parameters.minimumReplicaSpread + } + +``` + +### Usage +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/template.yaml +``` +## Examples +
+horizontal-pod-autoscaler
+ +
+constraint + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sHorizontalPodAutoscaler +metadata: + name: horizontal-pod-autoscaler +spec: + enforcementAction: deny + match: + kinds: + - apiGroups: ["autoscaling"] + kinds: ["HorizontalPodAutoscaler"] + parameters: + minimumReplicaSpread: 1 + enforceScaleTargetRef: true + ranges: + - min_replicas: 3 + max_replicas: 6 + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/constraint.yaml +``` + +
+ +
+example-allowed-hpa + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-allowed + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_allowed_hpa.yaml +``` + +
+
+example-disallowed-hpa-replicas + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicas + namespace: default +spec: + minReplicas: 2 + maxReplicas: 7 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml +``` + +
+
+example-disallowed-hpa-replicaspread + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicaspread + namespace: default +spec: + minReplicas: 4 + maxReplicas: 4 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml +``` + +
+
+example-disallowed-scaletarget + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-scaletarget + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment-missing + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml +``` + +
+ + +
\ No newline at end of file From c4a2a404ba01942912eb6b939c48faa8ff686e78 Mon Sep 17 00:00:00 2001 From: Thomas Spear Date: Thu, 6 Apr 2023 19:05:08 -0500 Subject: [PATCH 08/14] Run 'make generate generate-website-docs generate-artifacthub-artifacts' again Signed-off-by: Thomas Spear --- .../1.0.1/artifacthub-pkg.yml | 22 ++++ .../1.0.1/kustomization.yaml | 2 + .../horizontalpodautoscaler/constraint.yaml | 16 +++ .../example_allowed_hpa.yaml | 19 ++++ .../example_disallowed_hpa_replicas.yaml | 19 ++++ .../example_disallowed_hpa_replicaspread.yaml | 19 ++++ .../example_disallowed_hpa_scaletarget.yaml | 19 ++++ .../example_inventory.yaml | 24 +++++ .../horizontalpodautoscaler/1.0.1/suite.yaml | 33 ++++++ .../horizontalpodautoscaler/1.0.1/sync.yaml | 14 +++ .../1.0.1/template.yaml | 102 ++++++++++++++++++ .../horizontalpodautoscaler/template.yaml | 2 +- .../validation/horizontalpodautoscaler.md | 2 +- 13 files changed, 291 insertions(+), 2 deletions(-) create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/artifacthub-pkg.yml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/kustomization.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/constraint.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_allowed_hpa.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_inventory.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/suite.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/sync.yaml create mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/template.yaml diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/artifacthub-pkg.yml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/artifacthub-pkg.yml new file mode 100644 index 000000000..96be28225 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/artifacthub-pkg.yml @@ -0,0 +1,22 @@ +version: 1.0.1 +name: k8shorizontalpodautoscaler +displayName: Horizontal Pod Autoscaler +createdAt: "2023-04-07T00:04:54Z" +description: Disallow the following scenarios when deploying `HorizontalPodAutoscalers` 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +digest: 6a9905011993f87ebda6b6da77c966654206c71b44bc0af085e932f3962888ce +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/horizontalpodautoscaler +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # Horizontal Pod Autoscaler + Disallow the following scenarios when deploying `HorizontalPodAutoscalers` 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/horizontalpodautoscaler/1.0.1/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/kustomization.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/constraint.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/constraint.yaml new file mode 100644 index 000000000..80d2c9a93 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/constraint.yaml @@ -0,0 +1,16 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sHorizontalPodAutoscaler +metadata: + name: horizontal-pod-autoscaler +spec: + enforcementAction: deny + match: + kinds: + - apiGroups: ["autoscaling"] + kinds: ["HorizontalPodAutoscaler"] + parameters: + minimumReplicaSpread: 1 + enforceScaleTargetRef: true + ranges: + - min_replicas: 3 + max_replicas: 6 diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_allowed_hpa.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_allowed_hpa.yaml new file mode 100644 index 000000000..5c0f0dc42 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_allowed_hpa.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-allowed + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml new file mode 100644 index 000000000..e7b8a984b --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicas + namespace: default +spec: + minReplicas: 2 + maxReplicas: 7 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml new file mode 100644 index 000000000..ee915d1ba --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicaspread + namespace: default +spec: + minReplicas: 4 + maxReplicas: 4 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml new file mode 100644 index 000000000..4c390f674 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-scaletarget + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment-missing diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_inventory.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_inventory.yaml new file mode 100644 index 000000000..507103931 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_inventory.yaml @@ -0,0 +1,24 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + namespace: default + labels: + app: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + example: allowed-deployment + template: + metadata: + labels: + app: nginx + example: allowed-deployment + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/suite.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/suite.yaml new file mode 100644 index 000000000..1500708b0 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/suite.yaml @@ -0,0 +1,33 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: horizontalpodautoscaler +tests: +- name: horizontal-pod-autoscaler + template: template.yaml + constraint: samples/horizontalpodautoscaler/constraint.yaml + cases: + - name: example-allowed-hpa + object: samples/horizontalpodautoscaler/example_allowed_hpa.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: no + - name: example-disallowed-hpa-replicas + object: samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: yes + - name: example-disallowed-hpa-replicaspread + object: samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: yes + - name: example-disallowed-scaletarget + object: samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml + inventory: + - samples/horizontalpodautoscaler/example_inventory.yaml + assertions: + - violations: yes diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/sync.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/sync.yaml new file mode 100644 index 000000000..abfbe2169 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/sync.yaml @@ -0,0 +1,14 @@ +apiVersion: config.gatekeeper.sh/v1alpha1 +kind: Config +metadata: + name: config + namespace: "gatekeeper-system" +spec: + sync: + syncOnly: + - group: "apps" + version: "v1" + kind: "Deployment" + - group: "apps" + version: "v1" + kind: "StatefulSet" diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/template.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/template.yaml new file mode 100644 index 000000000..28154c2a9 --- /dev/null +++ b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/template.yaml @@ -0,0 +1,102 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8shorizontalpodautoscaler + annotations: + metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" + metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/requiresSyncData: | + "[ + [ + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["Deployment"] + }, + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["StatefulSet"] + } + ] + ]" + description: >- + Disallow the following scenarios when deploying `HorizontalPodAutoscalers` + 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint + 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` + 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +spec: + crd: + spec: + names: + kind: K8sHorizontalPodAutoscaler + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + properties: + enforceScaleTargetRef: + description: If set to true it validates the HPA scaleTargetRef exists + type: boolean + minimumReplicaSpread: + description: If configured it enforces the minReplicas and maxReplicas in an HPA must have a spread of at least this many replicas + type: integer + ranges: + type: array + description: Allowed ranges for numbers of replicas. Values are inclusive. + items: + type: object + description: A range of allowed replicas. Values are inclusive. + properties: + min_replicas: + description: The minimum number of replicas allowed, inclusive. + type: integer + max_replicas: + description: The maximum number of replicas allowed, inclusive. + type: integer + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package k8shorizontalpodautoscaler + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_limit(hpa) + msg := sprintf("The %v <%v> minReplicas %v or maxReplicas %v is not allowed: %v. Allowed ranges: %v", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, input.parameters.ranges]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_spread(hpa) + + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + input.parameters.enforceScaleTargetRef + + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] + msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) + } + + input_replica_limit(hpa) { + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, hpa.spec.minReplicas, hpa.spec.maxReplicas) + } + + value_within_range(range, min_provided, max_provided) { + range.min_replicas <= min_provided + range.max_replicas >= max_provided + } + + input_replica_spread(hpa) { + input.parameters.minimumReplicaSpread + (hpa.spec.maxReplicas - hpa.spec.minReplicas) >= input.parameters.minimumReplicaSpread + } diff --git a/library/general/horizontalpodautoscaler/template.yaml b/library/general/horizontalpodautoscaler/template.yaml index 34cc720de..28154c2a9 100644 --- a/library/general/horizontalpodautoscaler/template.yaml +++ b/library/general/horizontalpodautoscaler/template.yaml @@ -4,7 +4,7 @@ metadata: name: k8shorizontalpodautoscaler annotations: metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" - metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/version: 1.0.1 metadata.gatekeeper.sh/requiresSyncData: | "[ [ diff --git a/website/docs/validation/horizontalpodautoscaler.md b/website/docs/validation/horizontalpodautoscaler.md index 8c6938ce4..9cb344bbc 100644 --- a/website/docs/validation/horizontalpodautoscaler.md +++ b/website/docs/validation/horizontalpodautoscaler.md @@ -16,7 +16,7 @@ metadata: name: k8shorizontalpodautoscaler annotations: metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" - metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/version: 1.0.1 metadata.gatekeeper.sh/requiresSyncData: | "[ [ From 3085f8d36e7b693c0c8a577c8f5dc3d152b4b2cf Mon Sep 17 00:00:00 2001 From: Thomas Spear Date: Thu, 6 Apr 2023 21:13:36 -0500 Subject: [PATCH 09/14] Revert "Run 'make generate generate-website-docs generate-artifacthub-artifacts' again" This reverts commit 673a63a08d120555585d4b32a463caa8eeba8a2c. Signed-off-by: Thomas Spear --- .../1.0.1/artifacthub-pkg.yml | 22 ---- .../1.0.1/kustomization.yaml | 2 - .../horizontalpodautoscaler/constraint.yaml | 16 --- .../example_allowed_hpa.yaml | 19 ---- .../example_disallowed_hpa_replicas.yaml | 19 ---- .../example_disallowed_hpa_replicaspread.yaml | 19 ---- .../example_disallowed_hpa_scaletarget.yaml | 19 ---- .../example_inventory.yaml | 24 ----- .../horizontalpodautoscaler/1.0.1/suite.yaml | 33 ------ .../horizontalpodautoscaler/1.0.1/sync.yaml | 14 --- .../1.0.1/template.yaml | 102 ------------------ .../horizontalpodautoscaler/template.yaml | 2 +- .../validation/horizontalpodautoscaler.md | 2 +- 13 files changed, 2 insertions(+), 291 deletions(-) delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/artifacthub-pkg.yml delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/kustomization.yaml delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/constraint.yaml delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_allowed_hpa.yaml delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_inventory.yaml delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/suite.yaml delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/sync.yaml delete mode 100644 artifacthub/library/general/horizontalpodautoscaler/1.0.1/template.yaml diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/artifacthub-pkg.yml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/artifacthub-pkg.yml deleted file mode 100644 index 96be28225..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/artifacthub-pkg.yml +++ /dev/null @@ -1,22 +0,0 @@ -version: 1.0.1 -name: k8shorizontalpodautoscaler -displayName: Horizontal Pod Autoscaler -createdAt: "2023-04-07T00:04:54Z" -description: Disallow the following scenarios when deploying `HorizontalPodAutoscalers` 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). -digest: 6a9905011993f87ebda6b6da77c966654206c71b44bc0af085e932f3962888ce -license: Apache-2.0 -homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/horizontalpodautoscaler -keywords: - - gatekeeper - - open-policy-agent - - policies -readme: |- - # Horizontal Pod Autoscaler - Disallow the following scenarios when deploying `HorizontalPodAutoscalers` 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). -install: |- - ### Usage - ```shell - kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/horizontalpodautoscaler/1.0.1/template.yaml - ``` -provider: - name: Gatekeeper Library diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/kustomization.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/kustomization.yaml deleted file mode 100644 index 7d70d11b7..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: - - template.yaml diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/constraint.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/constraint.yaml deleted file mode 100644 index 80d2c9a93..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/constraint.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: constraints.gatekeeper.sh/v1beta1 -kind: K8sHorizontalPodAutoscaler -metadata: - name: horizontal-pod-autoscaler -spec: - enforcementAction: deny - match: - kinds: - - apiGroups: ["autoscaling"] - kinds: ["HorizontalPodAutoscaler"] - parameters: - minimumReplicaSpread: 1 - enforceScaleTargetRef: true - ranges: - - min_replicas: 3 - max_replicas: 6 diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_allowed_hpa.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_allowed_hpa.yaml deleted file mode 100644 index 5c0f0dc42..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_allowed_hpa.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: nginx-hpa-allowed - namespace: default -spec: - minReplicas: 3 - maxReplicas: 6 - metrics: - - resource: - name: cpu - target: - averageUtilization: 900 - type: Utilization - type: Resource - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: nginx-deployment diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml deleted file mode 100644 index e7b8a984b..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: nginx-hpa-disallowed-replicas - namespace: default -spec: - minReplicas: 2 - maxReplicas: 7 - metrics: - - resource: - name: cpu - target: - averageUtilization: 900 - type: Utilization - type: Resource - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: nginx-deployment diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml deleted file mode 100644 index ee915d1ba..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: nginx-hpa-disallowed-replicaspread - namespace: default -spec: - minReplicas: 4 - maxReplicas: 4 - metrics: - - resource: - name: cpu - target: - averageUtilization: 900 - type: Utilization - type: Resource - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: nginx-deployment diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml deleted file mode 100644 index 4c390f674..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: nginx-hpa-disallowed-scaletarget - namespace: default -spec: - minReplicas: 3 - maxReplicas: 6 - metrics: - - resource: - name: cpu - target: - averageUtilization: 900 - type: Utilization - type: Resource - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: nginx-deployment-missing diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_inventory.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_inventory.yaml deleted file mode 100644 index 507103931..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/samples/horizontalpodautoscaler/example_inventory.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx-deployment - namespace: default - labels: - app: nginx -spec: - replicas: 3 - selector: - matchLabels: - app: nginx - example: allowed-deployment - template: - metadata: - labels: - app: nginx - example: allowed-deployment - spec: - containers: - - name: nginx - image: nginx:1.14.2 - ports: - - containerPort: 80 diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/suite.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/suite.yaml deleted file mode 100644 index 1500708b0..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/suite.yaml +++ /dev/null @@ -1,33 +0,0 @@ -kind: Suite -apiVersion: test.gatekeeper.sh/v1alpha1 -metadata: - name: horizontalpodautoscaler -tests: -- name: horizontal-pod-autoscaler - template: template.yaml - constraint: samples/horizontalpodautoscaler/constraint.yaml - cases: - - name: example-allowed-hpa - object: samples/horizontalpodautoscaler/example_allowed_hpa.yaml - inventory: - - samples/horizontalpodautoscaler/example_inventory.yaml - assertions: - - violations: no - - name: example-disallowed-hpa-replicas - object: samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml - inventory: - - samples/horizontalpodautoscaler/example_inventory.yaml - assertions: - - violations: yes - - name: example-disallowed-hpa-replicaspread - object: samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml - inventory: - - samples/horizontalpodautoscaler/example_inventory.yaml - assertions: - - violations: yes - - name: example-disallowed-scaletarget - object: samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml - inventory: - - samples/horizontalpodautoscaler/example_inventory.yaml - assertions: - - violations: yes diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/sync.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/sync.yaml deleted file mode 100644 index abfbe2169..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/sync.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: config.gatekeeper.sh/v1alpha1 -kind: Config -metadata: - name: config - namespace: "gatekeeper-system" -spec: - sync: - syncOnly: - - group: "apps" - version: "v1" - kind: "Deployment" - - group: "apps" - version: "v1" - kind: "StatefulSet" diff --git a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/template.yaml b/artifacthub/library/general/horizontalpodautoscaler/1.0.1/template.yaml deleted file mode 100644 index 28154c2a9..000000000 --- a/artifacthub/library/general/horizontalpodautoscaler/1.0.1/template.yaml +++ /dev/null @@ -1,102 +0,0 @@ -apiVersion: templates.gatekeeper.sh/v1 -kind: ConstraintTemplate -metadata: - name: k8shorizontalpodautoscaler - annotations: - metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" - metadata.gatekeeper.sh/version: 1.0.1 - metadata.gatekeeper.sh/requiresSyncData: | - "[ - [ - { - "groups":["apps"], - "versions": ["v1"], - "kinds": ["Deployment"] - }, - { - "groups":["apps"], - "versions": ["v1"], - "kinds": ["StatefulSet"] - } - ] - ]" - description: >- - Disallow the following scenarios when deploying `HorizontalPodAutoscalers` - 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint - 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` - 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). -spec: - crd: - spec: - names: - kind: K8sHorizontalPodAutoscaler - validation: - # Schema for the `parameters` field - openAPIV3Schema: - type: object - properties: - enforceScaleTargetRef: - description: If set to true it validates the HPA scaleTargetRef exists - type: boolean - minimumReplicaSpread: - description: If configured it enforces the minReplicas and maxReplicas in an HPA must have a spread of at least this many replicas - type: integer - ranges: - type: array - description: Allowed ranges for numbers of replicas. Values are inclusive. - items: - type: object - description: A range of allowed replicas. Values are inclusive. - properties: - min_replicas: - description: The minimum number of replicas allowed, inclusive. - type: integer - max_replicas: - description: The maximum number of replicas allowed, inclusive. - type: integer - targets: - - target: admission.k8s.gatekeeper.sh - rego: | - package k8shorizontalpodautoscaler - - violation[{"msg": msg}] { - input.review.kind.kind == "HorizontalPodAutoscaler" - hpa := input.review.object - - not input_replica_limit(hpa) - msg := sprintf("The %v <%v> minReplicas %v or maxReplicas %v is not allowed: %v. Allowed ranges: %v", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, input.parameters.ranges]) - } - - violation[{"msg": msg}] { - input.review.kind.kind == "HorizontalPodAutoscaler" - hpa := input.review.object - - not input_replica_spread(hpa) - - msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) - } - - violation[{"msg": msg}] { - input.review.kind.kind == "HorizontalPodAutoscaler" - hpa := input.review.object - input.parameters.enforceScaleTargetRef - - not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] - msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) - } - - input_replica_limit(hpa) { - count(input.parameters.ranges) > 0 - range := input.parameters.ranges[_] - value_within_range(range, hpa.spec.minReplicas, hpa.spec.maxReplicas) - } - - value_within_range(range, min_provided, max_provided) { - range.min_replicas <= min_provided - range.max_replicas >= max_provided - } - - input_replica_spread(hpa) { - input.parameters.minimumReplicaSpread - (hpa.spec.maxReplicas - hpa.spec.minReplicas) >= input.parameters.minimumReplicaSpread - } diff --git a/library/general/horizontalpodautoscaler/template.yaml b/library/general/horizontalpodautoscaler/template.yaml index 28154c2a9..34cc720de 100644 --- a/library/general/horizontalpodautoscaler/template.yaml +++ b/library/general/horizontalpodautoscaler/template.yaml @@ -4,7 +4,7 @@ metadata: name: k8shorizontalpodautoscaler annotations: metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.0.0 metadata.gatekeeper.sh/requiresSyncData: | "[ [ diff --git a/website/docs/validation/horizontalpodautoscaler.md b/website/docs/validation/horizontalpodautoscaler.md index 9cb344bbc..8c6938ce4 100644 --- a/website/docs/validation/horizontalpodautoscaler.md +++ b/website/docs/validation/horizontalpodautoscaler.md @@ -16,7 +16,7 @@ metadata: name: k8shorizontalpodautoscaler annotations: metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.0.0 metadata.gatekeeper.sh/requiresSyncData: | "[ [ From 5d0c8965d1ad836b42bbd920ac5a5d9a623407e3 Mon Sep 17 00:00:00 2001 From: Thomas Spear Date: Thu, 6 Apr 2023 21:14:11 -0500 Subject: [PATCH 10/14] Revert "Run 'make generate generate-website-docs generate-artifacthub-artifacts'" This reverts commit 54b069c6e58963f37bad715da6a8c88378e32d2e. Signed-off-by: Thomas Spear --- .../1.0.0/artifacthub-pkg.yml | 22 -- .../1.0.0/kustomization.yaml | 2 - .../no-interactive-containers/constraint.yaml | 9 - .../example_allowed.yaml | 12 - .../example_disallowed.yaml | 12 - .../disallowinteractive/1.0.0/suite.yaml | 17 - .../disallowinteractive/1.0.0/template.yaml | 67 ---- .../general/disallowinteractive/template.yaml | 84 +++-- .../horizontalpodautoscaler/template.yaml | 4 +- .../horizontalpodautoscaler/constraint.tmpl | 2 +- src/general/horizontalpodautoscaler/src.rego | 4 +- .../docs/validation/disallowinteractive.md | 169 ---------- .../validation/horizontalpodautoscaler.md | 291 ------------------ 13 files changed, 57 insertions(+), 638 deletions(-) delete mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml delete mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml delete mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml delete mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml delete mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml delete mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml delete mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/template.yaml delete mode 100644 website/docs/validation/disallowinteractive.md delete mode 100644 website/docs/validation/horizontalpodautoscaler.md diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml b/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml deleted file mode 100644 index 314750a7d..000000000 --- a/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml +++ /dev/null @@ -1,22 +0,0 @@ -version: 1.0.0 -name: k8sdisallowinteractivetty -displayName: Disallow Interactive TTY Containers -createdAt: "2023-04-06T23:43:38Z" -description: Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. -digest: 485078cadf4c4a849eb3a89071ee058c9d4bf7aec9cf5e6e448bd356eda80c92 -license: Apache-2.0 -homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/disallowinteractive -keywords: - - gatekeeper - - open-policy-agent - - policies -readme: |- - # Disallow Interactive TTY Containers - Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. -install: |- - ### Usage - ```shell - kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml - ``` -provider: - name: Gatekeeper Library diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml deleted file mode 100644 index 7d70d11b7..000000000 --- a/artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: - - template.yaml diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml deleted file mode 100644 index cf5eac82b..000000000 --- a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: constraints.gatekeeper.sh/v1beta1 -kind: K8sDisallowInteractiveTTY -metadata: - name: no-interactive-tty-containers -spec: - match: - kinds: - - apiGroups: [""] - kinds: ["Pod"] diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml deleted file mode 100644 index bed6b5954..000000000 --- a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: nginx-interactive-tty-allowed - labels: - app: nginx-interactive-tty -spec: - containers: - - name: nginx - image: nginx - stdin: false - tty: false diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml deleted file mode 100644 index aa4949323..000000000 --- a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: nginx-privilege-escalation-disallowed - labels: - app: nginx-privilege-escalation -spec: - containers: - - name: nginx - image: nginx - stdin: true - tty: true diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml deleted file mode 100644 index ed9acb08f..000000000 --- a/artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml +++ /dev/null @@ -1,17 +0,0 @@ -kind: Suite -apiVersion: test.gatekeeper.sh/v1alpha1 -metadata: - name: disallowinteractive -tests: -- name: disallow-interactive - template: template.yaml - constraint: samples/no-interactive-containers/constraint.yaml - cases: - - name: example-allowed - object: samples/no-interactive-containers/example_allowed.yaml - assertions: - - violations: no - - name: example-disallowed - object: samples/no-interactive-containers/example_disallowed.yaml - assertions: - - violations: yes diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml deleted file mode 100644 index 6cc1ab39d..000000000 --- a/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: templates.gatekeeper.sh/v1 -kind: ConstraintTemplate -metadata: - name: k8sdisallowinteractivetty - annotations: - metadata.gatekeeper.sh/title: "Disallow Interactive TTY Containers" - metadata.gatekeeper.sh/version: 1.0.0 - description: >- - Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. -spec: - crd: - spec: - names: - kind: K8sDisallowInteractiveTTY - validation: - # Schema for the `parameters` field - openAPIV3Schema: - type: object - description: >- - Controls use of fields related to gaining an interactive session. Corresponds to the `tty` and - `stdin` fields in the Pod `spec.containers`, `spec.ephemeralContainers`, and `spec.initContainers`. - 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 k8sdisallowinteractivetty - - import data.lib.exempt_container.is_exempt - - violation[{"msg": msg, "details": {}}] { - c := input_containers[_] - not is_exempt(c) - input_allow_interactive_fields(c) - msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) - } - - input_allow_interactive_fields(c) { - has_field(c, "stdin") - not c.stdin == false - } - input_allow_interactive_fields(c) { - has_field(c, "tty") - not c.tty == false - } - input_containers[c] { - c := input.review.object.spec.containers[_] - } - input_containers[c] { - c := input.review.object.spec.ephemeralContainers[_] - } - input_containers[c] { - c := input.review.object.spec.initContainers[_] - } - # has_field returns whether an object has a field - has_field(object, field) = true { - object[field] - } diff --git a/library/general/disallowinteractive/template.yaml b/library/general/disallowinteractive/template.yaml index 6cc1ab39d..8a82bc49b 100644 --- a/library/general/disallowinteractive/template.yaml +++ b/library/general/disallowinteractive/template.yaml @@ -13,7 +13,6 @@ spec: names: kind: K8sDisallowInteractiveTTY validation: - # Schema for the `parameters` field openAPIV3Schema: type: object description: >- @@ -31,37 +30,58 @@ spec: items: type: string targets: - - target: admission.k8s.gatekeeper.sh - rego: | - package k8sdisallowinteractivetty + - rego: | + package k8sdisallowinteractivetty - import data.lib.exempt_container.is_exempt + import data.lib.exempt_container.is_exempt - violation[{"msg": msg, "details": {}}] { - c := input_containers[_] - not is_exempt(c) - input_allow_interactive_fields(c) - msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) - } + violation[{"msg": msg, "details": {}}] { + c := input_containers[_] + not is_exempt(c) + input_allow_interactive_fields(c) + msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) + } - input_allow_interactive_fields(c) { - has_field(c, "stdin") - not c.stdin == false - } - input_allow_interactive_fields(c) { - has_field(c, "tty") - not c.tty == false - } - input_containers[c] { - c := input.review.object.spec.containers[_] - } - input_containers[c] { - c := input.review.object.spec.ephemeralContainers[_] - } - input_containers[c] { - c := input.review.object.spec.initContainers[_] - } - # has_field returns whether an object has a field - has_field(object, field) = true { - object[field] - } + input_allow_interactive_fields(c) { + has_field(c, "stdin") + not c.stdin == false + } + input_allow_interactive_fields(c) { + has_field(c, "tty") + not c.tty == false + } + input_containers[c] { + c := input.review.object.spec.containers[_] + } + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + # has_field returns whether an object has a field + has_field(object, field) = true { + object[field] + } + 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) + } + target: admission.k8s.gatekeeper.sh diff --git a/library/general/horizontalpodautoscaler/template.yaml b/library/general/horizontalpodautoscaler/template.yaml index 34cc720de..1e15438db 100644 --- a/library/general/horizontalpodautoscaler/template.yaml +++ b/library/general/horizontalpodautoscaler/template.yaml @@ -72,7 +72,7 @@ spec: hpa := input.review.object not input_replica_spread(hpa) - + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) } @@ -80,7 +80,7 @@ spec: input.review.kind.kind == "HorizontalPodAutoscaler" hpa := input.review.object input.parameters.enforceScaleTargetRef - + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) } diff --git a/src/general/horizontalpodautoscaler/constraint.tmpl b/src/general/horizontalpodautoscaler/constraint.tmpl index 914496b9f..db32145fb 100644 --- a/src/general/horizontalpodautoscaler/constraint.tmpl +++ b/src/general/horizontalpodautoscaler/constraint.tmpl @@ -4,7 +4,7 @@ metadata: name: k8shorizontalpodautoscaler annotations: metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.0.0 metadata.gatekeeper.sh/requiresSyncData: | "[ [ diff --git a/src/general/horizontalpodautoscaler/src.rego b/src/general/horizontalpodautoscaler/src.rego index 45cc10bc6..536393b55 100644 --- a/src/general/horizontalpodautoscaler/src.rego +++ b/src/general/horizontalpodautoscaler/src.rego @@ -13,7 +13,7 @@ violation[{"msg": msg}] { hpa := input.review.object not input_replica_spread(hpa) - + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) } @@ -21,7 +21,7 @@ violation[{"msg": msg}] { input.review.kind.kind == "HorizontalPodAutoscaler" hpa := input.review.object input.parameters.enforceScaleTargetRef - + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) } diff --git a/website/docs/validation/disallowinteractive.md b/website/docs/validation/disallowinteractive.md deleted file mode 100644 index 39cba15d0..000000000 --- a/website/docs/validation/disallowinteractive.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -id: disallowinteractive -title: Disallow Interactive TTY Containers ---- - -# Disallow Interactive TTY Containers - -## Description -Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. - -## Template -```yaml -apiVersion: templates.gatekeeper.sh/v1 -kind: ConstraintTemplate -metadata: - name: k8sdisallowinteractivetty - annotations: - metadata.gatekeeper.sh/title: "Disallow Interactive TTY Containers" - metadata.gatekeeper.sh/version: 1.0.0 - description: >- - Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. -spec: - crd: - spec: - names: - kind: K8sDisallowInteractiveTTY - validation: - # Schema for the `parameters` field - openAPIV3Schema: - type: object - description: >- - Controls use of fields related to gaining an interactive session. Corresponds to the `tty` and - `stdin` fields in the Pod `spec.containers`, `spec.ephemeralContainers`, and `spec.initContainers`. - 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 k8sdisallowinteractivetty - - import data.lib.exempt_container.is_exempt - - violation[{"msg": msg, "details": {}}] { - c := input_containers[_] - not is_exempt(c) - input_allow_interactive_fields(c) - msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) - } - - input_allow_interactive_fields(c) { - has_field(c, "stdin") - not c.stdin == false - } - input_allow_interactive_fields(c) { - has_field(c, "tty") - not c.tty == false - } - input_containers[c] { - c := input.review.object.spec.containers[_] - } - input_containers[c] { - c := input.review.object.spec.ephemeralContainers[_] - } - input_containers[c] { - c := input.review.object.spec.initContainers[_] - } - # has_field returns whether an object has a field - has_field(object, field) = true { - object[field] - } - -``` - -### Usage -```shell -kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/template.yaml -``` -## Examples -
-disallow-interactive
- -
-constraint - -```yaml -apiVersion: constraints.gatekeeper.sh/v1beta1 -kind: K8sDisallowInteractiveTTY -metadata: - name: no-interactive-tty-containers -spec: - match: - kinds: - - apiGroups: [""] - kinds: ["Pod"] - -``` - -Usage - -```shell -kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/samples/no-interactive-containers/constraint.yaml -``` - -
- -
-example-allowed - -```yaml -apiVersion: v1 -kind: Pod -metadata: - name: nginx-interactive-tty-allowed - labels: - app: nginx-interactive-tty -spec: - containers: - - name: nginx - image: nginx - stdin: false - tty: false - -``` - -Usage - -```shell -kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/samples/no-interactive-containers/example_allowed.yaml -``` - -
-
-example-disallowed - -```yaml -apiVersion: v1 -kind: Pod -metadata: - name: nginx-privilege-escalation-disallowed - labels: - app: nginx-privilege-escalation -spec: - containers: - - name: nginx - image: nginx - stdin: true - tty: true - -``` - -Usage - -```shell -kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/samples/no-interactive-containers/example_disallowed.yaml -``` - -
- - -
\ No newline at end of file diff --git a/website/docs/validation/horizontalpodautoscaler.md b/website/docs/validation/horizontalpodautoscaler.md deleted file mode 100644 index 8c6938ce4..000000000 --- a/website/docs/validation/horizontalpodautoscaler.md +++ /dev/null @@ -1,291 +0,0 @@ ---- -id: horizontalpodautoscaler -title: Horizontal Pod Autoscaler ---- - -# Horizontal Pod Autoscaler - -## Description -Disallow the following scenarios when deploying `HorizontalPodAutoscalers` 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). - -## Template -```yaml -apiVersion: templates.gatekeeper.sh/v1 -kind: ConstraintTemplate -metadata: - name: k8shorizontalpodautoscaler - annotations: - metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" - metadata.gatekeeper.sh/version: 1.0.0 - metadata.gatekeeper.sh/requiresSyncData: | - "[ - [ - { - "groups":["apps"], - "versions": ["v1"], - "kinds": ["Deployment"] - }, - { - "groups":["apps"], - "versions": ["v1"], - "kinds": ["StatefulSet"] - } - ] - ]" - description: >- - Disallow the following scenarios when deploying `HorizontalPodAutoscalers` - 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint - 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` - 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). -spec: - crd: - spec: - names: - kind: K8sHorizontalPodAutoscaler - validation: - # Schema for the `parameters` field - openAPIV3Schema: - type: object - properties: - enforceScaleTargetRef: - description: If set to true it validates the HPA scaleTargetRef exists - type: boolean - minimumReplicaSpread: - description: If configured it enforces the minReplicas and maxReplicas in an HPA must have a spread of at least this many replicas - type: integer - ranges: - type: array - description: Allowed ranges for numbers of replicas. Values are inclusive. - items: - type: object - description: A range of allowed replicas. Values are inclusive. - properties: - min_replicas: - description: The minimum number of replicas allowed, inclusive. - type: integer - max_replicas: - description: The maximum number of replicas allowed, inclusive. - type: integer - targets: - - target: admission.k8s.gatekeeper.sh - rego: | - package k8shorizontalpodautoscaler - - violation[{"msg": msg}] { - input.review.kind.kind == "HorizontalPodAutoscaler" - hpa := input.review.object - - not input_replica_limit(hpa) - msg := sprintf("The %v <%v> minReplicas %v or maxReplicas %v is not allowed: %v. Allowed ranges: %v", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, input.parameters.ranges]) - } - - violation[{"msg": msg}] { - input.review.kind.kind == "HorizontalPodAutoscaler" - hpa := input.review.object - - not input_replica_spread(hpa) - - msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) - } - - violation[{"msg": msg}] { - input.review.kind.kind == "HorizontalPodAutoscaler" - hpa := input.review.object - input.parameters.enforceScaleTargetRef - - not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] - msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) - } - - input_replica_limit(hpa) { - count(input.parameters.ranges) > 0 - range := input.parameters.ranges[_] - value_within_range(range, hpa.spec.minReplicas, hpa.spec.maxReplicas) - } - - value_within_range(range, min_provided, max_provided) { - range.min_replicas <= min_provided - range.max_replicas >= max_provided - } - - input_replica_spread(hpa) { - input.parameters.minimumReplicaSpread - (hpa.spec.maxReplicas - hpa.spec.minReplicas) >= input.parameters.minimumReplicaSpread - } - -``` - -### Usage -```shell -kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/template.yaml -``` -## Examples -
-horizontal-pod-autoscaler
- -
-constraint - -```yaml -apiVersion: constraints.gatekeeper.sh/v1beta1 -kind: K8sHorizontalPodAutoscaler -metadata: - name: horizontal-pod-autoscaler -spec: - enforcementAction: deny - match: - kinds: - - apiGroups: ["autoscaling"] - kinds: ["HorizontalPodAutoscaler"] - parameters: - minimumReplicaSpread: 1 - enforceScaleTargetRef: true - ranges: - - min_replicas: 3 - max_replicas: 6 - -``` - -Usage - -```shell -kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/constraint.yaml -``` - -
- -
-example-allowed-hpa - -```yaml -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: nginx-hpa-allowed - namespace: default -spec: - minReplicas: 3 - maxReplicas: 6 - metrics: - - resource: - name: cpu - target: - averageUtilization: 900 - type: Utilization - type: Resource - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: nginx-deployment - -``` - -Usage - -```shell -kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_allowed_hpa.yaml -``` - -
-
-example-disallowed-hpa-replicas - -```yaml -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: nginx-hpa-disallowed-replicas - namespace: default -spec: - minReplicas: 2 - maxReplicas: 7 - metrics: - - resource: - name: cpu - target: - averageUtilization: 900 - type: Utilization - type: Resource - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: nginx-deployment - -``` - -Usage - -```shell -kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml -``` - -
-
-example-disallowed-hpa-replicaspread - -```yaml -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: nginx-hpa-disallowed-replicaspread - namespace: default -spec: - minReplicas: 4 - maxReplicas: 4 - metrics: - - resource: - name: cpu - target: - averageUtilization: 900 - type: Utilization - type: Resource - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: nginx-deployment - -``` - -Usage - -```shell -kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml -``` - -
-
-example-disallowed-scaletarget - -```yaml -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: nginx-hpa-disallowed-scaletarget - namespace: default -spec: - minReplicas: 3 - maxReplicas: 6 - metrics: - - resource: - name: cpu - target: - averageUtilization: 900 - type: Utilization - type: Resource - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: nginx-deployment-missing - -``` - -Usage - -```shell -kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml -``` - -
- - -
\ No newline at end of file From 0547a501d08783dfc7ab4bf6a2684995f6b031b6 Mon Sep 17 00:00:00 2001 From: Thomas Spear Date: Thu, 6 Apr 2023 21:15:52 -0500 Subject: [PATCH 11/14] Run 'make generate generate-website-docs generate-artifacthub-artifacts' Signed-off-by: Thomas Spear --- .../1.0.0/artifacthub-pkg.yml | 22 ++ .../1.0.0/kustomization.yaml | 2 + .../no-interactive-containers/constraint.yaml | 9 + .../example_allowed.yaml | 12 + .../example_disallowed.yaml | 12 + .../disallowinteractive/1.0.0/suite.yaml | 17 + .../disallowinteractive/1.0.0/template.yaml | 67 ++++ .../general/disallowinteractive/template.yaml | 84 ++--- .../docs/validation/disallowinteractive.md | 169 ++++++++++ .../validation/horizontalpodautoscaler.md | 291 ++++++++++++++++++ 10 files changed, 633 insertions(+), 52 deletions(-) create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml create mode 100644 artifacthub/library/general/disallowinteractive/1.0.0/template.yaml create mode 100644 website/docs/validation/disallowinteractive.md create mode 100644 website/docs/validation/horizontalpodautoscaler.md diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml b/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml new file mode 100644 index 000000000..ee0c87283 --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml @@ -0,0 +1,22 @@ +version: 1.0.0 +name: k8sdisallowinteractivetty +displayName: Disallow Interactive TTY Containers +createdAt: "2023-04-07T02:14:48Z" +description: Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. +digest: 485078cadf4c4a849eb3a89071ee058c9d4bf7aec9cf5e6e448bd356eda80c92 +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/disallowinteractive +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # Disallow Interactive TTY Containers + Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml new file mode 100644 index 000000000..cf5eac82b --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/constraint.yaml @@ -0,0 +1,9 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sDisallowInteractiveTTY +metadata: + name: no-interactive-tty-containers +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml new file mode 100644 index 000000000..bed6b5954 --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_allowed.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-interactive-tty-allowed + labels: + app: nginx-interactive-tty +spec: + containers: + - name: nginx + image: nginx + stdin: false + tty: false diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml new file mode 100644 index 000000000..aa4949323 --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/samples/no-interactive-containers/example_disallowed.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-privilege-escalation-disallowed + labels: + app: nginx-privilege-escalation +spec: + containers: + - name: nginx + image: nginx + stdin: true + tty: true diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml new file mode 100644 index 000000000..ed9acb08f --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/suite.yaml @@ -0,0 +1,17 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: disallowinteractive +tests: +- name: disallow-interactive + template: template.yaml + constraint: samples/no-interactive-containers/constraint.yaml + cases: + - name: example-allowed + object: samples/no-interactive-containers/example_allowed.yaml + assertions: + - violations: no + - name: example-disallowed + object: samples/no-interactive-containers/example_disallowed.yaml + assertions: + - violations: yes diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml new file mode 100644 index 000000000..6cc1ab39d --- /dev/null +++ b/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml @@ -0,0 +1,67 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8sdisallowinteractivetty + annotations: + metadata.gatekeeper.sh/title: "Disallow Interactive TTY Containers" + metadata.gatekeeper.sh/version: 1.0.0 + description: >- + Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. +spec: + crd: + spec: + names: + kind: K8sDisallowInteractiveTTY + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + description: >- + Controls use of fields related to gaining an interactive session. Corresponds to the `tty` and + `stdin` fields in the Pod `spec.containers`, `spec.ephemeralContainers`, and `spec.initContainers`. + 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 k8sdisallowinteractivetty + + import data.lib.exempt_container.is_exempt + + violation[{"msg": msg, "details": {}}] { + c := input_containers[_] + not is_exempt(c) + input_allow_interactive_fields(c) + msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) + } + + input_allow_interactive_fields(c) { + has_field(c, "stdin") + not c.stdin == false + } + input_allow_interactive_fields(c) { + has_field(c, "tty") + not c.tty == false + } + input_containers[c] { + c := input.review.object.spec.containers[_] + } + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + # has_field returns whether an object has a field + has_field(object, field) = true { + object[field] + } diff --git a/library/general/disallowinteractive/template.yaml b/library/general/disallowinteractive/template.yaml index 8a82bc49b..6cc1ab39d 100644 --- a/library/general/disallowinteractive/template.yaml +++ b/library/general/disallowinteractive/template.yaml @@ -13,6 +13,7 @@ spec: names: kind: K8sDisallowInteractiveTTY validation: + # Schema for the `parameters` field openAPIV3Schema: type: object description: >- @@ -30,58 +31,37 @@ spec: items: type: string targets: - - rego: | - package k8sdisallowinteractivetty + - target: admission.k8s.gatekeeper.sh + rego: | + package k8sdisallowinteractivetty - import data.lib.exempt_container.is_exempt + import data.lib.exempt_container.is_exempt - violation[{"msg": msg, "details": {}}] { - c := input_containers[_] - not is_exempt(c) - input_allow_interactive_fields(c) - msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) - } + violation[{"msg": msg, "details": {}}] { + c := input_containers[_] + not is_exempt(c) + input_allow_interactive_fields(c) + msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) + } - input_allow_interactive_fields(c) { - has_field(c, "stdin") - not c.stdin == false - } - input_allow_interactive_fields(c) { - has_field(c, "tty") - not c.tty == false - } - input_containers[c] { - c := input.review.object.spec.containers[_] - } - input_containers[c] { - c := input.review.object.spec.ephemeralContainers[_] - } - input_containers[c] { - c := input.review.object.spec.initContainers[_] - } - # has_field returns whether an object has a field - has_field(object, field) = true { - object[field] - } - 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) - } - target: admission.k8s.gatekeeper.sh + input_allow_interactive_fields(c) { + has_field(c, "stdin") + not c.stdin == false + } + input_allow_interactive_fields(c) { + has_field(c, "tty") + not c.tty == false + } + input_containers[c] { + c := input.review.object.spec.containers[_] + } + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + # has_field returns whether an object has a field + has_field(object, field) = true { + object[field] + } diff --git a/website/docs/validation/disallowinteractive.md b/website/docs/validation/disallowinteractive.md new file mode 100644 index 000000000..39cba15d0 --- /dev/null +++ b/website/docs/validation/disallowinteractive.md @@ -0,0 +1,169 @@ +--- +id: disallowinteractive +title: Disallow Interactive TTY Containers +--- + +# Disallow Interactive TTY Containers + +## Description +Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. + +## Template +```yaml +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8sdisallowinteractivetty + annotations: + metadata.gatekeeper.sh/title: "Disallow Interactive TTY Containers" + metadata.gatekeeper.sh/version: 1.0.0 + description: >- + Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. +spec: + crd: + spec: + names: + kind: K8sDisallowInteractiveTTY + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + description: >- + Controls use of fields related to gaining an interactive session. Corresponds to the `tty` and + `stdin` fields in the Pod `spec.containers`, `spec.ephemeralContainers`, and `spec.initContainers`. + 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 k8sdisallowinteractivetty + + import data.lib.exempt_container.is_exempt + + violation[{"msg": msg, "details": {}}] { + c := input_containers[_] + not is_exempt(c) + input_allow_interactive_fields(c) + msg := sprintf("Containers using tty or stdin (%v) are not allowed running image: %v", [c.name, c.image]) + } + + input_allow_interactive_fields(c) { + has_field(c, "stdin") + not c.stdin == false + } + input_allow_interactive_fields(c) { + has_field(c, "tty") + not c.tty == false + } + input_containers[c] { + c := input.review.object.spec.containers[_] + } + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + # has_field returns whether an object has a field + has_field(object, field) = true { + object[field] + } + +``` + +### Usage +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/template.yaml +``` +## Examples +
+disallow-interactive
+ +
+constraint + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sDisallowInteractiveTTY +metadata: + name: no-interactive-tty-containers +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/samples/no-interactive-containers/constraint.yaml +``` + +
+ +
+example-allowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-interactive-tty-allowed + labels: + app: nginx-interactive-tty +spec: + containers: + - name: nginx + image: nginx + stdin: false + tty: false + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/samples/no-interactive-containers/example_allowed.yaml +``` + +
+
+example-disallowed + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-privilege-escalation-disallowed + labels: + app: nginx-privilege-escalation +spec: + containers: + - name: nginx + image: nginx + stdin: true + tty: true + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/disallowinteractive/samples/no-interactive-containers/example_disallowed.yaml +``` + +
+ + +
\ No newline at end of file diff --git a/website/docs/validation/horizontalpodautoscaler.md b/website/docs/validation/horizontalpodautoscaler.md new file mode 100644 index 000000000..3431a1c7e --- /dev/null +++ b/website/docs/validation/horizontalpodautoscaler.md @@ -0,0 +1,291 @@ +--- +id: horizontalpodautoscaler +title: Horizontal Pod Autoscaler +--- + +# Horizontal Pod Autoscaler + +## Description +Disallow the following scenarios when deploying `HorizontalPodAutoscalers` 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). + +## Template +```yaml +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8shorizontalpodautoscaler + annotations: + metadata.gatekeeper.sh/title: "Horizontal Pod Autoscaler" + metadata.gatekeeper.sh/version: 1.0.0 + metadata.gatekeeper.sh/requiresSyncData: | + "[ + [ + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["Deployment"] + }, + { + "groups":["apps"], + "versions": ["v1"], + "kinds": ["StatefulSet"] + } + ] + ]" + description: >- + Disallow the following scenarios when deploying `HorizontalPodAutoscalers` + 1. Deployment of HorizontalPodAutoscalers with `.spec.minReplicas` or `.spec.maxReplicas` outside the ranges defined in the constraint + 2. Deployment of HorizontalPodAutoscalers where the difference between `.spec.minReplicas` and `.spec.maxReplicas` is less than the configured `minimumReplicaSpread` + 3. Deployment of HorizontalPodAutoscalers that do not reference a valid `scaleTargetRef` (e.g. Deployment, ReplicationController, ReplicaSet, StatefulSet). +spec: + crd: + spec: + names: + kind: K8sHorizontalPodAutoscaler + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + properties: + enforceScaleTargetRef: + description: If set to true it validates the HPA scaleTargetRef exists + type: boolean + minimumReplicaSpread: + description: If configured it enforces the minReplicas and maxReplicas in an HPA must have a spread of at least this many replicas + type: integer + ranges: + type: array + description: Allowed ranges for numbers of replicas. Values are inclusive. + items: + type: object + description: A range of allowed replicas. Values are inclusive. + properties: + min_replicas: + description: The minimum number of replicas allowed, inclusive. + type: integer + max_replicas: + description: The maximum number of replicas allowed, inclusive. + type: integer + targets: + - target: admission.k8s.gatekeeper.sh + rego: | + package k8shorizontalpodautoscaler + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_limit(hpa) + msg := sprintf("The %v <%v> minReplicas %v or maxReplicas %v is not allowed: %v. Allowed ranges: %v", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, input.parameters.ranges]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + + not input_replica_spread(hpa) + + msg := sprintf("The %v <%v> is configured with minReplicas %v and maxReplicas %v which is a spread of %v replica(s). The spread must be at least %v replica(s)", [hpa.kind, hpa.metadata.name, hpa.spec.minReplicas, hpa.spec.maxReplicas, hpa.spec.maxReplicas - hpa.spec.minReplicas, input.parameters.minimumReplicaSpread]) + } + + violation[{"msg": msg}] { + input.review.kind.kind == "HorizontalPodAutoscaler" + hpa := input.review.object + input.parameters.enforceScaleTargetRef + + not data.inventory.namespace[hpa.metadata.namespace][hpa.spec.scaleTargetRef.apiVersion][hpa.spec.scaleTargetRef.kind][hpa.spec.scaleTargetRef.name] + msg := sprintf("The HorizontalPodAutoscaler <%v> has a scaleTargetRef of <%v/%v> but it does not exist. The scaleTargetRef for the HorizontalPodAutoscaler must exist", [hpa.metadata.name, hpa.spec.scaleTargetRef.kind, hpa.spec.scaleTargetRef.name]) + } + + input_replica_limit(hpa) { + count(input.parameters.ranges) > 0 + range := input.parameters.ranges[_] + value_within_range(range, hpa.spec.minReplicas, hpa.spec.maxReplicas) + } + + value_within_range(range, min_provided, max_provided) { + range.min_replicas <= min_provided + range.max_replicas >= max_provided + } + + input_replica_spread(hpa) { + input.parameters.minimumReplicaSpread + (hpa.spec.maxReplicas - hpa.spec.minReplicas) >= input.parameters.minimumReplicaSpread + } + +``` + +### Usage +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/template.yaml +``` +## Examples +
+horizontal-pod-autoscaler
+ +
+constraint + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sHorizontalPodAutoscaler +metadata: + name: horizontal-pod-autoscaler +spec: + enforcementAction: deny + match: + kinds: + - apiGroups: ["autoscaling"] + kinds: ["HorizontalPodAutoscaler"] + parameters: + minimumReplicaSpread: 1 + enforceScaleTargetRef: true + ranges: + - min_replicas: 3 + max_replicas: 6 + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/constraint.yaml +``` + +
+ +
+example-allowed-hpa + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-allowed + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_allowed_hpa.yaml +``` + +
+
+example-disallowed-hpa-replicas + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicas + namespace: default +spec: + minReplicas: 2 + maxReplicas: 7 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicas.yaml +``` + +
+
+example-disallowed-hpa-replicaspread + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-replicaspread + namespace: default +spec: + minReplicas: 4 + maxReplicas: 4 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_replicaspread.yaml +``` + +
+
+example-disallowed-scaletarget + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: nginx-hpa-disallowed-scaletarget + namespace: default +spec: + minReplicas: 3 + maxReplicas: 6 + metrics: + - resource: + name: cpu + target: + averageUtilization: 900 + type: Utilization + type: Resource + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: nginx-deployment-missing + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/general/horizontalpodautoscaler/samples/horizontalpodautoscaler/example_disallowed_hpa_scaletarget.yaml +``` + +
+ + +
\ No newline at end of file From b2d9eb8ed92704f958f2ea0e7e2d03b947f76d09 Mon Sep 17 00:00:00 2001 From: Thomas Spear Date: Fri, 7 Apr 2023 01:47:12 -0500 Subject: [PATCH 12/14] Fix missing libs Signed-off-by: Thomas Spear --- src/general/disallowinteractive/constraint.tmpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/general/disallowinteractive/constraint.tmpl b/src/general/disallowinteractive/constraint.tmpl index 80b89e606..7af7d52c3 100644 --- a/src/general/disallowinteractive/constraint.tmpl +++ b/src/general/disallowinteractive/constraint.tmpl @@ -34,3 +34,6 @@ spec: - target: admission.k8s.gatekeeper.sh rego: | {{ file.Read "src/general/disallowinteractive/src.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} + libs: + - | +{{ file.Read "src/general/disallowinteractive/lib_exempt_container.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} From c7b3e61fb3b19a0381af5762094759cb24f37517 Mon Sep 17 00:00:00 2001 From: Thomas Spear Date: Thu, 27 Apr 2023 16:41:40 -0500 Subject: [PATCH 13/14] Fix CI Signed-off-by: Thomas Spear --- .../1.0.0/artifacthub-pkg.yml | 4 ++-- .../disallowinteractive/1.0.0/template.yaml | 21 +++++++++++++++++++ .../general/disallowinteractive/template.yaml | 21 +++++++++++++++++++ .../docs/validation/disallowinteractive.md | 21 +++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml b/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml index ee0c87283..ce76a2d24 100644 --- a/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml +++ b/artifacthub/library/general/disallowinteractive/1.0.0/artifacthub-pkg.yml @@ -1,9 +1,9 @@ version: 1.0.0 name: k8sdisallowinteractivetty displayName: Disallow Interactive TTY Containers -createdAt: "2023-04-07T02:14:48Z" +createdAt: "2023-04-27T21:41:24Z" description: Requires that objects have the fields `spec.tty` and `spec.stdin` set to false or unset. -digest: 485078cadf4c4a849eb3a89071ee058c9d4bf7aec9cf5e6e448bd356eda80c92 +digest: c462c392ee271922f97e53d084646857dbbc97f496382e9b5c117532ccf3b5bc license: Apache-2.0 homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/disallowinteractive keywords: diff --git a/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml b/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml index 6cc1ab39d..63bc5d179 100644 --- a/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml +++ b/artifacthub/library/general/disallowinteractive/1.0.0/template.yaml @@ -65,3 +65,24 @@ spec: has_field(object, field) = true { object[field] } + 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/disallowinteractive/template.yaml b/library/general/disallowinteractive/template.yaml index 6cc1ab39d..63bc5d179 100644 --- a/library/general/disallowinteractive/template.yaml +++ b/library/general/disallowinteractive/template.yaml @@ -65,3 +65,24 @@ spec: has_field(object, field) = true { object[field] } + 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/disallowinteractive.md b/website/docs/validation/disallowinteractive.md index 39cba15d0..4c1f9d1a0 100644 --- a/website/docs/validation/disallowinteractive.md +++ b/website/docs/validation/disallowinteractive.md @@ -77,6 +77,27 @@ spec: has_field(object, field) = true { object[field] } + 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) + } ``` From 696297927d048a95d33621d5ee5cfa2d77efa6b7 Mon Sep 17 00:00:00 2001 From: Thomas Spear Date: Mon, 22 May 2023 15:08:11 -0500 Subject: [PATCH 14/14] Run make generate generate-website-docs generate-artifacthub-artifacts Signed-off-by: Thomas Spear --- website/sidebars.js | 1 + 1 file changed, 1 insertion(+) diff --git a/website/sidebars.js b/website/sidebars.js index 0d88e156e..a14f9496c 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -31,6 +31,7 @@ module.exports = { 'validation/disallowanonymous', 'validation/disallowedrepos', 'validation/disallowedtags', + 'validation/disallowinteractive', 'validation/ephemeralstoragelimit', 'validation/externalip', 'validation/horizontalpodautoscaler',