From 4ad966ce2b568648636599f7952cf6850d5c6169 Mon Sep 17 00:00:00 2001 From: Jaydipkumar Arvindbhai Gabani Date: Wed, 5 Jun 2024 13:40:58 -0700 Subject: [PATCH] chore: adding cel for psp-privileged-containers (#543) Fixes #541 Signed-off-by: Jaydip Gabani --- .../1.1.0/artifacthub-pkg.yml | 22 +++ .../1.1.0/kustomization.yaml | 2 + .../psp-privileged-container/constraint.yaml | 10 ++ .../disallowed_ephemeral.yaml | 12 ++ .../example_allowed.yaml | 12 ++ .../example_disallowed.yaml | 12 ++ .../psp-privileged-container/update.yaml | 17 +++ .../privileged-containers/1.1.0/suite.yaml | 25 +++ .../privileged-containers/1.1.0/template.yaml | 126 +++++++++++++++ .../privileged-containers/template.yaml | 124 +++++++++------ .../privileged-containers/constraint.tmpl | 22 ++- .../privileged-containers/src.cel | 29 ++++ .../privileged-containers/src_test.rego | 10 +- .../docs/validation/privileged-containers.md | 144 +++++++++++------- 14 files changed, 457 insertions(+), 110 deletions(-) create mode 100644 artifacthub/library/pod-security-policy/privileged-containers/1.1.0/artifacthub-pkg.yml create mode 100644 artifacthub/library/pod-security-policy/privileged-containers/1.1.0/kustomization.yaml create mode 100644 artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/constraint.yaml create mode 100644 artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/disallowed_ephemeral.yaml create mode 100644 artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/example_allowed.yaml create mode 100644 artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/example_disallowed.yaml create mode 100644 artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/update.yaml create mode 100644 artifacthub/library/pod-security-policy/privileged-containers/1.1.0/suite.yaml create mode 100644 artifacthub/library/pod-security-policy/privileged-containers/1.1.0/template.yaml create mode 100644 src/pod-security-policy/privileged-containers/src.cel diff --git a/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/artifacthub-pkg.yml b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/artifacthub-pkg.yml new file mode 100644 index 000000000..0409a1259 --- /dev/null +++ b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/artifacthub-pkg.yml @@ -0,0 +1,22 @@ +version: 1.1.0 +name: k8spspprivilegedcontainer +displayName: Privileged Container +createdAt: "2024-06-03T23:35:27Z" +description: Controls the ability of any container to enable privileged mode. Corresponds to the `privileged` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privileged +digest: 967e9d9abf48b1686b497dd3e2f3a870b9f5b51c5ab1d837ea0ff680fbfaa7c6 +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/privileged-containers +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # Privileged Container + Controls the ability of any container to enable privileged mode. Corresponds to the `privileged` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privileged +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/kustomization.yaml b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/constraint.yaml b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/constraint.yaml new file mode 100644 index 000000000..b246b244a --- /dev/null +++ b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/constraint.yaml @@ -0,0 +1,10 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPPrivilegedContainer +metadata: + name: psp-privileged-container +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + excludedNamespaces: ["kube-system"] diff --git a/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/disallowed_ephemeral.yaml b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/disallowed_ephemeral.yaml new file mode 100644 index 000000000..e8c8b9945 --- /dev/null +++ b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/disallowed_ephemeral.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-privileged-disallowed + labels: + app: nginx-privileged +spec: + ephemeralContainers: + - name: nginx + image: nginx + securityContext: + privileged: true diff --git a/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/example_allowed.yaml b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/example_allowed.yaml new file mode 100644 index 000000000..bb65a2c0e --- /dev/null +++ b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/example_allowed.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-privileged-allowed + labels: + app: nginx-privileged +spec: + containers: + - name: nginx + image: nginx + securityContext: + privileged: false diff --git a/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/example_disallowed.yaml b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/example_disallowed.yaml new file mode 100644 index 000000000..936a24f8e --- /dev/null +++ b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/example_disallowed.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-privileged-disallowed + labels: + app: nginx-privileged +spec: + containers: + - name: nginx + image: nginx + securityContext: + privileged: true diff --git a/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/update.yaml b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/update.yaml new file mode 100644 index 000000000..08f36044c --- /dev/null +++ b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/samples/psp-privileged-container/update.yaml @@ -0,0 +1,17 @@ +kind: AdmissionReview +apiVersion: admission.k8s.io/v1beta1 +request: + operation: "UPDATE" + object: + apiVersion: v1 + kind: Pod + metadata: + name: nginx-privileged-disallowed + labels: + app: nginx-privileged + spec: + containers: + - name: nginx + image: nginx + securityContext: + privileged: true diff --git a/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/suite.yaml b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/suite.yaml new file mode 100644 index 000000000..c2e484fc5 --- /dev/null +++ b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/suite.yaml @@ -0,0 +1,25 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: privileged-containers +tests: +- name: privileged-containers-disallowed + template: template.yaml + constraint: samples/psp-privileged-container/constraint.yaml + cases: + - name: example-disallowed + object: samples/psp-privileged-container/example_disallowed.yaml + assertions: + - violations: yes + - name: example-allowed + object: samples/psp-privileged-container/example_allowed.yaml + assertions: + - violations: no + - name: disallowed-ephemeral + object: samples/psp-privileged-container/disallowed_ephemeral.yaml + assertions: + - violations: yes + - name: update + object: samples/psp-privileged-container/update.yaml + assertions: + - violations: no diff --git a/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/template.yaml b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/template.yaml new file mode 100644 index 000000000..bd8452e67 --- /dev/null +++ b/artifacthub/library/pod-security-policy/privileged-containers/1.1.0/template.yaml @@ -0,0 +1,126 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8spspprivilegedcontainer + annotations: + metadata.gatekeeper.sh/title: "Privileged Container" + metadata.gatekeeper.sh/version: 1.1.0 + description: >- + Controls the ability of any container to enable privileged mode. + Corresponds to the `privileged` field in a PodSecurityPolicy. For more + information, see + https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privileged +spec: + crd: + spec: + names: + kind: K8sPSPPrivilegedContainer + validation: + openAPIV3Schema: + type: object + description: >- + Controls the ability of any container to enable privileged mode. + Corresponds to the `privileged` field in a PodSecurityPolicy. For more + information, see + https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privileged + properties: + exemptImages: + description: >- + Any container that uses an image that matches an entry in this list will be excluded + from enforcement. Prefix-matching can be signified with `*`. For example: `my-image-*`. + + It is recommended that users use the fully-qualified Docker image name (e.g. start with a domain name) + in order to avoid unexpectedly exempting images from an untrusted repository. + type: array + items: + type: string + targets: + - target: admission.k8s.gatekeeper.sh + code: + - engine: K8sNativeValidation + source: + variables: + - name: containers + expression: 'has(variables.anyObject.spec.containers) ? variables.anyObject.spec.containers : []' + - name: initContainers + expression: 'has(variables.anyObject.spec.initContainers) ? variables.anyObject.spec.initContainers : []' + - name: ephemeralContainers + expression: 'has(variables.anyObject.spec.ephemeralContainers) ? variables.anyObject.spec.ephemeralContainers : []' + - name: exemptImagePrefixes + expression: | + !has(variables.params.exemptImages) ? [] : + variables.params.exemptImages.filter(image, image.endsWith("*")).map(image, string(image).replace("*", "")) + - name: exemptImageExplicit + expression: | + !has(variables.params.exemptImages) ? [] : + variables.params.exemptImages.filter(image, !image.endsWith("*")) + - name: exemptImages + expression: | + (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, + container.image in variables.exemptImageExplicit || + variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption))) + - name: badContainers + expression: | + (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, + !(container.image in variables.exemptImages) && + (has(container.securityContext) && has(container.securityContext.privileged) && container.securityContext.privileged == true) + ).map(container, "Privileged container is not allowed: " + container.name +", securityContext: " + container.securityContext) + validations: + - expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0' + messageExpression: 'variables.badContainers.join("\n")' + - engine: Rego + source: + rego: | + package k8spspprivileged + + import data.lib.exclude_update.is_update + import data.lib.exempt_container.is_exempt + + violation[{"msg": msg, "details": {}}] { + # spec.containers.privileged field is immutable. + not is_update(input.review) + + c := input_containers[_] + not is_exempt(c) + c.securityContext.privileged + msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext]) + } + + input_containers[c] { + c := input.review.object.spec.containers[_] + } + + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + libs: + - | + package lib.exclude_update + + is_update(review) { + review.operation == "UPDATE" + } + - | + package lib.exempt_container + + is_exempt(container) { + exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", []) + img := container.image + exemption := exempt_images[_] + _matches_exemption(img, exemption) + } + + _matches_exemption(img, exemption) { + not endswith(exemption, "*") + exemption == img + } + + _matches_exemption(img, exemption) { + endswith(exemption, "*") + prefix := trim_suffix(exemption, "*") + startswith(img, prefix) + } diff --git a/library/pod-security-policy/privileged-containers/template.yaml b/library/pod-security-policy/privileged-containers/template.yaml index c552c193d..bd8452e67 100644 --- a/library/pod-security-policy/privileged-containers/template.yaml +++ b/library/pod-security-policy/privileged-containers/template.yaml @@ -4,7 +4,7 @@ metadata: name: k8spspprivilegedcontainer annotations: metadata.gatekeeper.sh/title: "Privileged Container" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.1.0 description: >- Controls the ability of any container to enable privileged mode. Corresponds to the `privileged` field in a PodSecurityPolicy. For more @@ -36,57 +36,91 @@ spec: type: string targets: - target: admission.k8s.gatekeeper.sh - rego: | - package k8spspprivileged + code: + - engine: K8sNativeValidation + source: + variables: + - name: containers + expression: 'has(variables.anyObject.spec.containers) ? variables.anyObject.spec.containers : []' + - name: initContainers + expression: 'has(variables.anyObject.spec.initContainers) ? variables.anyObject.spec.initContainers : []' + - name: ephemeralContainers + expression: 'has(variables.anyObject.spec.ephemeralContainers) ? variables.anyObject.spec.ephemeralContainers : []' + - name: exemptImagePrefixes + expression: | + !has(variables.params.exemptImages) ? [] : + variables.params.exemptImages.filter(image, image.endsWith("*")).map(image, string(image).replace("*", "")) + - name: exemptImageExplicit + expression: | + !has(variables.params.exemptImages) ? [] : + variables.params.exemptImages.filter(image, !image.endsWith("*")) + - name: exemptImages + expression: | + (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, + container.image in variables.exemptImageExplicit || + variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption))) + - name: badContainers + expression: | + (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, + !(container.image in variables.exemptImages) && + (has(container.securityContext) && has(container.securityContext.privileged) && container.securityContext.privileged == true) + ).map(container, "Privileged container is not allowed: " + container.name +", securityContext: " + container.securityContext) + validations: + - expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0' + messageExpression: 'variables.badContainers.join("\n")' + - engine: Rego + source: + rego: | + package k8spspprivileged - import data.lib.exclude_update.is_update - import data.lib.exempt_container.is_exempt + import data.lib.exclude_update.is_update + import data.lib.exempt_container.is_exempt - violation[{"msg": msg, "details": {}}] { - # spec.containers.privileged field is immutable. - not is_update(input.review) + violation[{"msg": msg, "details": {}}] { + # spec.containers.privileged field is immutable. + not is_update(input.review) - c := input_containers[_] - not is_exempt(c) - c.securityContext.privileged - msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext]) - } + c := input_containers[_] + not is_exempt(c) + c.securityContext.privileged + msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext]) + } - input_containers[c] { - c := input.review.object.spec.containers[_] - } + input_containers[c] { + c := input.review.object.spec.containers[_] + } - input_containers[c] { - c := input.review.object.spec.initContainers[_] - } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } - input_containers[c] { - c := input.review.object.spec.ephemeralContainers[_] - } - libs: - - | - package lib.exclude_update + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + libs: + - | + package lib.exclude_update - is_update(review) { - review.operation == "UPDATE" - } - - | - package lib.exempt_container + is_update(review) { + review.operation == "UPDATE" + } + - | + package lib.exempt_container - is_exempt(container) { - exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", []) - img := container.image - exemption := exempt_images[_] - _matches_exemption(img, exemption) - } + is_exempt(container) { + exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", []) + img := container.image + exemption := exempt_images[_] + _matches_exemption(img, exemption) + } - _matches_exemption(img, exemption) { - not endswith(exemption, "*") - exemption == img - } + _matches_exemption(img, exemption) { + not endswith(exemption, "*") + exemption == img + } - _matches_exemption(img, exemption) { - endswith(exemption, "*") - prefix := trim_suffix(exemption, "*") - startswith(img, prefix) - } + _matches_exemption(img, exemption) { + endswith(exemption, "*") + prefix := trim_suffix(exemption, "*") + startswith(img, prefix) + } diff --git a/src/pod-security-policy/privileged-containers/constraint.tmpl b/src/pod-security-policy/privileged-containers/constraint.tmpl index 90a195925..d5c87bb9d 100644 --- a/src/pod-security-policy/privileged-containers/constraint.tmpl +++ b/src/pod-security-policy/privileged-containers/constraint.tmpl @@ -4,7 +4,7 @@ metadata: name: k8spspprivilegedcontainer annotations: metadata.gatekeeper.sh/title: "Privileged Container" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.1.0 description: >- Controls the ability of any container to enable privileged mode. Corresponds to the `privileged` field in a PodSecurityPolicy. For more @@ -36,10 +36,16 @@ spec: type: string targets: - target: admission.k8s.gatekeeper.sh - rego: | -{{ file.Read "src/pod-security-policy/privileged-containers/src.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} - libs: - - | -{{ file.Read "src/pod-security-policy/privileged-containers/lib_exclude_update.rego" | strings.Indent 10 | strings.TrimSuffix "\n" }} - - | -{{ file.Read "src/pod-security-policy/privileged-containers/lib_exempt_container.rego" | strings.Indent 10 | strings.TrimSuffix "\n" }} + code: + - engine: K8sNativeValidation + source: +{{ file.Read "src/pod-security-policy/privileged-containers/src.cel" | strings.Indent 10 | strings.TrimSuffix "\n" }} + - engine: Rego + source: + rego: | +{{ file.Read "src/pod-security-policy/privileged-containers/src.rego" | strings.Indent 12 | strings.TrimSuffix "\n" }} + libs: + - | +{{ file.Read "src/pod-security-policy/privileged-containers/lib_exclude_update.rego" | strings.Indent 14 | strings.TrimSuffix "\n" }} + - | +{{ file.Read "src/pod-security-policy/privileged-containers/lib_exempt_container.rego" | strings.Indent 14 | strings.TrimSuffix "\n" }} diff --git a/src/pod-security-policy/privileged-containers/src.cel b/src/pod-security-policy/privileged-containers/src.cel new file mode 100644 index 000000000..99c7b4a45 --- /dev/null +++ b/src/pod-security-policy/privileged-containers/src.cel @@ -0,0 +1,29 @@ +variables: +- name: containers + expression: 'has(variables.anyObject.spec.containers) ? variables.anyObject.spec.containers : []' +- name: initContainers + expression: 'has(variables.anyObject.spec.initContainers) ? variables.anyObject.spec.initContainers : []' +- name: ephemeralContainers + expression: 'has(variables.anyObject.spec.ephemeralContainers) ? variables.anyObject.spec.ephemeralContainers : []' +- name: exemptImagePrefixes + expression: | + !has(variables.params.exemptImages) ? [] : + variables.params.exemptImages.filter(image, image.endsWith("*")).map(image, string(image).replace("*", "")) +- name: exemptImageExplicit + expression: | + !has(variables.params.exemptImages) ? [] : + variables.params.exemptImages.filter(image, !image.endsWith("*")) +- name: exemptImages + expression: | + (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, + container.image in variables.exemptImageExplicit || + variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption))) +- name: badContainers + expression: | + (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, + !(container.image in variables.exemptImages) && + (has(container.securityContext) && has(container.securityContext.privileged) && container.securityContext.privileged == true) + ).map(container, "Privileged container is not allowed: " + container.name +", securityContext: " + container.securityContext) +validations: +- expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0' + messageExpression: 'variables.badContainers.join("\n")' \ No newline at end of file diff --git a/src/pod-security-policy/privileged-containers/src_test.rego b/src/pod-security-policy/privileged-containers/src_test.rego index c2b030e68..7c0f435c8 100644 --- a/src/pod-security-policy/privileged-containers/src_test.rego +++ b/src/pod-security-policy/privileged-containers/src_test.rego @@ -20,12 +20,12 @@ test_input_container_many_mixed_privileged_not_allowed { results := violation with input as inp count(results) > 0 } -test_input_container_many_mixed_privileged_not_allowed_two { +test_input_container_many_mixed_privileged_not_allowed_three { inp := { "review": input_review_many_mixed_two} results := violation with input as inp count(results) == 2 } -test_input_container_many_mixed_privileged_not_allowed_two_but_exempt { +test_input_container_many_mixed_privileged_not_allowed_three_but_exempt { inp := { "review": input_review_many_mixed_two, "parameters": {"exemptImages": ["nginx"]}} results := violation with input as inp count(results) == 0 @@ -139,4 +139,10 @@ input_containers_many_mixed = [ "securityContext": { "privileged": true } +}, +{ + "name": "nginx2", + "image": "nginx", + "securityContext": { + } }] diff --git a/website/docs/validation/privileged-containers.md b/website/docs/validation/privileged-containers.md index c73b4fe8a..bf0d8717f 100644 --- a/website/docs/validation/privileged-containers.md +++ b/website/docs/validation/privileged-containers.md @@ -16,7 +16,7 @@ metadata: name: k8spspprivilegedcontainer annotations: metadata.gatekeeper.sh/title: "Privileged Container" - metadata.gatekeeper.sh/version: 1.0.1 + metadata.gatekeeper.sh/version: 1.1.0 description: >- Controls the ability of any container to enable privileged mode. Corresponds to the `privileged` field in a PodSecurityPolicy. For more @@ -48,60 +48,94 @@ spec: type: string targets: - target: admission.k8s.gatekeeper.sh - rego: | - package k8spspprivileged - - import data.lib.exclude_update.is_update - import data.lib.exempt_container.is_exempt - - violation[{"msg": msg, "details": {}}] { - # spec.containers.privileged field is immutable. - not is_update(input.review) - - c := input_containers[_] - not is_exempt(c) - c.securityContext.privileged - msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext]) - } - - input_containers[c] { - c := input.review.object.spec.containers[_] - } - - input_containers[c] { - c := input.review.object.spec.initContainers[_] - } - - input_containers[c] { - c := input.review.object.spec.ephemeralContainers[_] - } - libs: - - | - package lib.exclude_update - - is_update(review) { - review.operation == "UPDATE" - } - - | - package lib.exempt_container - - is_exempt(container) { - exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", []) - img := container.image - exemption := exempt_images[_] - _matches_exemption(img, exemption) - } - - _matches_exemption(img, exemption) { - not endswith(exemption, "*") - exemption == img - } - - _matches_exemption(img, exemption) { - endswith(exemption, "*") - prefix := trim_suffix(exemption, "*") - startswith(img, prefix) - } + code: + - engine: K8sNativeValidation + source: + variables: + - name: containers + expression: 'has(variables.anyObject.spec.containers) ? variables.anyObject.spec.containers : []' + - name: initContainers + expression: 'has(variables.anyObject.spec.initContainers) ? variables.anyObject.spec.initContainers : []' + - name: ephemeralContainers + expression: 'has(variables.anyObject.spec.ephemeralContainers) ? variables.anyObject.spec.ephemeralContainers : []' + - name: exemptImagePrefixes + expression: | + !has(variables.params.exemptImages) ? [] : + variables.params.exemptImages.filter(image, image.endsWith("*")).map(image, string(image).replace("*", "")) + - name: exemptImageExplicit + expression: | + !has(variables.params.exemptImages) ? [] : + variables.params.exemptImages.filter(image, !image.endsWith("*")) + - name: exemptImages + expression: | + (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, + container.image in variables.exemptImageExplicit || + variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption))) + - name: badContainers + expression: | + (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, + !(container.image in variables.exemptImages) && + (has(container.securityContext) && has(container.securityContext.privileged) && container.securityContext.privileged == true) + ).map(container, "Privileged container is not allowed: " + container.name +", securityContext: " + container.securityContext) + validations: + - expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0' + messageExpression: 'variables.badContainers.join("\n")' + - engine: Rego + source: + rego: | + package k8spspprivileged + + import data.lib.exclude_update.is_update + import data.lib.exempt_container.is_exempt + + violation[{"msg": msg, "details": {}}] { + # spec.containers.privileged field is immutable. + not is_update(input.review) + + c := input_containers[_] + not is_exempt(c) + c.securityContext.privileged + msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext]) + } + + input_containers[c] { + c := input.review.object.spec.containers[_] + } + + input_containers[c] { + c := input.review.object.spec.initContainers[_] + } + + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + } + libs: + - | + package lib.exclude_update + + is_update(review) { + review.operation == "UPDATE" + } + - | + package lib.exempt_container + + is_exempt(container) { + exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", []) + img := container.image + exemption := exempt_images[_] + _matches_exemption(img, exemption) + } + + _matches_exemption(img, exemption) { + not endswith(exemption, "*") + exemption == img + } + + _matches_exemption(img, exemption) { + endswith(exemption, "*") + prefix := trim_suffix(exemption, "*") + startswith(img, prefix) + } ```