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

fix: procmount should allow empty values #621

Merged
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.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
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,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/*"
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
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,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
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions library/pod-security-policy/proc-mount/suite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading
Loading