From 6e785999356b5ce187ae57479d6293dd59537de4 Mon Sep 17 00:00:00 2001 From: wwwil Date: Fri, 17 Jan 2020 13:10:40 +0000 Subject: [PATCH 1/5] Add pods package Signed-off-by: wwwil --- preflight-packages/jetstack.io/pods/pods.rego | 201 ++ .../jetstack.io/pods/pods_test.rego | 1828 +++++++++++++++++ .../jetstack.io/pods/policy-manifest.yaml | 279 +++ 3 files changed, 2308 insertions(+) create mode 100644 preflight-packages/jetstack.io/pods/pods.rego create mode 100644 preflight-packages/jetstack.io/pods/pods_test.rego create mode 100644 preflight-packages/jetstack.io/pods/policy-manifest.yaml diff --git a/preflight-packages/jetstack.io/pods/pods.rego b/preflight-packages/jetstack.io/pods/pods.rego new file mode 100644 index 00000000..595992fe --- /dev/null +++ b/preflight-packages/jetstack.io/pods/pods.rego @@ -0,0 +1,201 @@ +package preflight._2_pods + +import input["k8s/pods"] as pods + +# 2.1 Resources + +# 2.1.1 CPU requests set +preflight_2_1_1[message] { + # find all containers in all pods + pod := pods.items[_] + container := pod.spec.containers[_] + # test if the limits are not set + not container.resources.requests.cpu + # bind a message for reporting + message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing a cpu request", [container.name, pod.metadata.name, pod.metadata.namespace]) +} +preflight_2_1_1[message] { + # find all initContainers in all pods + pod := pods.items[_] + container := pod.spec.initContainers[_] + # test if the limits are not set + not container.resources.requests.cpu + # bind a message for reporting + message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing a cpu request", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +# 2.1.2 Memory requests set +preflight_2_1_2[message] { + # find all containers in all pods + pod := pods.items[_] + container := pod.spec.containers[_] + # test if the limits are not set + not container.resources.requests.memory + # bind a message for reporting + message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing a memory request", [container.name, pod.metadata.name, pod.metadata.namespace]) +} +preflight_2_1_2[message] { + # find all initContainers in all pods + pod := pods.items[_] + container := pod.spec.initContainers[_] + # test if the limits are not set + not container.resources.requests.memory + # bind a message for reporting + message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing a memory request", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +# 2.1.3 CPU limits set +preflight_2_1_3[message] { + # find all containers in all pods + pod := pods.items[_] + container := pod.spec.containers[_] + # test if the limits are not set + not container.resources.limits.cpu + # bind a message for reporting + message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing a cpu limit", [container.name, pod.metadata.name, pod.metadata.namespace]) +} +preflight_2_1_3[message] { + # find all initContainers in all pods + pod := pods.items[_] + container := pod.spec.initContainers[_] + # test if the limits are not set + not container.resources.limits.cpu + # bind a message for reporting + message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing a cpu limit", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +# 2.1.4 Memory limits set +preflight_2_1_4[message] { + # find all containers in all pods + pod := pods.items[_] + container := pod.spec.containers[_] + # test if the limits are not set + not container.resources.limits.memory + # bind a message for reporting + message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing a memory limit", [container.name, pod.metadata.name, pod.metadata.namespace]) +} +preflight_2_1_4[message] { + # find all initContainers in all pods + pod := pods.items[_] + container := pod.spec.initContainers[_] + # test if the limits are not set + not container.resources.limits.memory + # bind a message for reporting + message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing a memory limit", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +# 2.1.5 Guaranteed QoS +preflight_2_1_5[message] { + pod := pods.items[_] + container := pod.spec.containers[_] + { container.resources.requests == {}, + container.resources.limits == {}, + container.resources.requests != container.resources.limits } & { true } != set() + + message := sprintf("container '%s' in pod '%s' in namespace '%s' is not Guaranteed QoS", [container.name, pod.metadata.name, pod.metadata.namespace]) +} +preflight_2_1_5[message] { + pod := pods.items[_] + container := pod.spec.containers[_] + not container.resources.requests + + message := sprintf("container '%s' in pod '%s' in namespace '%s' is not Guaranteed QoS", [container.name, pod.metadata.name, pod.metadata.namespace]) +} +preflight_2_1_5[message] { + pod := pods.items[_] + container := pod.spec.containers[_] + not container.resources.limits + + message := sprintf("container '%s' in pod '%s' in namespace '%s' is not Guaranteed QoS", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +# 2.2 Monitoring + +# 2.2.1 Liveness probe set +preflight_2_2_1[message] { + pod := pods.items[_] + container := pod.spec.containers[_] + container.livenessProbe == {} + + message := sprintf("livenessProbe not set in container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) +} +preflight_2_2_1[message] { + pod := pods.items[_] + container := pod.spec.containers[_] + not container.livenessProbe + + message := sprintf("livenessProbe not set in container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +# 2.2.2 Readiness probe set +preflight_2_2_2[message] { + pod := pods.items[_] + container := pod.spec.containers[_] + container.readinessProbe == {} + message := sprintf("readinessProbe not set in container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +preflight_2_2_2[message] { + pod := pods.items[_] + container := pod.spec.containers[_] + not container.readinessProbe + + message := sprintf("readinessProbe not set in container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +# 2.2.3 Liveness and readiness probes are different +preflight_2_2_3[message] { + pod := pods.items[_] + container := pod.spec.containers[_] + r := container.readinessProbe + r != {} + l := container.livenessProbe + l != {} + + l == r + + message := sprintf("container '%s' in pod '%s' in namespace '%s' has equal probes", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +# 2.3 Images + +# 2.3.1 imagePullPolicy is ifNotPresent +preflight_2_3_1[message] { + pod := pods.items[_] + container := pod.spec.containers[_] + container.imagePullPolicy != "IfNotPresent" + + message := sprintf("imagePullPolicy is not IfNotPresent for container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) +} +preflight_2_3_1[message] { + pod := pods.items[_] + container := pod.spec.containers[_] + not container.imagePullPolicy + + message := sprintf("imagePullPolicy is not IfNotPresent for container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +# 2.3.2 Image has explicit tag or SHA +preflight_2_3_2[message] { + # find all containers in all pods + pod := pods.items[_] + container := pod.spec.containers[_] + # validate that the image value contains an explicit tag + { re_match(`latest$`, container.image), + re_match(`^[^:]+$`, container.image) } & { true } != set() + + # bind a message for reporting + message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing an explicit image tag", [container.name, pod.metadata.name, pod.metadata.namespace]) +} + +# 2.4 Namespaces + +# 2.4.1 Deployments across multiple namespaces +preflight_2_4_1[message] { + pod := pods.items[_] + # Don't output the namespace too, it's obviously in the 'default' namespace + pod_name = pod.metadata.name + pod.metadata.namespace == "default" + + message := sprintf("pod '%s' is running in default namespace", [pod.metadata.name]) +} diff --git a/preflight-packages/jetstack.io/pods/pods_test.rego b/preflight-packages/jetstack.io/pods/pods_test.rego new file mode 100644 index 00000000..3455cfde --- /dev/null +++ b/preflight-packages/jetstack.io/pods/pods_test.rego @@ -0,0 +1,1828 @@ +package preflight._2_pods + +assert_allowed(output) = output { + trace(sprintf("GOT: %s", [concat(",", output)])) + trace("WANT: empty set") + output == set() +} + +assert_violates(output, messages) = output { + trace(sprintf("GOT: %s", [concat(",", output)])) + trace(sprintf("WANT: %s", [concat(",", messages)])) + output == messages +} + +pods(x) = y { y := {"k8s/pods": {"items": x }} } + +# 2.1 Resources + +# 2.1.1 CPU requests set +test_2_1_1_no_pods { + output := preflight_2_1_1 with input as pods([]) + assert_allowed(output) +} +test_2_1_1_cpu_requests_set { + output := preflight_2_1_1 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "containers":[ + { + "name":"container-one", + "resources":{ + "requests":{ + "cpu":"500m" + } + } + }, + { + "name":"container-two", + "resources":{ + "requests":{ + "cpu":"100m" + } + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_1_1_init_containers_unset { + output := preflight_2_1_1 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "initContainers":[ + { + "name":"init-one" + } + ], + "containers":[ + { + "name":"container-one", + "resources":{ + "requests":{ + "cpu":"500m" + } + } + } + ] + } + } + ] + ) + assert_violates(output, + { + "init container 'init-one' in pod 'foo' in namespace 'default' is missing a cpu request" + } + ) +} +test_2_1_1_init_containers_set { + output := preflight_2_1_1 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "initContainers":[ + { + "name":"init-one", + "resources":{ + "requests":{ + "cpu":"100m" + } + } + } + ], + "containers":[ + { + "name":"container-one", + "resources":{ + "requests":{ + "cpu":"500m" + } + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_1_1_cpu_requests_unset { + output := preflight_2_1_1 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "containers":[ + { + "name":"container-one" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is missing a cpu request" + } + ) +} +test_2_1_1_cpu_requests_some_unset { + output := preflight_2_1_1 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "containers":[ + { + "name":"container-one", + "resources":{ + "requests":{ + "cpu":"500m" + } + } + }, + { + "name":"container-two" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-two' in pod 'foo' in namespace 'default' is missing a cpu request" + } + ) +} + +# 2.1.2 Memory requests set +test_2_1_2_no_pods { + output := preflight_2_1_2 with input as pods([]) + assert_allowed(output) +} +test_2_1_2_memory_requests_set { + output := preflight_2_1_2 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "containers":[ + { + "name":"container-one", + "resources":{ + "requests":{ + "memory":"500m" + } + } + }, + { + "name":"container-two", + "resources":{ + "requests":{ + "memory":"100m" + } + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_1_2_init_containers_unset { + output := preflight_2_1_2 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "initContainers":[ + { + "name":"init-one" + } + ], + "containers":[ + { + "name":"container-one", + "resources":{ + "requests":{ + "memory":"500m" + } + } + } + ] + } + } + ] + ) + assert_violates(output, + { + "init container 'init-one' in pod 'foo' in namespace 'default' is missing a memory request" + } + ) +} +test_2_1_2_init_containers_set { + output := preflight_2_1_2 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "initContainers":[ + { + "name":"init-one", + "resources":{ + "requests":{ + "memory":"100m" + } + } + } + ], + "containers":[ + { + "name":"container-one", + "resources":{ + "requests":{ + "memory":"500m" + } + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_1_2_memory_requests_unset { + output := preflight_2_1_2 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "containers":[ + { + "name":"container-one" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is missing a memory request" + } + ) +} +test_2_1_2_memory_requests_some_unset { + output := preflight_2_1_2 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "containers":[ + { + "name":"container-one", + "resources":{ + "requests":{ + "memory":"500m" + } + } + }, + { + "name":"container-two" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-two' in pod 'foo' in namespace 'default' is missing a memory request" + } + ) +} + + +# 2.1.3 CPU limits set +test_2_1_3_no_pods { + output := preflight_2_1_3 with input as pods([]) + assert_allowed(output) +} +test_2_1_3_cpu_limits_set { + output := preflight_2_1_3 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "containers":[ + { + "name":"container-one", + "resources":{ + "limits":{ + "cpu":"500m" + } + } + }, + { + "name":"container-two", + "resources":{ + "limits":{ + "cpu":"100m" + } + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_1_3_init_containers_unset { + output := preflight_2_1_3 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "initContainers":[ + { + "name":"init-one" + } + ], + "containers":[ + { + "name":"container-one", + "resources":{ + "limits":{ + "cpu":"500m" + } + } + } + ] + } + } + ] + ) + assert_violates(output, + { + "init container 'init-one' in pod 'foo' in namespace 'default' is missing a cpu limit" + } + ) +} +test_2_1_3_init_containers_set { + output := preflight_2_1_3 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "initContainers":[ + { + "name":"init-one", + "resources":{ + "limits":{ + "cpu":"100m" + } + } + } + ], + "containers":[ + { + "name":"container-one", + "resources":{ + "limits":{ + "cpu":"500m" + } + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_1_3_cpu_limits_unset { + output := preflight_2_1_3 with input as pods( + [ + { + "metadata":{ + "name":"foo", + "namespace":"default" + }, + "spec":{ + "containers":[ + { + "name":"container-one" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is missing a cpu limit" + } + ) +} +test_2_1_3_cpu_limits_some_unset { + output := preflight_2_1_3 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "resources": { + "limits": { + "cpu": "500m" + } + } + }, + { + "name": "container-two" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-two' in pod 'foo' in namespace 'default' is missing a cpu limit" + } + ) +} + +# 2.1.4 Memory limits set +test_2_1_4_no_pods { + output := preflight_2_1_4 with input as pods([]) + assert_allowed(output) +} +test_2_1_4_memory_limits_set { + output := preflight_2_1_4 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "resources": { + "limits": { + "memory": "500m" + } + } + }, + { + "name": "container-two", + "resources": { + "limits": { + "memory": "100m" + } + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_1_4_init_containers_unset { + output := preflight_2_1_4 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "initContainers": [ + { + "name": "init-one" + } + ], + "containers": [ + { + "name": "container-one", + "resources": { + "limits": { + "memory": "500m" + } + } + } + ] + } + } + ] + ) + assert_violates(output, + { + "init container 'init-one' in pod 'foo' in namespace 'default' is missing a memory limit" + } + ) +} +test_2_1_4_init_containers_set { + output := preflight_2_1_4 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "initContainers": [ + { + "name": "init-one", + "resources": { + "limits": { + "memory": "100m" + } + } + } + ], + "containers": [ + { + "name": "container-one", + "resources": { + "limits": { + "memory": "500m" + } + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_1_4_memory_limits_unset { + output := preflight_2_1_4 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is missing a memory limit" + } + ) +} +test_2_1_4_memory_limits_some_unset { + output := preflight_2_1_4 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "resources": { + "limits": { + "memory": "500m" + } + } + }, + { + "name": "container-two" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-two' in pod 'foo' in namespace 'default' is missing a memory limit" + } + ) +} + +# 2.1.5 Guarantead QoS +test_2_1_5_no_pods { + output := preflight_2_1_5 with input as pods([]) + assert_allowed(output) +} +test_2_1_5_requests_limits_equal { + output := preflight_2_1_5 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "resources": { + "requests": { + "cpu": "500m", + "memory": "300Mi" + }, + "limits": { + "cpu": "500m", + "memory": "300Mi" + } + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_1_5_requests_missing { + output := preflight_2_1_5 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "resources": { + "limits": { + "cpu": "500m", + "memory": "300Mi" + } + } + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is not Guaranteed QoS" + } + ) +} +test_2_1_5_limits_missing { + output := preflight_2_1_5 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "resources": { + "requests": { + "cpu": "500m", + "memory": "300Mi" + } + } + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is not Guaranteed QoS" + } + ) +} +test_2_1_5_requests_limits_absent { + output := preflight_2_1_5 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is not Guaranteed QoS" + } + ) +} +test_2_1_5_requests_limits_not_set { + output := preflight_2_1_5 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "resources": {} + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is not Guaranteed QoS" + } + ) +} +test_2_1_5_requests_limits_blank { + output := preflight_2_1_5 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "resources": { + "requests": {}, + "limits": {} + } + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is not Guaranteed QoS" + } + ) +} +test_2_1_5_requests_limits_some_not_set { + output := preflight_2_1_5 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "resources": {} + }, + { + "name": "container-two", + "resources": { + "requests": { + "cpu": "500m", + "memory": "300Mi" + }, + "limits": { + "cpu": "500m", + "memory": "300Mi" + } + } + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is not Guaranteed QoS" + } + ) +} +test_2_1_5_requests_limits_all_set { + output := preflight_2_1_5 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "resources": { + "requests": { + "cpu": "500m", + "memory": "300Mi" + }, + "limits": { + "cpu": "500m", + "memory": "300Mi" + } + } + }, + { + "name": "container-two", + "resources": { + "requests": { + "cpu": "500m", + "memory": "300Mi" + }, + "limits": { + "cpu": "500m", + "memory": "300Mi" + } + } + } + ] + } + } + ] + ) + assert_allowed(output) +} + +# 2.2 Monitoring + +# 2.2.1 Liveness probe set +test_2_2_1_no_pods { + output := preflight_2_2_1 with input as pods([]) + assert_allowed(output) +} +test_2_2_1_liveness_probe_set { + output := preflight_2_2_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "livenessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_2_1_liveness_probe_unset { + output := preflight_2_2_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one" + } + ] + } + } + ] + ) + assert_violates(output, + { + "livenessProbe not set in container 'container-one' in pod 'foo' in namespace 'default'" + } + ) +} +test_2_2_1_liveness_probe_empty { + output := preflight_2_2_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "livenessProbe": {} + } + ] + } + } + ] + ) + assert_violates(output, + { + "livenessProbe not set in container 'container-one' in pod 'foo' in namespace 'default'" + } + ) +} +test_2_2_1_liveness_probe_some_unset { + output := preflight_2_2_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "livenessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + } + }, + { + "name": "container-two" + } + ] + } + } + ] + ) + assert_violates(output, + { + "livenessProbe not set in container 'container-two' in pod 'foo' in namespace 'default'" + } + ) +} +test_2_2_1_liveness_probe_all_set { + output := preflight_2_2_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "livenessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + } + }, + { + "name": "container-two", + "livenessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + } + } + ] + } + } + ] + ) + assert_allowed(output) +} + +# 2.2.2 Readiness probe set +test_2_2_2_no_pods { + output := preflight_2_2_2 with input as pods([]) + assert_allowed(output) +} +test_2_2_2_readiness_probe_set { + output := preflight_2_2_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "readinessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_2_2_readiness_probe_unset { + output := preflight_2_2_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one" + } + ] + } + } + ] + ) + assert_violates(output, + { + "readinessProbe not set in container 'container-one' in pod 'foo' in namespace 'default'" + } + ) +} +# TODO, is this possible? +test_2_2_2_readiness_probe_empty { + output := preflight_2_2_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "readinessProbe": {} + } + ] + } + } + ] + ) + assert_violates(output, + { + "readinessProbe not set in container 'container-one' in pod 'foo' in namespace 'default'" + } + ) +} +test_2_2_2_readiness_probe_some_unset { + output := preflight_2_2_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "readinessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + } + }, + { + "name": "container-two" + } + ] + } + } + ] + ) + assert_violates(output, + { + "readinessProbe not set in container 'container-two' in pod 'foo' in namespace 'default'" + } + ) +} +test_2_2_2_readiness_probe_all_set { + output := preflight_2_2_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "readinessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + } + }, + { + "name": "container-two", + "readinessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + } + } + ] + } + } + ] + ) + assert_allowed(output) +} + +# 2.2.3 +test_2_2_3_no_pods { + output := preflight_2_2_3 with input as pods([]) + assert_allowed(output) +} +test_2_2_3_liveness_readiness_not_equal { + output := preflight_2_2_3 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "livenessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + }, + "readinessProbe": { + "httpGet": { + "path": "/healthz", + "port": 8080 + }, + "initialDelaySeconds": 3, + "periodSeconds": 3 + } + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_2_3_liveness_readiness_equal { + output := preflight_2_2_3 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "livenessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + }, + "readinessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + } + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' has equal probes" + } + ) +} +test_2_2_3_liveness_readiness_some_pods_equal { + output := preflight_2_2_3 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "livenessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + }, + "readinessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + } + } + ] + } + }, + { + "metadata": { + "name": "bar", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "livenessProbe": { + "exec": { + "command": [ + "cat", + "/tmp/healthy" + ] + }, + "initialDelaySeconds": 5, + "periodSeconds": 5 + }, + "readinessProbe": { + "httpGet": { + "path": "/healthz", + "port": 8080 + }, + "initialDelaySeconds": 3, + "periodSeconds": 3 + } + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' has equal probes" + } + ) +} + +# 2.3 Images + +# 2.3.1 Image pull policy is ifNotPresent +test_2_3_1_no_pods { + output := preflight_2_3_1 with input as pods([]) + assert_allowed(output) +} +test_2_3_1_no_pull_policy { + output := preflight_2_3_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one" + } + ] + } + } + ] + ) + assert_violates(output, + { + "imagePullPolicy is not IfNotPresent for container 'container-one' in pod 'foo' in namespace 'default'" + } + ) +} +test_2_3_1_always { + output := preflight_2_3_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "imagePullPolicy": "Always" + } + ] + } + } + ] + ) + assert_violates(output, + { + "imagePullPolicy is not IfNotPresent for container 'container-one' in pod 'foo' in namespace 'default'" + } + ) +} +test_2_3_1_if_not_present { + output := preflight_2_3_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "imagePullPolicy": "IfNotPresent" + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_3_1_some_pods_always { + output := preflight_2_3_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "imagePullPolicy": "Always" + } + ] + } + }, + { + "metadata": { + "name": "bar", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "imagePullPolicy": "IfNotPresent" + } + ] + } + } + ] + ) + assert_violates(output, + { + "imagePullPolicy is not IfNotPresent for container 'container-one' in pod 'foo' in namespace 'default'" + } + ) +} +test_2_3_1_all_pods_ifnotpresent { + output := preflight_2_3_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "imagePullPolicy": "IfNotPresent" + } + ] + } + }, + { + "metadata": { + "name": "bar", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "imagePullPolicy": "IfNotPresent" + } + ] + } + } + ] + ) + assert_allowed(output) +} + +# 2.3.2 Image has explicit tag or SHA +test_2_3_2_no_pods { + output := preflight_2_3_2 with input as pods([]) + assert_allowed(output) +} +test_2_3_2_named_tag { + output := preflight_2_3_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "image": "gcr.io/my-project/my-image:v0.1" + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_3_2_latest_tag { + output := preflight_2_3_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "image": "gcr.io/my-project/my-image:latest" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is missing an explicit image tag" + } + ) +} +test_2_3_2_missing_tag { + output := preflight_2_3_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "image": "gcr.io/my-project/my-image" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is missing an explicit image tag" + } + ) +} +test_2_3_2_sha { + output := preflight_2_3_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "image": "gcr.io/my-project/my-image@sha256:4bdd623e848417d96127e16037743f0cd8b528c026e9175e22a84f639eca58ff" + } + ] + } + } + ] + ) + assert_allowed(output) +} +test_2_3_2_some_pods_latest { + output := preflight_2_3_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "image": "gcr.io/my-project/my-image:latest" + } + ] + } + }, + { + "metadata": { + "name": "bar", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "image": "gcr.io/my-project/my-image:v0.2" + } + ] + } + } + ] + ) + assert_violates(output, + { + "container 'container-one' in pod 'foo' in namespace 'default' is missing an explicit image tag" + } + ) +} +test_2_3_2_all_pods_complient { + output := preflight_2_3_2 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "image": "gcr.io/my-project/my-image:v0.2" + } + ] + } + }, + { + "metadata": { + "name": "bar", + "namespace": "default" + }, + "spec": { + "containers": [ + { + "name": "container-one", + "image": "gcr.io/my-project/another-image:v0.3" + } + ] + } + } + ] + ) + assert_allowed(output) +} + +# 2.4 Namespaces + +# 2.4.1 Pods across multiple namespaces +test_2_4_1_no_pods { + output := preflight_2_4_1 with input as pods([]) + assert_allowed(output) +} +test_2_4_1_no_default { + output := preflight_2_4_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "myapp" + } + } + ] + ) + assert_allowed(output) +} +test_2_4_1_default { + output := preflight_2_4_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + } + } + ] + ) + assert_violates(output, + { + "pod 'foo' is running in default namespace" + } + ) +} +test_2_4_1_multiple_no_default { + output := preflight_2_4_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "myapp" + } + }, + { + "metadata": { + "name": "bar", + "namespace": "myotherapp" + } + } + ] + ) + assert_allowed(output) +} +test_2_4_1_multiple_default { + output := preflight_2_4_1 with input as pods( + [ + { + "metadata": { + "name": "foo", + "namespace": "default" + } + }, + { + "metadata": { + "name": "bar", + "namespace": "myapp" + } + } + ] + ) + assert_violates(output, + { + "pod 'foo' is running in default namespace" + } + ) +} diff --git a/preflight-packages/jetstack.io/pods/policy-manifest.yaml b/preflight-packages/jetstack.io/pods/policy-manifest.yaml new file mode 100644 index 00000000..a41d588c --- /dev/null +++ b/preflight-packages/jetstack.io/pods/policy-manifest.yaml @@ -0,0 +1,279 @@ +schema-version: "1.0.0" +id: "pods" +namespace: "jetstack.io" +package-version: "1.1.0" +data-gatherers: +- k8s/pods +root-query: "data.preflight._2_pods" +name: Pods +description: > + This policy gives rules for how Kubernetes Pods should be configured. Pods are + the basic units of a cluster workload, so it's important to get the details + correct to maximise performance and minimise faults. +sections: +- id: "2.1" + name: Resources + description: > + Pods can specify resource requests and limits for CPU and memory. The + requests are used to inform the scheduler about which Nodes to run different + Pods on. These are important so that the scheduler can make good decisions + about which Nodes to use; requests that are greater than a Pod needs will + result in allocated resources that are not used, whereas requests that are + below what a Pod needs will result in Nodes being overloaded. The limits + define the maximum resources Pods can use and are used by the Kubelet to + determine if a Pod should be killed. These are also important; a pod with + limits greater than it needs may consume excessive resources due to an + error, whereas limits that are set too low can result in poor performance or + a Pod being unnecessarily restarted. + rules: + - id: "2.1.1" + name: CPU requests set + description: > + The CPU requests of containers specify how much CPU time that container + needs, and are summed together to determine the total CPU that time a Pod + needs. Containers in a Pod can exceed the requested CPU time. + remediation: > + All `containers` in a Pod should have a `resources.requests.cpu` value + set, which is given in relative `CPU` units based on a single virtual CPU + core or hyperthread. Choosing appropriate CPU request values generally + requires some experimentation and benchmarking. The chosen value should be + just above the normal CPU utilisation of the workload. + links: + - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu" + - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#how-pods-with-resource-requests-are-scheduled" + - id: "2.1.2" + name: Memory requests set + description: > + The memory requests of containers specify how much memory that container + needs, and are summed together to determine the total memory that a Pod + needs. Containers in a Pod can exceed the requested memory. + remediation: > + All `containers` in a Pod should have a `resources.requests.memory` value + set, which is given in bytes with optional units such as `M` for multiples + of 1,000,000 bytes, or `Ki` for multiples of 1,024 bytes. Choosing + appropriate memory request values generally requires some experimentation + and benchmarking. The chosen value should be just above the normal memory + usage of the workload. + links: + - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory" + - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#how-pods-with-resource-requests-are-scheduled" + - id: "2.1.3" + name: CPU limits set + description: > + The CPU limits of containers specify the maximum amount of CPU time that + container should get, and are summed together to determine the total + maximum CPU time that a Pod should get. It a Pod exceeds this it will + experience CPU throttling. + remediation: > + All `containers` in a Pod should have a `resources.limits.cpu` value set, + which is given in relative `CPU` units based on a single virtual CPU core + or hyperthread. Choosing appropriate CPU limit values generally requires + some experimentation and benchmarking. The chosen value should be just + above the normal maximum CPU utilisation of the workload. + links: + - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu" + - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#how-pods-with-resource-limits-are-run" + - id: "2.1.4" + name: Memory limits set + description: > + The memory limits of containers specify the maximum amount of memory that + container should get, and are summed together to determine the total + maximum memory that a Pod should get. If a Pod exceeds this it will + experience an ‘out of memory’ (OOM) termination. + remediation: > + All `containers` in a Pod should have a `resources.limits.memory` value + set, which is given in bytes with optional units such as `M` for multiples + of 1,000,000 bytes, or `Ki` for multiples of 1,024 bytes. Choosing + appropriate memory limit values generally requires some experimentation + and benchmarking. The chosen value should be just above the normal maximum + memory usage of the workload. + links: + - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory" + - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#how-pods-with-resource-limits-are-run" + - id: "2.1.5" + name: Guarantead QoS + description: > + Kubernetes uses requests and limits of a Pod's containers in order to + determine the Quality of Service (QoS) class of the Pod. This class + dictates how the Pod should be treated when the Node it’s running on is + under resource pressure for CPU or memory. If limits and requests are set + for all containers in a Pod, and they are equal, then the Pod is + classified as `Guaranteed`. If requests or limits are set for at least one + container in a Pod, then the Pod is classified as `Burstable`. If no + requests or limits are not set for any of the containers in the Pod, then + it is classified as `Best-Effort`. Pods with a QoS class less than + `Guaranteed` may be killed when the Node is under resource pressure to + avoid costly OOM terminations and pro-actively maintain Node stability. + Therefore, for important workloads such as those used in production, the + request and limit values should be the same to ensure they have + `Guaranteed` QoS class. + remediation: > + All `containers` in a Pod should have `resources.requests` equal to + `resources.limits`. This will likely mean increasing the request values to + match the limits, however if the limits are set very high both may need to + be reduced. This may result in more resources being allocated but not + used. For example, in the case of workloads that normally have low + resource use but occasional necessary spikes, the requests and limits will + have to be greater than the resource use during the spikes to avoid being + killed, and will thus be much greater than is normally used. This is a + trade-off to ensure that important workloads are prioritised + appropriately. + links: + - "https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/resource-qos.md" + - id: "2.1.6" + name: CPU limits disabled for latency sensitive workloads + manual: true + description: > + Kubernetes implements a container’s CPU limit by configuring CPU quota in + its corresponding `cgroup`. The period over which this quota is considered + is hard coded to `100ms`. This can cause issues for latency sensitive + workloads, since if they happen to use up their quota within a particular + `100ms` period, they will not be scheduled again until the next period + comes around. This could result in up to `100ms` of inactivity. + remediation: > + For latency sensitive workloads it is better to remove the CPU limits. + This goes against the normal recommendation that all containers have + limits set, but unfortunately is required due to limitations of how + resource management works. All containers should still have requests set. + links: + - "https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/cm/helpers_linux.go#L44" + - "https://github.com/kubernetes/kubernetes/issues/51135" + - "https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/" + - "https://github.com/kubernetes/kubernetes/issues/67577" +- id: "2.2" + name: Monitoring + rules: + - id: "2.2.1" + name: Liveness probe set + description: > + The liveness probe should indicate whether a container in a Pod is + healthy. This means not just running, but whether the application in that + containers is functional. This does not have to cover whether the + application is ready to serve, it could be healthy but still completing + some initialisation. The liveness probe must not require any external + dependencies. If the liveness probe fails, the Kubelet kills the container + and it is restarted. If a container does not provide a liveness probe the + default state is `Success`, meaning that even if the application has + crashed the container will not be restarted. + remediation: > + All `containers` in a Pod should have a `livenessProbe` specified, which + can indicate whether the application in the container is running and + healthy, without external dependencies. A good probe for web applications + is an HTTP check that gets the status page of the application. + Alternatively a good check could be a command that is executed inside the + container to check the status of a process. Only in a few cases would it + be effective to just use TCP checks as these won't give much insight into + the state of the application. + links: + - "https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/" + - id: "2.2.2" + name: Readiness probe set + description: > + The readiness probe should indicate when an application is ready to serve. + This is beyond the running and healthy checks of a liveness probe, and + means that any initialisation is complete and that any external + dependencies are available. Depending on the probe's result, Services that + match the Pod will be updated to add or removes the Pod’s IP address. If a + readiness probe is not specified the default state is `Success`, meaning + that even if the Pod is not yet ready it will be sent traffic, likely + resulting in failed connections and errors. + remediation: > + All `containers` in a Pod should have a `readinessProbe` specified, which + can indicate whether the application in the container is running and ready + to serve connections, including any external dependencies that it needs to + do this. + links: + - "https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/" + - id: "2.2.3" + name: Liveness and readiness probes are different + description: > + The liveness and readiness probes are intended to check distinct + properties of containers. While it can be tempting to reduce work by + sharing the same check for both it's unlikely that they should be the same + as they need to check different properties in order to work effectively. + remediation: > + Ensure that the liveness probe is only checking the health of the + container, and that the readiness probe is more comprehensive and check + that the container is ready to serve. + links: + - "https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/" +- id: "2.3" + name: Images + rules: + - id: "2.3.1" + name: imagePullPolicy is ifNotPresent + description: > + For faster Pod start-ups and reduced network usage, it’s advised that the + `imagePullPolicy` for is set to `IfNotPresent` rather than `Always`. This + avoids forcing the Kubelet to download images it already has. To force + downloading an image because the software is updated, a new image should + be created with a different tag. A rolling update can then be carried out + to switch to the new image without downtime. This also allows for a + roll-back if needed because the previous image still exists on its unique + tag. + remediation: > + The `IfNotPresent` policy is the default, so simply removing + `imagePullPolicy: Always` from any specified `containers` will resolve + this problem. + links: + - "https://kubernetes.io/docs/concepts/containers/images/" + - id: "2.3.2" + name: Image has explicit tag or SHA + description: > + By default images without a tag specified used the `latest` tag. This + means if a new image is released it will be used on new Pods without any + explicit upgrade. If the functionality of the image has changed, for + example with new versions of software, then this can cause unexpected + behaviour. Because the upgrade happens implicitly there is no opportunity + to review changes. There is also no way to roll back, or run multiple + Deployments to phase a new version in. As the image is fetched when a Pod + starts Deployment can have multiple versions of an image in use, which can + cause inconsistent behaviour between replicas. + remediation: > + Determine the version of images in use and set them explicitly in the + `image` specification for all `containers`. When new versions become + available change the tag explicitly to upgrade, and ensure the new image + version still works as desired, rolling back to the previous one if + required. + links: + - "https://kubernetes.io/docs/concepts/containers/images/" + - id: "2.3.3" + name: Container registry close to cluster + manual: true + description: > + It is best practice to keep the container registry as close as possible to + the Kubernetes cluster. Although Docker performs a lot of caching, it can + still take a long time to download images when the layers are large. When + the container registry is closer to the cluster and on a high-speed + networking interface, this will increase the download speed of the image + and thereby reduce the start-up time of a pod. This can also have a + positive effect on the network costs as local traffic is frequently + cheaper than traffic from the internet. + remediation: > + Make use of a container registry close to your cluster. For example, if + the cluster is running on Google Kubernetes Engine use Google Container + Registry. Most container registries allow mirroring of public images +- id: "2.4" + name: Namespaces + rules: + - id: "2.4.1" + name: Deployments across multiple Namespaces + description: > + Using Namespaces is the ideal way to divide resources in a logical way. + They are perfect for dividing multiple microservices, or having multiple + environments on one Kubernetes cluster. The network layer is still shared + over the whole Kubernetes cluster, meaning that applications are still + able to talk to each other even when divided into separate Namespaces. + Though NetworkPolicies and other restrictions can be more easily applied + to all resources in the Namespace if required. Namespacing makes it easier + to set the correct access rights to resources. For example, ensuring team + A can only access the resources in Namespaces belonging to team A. It can + also be used to create resource quotas for your teams/applications. + remediation: > + Determine logical ways to divide up your Kubernetes workloads, for example + based on what services they provide, or who should be able to access them. + Create corresponding Namespace resources in the cluster with appropriate + names. Then update your Deployment, Service, etc. resources `metadata` + section to include a `namespace`. + links: + - "https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/" From 73033fea07aba0d1f9c86634d6fab82a9421b6c2 Mon Sep 17 00:00:00 2001 From: wwwil Date: Fri, 17 Jan 2020 13:36:09 +0000 Subject: [PATCH 2/5] Rename Pods rules and remove old ID number Signed-off-by: wwwil --- preflight-packages/jetstack.io/pods/pods.rego | 85 +++-- .../jetstack.io/pods/pods_test.rego | 296 +++++++++--------- .../jetstack.io/pods/policy-manifest.yaml | 36 +-- 3 files changed, 202 insertions(+), 215 deletions(-) diff --git a/preflight-packages/jetstack.io/pods/pods.rego b/preflight-packages/jetstack.io/pods/pods.rego index 595992fe..52444617 100644 --- a/preflight-packages/jetstack.io/pods/pods.rego +++ b/preflight-packages/jetstack.io/pods/pods.rego @@ -1,11 +1,11 @@ -package preflight._2_pods +package preflight.pods import input["k8s/pods"] as pods -# 2.1 Resources +# Resources -# 2.1.1 CPU requests set -preflight_2_1_1[message] { +# CPU requests set +cpu_requests_set[message] { # find all containers in all pods pod := pods.items[_] container := pod.spec.containers[_] @@ -14,7 +14,7 @@ preflight_2_1_1[message] { # bind a message for reporting message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing a cpu request", [container.name, pod.metadata.name, pod.metadata.namespace]) } -preflight_2_1_1[message] { +cpu_requests_set[message] { # find all initContainers in all pods pod := pods.items[_] container := pod.spec.initContainers[_] @@ -24,8 +24,8 @@ preflight_2_1_1[message] { message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing a cpu request", [container.name, pod.metadata.name, pod.metadata.namespace]) } -# 2.1.2 Memory requests set -preflight_2_1_2[message] { +# Memory requests set +memory_requests_set[message] { # find all containers in all pods pod := pods.items[_] container := pod.spec.containers[_] @@ -34,7 +34,7 @@ preflight_2_1_2[message] { # bind a message for reporting message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing a memory request", [container.name, pod.metadata.name, pod.metadata.namespace]) } -preflight_2_1_2[message] { +memory_requests_set[message] { # find all initContainers in all pods pod := pods.items[_] container := pod.spec.initContainers[_] @@ -44,8 +44,8 @@ preflight_2_1_2[message] { message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing a memory request", [container.name, pod.metadata.name, pod.metadata.namespace]) } -# 2.1.3 CPU limits set -preflight_2_1_3[message] { +# CPU limits set +cpu_limits_set[message] { # find all containers in all pods pod := pods.items[_] container := pod.spec.containers[_] @@ -54,7 +54,7 @@ preflight_2_1_3[message] { # bind a message for reporting message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing a cpu limit", [container.name, pod.metadata.name, pod.metadata.namespace]) } -preflight_2_1_3[message] { +cpu_limits_set[message] { # find all initContainers in all pods pod := pods.items[_] container := pod.spec.initContainers[_] @@ -64,8 +64,8 @@ preflight_2_1_3[message] { message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing a cpu limit", [container.name, pod.metadata.name, pod.metadata.namespace]) } -# 2.1.4 Memory limits set -preflight_2_1_4[message] { +# Memory limits set +memory_limits_set[message] { # find all containers in all pods pod := pods.items[_] container := pod.spec.containers[_] @@ -74,7 +74,7 @@ preflight_2_1_4[message] { # bind a message for reporting message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing a memory limit", [container.name, pod.metadata.name, pod.metadata.namespace]) } -preflight_2_1_4[message] { +memory_limits_set[message] { # find all initContainers in all pods pod := pods.items[_] container := pod.spec.initContainers[_] @@ -84,118 +84,105 @@ preflight_2_1_4[message] { message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing a memory limit", [container.name, pod.metadata.name, pod.metadata.namespace]) } -# 2.1.5 Guaranteed QoS -preflight_2_1_5[message] { +# Guaranteed QoS +guaranteed_qos[message] { pod := pods.items[_] container := pod.spec.containers[_] { container.resources.requests == {}, container.resources.limits == {}, container.resources.requests != container.resources.limits } & { true } != set() - message := sprintf("container '%s' in pod '%s' in namespace '%s' is not Guaranteed QoS", [container.name, pod.metadata.name, pod.metadata.namespace]) } -preflight_2_1_5[message] { +guaranteed_qos[message] { pod := pods.items[_] container := pod.spec.containers[_] not container.resources.requests - message := sprintf("container '%s' in pod '%s' in namespace '%s' is not Guaranteed QoS", [container.name, pod.metadata.name, pod.metadata.namespace]) } -preflight_2_1_5[message] { +guaranteed_qos[message] { pod := pods.items[_] container := pod.spec.containers[_] not container.resources.limits - message := sprintf("container '%s' in pod '%s' in namespace '%s' is not Guaranteed QoS", [container.name, pod.metadata.name, pod.metadata.namespace]) } -# 2.2 Monitoring +# Monitoring -# 2.2.1 Liveness probe set -preflight_2_2_1[message] { +# Liveness probe set +liveness_probe_set[message] { pod := pods.items[_] container := pod.spec.containers[_] container.livenessProbe == {} - message := sprintf("livenessProbe not set in container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) } -preflight_2_2_1[message] { +liveness_probe_set[message] { pod := pods.items[_] container := pod.spec.containers[_] not container.livenessProbe - message := sprintf("livenessProbe not set in container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) } -# 2.2.2 Readiness probe set -preflight_2_2_2[message] { +# Readiness probe set +readiness_probe_set[message] { pod := pods.items[_] container := pod.spec.containers[_] container.readinessProbe == {} message := sprintf("readinessProbe not set in container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) } - -preflight_2_2_2[message] { +readiness_probe_set[message] { pod := pods.items[_] container := pod.spec.containers[_] not container.readinessProbe - message := sprintf("readinessProbe not set in container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) } -# 2.2.3 Liveness and readiness probes are different -preflight_2_2_3[message] { +# Liveness and readiness probes are different +liveness_and_readiness_probes_are_different[message] { pod := pods.items[_] container := pod.spec.containers[_] r := container.readinessProbe r != {} l := container.livenessProbe l != {} - l == r - message := sprintf("container '%s' in pod '%s' in namespace '%s' has equal probes", [container.name, pod.metadata.name, pod.metadata.namespace]) } -# 2.3 Images +# Images -# 2.3.1 imagePullPolicy is ifNotPresent -preflight_2_3_1[message] { +# imagePullPolicy is ifNotPresent +imagepullpolicy_is_ifnotpresent[message] { pod := pods.items[_] container := pod.spec.containers[_] container.imagePullPolicy != "IfNotPresent" - message := sprintf("imagePullPolicy is not IfNotPresent for container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) } -preflight_2_3_1[message] { +imagepullpolicy_is_ifnotpresent[message] { pod := pods.items[_] container := pod.spec.containers[_] not container.imagePullPolicy - message := sprintf("imagePullPolicy is not IfNotPresent for container '%s' in pod '%s' in namespace '%s'", [container.name, pod.metadata.name, pod.metadata.namespace]) } -# 2.3.2 Image has explicit tag or SHA -preflight_2_3_2[message] { +# Image has explicit tag or SHA +image_has_explicit_tag_or_sha[message] { # find all containers in all pods pod := pods.items[_] container := pod.spec.containers[_] # validate that the image value contains an explicit tag { re_match(`latest$`, container.image), re_match(`^[^:]+$`, container.image) } & { true } != set() - # bind a message for reporting message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing an explicit image tag", [container.name, pod.metadata.name, pod.metadata.namespace]) } -# 2.4 Namespaces +# Namespaces -# 2.4.1 Deployments across multiple namespaces -preflight_2_4_1[message] { +# Deployments across multiple namespaces +deployments_across_multiple_namespaces[message] { pod := pods.items[_] # Don't output the namespace too, it's obviously in the 'default' namespace pod_name = pod.metadata.name pod.metadata.namespace == "default" - message := sprintf("pod '%s' is running in default namespace", [pod.metadata.name]) } diff --git a/preflight-packages/jetstack.io/pods/pods_test.rego b/preflight-packages/jetstack.io/pods/pods_test.rego index 3455cfde..1b72157a 100644 --- a/preflight-packages/jetstack.io/pods/pods_test.rego +++ b/preflight-packages/jetstack.io/pods/pods_test.rego @@ -1,4 +1,4 @@ -package preflight._2_pods +package preflight.pods assert_allowed(output) = output { trace(sprintf("GOT: %s", [concat(",", output)])) @@ -17,12 +17,12 @@ pods(x) = y { y := {"k8s/pods": {"items": x }} } # 2.1 Resources # 2.1.1 CPU requests set -test_2_1_1_no_pods { - output := preflight_2_1_1 with input as pods([]) +test_cpu_requests_set_no_pods { + output := cpu_requests_set with input as pods([]) assert_allowed(output) } -test_2_1_1_cpu_requests_set { - output := preflight_2_1_1 with input as pods( +test_cpu_requests_set_cpu_requests_set { + output := cpu_requests_set with input as pods( [ { "metadata":{ @@ -54,8 +54,8 @@ test_2_1_1_cpu_requests_set { ) assert_allowed(output) } -test_2_1_1_init_containers_unset { - output := preflight_2_1_1 with input as pods( +test_cpu_requests_set_init_containers_unset { + output := cpu_requests_set with input as pods( [ { "metadata":{ @@ -88,8 +88,8 @@ test_2_1_1_init_containers_unset { } ) } -test_2_1_1_init_containers_set { - output := preflight_2_1_1 with input as pods( +test_cpu_requests_set_init_containers_set { + output := cpu_requests_set with input as pods( [ { "metadata":{ @@ -123,8 +123,8 @@ test_2_1_1_init_containers_set { ) assert_allowed(output) } -test_2_1_1_cpu_requests_unset { - output := preflight_2_1_1 with input as pods( +test_cpu_requests_set_cpu_requests_unset { + output := cpu_requests_set with input as pods( [ { "metadata":{ @@ -147,8 +147,8 @@ test_2_1_1_cpu_requests_unset { } ) } -test_2_1_1_cpu_requests_some_unset { - output := preflight_2_1_1 with input as pods( +test_cpu_requests_set_cpu_requests_some_unset { + output := cpu_requests_set with input as pods( [ { "metadata":{ @@ -180,13 +180,13 @@ test_2_1_1_cpu_requests_some_unset { ) } -# 2.1.2 Memory requests set -test_2_1_2_no_pods { - output := preflight_2_1_2 with input as pods([]) +# Memory requests set +test_memory_requests_set_no_pods { + output := memory_requests_set with input as pods([]) assert_allowed(output) } -test_2_1_2_memory_requests_set { - output := preflight_2_1_2 with input as pods( +test_memory_requests_set_memory_requests_set { + output := memory_requests_set with input as pods( [ { "metadata":{ @@ -218,8 +218,8 @@ test_2_1_2_memory_requests_set { ) assert_allowed(output) } -test_2_1_2_init_containers_unset { - output := preflight_2_1_2 with input as pods( +test_memory_requests_set_init_containers_unset { + output := memory_requests_set with input as pods( [ { "metadata":{ @@ -252,8 +252,8 @@ test_2_1_2_init_containers_unset { } ) } -test_2_1_2_init_containers_set { - output := preflight_2_1_2 with input as pods( +test_memory_requests_set_init_containers_set { + output := memory_requests_set with input as pods( [ { "metadata":{ @@ -287,8 +287,8 @@ test_2_1_2_init_containers_set { ) assert_allowed(output) } -test_2_1_2_memory_requests_unset { - output := preflight_2_1_2 with input as pods( +test_memory_requests_set_memory_requests_unset { + output := memory_requests_set with input as pods( [ { "metadata":{ @@ -311,8 +311,8 @@ test_2_1_2_memory_requests_unset { } ) } -test_2_1_2_memory_requests_some_unset { - output := preflight_2_1_2 with input as pods( +test_memory_requests_set_memory_requests_some_unset { + output := memory_requests_set with input as pods( [ { "metadata":{ @@ -345,13 +345,13 @@ test_2_1_2_memory_requests_some_unset { } -# 2.1.3 CPU limits set -test_2_1_3_no_pods { - output := preflight_2_1_3 with input as pods([]) +# CPU limits set +test_cpu_limits_set_no_pods { + output := cpu_limits_set with input as pods([]) assert_allowed(output) } -test_2_1_3_cpu_limits_set { - output := preflight_2_1_3 with input as pods( +test_cpu_limits_set_cpu_limits_set { + output := cpu_limits_set with input as pods( [ { "metadata":{ @@ -383,8 +383,8 @@ test_2_1_3_cpu_limits_set { ) assert_allowed(output) } -test_2_1_3_init_containers_unset { - output := preflight_2_1_3 with input as pods( +test_cpu_limits_set_init_containers_unset { + output := cpu_limits_set with input as pods( [ { "metadata":{ @@ -417,8 +417,8 @@ test_2_1_3_init_containers_unset { } ) } -test_2_1_3_init_containers_set { - output := preflight_2_1_3 with input as pods( +test_cpu_limits_set_init_containers_set { + output := cpu_limits_set with input as pods( [ { "metadata":{ @@ -452,8 +452,8 @@ test_2_1_3_init_containers_set { ) assert_allowed(output) } -test_2_1_3_cpu_limits_unset { - output := preflight_2_1_3 with input as pods( +test_cpu_limits_set_cpu_limits_unset { + output := cpu_limits_set with input as pods( [ { "metadata":{ @@ -476,8 +476,8 @@ test_2_1_3_cpu_limits_unset { } ) } -test_2_1_3_cpu_limits_some_unset { - output := preflight_2_1_3 with input as pods( +test_cpu_limits_set_cpu_limits_some_unset { + output := cpu_limits_set with input as pods( [ { "metadata": { @@ -509,13 +509,13 @@ test_2_1_3_cpu_limits_some_unset { ) } -# 2.1.4 Memory limits set -test_2_1_4_no_pods { - output := preflight_2_1_4 with input as pods([]) +# Memory limits set +test_memory_limits_set_no_pods { + output := memory_limits_set with input as pods([]) assert_allowed(output) } -test_2_1_4_memory_limits_set { - output := preflight_2_1_4 with input as pods( +test_memory_limits_set_memory_limits_set { + output := memory_limits_set with input as pods( [ { "metadata": { @@ -547,8 +547,8 @@ test_2_1_4_memory_limits_set { ) assert_allowed(output) } -test_2_1_4_init_containers_unset { - output := preflight_2_1_4 with input as pods( +test_memory_limits_set_init_containers_unset { + output := memory_limits_set with input as pods( [ { "metadata": { @@ -581,8 +581,8 @@ test_2_1_4_init_containers_unset { } ) } -test_2_1_4_init_containers_set { - output := preflight_2_1_4 with input as pods( +test_memory_limits_set_init_containers_set { + output := memory_limits_set with input as pods( [ { "metadata": { @@ -616,8 +616,8 @@ test_2_1_4_init_containers_set { ) assert_allowed(output) } -test_2_1_4_memory_limits_unset { - output := preflight_2_1_4 with input as pods( +test_memory_limits_set_memory_limits_unset { + output := memory_limits_set with input as pods( [ { "metadata": { @@ -640,8 +640,8 @@ test_2_1_4_memory_limits_unset { } ) } -test_2_1_4_memory_limits_some_unset { - output := preflight_2_1_4 with input as pods( +test_memory_limits_set_memory_limits_some_unset { + output := memory_limits_set with input as pods( [ { "metadata": { @@ -673,13 +673,13 @@ test_2_1_4_memory_limits_some_unset { ) } -# 2.1.5 Guarantead QoS -test_2_1_5_no_pods { - output := preflight_2_1_5 with input as pods([]) +# Guarantead QoS +test_guaranteed_qos_no_pods { + output := guaranteed_qos with input as pods([]) assert_allowed(output) } -test_2_1_5_requests_limits_equal { - output := preflight_2_1_5 with input as pods( +test_guaranteed_qos_requests_limits_equal { + output := guaranteed_qos with input as pods( [ { "metadata": { @@ -708,8 +708,8 @@ test_2_1_5_requests_limits_equal { ) assert_allowed(output) } -test_2_1_5_requests_missing { - output := preflight_2_1_5 with input as pods( +test_guaranteed_qos_requests_missing { + output := guaranteed_qos with input as pods( [ { "metadata": { @@ -738,8 +738,8 @@ test_2_1_5_requests_missing { } ) } -test_2_1_5_limits_missing { - output := preflight_2_1_5 with input as pods( +test_guaranteed_qos_limits_missing { + output := guaranteed_qos with input as pods( [ { "metadata": { @@ -768,8 +768,8 @@ test_2_1_5_limits_missing { } ) } -test_2_1_5_requests_limits_absent { - output := preflight_2_1_5 with input as pods( +test_guaranteed_qos_requests_limits_absent { + output := guaranteed_qos with input as pods( [ { "metadata": { @@ -792,8 +792,8 @@ test_2_1_5_requests_limits_absent { } ) } -test_2_1_5_requests_limits_not_set { - output := preflight_2_1_5 with input as pods( +test_guaranteed_qos_requests_limits_not_set { + output := guaranteed_qos with input as pods( [ { "metadata": { @@ -817,8 +817,8 @@ test_2_1_5_requests_limits_not_set { } ) } -test_2_1_5_requests_limits_blank { - output := preflight_2_1_5 with input as pods( +test_guaranteed_qos_requests_limits_blank { + output := guaranteed_qos with input as pods( [ { "metadata": { @@ -845,8 +845,8 @@ test_2_1_5_requests_limits_blank { } ) } -test_2_1_5_requests_limits_some_not_set { - output := preflight_2_1_5 with input as pods( +test_guaranteed_qos_requests_limits_some_not_set { + output := guaranteed_qos with input as pods( [ { "metadata": { @@ -883,8 +883,8 @@ test_2_1_5_requests_limits_some_not_set { } ) } -test_2_1_5_requests_limits_all_set { - output := preflight_2_1_5 with input as pods( +test_guaranteed_qos_requests_limits_all_set { + output := guaranteed_qos with input as pods( [ { "metadata": { @@ -927,15 +927,15 @@ test_2_1_5_requests_limits_all_set { assert_allowed(output) } -# 2.2 Monitoring +# Monitoring -# 2.2.1 Liveness probe set -test_2_2_1_no_pods { - output := preflight_2_2_1 with input as pods([]) +# Liveness probe set +test_liveness_probe_set_no_pods { + output := liveness_probe_set with input as pods([]) assert_allowed(output) } -test_2_2_1_liveness_probe_set { - output := preflight_2_2_1 with input as pods( +test_liveness_probe_set_liveness_probe_set { + output := liveness_probe_set with input as pods( [ { "metadata": { @@ -964,8 +964,8 @@ test_2_2_1_liveness_probe_set { ) assert_allowed(output) } -test_2_2_1_liveness_probe_unset { - output := preflight_2_2_1 with input as pods( +test_liveness_probe_set_liveness_probe_unset { + output := liveness_probe_set with input as pods( [ { "metadata": { @@ -988,8 +988,8 @@ test_2_2_1_liveness_probe_unset { } ) } -test_2_2_1_liveness_probe_empty { - output := preflight_2_2_1 with input as pods( +test_liveness_probe_set_liveness_probe_empty { + output := liveness_probe_set with input as pods( [ { "metadata": { @@ -1013,8 +1013,8 @@ test_2_2_1_liveness_probe_empty { } ) } -test_2_2_1_liveness_probe_some_unset { - output := preflight_2_2_1 with input as pods( +test_liveness_probe_set_liveness_probe_some_unset { + output := liveness_probe_set with input as pods( [ { "metadata": { @@ -1050,8 +1050,8 @@ test_2_2_1_liveness_probe_some_unset { } ) } -test_2_2_1_liveness_probe_all_set { - output := preflight_2_2_1 with input as pods( +test_liveness_probe_set_liveness_probe_all_set { + output := liveness_probe_set with input as pods( [ { "metadata": { @@ -1094,13 +1094,13 @@ test_2_2_1_liveness_probe_all_set { assert_allowed(output) } -# 2.2.2 Readiness probe set -test_2_2_2_no_pods { - output := preflight_2_2_2 with input as pods([]) +# Readiness probe set +test_readiness_probe_set_no_pods { + output := readiness_probe_set with input as pods([]) assert_allowed(output) } -test_2_2_2_readiness_probe_set { - output := preflight_2_2_2 with input as pods( +test_readiness_probe_set_readiness_probe_set { + output := readiness_probe_set with input as pods( [ { "metadata": { @@ -1129,8 +1129,8 @@ test_2_2_2_readiness_probe_set { ) assert_allowed(output) } -test_2_2_2_readiness_probe_unset { - output := preflight_2_2_2 with input as pods( +test_readiness_probe_set_readiness_probe_unset { + output := readiness_probe_set with input as pods( [ { "metadata": { @@ -1154,8 +1154,8 @@ test_2_2_2_readiness_probe_unset { ) } # TODO, is this possible? -test_2_2_2_readiness_probe_empty { - output := preflight_2_2_2 with input as pods( +test_readiness_probe_set_readiness_probe_empty { + output := readiness_probe_set with input as pods( [ { "metadata": { @@ -1179,8 +1179,8 @@ test_2_2_2_readiness_probe_empty { } ) } -test_2_2_2_readiness_probe_some_unset { - output := preflight_2_2_2 with input as pods( +test_readiness_probe_set_readiness_probe_some_unset { + output := readiness_probe_set with input as pods( [ { "metadata": { @@ -1216,8 +1216,8 @@ test_2_2_2_readiness_probe_some_unset { } ) } -test_2_2_2_readiness_probe_all_set { - output := preflight_2_2_2 with input as pods( +test_readiness_probe_set_readiness_probe_all_set { + output := readiness_probe_set with input as pods( [ { "metadata": { @@ -1260,13 +1260,13 @@ test_2_2_2_readiness_probe_all_set { assert_allowed(output) } -# 2.2.3 -test_2_2_3_no_pods { - output := preflight_2_2_3 with input as pods([]) +# Liveness and readiness probes are different +test_liveness_and_readiness_probes_are_different_no_pods { + output := liveness_and_readiness_probes_are_different with input as pods([]) assert_allowed(output) } -test_2_2_3_liveness_readiness_not_equal { - output := preflight_2_2_3 with input as pods( +test_liveness_and_readiness_probes_are_different_liveness_readiness_not_equal { + output := liveness_and_readiness_probes_are_different with input as pods( [ { "metadata": { @@ -1303,8 +1303,8 @@ test_2_2_3_liveness_readiness_not_equal { ) assert_allowed(output) } -test_2_2_3_liveness_readiness_equal { - output := preflight_2_2_3 with input as pods( +test_liveness_and_readiness_probes_are_different_liveness_readiness_equal { + output := liveness_and_readiness_probes_are_different with input as pods( [ { "metadata": { @@ -1347,8 +1347,8 @@ test_2_2_3_liveness_readiness_equal { } ) } -test_2_2_3_liveness_readiness_some_pods_equal { - output := preflight_2_2_3 with input as pods( +test_liveness_and_readiness_probes_are_different_liveness_readiness_some_pods_equal { + output := liveness_and_readiness_probes_are_different with input as pods( [ { "metadata": { @@ -1423,15 +1423,15 @@ test_2_2_3_liveness_readiness_some_pods_equal { ) } -# 2.3 Images +# Images -# 2.3.1 Image pull policy is ifNotPresent -test_2_3_1_no_pods { - output := preflight_2_3_1 with input as pods([]) +# Image pull policy is ifNotPresent +test_imagepullpolicy_is_ifnotpresent_no_pods { + output := imagepullpolicy_is_ifnotpresent with input as pods([]) assert_allowed(output) } -test_2_3_1_no_pull_policy { - output := preflight_2_3_1 with input as pods( +test_imagepullpolicy_is_ifnotpresent_no_pull_policy { + output := imagepullpolicy_is_ifnotpresent with input as pods( [ { "metadata": { @@ -1454,8 +1454,8 @@ test_2_3_1_no_pull_policy { } ) } -test_2_3_1_always { - output := preflight_2_3_1 with input as pods( +test_imagepullpolicy_is_ifnotpresent_always { + output := imagepullpolicy_is_ifnotpresent with input as pods( [ { "metadata": { @@ -1479,8 +1479,8 @@ test_2_3_1_always { } ) } -test_2_3_1_if_not_present { - output := preflight_2_3_1 with input as pods( +test_imagepullpolicy_is_ifnotpresent_if_not_present { + output := imagepullpolicy_is_ifnotpresent with input as pods( [ { "metadata": { @@ -1500,8 +1500,8 @@ test_2_3_1_if_not_present { ) assert_allowed(output) } -test_2_3_1_some_pods_always { - output := preflight_2_3_1 with input as pods( +test_imagepullpolicy_is_ifnotpresent_some_pods_always { + output := imagepullpolicy_is_ifnotpresent with input as pods( [ { "metadata": { @@ -1539,8 +1539,8 @@ test_2_3_1_some_pods_always { } ) } -test_2_3_1_all_pods_ifnotpresent { - output := preflight_2_3_1 with input as pods( +test_imagepullpolicy_is_ifnotpresent_all_pods_ifnotpresent { + output := imagepullpolicy_is_ifnotpresent with input as pods( [ { "metadata": { @@ -1575,13 +1575,13 @@ test_2_3_1_all_pods_ifnotpresent { assert_allowed(output) } -# 2.3.2 Image has explicit tag or SHA -test_2_3_2_no_pods { - output := preflight_2_3_2 with input as pods([]) +# Image has explicit tag or SHA +test_image_has_explicit_tag_or_sha_no_pods { + output := image_has_explicit_tag_or_sha with input as pods([]) assert_allowed(output) } -test_2_3_2_named_tag { - output := preflight_2_3_2 with input as pods( +test_image_has_explicit_tag_or_sha_named_tag { + output := image_has_explicit_tag_or_sha with input as pods( [ { "metadata": { @@ -1601,8 +1601,8 @@ test_2_3_2_named_tag { ) assert_allowed(output) } -test_2_3_2_latest_tag { - output := preflight_2_3_2 with input as pods( +test_image_has_explicit_tag_or_sha_latest_tag { + output := image_has_explicit_tag_or_sha with input as pods( [ { "metadata": { @@ -1626,8 +1626,8 @@ test_2_3_2_latest_tag { } ) } -test_2_3_2_missing_tag { - output := preflight_2_3_2 with input as pods( +test_image_has_explicit_tag_or_sha_missing_tag { + output := image_has_explicit_tag_or_sha with input as pods( [ { "metadata": { @@ -1651,8 +1651,8 @@ test_2_3_2_missing_tag { } ) } -test_2_3_2_sha { - output := preflight_2_3_2 with input as pods( +test_image_has_explicit_tag_or_sha_sha { + output := image_has_explicit_tag_or_sha with input as pods( [ { "metadata": { @@ -1672,8 +1672,8 @@ test_2_3_2_sha { ) assert_allowed(output) } -test_2_3_2_some_pods_latest { - output := preflight_2_3_2 with input as pods( +test_image_has_explicit_tag_or_sha_some_pods_latest { + output := image_has_explicit_tag_or_sha with input as pods( [ { "metadata": { @@ -1711,8 +1711,8 @@ test_2_3_2_some_pods_latest { } ) } -test_2_3_2_all_pods_complient { - output := preflight_2_3_2 with input as pods( +test_image_has_explicit_tag_or_sha_all_pods_complient { + output := image_has_explicit_tag_or_sha with input as pods( [ { "metadata": { @@ -1747,15 +1747,15 @@ test_2_3_2_all_pods_complient { assert_allowed(output) } -# 2.4 Namespaces +# Namespaces -# 2.4.1 Pods across multiple namespaces -test_2_4_1_no_pods { - output := preflight_2_4_1 with input as pods([]) +# Pods across multiple namespaces +test_deployments_across_multiple_namespaces_no_pods { + output := deployments_across_multiple_namespaces with input as pods([]) assert_allowed(output) } -test_2_4_1_no_default { - output := preflight_2_4_1 with input as pods( +test_deployments_across_multiple_namespaces_no_default { + output := deployments_across_multiple_namespaces with input as pods( [ { "metadata": { @@ -1767,8 +1767,8 @@ test_2_4_1_no_default { ) assert_allowed(output) } -test_2_4_1_default { - output := preflight_2_4_1 with input as pods( +test_deployments_across_multiple_namespaces_default { + output := deployments_across_multiple_namespaces with input as pods( [ { "metadata": { @@ -1784,8 +1784,8 @@ test_2_4_1_default { } ) } -test_2_4_1_multiple_no_default { - output := preflight_2_4_1 with input as pods( +test_deployments_across_multiple_namespaces_multiple_no_default { + output := deployments_across_multiple_namespaces with input as pods( [ { "metadata": { @@ -1803,8 +1803,8 @@ test_2_4_1_multiple_no_default { ) assert_allowed(output) } -test_2_4_1_multiple_default { - output := preflight_2_4_1 with input as pods( +test_deployments_across_multiple_namespaces_multiple_default { + output := deployments_across_multiple_namespaces with input as pods( [ { "metadata": { diff --git a/preflight-packages/jetstack.io/pods/policy-manifest.yaml b/preflight-packages/jetstack.io/pods/policy-manifest.yaml index a41d588c..b473357c 100644 --- a/preflight-packages/jetstack.io/pods/policy-manifest.yaml +++ b/preflight-packages/jetstack.io/pods/policy-manifest.yaml @@ -11,7 +11,7 @@ description: > the basic units of a cluster workload, so it's important to get the details correct to maximise performance and minimise faults. sections: -- id: "2.1" +- id: "resources" name: Resources description: > Pods can specify resource requests and limits for CPU and memory. The @@ -26,7 +26,7 @@ sections: error, whereas limits that are set too low can result in poor performance or a Pod being unnecessarily restarted. rules: - - id: "2.1.1" + - id: cpu_requests_set name: CPU requests set description: > The CPU requests of containers specify how much CPU time that container @@ -41,7 +41,7 @@ sections: links: - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu" - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#how-pods-with-resource-requests-are-scheduled" - - id: "2.1.2" + - id: memory_requests_set name: Memory requests set description: > The memory requests of containers specify how much memory that container @@ -57,7 +57,7 @@ sections: links: - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory" - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#how-pods-with-resource-requests-are-scheduled" - - id: "2.1.3" + - id: cpu_limits_set name: CPU limits set description: > The CPU limits of containers specify the maximum amount of CPU time that @@ -73,7 +73,7 @@ sections: links: - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu" - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#how-pods-with-resource-limits-are-run" - - id: "2.1.4" + - id: memory_limits_set name: Memory limits set description: > The memory limits of containers specify the maximum amount of memory that @@ -90,8 +90,8 @@ sections: links: - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory" - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#how-pods-with-resource-limits-are-run" - - id: "2.1.5" - name: Guarantead QoS + - id: guaranteed_qos + name: Guaranteed QoS description: > Kubernetes uses requests and limits of a Pod's containers in order to determine the Quality of Service (QoS) class of the Pod. This class @@ -120,7 +120,7 @@ sections: appropriately. links: - "https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/resource-qos.md" - - id: "2.1.6" + - id: cpu_limits_disabled_for_latency_sensitive_workloads name: CPU limits disabled for latency sensitive workloads manual: true description: > @@ -140,10 +140,10 @@ sections: - "https://github.com/kubernetes/kubernetes/issues/51135" - "https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/" - "https://github.com/kubernetes/kubernetes/issues/67577" -- id: "2.2" +- id: monitoring name: Monitoring rules: - - id: "2.2.1" + - id: liveness_probe_set name: Liveness probe set description: > The liveness probe should indicate whether a container in a Pod is @@ -166,7 +166,7 @@ sections: the state of the application. links: - "https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/" - - id: "2.2.2" + - id: readiness_probe_set name: Readiness probe set description: > The readiness probe should indicate when an application is ready to serve. @@ -184,7 +184,7 @@ sections: do this. links: - "https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/" - - id: "2.2.3" + - id: liveness_and_readiness_probes_are_different name: Liveness and readiness probes are different description: > The liveness and readiness probes are intended to check distinct @@ -197,10 +197,10 @@ sections: that the container is ready to serve. links: - "https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/" -- id: "2.3" +- id: images name: Images rules: - - id: "2.3.1" + - id: imagepullpolicy_is_ifnotpresent name: imagePullPolicy is ifNotPresent description: > For faster Pod start-ups and reduced network usage, it’s advised that the @@ -217,7 +217,7 @@ sections: this problem. links: - "https://kubernetes.io/docs/concepts/containers/images/" - - id: "2.3.2" + - id: image_has_explicit_tag_or_sha name: Image has explicit tag or SHA description: > By default images without a tag specified used the `latest` tag. This @@ -237,7 +237,7 @@ sections: required. links: - "https://kubernetes.io/docs/concepts/containers/images/" - - id: "2.3.3" + - id: container_registry_close_to_cluster name: Container registry close to cluster manual: true description: > @@ -253,10 +253,10 @@ sections: Make use of a container registry close to your cluster. For example, if the cluster is running on Google Kubernetes Engine use Google Container Registry. Most container registries allow mirroring of public images -- id: "2.4" +- id: namespaces name: Namespaces rules: - - id: "2.4.1" + - id: deployments_across_multiple_namespaces name: Deployments across multiple Namespaces description: > Using Namespaces is the ideal way to divide resources in a logical way. From 2749f75529859a6dc9a2a00a74528ca4a6507cc6 Mon Sep 17 00:00:00 2001 From: wwwil Date: Fri, 17 Jan 2020 13:41:31 +0000 Subject: [PATCH 3/5] Remove basic version of pods package Signed-off-by: wwwil --- .../examples.jetstack.io/pods_basic/pods.rego | 69 ---- .../pods_basic/pods_test.rego | 332 ------------------ .../pods_basic/policy-manifest.yaml | 76 ---- 3 files changed, 477 deletions(-) delete mode 100644 preflight-packages/examples.jetstack.io/pods_basic/pods.rego delete mode 100644 preflight-packages/examples.jetstack.io/pods_basic/pods_test.rego delete mode 100644 preflight-packages/examples.jetstack.io/pods_basic/policy-manifest.yaml diff --git a/preflight-packages/examples.jetstack.io/pods_basic/pods.rego b/preflight-packages/examples.jetstack.io/pods_basic/pods.rego deleted file mode 100644 index df045005..00000000 --- a/preflight-packages/examples.jetstack.io/pods_basic/pods.rego +++ /dev/null @@ -1,69 +0,0 @@ -package pods - -# See https://github.com/jetstack/preflight/blob/master/docs/datagatherers/k8s_pods.md for more details -import input["k8s/pods"] as pods - -# Rule 'container_cpu_limit' -preflight_container_cpu_limit[message] { - # find all containers in all pods - pod := pods.items[_] - container := pod.spec.containers[_] - # test if the limits are not set - not container.resources.limits.cpu - # bind a message for reporting - message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing a cpu limit", [container.name, pod.metadata.name, pod.metadata.namespace]) -} -preflight_container_cpu_limit[message] { - # find all initContainers in all pods - pod := pods.items[_] - container := pod.spec.initContainers[_] - # test if the limits are not set - not container.resources.limits.cpu - # bind a message for reporting - message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing a cpu limit", [container.name, pod.metadata.name, pod.metadata.namespace]) -} - -# Rule 'container_mem_limit' -preflight_container_mem_limit[message] { - # find all containers in all pods - pod := pods.items[_] - container := pod.spec.containers[_] - # test if the limits are not set - not container.resources.limits.memory - # bind a message for reporting - message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing a memory limit", [container.name, pod.metadata.name, pod.metadata.namespace]) -} -preflight_container_mem_limit[message] { - # find all initContainers in all pods - pod := pods.items[_] - container := pod.spec.initContainers[_] - # test if the limits are not set - not container.resources.limits.memory - # bind a message for reporting - message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing a memory limit", [container.name, pod.metadata.name, pod.metadata.namespace]) -} - -# Rule 'explicit_image_tag' -preflight_explicit_image_tag[message] { - # find all containers in all pods - pod := pods.items[_] - container := pod.spec.containers[_] - # validate that the image value contains an explicit tag - { re_match(`latest$`, container.image), - re_match(`^[^:]+$`, container.image) } & { true } != set() - - # bind a message for reporting - message := sprintf("container '%s' in pod '%s' in namespace '%s' is missing an explicit image tag", [container.name, pod.metadata.name, pod.metadata.namespace]) -} - -preflight_explicit_image_tag[message] { - # find all containers in all pods - pod := pods.items[_] - container := pod.spec.initContainers[_] - # validate that the image value contains an explicit tag - { re_match(`latest$`, container.image), - re_match(`^[^:]+$`, container.image) } & { true } != set() - - # bind a message for reporting - message := sprintf("init container '%s' in pod '%s' in namespace '%s' is missing an explicit image tag", [container.name, pod.metadata.name, pod.metadata.namespace]) -} diff --git a/preflight-packages/examples.jetstack.io/pods_basic/pods_test.rego b/preflight-packages/examples.jetstack.io/pods_basic/pods_test.rego deleted file mode 100644 index 01294811..00000000 --- a/preflight-packages/examples.jetstack.io/pods_basic/pods_test.rego +++ /dev/null @@ -1,332 +0,0 @@ -package pods - -assert_allowed(output) = output { - trace(sprintf("GOT: %s", [concat(",", output)])) - trace("WANT: empty set") - output == set() -} - -assert_violates(output, messages) = output { - trace(sprintf("GOT: %s", [concat(",", output)])) - trace(sprintf("WANT: %s", [concat(",", messages)])) - - output == messages -} - -pods(x) = y { y := {"k8s/pods": {"items": x }} } - -# Rule 'container_cpu_limit' -test_container_cpu_limit_no_pods { - output := preflight_container_cpu_limit with input as pods([]) - - # no validation messages should be returned - assert_allowed(output) -} -test_container_cpu_limit_cpu_limits_set { - output := preflight_container_cpu_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "resources":{"limits":{"cpu": "500m"}}}, - {"name": "container-two", - "resources":{"limits":{"cpu": "100m"}}} - ]}}]) - - # no validation messages should be returned - assert_allowed(output) -} -test_container_cpu_limit_init_containers_unset { - output := preflight_container_cpu_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{ - "initContainers":[ - {"name": "init-one"} - ], - "containers":[ - {"name": "container-one", - "resources":{"limits":{"cpu": "500m"}}}, - ] - }}]) - - # ensure validation message is returned - assert_violates(output, {"init container 'init-one' in pod 'foo' in namespace 'default' is missing a cpu limit"}) -} -test_container_cpu_limit_init_containers_set { - output := preflight_container_cpu_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{ - "initContainers":[ - {"name": "init-one", - "resources": {"limits": {"cpu": "100m"}}} - ], - "containers":[ - {"name": "container-one", - "resources":{"limits":{"cpu": "500m"}}}, - ] - }}]) - - # no validation messages should be returned - assert_allowed(output) -} -test_container_cpu_limit_cpu_limits_unset { - output := preflight_container_cpu_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one"} - ]}}]) - - # ensure validation message is returned - assert_violates(output, {"container 'container-one' in pod 'foo' in namespace 'default' is missing a cpu limit"}) -} -test_container_cpu_limit_cpu_limits_some_unset { - output := preflight_container_cpu_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "resources":{"limits":{"cpu": "500m"}}}, - {"name": "container-two"} - ]}}]) - - # ensure validation message is returned - assert_violates(output, {"container 'container-two' in pod 'foo' in namespace 'default' is missing a cpu limit"}) -} -test_container_cpu_limit_cpu_limits_many_unset { - output := preflight_container_cpu_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{ - "initContainers":[ - {"name": "init-one", - "resources": {}} - ], - "containers":[ - {"name": "container-one", - "resources":{}}, - ] - }}]) - - # ensure validation message for each container is returned - assert_violates(output, { - "container 'container-one' in pod 'foo' in namespace 'default' is missing a cpu limit", - "init container 'init-one' in pod 'foo' in namespace 'default' is missing a cpu limit" - }) -} - -# Rule 'container_mem_limit' -test_container_mem_limit_no_pods { - output := preflight_container_mem_limit with input as pods([]) - - # no validation messages should be returned - assert_allowed(output) -} -test_container_mem_limit_memory_limits_set { - output := preflight_container_mem_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "resources":{"limits":{"memory": "500m"}}}, - {"name": "container-two", - "resources":{"limits":{"memory": "100m"}}} - ]}}]) - - # no validation messages should be returned - assert_allowed(output) -} -test_container_mem_limit_init_containers_unset { - output := preflight_container_mem_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{ - "initContainers":[ - {"name": "init-one"} - ], - "containers":[ - {"name": "container-one", - "resources":{"limits":{"memory": "500m"}}}, - ] - }}]) - - assert_violates(output, { - "init container 'init-one' in pod 'foo' in namespace 'default' is missing a memory limit" - }) -} -test_container_mem_limit_init_containers_set { - output := preflight_container_mem_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{ - "initContainers":[ - {"name": "init-one", - "resources": {"limits": {"memory": "100m"}}} - ], - "containers":[ - {"name": "container-one", - "resources":{"limits":{"memory": "500m"}}}, - ] - }}]) - - # no validation messages should be returned - assert_allowed(output) -} -test_container_mem_limit_memory_limits_unset { - output := preflight_container_mem_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one"} - ]}}]) - - assert_violates(output, { - "container 'container-one' in pod 'foo' in namespace 'default' is missing a memory limit" - }) -} -test_container_mem_limit_memory_limits_some_unset { - output := preflight_container_mem_limit with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "resources":{"limits":{"memory": "500m"}}}, - {"name": "container-two"} - ]}}]) - - assert_violates(output, { - "container 'container-two' in pod 'foo' in namespace 'default' is missing a memory limit" - }) -} - -# Rule 'explicit_image_tag' -test_explicit_image_tag_no_pods { - output := preflight_explicit_image_tag with input as pods([]) - assert_allowed(output) -} -test_explicit_image_tag_named_tag { - output := preflight_explicit_image_tag with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "image": "gcr.io/my-project/my-image:v0.1"} - ]}}]) - - assert_allowed(output) -} -test_explicit_image_tag_latest_tag { - output := preflight_explicit_image_tag with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "image": "gcr.io/my-project/my-image:latest"} - ]}}]) - - assert_violates(output, { - "container 'container-one' in pod 'foo' in namespace 'default' is missing an explicit image tag" - }) -} -test_explicit_image_tag_missing_tag { - output := preflight_explicit_image_tag with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "image": "gcr.io/my-project/my-image"} - ]}}]) - - assert_violates(output, { - "container 'container-one' in pod 'foo' in namespace 'default' is missing an explicit image tag" - }) -} -test_explicit_image_tag_sha { - output := preflight_explicit_image_tag with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "image": "gcr.io/my-project/my-image@sha256:4bdd623e848417d96127e16037743f0cd8b528c026e9175e22a84f639eca58ff"} - ]}}]) - - assert_allowed(output) -} -test_explicit_image_tag_some_pods_latest { - output := preflight_explicit_image_tag with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "image": "gcr.io/my-project/my-image:latest"} - ]}}, - {"metadata": { - "name": "bar", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "image": "gcr.io/my-project/my-image:v0.2"} - ]}} - ]) - - assert_violates(output, { - "container 'container-one' in pod 'foo' in namespace 'default' is missing an explicit image tag" - }) -} -test_explicit_image_tag_all_pods_compliant { - output := preflight_explicit_image_tag with input as pods([ - {"metadata": { - "name": "foo", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "image": "gcr.io/my-project/my-image:v0.2"} - ]}}, - {"metadata": { - "name": "bar", - "namespace": "default" - }, - "spec":{"containers":[ - {"name": "container-one", - "image": "gcr.io/my-project/another-image:v0.3"} - ]}} - ]) - - assert_allowed(output) -} diff --git a/preflight-packages/examples.jetstack.io/pods_basic/policy-manifest.yaml b/preflight-packages/examples.jetstack.io/pods_basic/policy-manifest.yaml deleted file mode 100644 index 1888ef7b..00000000 --- a/preflight-packages/examples.jetstack.io/pods_basic/policy-manifest.yaml +++ /dev/null @@ -1,76 +0,0 @@ -schema-version: "1.0.0" -id: "pods_basic" -namespace: "examples.jetstack.io" -package-version: "1.0.0" -data-gatherers: -- k8s/pods -# `root-query` selects selects the rego package: `data.`. -# In this case, it corresponds to the package in ./pods.rego. -root-query: "data.pods" -name: Pods -description: > - This policy contains example rules to describe best-practice Pod configuration. -sections: -- id: "resources" - name: Resources - description: > - Containers can specify resource requests and limits for CPU and memory. - The limits define the maximum resources they can use and are used by Kubelet - to determine if a Pod should be killed. - rules: - - id: "container_cpu_limit" - name: CPU limits set - description: > - The CPU limits of containers specify the maximum amount of CPU time that - container should get, and are summed together to determine the total - maximum CPU time that a Pod should get. It a Pod exceeds this it will - experience CPU throttling. - remediation: > - All `containers` in a Pod should have a `resources.limits.cpu` value set, - which is given in relative `CPU` units based on a single virtual CPU core - or hyperthread. Choosing appropriate CPU limit values generally requires - some experimentation and benchmarking. The chosen value should be just - above the normal maximum CPU utilisation of the workload. - links: - - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu" - - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#how-pods-with-resource-limits-are-run" - - id: "container_mem_limit" - name: Memory limits set - description: > - The memory limits of containers specify the maximum amount of memory that - container should get, and are summed together to determine the total - maximum memory that a Pod should get. If a Pod exceeds this it will - experience an ‘out of memory’ (OOM) termination. - remediation: > - All `containers` in a Pod should have a `resources.limits.memory` value - set, which is given in bytes with optional units such as `M` for multiples - of 1,000,000 bytes, or `Ki` for multiples of 1,024 bytes. Choosing - appropriate memory limit values generally requires some experimentation - and benchmarking. The chosen value should be just above the normal maximum - memory usage of the workload. - links: - - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory" - - "https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#how-pods-with-resource-limits-are-run" -- id: "2.3" - name: Images - rules: - - id: "explicit_image_tag" - name: Image has explicit tag or SHA - description: > - By default images without a tag specified used the `latest` tag. This - means if a new image is released it will be used on new Pods without any - explicit upgrade. If the functionality of the image has changed, for - example with new versions of software, then this can cause unexpected - behaviour. Because the upgrade happens implicitly there is no opportunity - to review changes. There is also no way to roll back, or run multiple - Deployments to phase a new version in. As the image is fetched when a Pod - starts Deployment can have multiple versions of an image in use, which can - cause inconsistent behaviour between replicas. - remediation: > - Determine the version of images in use and set them explicitly in the - `image` specification for all `containers`. When new versions become - available change the tag explicitly to upgrade, and ensure the new image - version still works as desired, rolling back to the previous one if - required. - links: - - "https://kubernetes.io/docs/concepts/containers/images/" From b1ef3c46f517f7b0ed1d3e51fa24aa69c45ca394 Mon Sep 17 00:00:00 2001 From: wwwil Date: Fri, 17 Jan 2020 13:45:22 +0000 Subject: [PATCH 4/5] Fix root query Signed-off-by: wwwil --- preflight-packages/jetstack.io/pods/policy-manifest.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preflight-packages/jetstack.io/pods/policy-manifest.yaml b/preflight-packages/jetstack.io/pods/policy-manifest.yaml index b473357c..794206d8 100644 --- a/preflight-packages/jetstack.io/pods/policy-manifest.yaml +++ b/preflight-packages/jetstack.io/pods/policy-manifest.yaml @@ -4,7 +4,7 @@ namespace: "jetstack.io" package-version: "1.1.0" data-gatherers: - k8s/pods -root-query: "data.preflight._2_pods" +root-query: "data.preflight.pods" name: Pods description: > This policy gives rules for how Kubernetes Pods should be configured. Pods are From 6f29c61b8aec1b4a84eea4780c2dd1a134009695 Mon Sep 17 00:00:00 2001 From: wwwil Date: Fri, 17 Jan 2020 14:33:55 +0000 Subject: [PATCH 5/5] Drop preflight prefix from package name Signed-off-by: wwwil --- preflight-packages/jetstack.io/pods/pods.rego | 2 +- preflight-packages/jetstack.io/pods/pods_test.rego | 2 +- preflight-packages/jetstack.io/pods/policy-manifest.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/preflight-packages/jetstack.io/pods/pods.rego b/preflight-packages/jetstack.io/pods/pods.rego index 52444617..3adb7025 100644 --- a/preflight-packages/jetstack.io/pods/pods.rego +++ b/preflight-packages/jetstack.io/pods/pods.rego @@ -1,4 +1,4 @@ -package preflight.pods +package pods import input["k8s/pods"] as pods diff --git a/preflight-packages/jetstack.io/pods/pods_test.rego b/preflight-packages/jetstack.io/pods/pods_test.rego index 1b72157a..d1916aa4 100644 --- a/preflight-packages/jetstack.io/pods/pods_test.rego +++ b/preflight-packages/jetstack.io/pods/pods_test.rego @@ -1,4 +1,4 @@ -package preflight.pods +package pods assert_allowed(output) = output { trace(sprintf("GOT: %s", [concat(",", output)])) diff --git a/preflight-packages/jetstack.io/pods/policy-manifest.yaml b/preflight-packages/jetstack.io/pods/policy-manifest.yaml index 794206d8..f5e139ee 100644 --- a/preflight-packages/jetstack.io/pods/policy-manifest.yaml +++ b/preflight-packages/jetstack.io/pods/policy-manifest.yaml @@ -4,7 +4,7 @@ namespace: "jetstack.io" package-version: "1.1.0" data-gatherers: - k8s/pods -root-query: "data.preflight.pods" +root-query: "data.pods" name: Pods description: > This policy gives rules for how Kubernetes Pods should be configured. Pods are