diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/README.md b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/README.md new file mode 100644 index 000000000..9e45b7207 --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/README.md @@ -0,0 +1,12 @@ +# ProcMount security context policy + +`procMount` denotes the type of proc mount to use for the containers. The default is `DefaultProcMount` which uses the container runtime defaults for readonly paths and masked paths. + +Types of proc mount are: + +- `DefaultProcMount` uses the container runtime default ProcType. Most container runtimes mask certain paths in /proc to avoid accidental security exposure of special devices or information. + +- `UnmaskedProcMount` bypasses the default masking behavior of the container runtime and ensures the newly created /proc the container stays in tact with no modifications. + +This requires the `ProcMountType` feature flag to be enabled. Set `--feature-gates=ProcMountType=true` in Kubernetes API Server to be able to use `Unmasked` procMount type (requires v1.12 and above). For more information, see +https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/#options and https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/. diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/artifacthub-pkg.yml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/artifacthub-pkg.yml new file mode 100644 index 000000000..2f7148320 --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/artifacthub-pkg.yml @@ -0,0 +1,22 @@ +version: 1.1.2 +name: k8spspprocmount +displayName: Proc Mount +createdAt: "2025-01-10T16:19:17Z" +description: Controls the allowed `procMount` types for the container. Corresponds to the `allowedProcMountTypes` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#allowedprocmounttypes +digest: bdec708081728bb3d1e48168ee36f4112b7fec284cb0482843cad793fff38b4f +license: Apache-2.0 +homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/proc-mount +keywords: + - gatekeeper + - open-policy-agent + - policies +readme: |- + # Proc Mount + Controls the allowed `procMount` types for the container. Corresponds to the `allowedProcMountTypes` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#allowedprocmounttypes +install: |- + ### Usage + ```shell + kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/proc-mount/1.1.2/template.yaml + ``` +provider: + name: Gatekeeper Library diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/kustomization.yaml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/kustomization.yaml new file mode 100644 index 000000000..7d70d11b7 --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - template.yaml diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/constraint.yaml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/constraint.yaml new file mode 100644 index 000000000..79ad221ab --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/constraint.yaml @@ -0,0 +1,13 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sPSPProcMount +metadata: + name: psp-proc-mount +spec: + match: + kinds: + - apiGroups: [""] + kinds: ["Pod"] + parameters: + procMount: Default + exemptImages: + - "safeimages.com/*" diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/disallowed_ephemeral.yaml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/disallowed_ephemeral.yaml new file mode 100644 index 000000000..74e034148 --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/disallowed_ephemeral.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-proc-mount-disallowed + labels: + app: nginx-proc-mount +spec: + hostUsers: false + ephemeralContainers: + - name: nginx + image: nginx + securityContext: + procMount: Unmasked #Default diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_allowed.yaml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_allowed.yaml new file mode 100644 index 000000000..cc272bafb --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_allowed.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-proc-mount-allowed + labels: + app: nginx-proc-mount +spec: + hostUsers: false + containers: + - name: nginx + image: nginx + securityContext: + procMount: Default diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_allowed_exempt_image.yaml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_allowed_exempt_image.yaml new file mode 100644 index 000000000..c197740a4 --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_allowed_exempt_image.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-proc-mount-exempt-image + labels: + app: nginx-proc-mount +spec: + hostUsers: false + containers: + - name: nginx + image: safeimages.com/nginx + securityContext: + procMount: Unmasked #Default diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_allowed_missing.yaml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_allowed_missing.yaml new file mode 100644 index 000000000..cf5f27386 --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_allowed_missing.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-proc-mount-disallowed + labels: + app: nginx-proc-mount +spec: + hostUsers: false + containers: + - name: no-proc-mount-value + image: nginx + securityContext: + procMount: null + - name: no-proc-mount + image: nginx + securityContext: {} + - name: no-context + image: nginx diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_disallowed.yaml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_disallowed.yaml new file mode 100644 index 000000000..f0c3b030a --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/example_disallowed.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-proc-mount-disallowed + labels: + app: nginx-proc-mount +spec: + hostUsers: false + containers: + - name: nginx + image: nginx + securityContext: + procMount: Unmasked #Default diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/update.yaml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/update.yaml new file mode 100644 index 000000000..dc21b1142 --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/samples/psp-proc-mount/update.yaml @@ -0,0 +1,17 @@ +kind: AdmissionReview +apiVersion: admission.k8s.io/v1beta1 +request: + operation: "UPDATE" + object: + apiVersion: v1 + kind: Pod + metadata: + name: nginx-proc-mount-disallowed + labels: + app: nginx-proc-mount + spec: + containers: + - name: nginx + image: nginx + securityContext: + procMount: Unmasked #Default diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/suite.yaml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/suite.yaml new file mode 100644 index 000000000..066655c04 --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/suite.yaml @@ -0,0 +1,33 @@ +kind: Suite +apiVersion: test.gatekeeper.sh/v1alpha1 +metadata: + name: proc-mount +tests: +- name: default-proc-mount-required + template: template.yaml + constraint: samples/psp-proc-mount/constraint.yaml + cases: + - name: example-disallowed + object: samples/psp-proc-mount/example_disallowed.yaml + assertions: + - violations: yes + - name: example-allowed + object: samples/psp-proc-mount/example_allowed.yaml + assertions: + - violations: no + - name: example-allowed-missing + object: samples/psp-proc-mount/example_allowed_missing.yaml + assertions: + - violations: no + - name: disallowed-ephemeral + object: samples/psp-proc-mount/disallowed_ephemeral.yaml + assertions: + - violations: yes + - name: update + object: samples/psp-proc-mount/update.yaml + assertions: + - violations: no + - name: image-exempt-prefix-match + object: samples/psp-proc-mount/example_allowed_exempt_image.yaml + assertions: + - violations: no diff --git a/artifacthub/library/pod-security-policy/proc-mount/1.1.2/template.yaml b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/template.yaml new file mode 100644 index 000000000..ce5015371 --- /dev/null +++ b/artifacthub/library/pod-security-policy/proc-mount/1.1.2/template.yaml @@ -0,0 +1,185 @@ +apiVersion: templates.gatekeeper.sh/v1 +kind: ConstraintTemplate +metadata: + name: k8spspprocmount + annotations: + metadata.gatekeeper.sh/title: "Proc Mount" + metadata.gatekeeper.sh/version: 1.1.2 + description: >- + Controls the allowed `procMount` types for the container. Corresponds to + the `allowedProcMountTypes` field in a PodSecurityPolicy. For more + information, see + https://kubernetes.io/docs/concepts/policy/pod-security-policy/#allowedprocmounttypes +spec: + crd: + spec: + names: + kind: K8sPSPProcMount + validation: + # Schema for the `parameters` field + openAPIV3Schema: + type: object + description: >- + Controls the allowed `procMount` types for the container. Corresponds to + the `allowedProcMountTypes` field in a PodSecurityPolicy. For more + information, see + https://kubernetes.io/docs/concepts/policy/pod-security-policy/#allowedprocmounttypes + 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 + procMount: + type: string + description: >- + Defines the strategy for the security exposure of certain paths + in `/proc` by the container runtime. Setting to `Default` uses + the runtime defaults, where `Unmasked` bypasses the default + behavior. + enum: + - Default + - Unmasked + 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) + ) + ).map(container, container.image) + - name: allowedProcMount + expression: | + !has(variables.params) ? "default" : + !has(variables.params.procMount) ? "default" : + (variables.params.procMount.lowerAscii() == "default" || variables.params.procMount.lowerAscii() == "unmasked") ? variables.params.procMount.lowerAscii() : "default" + - name: badContainers + expression: | + (variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container, + !(container.image in variables.exemptImages) && + !( + (variables.allowedProcMount == "unmasked") || + (variables.allowedProcMount == "default" && (!has(container.securityContext) || !has(container.securityContext.procMount) || container.securityContext.procMount == null || container.securityContext.procMount.lowerAscii() == "default")) + ) + ).map(container, "ProcMount type is not allowed, container: " + container.name +". Allowed procMount types: " + variables.allowedProcMount) + validations: + - expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0' + messageExpression: 'variables.badContainers.join("\n")' + - engine: Rego + source: + rego: | + package k8spspprocmount + + import data.lib.exclude_update.is_update + import data.lib.exempt_container.is_exempt + + violation[{"msg": msg, "details": {}}] { + # spec.containers.securityContext.procMount field is immutable. + not is_update(input.review) + + c := input_containers[_] + not is_exempt(c) + allowedProcMount := get_allowed_proc_mount(input) + not input_proc_mount_type_allowed(allowedProcMount, c) + msg := sprintf("ProcMount type is not allowed, container: %v. Allowed procMount types: %v", [c.name, allowedProcMount]) + } + + input_proc_mount_type_allowed(allowedProcMount, c) { + allowedProcMount == "default" + lower(c.securityContext.procMount) == "default" + } + input_proc_mount_type_allowed(allowedProcMount, _) { + allowedProcMount == "unmasked" + } + + input_containers[c] { + c := input.review.object.spec.containers[_] + c.securityContext.procMount != null + } + input_containers[c] { + c := input.review.object.spec.initContainers[_] + c.securityContext.procMount != null + } + input_containers[c] { + c := input.review.object.spec.ephemeralContainers[_] + c.securityContext.procMount != null + } + + get_allowed_proc_mount(arg) = out { + not arg.parameters + out = "default" + } + get_allowed_proc_mount(arg) = out { + not arg.parameters.procMount + out = "default" + } + get_allowed_proc_mount(arg) = out { + arg.parameters.procMount + not valid_proc_mount(arg.parameters.procMount) + out = "default" + } + get_allowed_proc_mount(arg) = out { + valid_proc_mount(arg.parameters.procMount) + out = lower(arg.parameters.procMount) + } + + valid_proc_mount(str) { + lower(str) == "default" + } + valid_proc_mount(str) { + lower(str) == "unmasked" + } + 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/proc-mount/samples/psp-proc-mount/example_allowed_missing.yaml b/library/pod-security-policy/proc-mount/samples/psp-proc-mount/example_allowed_missing.yaml new file mode 100644 index 000000000..cf5f27386 --- /dev/null +++ b/library/pod-security-policy/proc-mount/samples/psp-proc-mount/example_allowed_missing.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nginx-proc-mount-disallowed + labels: + app: nginx-proc-mount +spec: + hostUsers: false + containers: + - name: no-proc-mount-value + image: nginx + securityContext: + procMount: null + - name: no-proc-mount + image: nginx + securityContext: {} + - name: no-context + image: nginx diff --git a/library/pod-security-policy/proc-mount/suite.yaml b/library/pod-security-policy/proc-mount/suite.yaml index 282246b5a..066655c04 100644 --- a/library/pod-security-policy/proc-mount/suite.yaml +++ b/library/pod-security-policy/proc-mount/suite.yaml @@ -15,6 +15,10 @@ tests: object: samples/psp-proc-mount/example_allowed.yaml assertions: - violations: no + - name: example-allowed-missing + object: samples/psp-proc-mount/example_allowed_missing.yaml + assertions: + - violations: no - name: disallowed-ephemeral object: samples/psp-proc-mount/disallowed_ephemeral.yaml assertions: diff --git a/library/pod-security-policy/proc-mount/template.yaml b/library/pod-security-policy/proc-mount/template.yaml index ed32cb383..ce5015371 100644 --- a/library/pod-security-policy/proc-mount/template.yaml +++ b/library/pod-security-policy/proc-mount/template.yaml @@ -4,7 +4,7 @@ metadata: name: k8spspprocmount annotations: metadata.gatekeeper.sh/title: "Proc Mount" - metadata.gatekeeper.sh/version: 1.1.1 + metadata.gatekeeper.sh/version: 1.1.2 description: >- Controls the allowed `procMount` types for the container. Corresponds to the `allowedProcMountTypes` field in a PodSecurityPolicy. For more @@ -86,7 +86,7 @@ spec: !(container.image in variables.exemptImages) && !( (variables.allowedProcMount == "unmasked") || - (variables.allowedProcMount == "default" && has(container.securityContext) && has(container.securityContext.procMount) && container.securityContext.procMount.lowerAscii() == "default") + (variables.allowedProcMount == "default" && (!has(container.securityContext) || !has(container.securityContext.procMount) || container.securityContext.procMount == null || container.securityContext.procMount.lowerAscii() == "default")) ) ).map(container, "ProcMount type is not allowed, container: " + container.name +". Allowed procMount types: " + variables.allowedProcMount) validations: @@ -121,15 +121,15 @@ spec: input_containers[c] { c := input.review.object.spec.containers[_] - c.securityContext.procMount + c.securityContext.procMount != null } input_containers[c] { c := input.review.object.spec.initContainers[_] - c.securityContext.procMount + c.securityContext.procMount != null } input_containers[c] { c := input.review.object.spec.ephemeralContainers[_] - c.securityContext.procMount + c.securityContext.procMount != null } get_allowed_proc_mount(arg) = out { diff --git a/src/pod-security-policy/proc-mount/constraint.tmpl b/src/pod-security-policy/proc-mount/constraint.tmpl index 0a43cd845..a81ae2e41 100644 --- a/src/pod-security-policy/proc-mount/constraint.tmpl +++ b/src/pod-security-policy/proc-mount/constraint.tmpl @@ -4,7 +4,7 @@ metadata: name: k8spspprocmount annotations: metadata.gatekeeper.sh/title: "Proc Mount" - metadata.gatekeeper.sh/version: 1.1.1 + metadata.gatekeeper.sh/version: 1.1.2 description: >- Controls the allowed `procMount` types for the container. Corresponds to the `allowedProcMountTypes` field in a PodSecurityPolicy. For more diff --git a/src/pod-security-policy/proc-mount/src.cel b/src/pod-security-policy/proc-mount/src.cel index df4331028..bd042f23a 100644 --- a/src/pod-security-policy/proc-mount/src.cel +++ b/src/pod-security-policy/proc-mount/src.cel @@ -34,7 +34,7 @@ variables: !(container.image in variables.exemptImages) && !( (variables.allowedProcMount == "unmasked") || - (variables.allowedProcMount == "default" && has(container.securityContext) && has(container.securityContext.procMount) && container.securityContext.procMount.lowerAscii() == "default") + (variables.allowedProcMount == "default" && (!has(container.securityContext) || !has(container.securityContext.procMount) || container.securityContext.procMount == null || container.securityContext.procMount.lowerAscii() == "default")) ) ).map(container, "ProcMount type is not allowed, container: " + container.name +". Allowed procMount types: " + variables.allowedProcMount) validations: diff --git a/src/pod-security-policy/proc-mount/src.rego b/src/pod-security-policy/proc-mount/src.rego index 222b5caca..8bf4a39b8 100644 --- a/src/pod-security-policy/proc-mount/src.rego +++ b/src/pod-security-policy/proc-mount/src.rego @@ -24,15 +24,15 @@ input_proc_mount_type_allowed(allowedProcMount, _) { input_containers[c] { c := input.review.object.spec.containers[_] - c.securityContext.procMount + c.securityContext.procMount != null } input_containers[c] { c := input.review.object.spec.initContainers[_] - c.securityContext.procMount + c.securityContext.procMount != null } input_containers[c] { c := input.review.object.spec.ephemeralContainers[_] - c.securityContext.procMount + c.securityContext.procMount != null } get_allowed_proc_mount(arg) = out { diff --git a/website/docs/validation/proc-mount.md b/website/docs/validation/proc-mount.md index 635e0c57e..d910c8fb8 100644 --- a/website/docs/validation/proc-mount.md +++ b/website/docs/validation/proc-mount.md @@ -16,7 +16,7 @@ metadata: name: k8spspprocmount annotations: metadata.gatekeeper.sh/title: "Proc Mount" - metadata.gatekeeper.sh/version: 1.1.1 + metadata.gatekeeper.sh/version: 1.1.2 description: >- Controls the allowed `procMount` types for the container. Corresponds to the `allowedProcMountTypes` field in a PodSecurityPolicy. For more @@ -98,7 +98,7 @@ spec: !(container.image in variables.exemptImages) && !( (variables.allowedProcMount == "unmasked") || - (variables.allowedProcMount == "default" && has(container.securityContext) && has(container.securityContext.procMount) && container.securityContext.procMount.lowerAscii() == "default") + (variables.allowedProcMount == "default" && (!has(container.securityContext) || !has(container.securityContext.procMount) || container.securityContext.procMount == null || container.securityContext.procMount.lowerAscii() == "default")) ) ).map(container, "ProcMount type is not allowed, container: " + container.name +". Allowed procMount types: " + variables.allowedProcMount) validations: @@ -133,15 +133,15 @@ spec: input_containers[c] { c := input.review.object.spec.containers[_] - c.securityContext.procMount + c.securityContext.procMount != null } input_containers[c] { c := input.review.object.spec.initContainers[_] - c.securityContext.procMount + c.securityContext.procMount != null } input_containers[c] { c := input.review.object.spec.ephemeralContainers[_] - c.securityContext.procMount + c.securityContext.procMount != null } get_allowed_proc_mount(arg) = out { @@ -287,6 +287,38 @@ Usage kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/proc-mount/samples/psp-proc-mount/example_allowed.yaml ``` + +
+example-allowed-missing + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: nginx-proc-mount-disallowed + labels: + app: nginx-proc-mount +spec: + hostUsers: false + containers: + - name: no-proc-mount-value + image: nginx + securityContext: + procMount: null + - name: no-proc-mount + image: nginx + securityContext: {} + - name: no-context + image: nginx + +``` + +Usage + +```shell +kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/proc-mount/samples/psp-proc-mount/example_allowed_missing.yaml +``` +
disallowed-ephemeral