Skip to content

Commit

Permalink
chore: adding CEL for psp-proc-mount policy (#545)
Browse files Browse the repository at this point in the history
Signed-off-by: Jaydip Gabani <[email protected]>
  • Loading branch information
JaydipGabani authored Jun 5, 2024
1 parent ecf1d6b commit ba29c0d
Show file tree
Hide file tree
Showing 14 changed files with 609 additions and 177 deletions.
12 changes: 12 additions & 0 deletions artifacthub/library/pod-security-policy/proc-mount/1.1.0/README.md
Original file line number Diff line number Diff line change
@@ -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/.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: 1.1.0
name: k8spspprocmount
displayName: Proc Mount
createdAt: "2024-06-04T00:44:32Z"
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: e42186f9f55856caa0970db8069acb9df8d844bf4f969297389581f214931978
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.0/template.yaml
```
provider:
name: Gatekeeper Library
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
resources:
- template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPProcMount
metadata:
name: psp-proc-mount
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
procMount: Default
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-proc-mount-disallowed
labels:
app: nginx-proc-mount
spec:
ephemeralContainers:
- name: nginx
image: nginx
securityContext:
procMount: Unmasked #Default
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-proc-mount-disallowed
labels:
app: nginx-proc-mount
spec:
containers:
- name: nginx
image: nginx
securityContext:
procMount: Default
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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: 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
180 changes: 180 additions & 0 deletions artifacthub/library/pod-security-policy/proc-mount/1.1.0/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8spspprocmount
annotations:
metadata.gatekeeper.sh/title: "Proc Mount"
metadata.gatekeeper.sh/version: 1.1.0
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)))
- 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.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
}
input_containers[c] {
c := input.review.object.spec.initContainers[_]
c.securityContext.procMount
}
input_containers[c] {
c := input.review.object.spec.ephemeralContainers[_]
c.securityContext.procMount
}
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)
}
Loading

0 comments on commit ba29c0d

Please sign in to comment.