Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: adding CEL for psp-proc-mount policy #545

Merged
merged 2 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
Loading