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/volume and psp/selinux #530

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
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,22 @@
version: 1.1.0
name: k8spspselinuxv2
displayName: SELinux V2
createdAt: "2024-05-20T18:10:16Z"
description: Defines an allow-list of seLinuxOptions configurations for pod containers. Corresponds to a PodSecurityPolicy requiring SELinux configs. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#selinux
digest: 15d870eec167f92a25cc85e9751bc2980d11457b3caab60d3ff2794a277c63c5
license: Apache-2.0
homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/selinux
keywords:
- gatekeeper
- open-policy-agent
- policies
readme: |-
# SELinux V2
Defines an allow-list of seLinuxOptions configurations for pod containers. Corresponds to a PodSecurityPolicy requiring SELinux configs. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#selinux
install: |-
### Usage
```shell
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/selinux/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,15 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPSELinuxV2
metadata:
name: psp-selinux-v2
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
allowedSELinuxOptions:
- level: s0:c123,c456
role: object_r
type: svirt_sandbox_file_t
user: system_u
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-selinux-disallowed
labels:
app: nginx-selinux
spec:
ephemeralContainers:
- name: nginx
image: nginx
securityContext:
seLinuxOptions:
level: s1:c234,c567
user: sysadm_u
role: sysadm_r
type: svirt_lxc_net_t
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-selinux-allowed
labels:
app: nginx-selinux
spec:
containers:
- name: nginx
image: nginx
securityContext:
seLinuxOptions:
level: s0:c123,c456
role: object_r
type: svirt_sandbox_file_t
user: system_u
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-selinux-allowed-without-selinux-opts
labels:
app: nginx-selinux
spec:
containers:
- name: nginx
image: nginx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-selinux-disallowed
labels:
app: nginx-selinux
spec:
containers:
- name: nginx
image: nginx
securityContext:
seLinuxOptions:
level: s1:c234,c567
user: sysadm_u
role: sysadm_r
type: svirt_lxc_net_t
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
kind: AdmissionReview
apiVersion: admission.k8s.io/v1beta1
request:
operation: "UPDATE"
object:
apiVersion: v1
kind: Pod
metadata:
name: nginx-selinux-disallowed
labels:
app: nginx-selinux
spec:
containers:
- name: nginx
image: nginx
securityContext:
seLinuxOptions:
level: s1:c234,c567
user: sysadm_u
role: sysadm_r
type: svirt_lxc_net_t
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
kind: Suite
apiVersion: test.gatekeeper.sh/v1alpha1
metadata:
name: selinux
tests:
- name: require-matching-selinux-options
template: template.yaml
constraint: samples/psp-selinux-v2/constraint.yaml
cases:
- name: example-disallowed
object: samples/psp-selinux-v2/example_disallowed.yaml
assertions:
- violations: yes
- name: example-allowed
object: samples/psp-selinux-v2/example_allowed.yaml
assertions:
- violations: no
- name: example-allowed-wihtout-selinux-opts
object: samples/psp-selinux-v2/example_allowed_without_selinux_opts.yaml
assertions:
- violations: no
- name: disallowed-ephemeral
object: samples/psp-selinux-v2/disallowed_ephemeral.yaml
assertions:
- violations: yes
- name: update
object: samples/psp-selinux-v2/update.yaml
assertions:
- violations: no
202 changes: 202 additions & 0 deletions artifacthub/library/pod-security-policy/selinux/1.1.0/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8spspselinuxv2
annotations:
metadata.gatekeeper.sh/title: "SELinux V2"
metadata.gatekeeper.sh/version: 1.1.0
description: >-
Defines an allow-list of seLinuxOptions configurations for pod
containers. Corresponds to a PodSecurityPolicy requiring SELinux configs.
For more information, see
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#selinux
spec:
crd:
spec:
names:
kind: K8sPSPSELinuxV2
validation:
# Schema for the `parameters` field
openAPIV3Schema:
type: object
description: >-
Defines an allow-list of seLinuxOptions configurations for pod
containers. Corresponds to a PodSecurityPolicy requiring SELinux configs.
For more information, see
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#selinux
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
allowedSELinuxOptions:
type: array
description: "An allow-list of SELinux options configurations."
items:
type: object
description: "An allowed configuration of SELinux options for a pod container."
properties:
level:
type: string
description: "An SELinux level."
role:
type: string
description: "An SELinux role."
type:
type: string
description: "An SELinux type."
user:
type: string
description: "An SELinux user."
targets:
- target: admission.k8s.gatekeeper.sh
code:
- engine: K8sNativeValidation
source:
variables:
- name: usingAllowedSELinuxOptions
expression: |
has(variables.anyObject.spec.securityContext) && has(variables.anyObject.spec.securityContext.seLinuxOptions) ?
(has(variables.params.allowedSELinuxOptions) ?
(
variables.params.allowedSELinuxOptions.all(opts,
(has(opts.level) ? has(variables.anyObject.spec.securityContext.seLinuxOptions.level) && (variables.anyObject.spec.securityContext.seLinuxOptions.level == opts.level) : !has(variables.anyObject.spec.securityContext.seLinuxOptions.level)) &&
(has(opts.role) ? has(variables.anyObject.spec.securityContext.seLinuxOptions.role) && (variables.anyObject.spec.securityContext.seLinuxOptions.role == opts.role) : !has(variables.anyObject.spec.securityContext.seLinuxOptions.role)) &&
(has(opts.type) ? has(variables.anyObject.spec.securityContext.seLinuxOptions.type) && (variables.anyObject.spec.securityContext.seLinuxOptions.type == opts.type) : !has(variables.anyObject.spec.securityContext.seLinuxOptions.type)) &&
(has(opts.user) ? has(variables.anyObject.spec.securityContext.seLinuxOptions.user) && (variables.anyObject.spec.securityContext.seLinuxOptions.user == opts.user) : !has(variables.anyObject.spec.securityContext.seLinuxOptions.user))
)
) :
true)
: true
- name: containers
expression: 'has(variables.anyObject.spec.containers) ? variables.anyObject.spec.containers.filter(c, has(c.securityContext) && has(c.securityContext.seLinuxOptions)) : []'
- name: initContainers
expression: 'has(variables.anyObject.spec.initContainers) ? variables.anyObject.spec.initContainers.filter(c, has(c.securityContext) && has(c.securityContext.seLinuxOptions)) : []'
- name: ephemeralContainers
expression: 'has(variables.anyObject.spec.ephemeralContainers) ? variables.anyObject.spec.ephemeralContainers.filter(c, has(c.securityContext) && has(c.securityContext.seLinuxOptions)) : []'
- 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(c, !(c.image in variables.exemptImages) && (has(c.securityContext.seLinuxOptions) ? (
has(variables.params.allowedSELinuxOptions) ?
(
!variables.params.allowedSELinuxOptions.all(opts,
(has(opts.level) ? has(c.securityContext.seLinuxOptions.level) && (c.securityContext.seLinuxOptions.level == opts.level) : !has(c.securityContext.seLinuxOptions.level)) &&
(has(opts.role) ? has(c.securityContext.seLinuxOptions.role) && (c.securityContext.seLinuxOptions.role == opts.role) : !has(c.securityContext.seLinuxOptions.role)) &&
(has(opts.type) ? has(c.securityContext.seLinuxOptions.type) && (c.securityContext.seLinuxOptions.type == opts.type) : !has(c.securityContext.seLinuxOptions.type)) &&
(has(opts.user) ? has(c.securityContext.seLinuxOptions.user) && (c.securityContext.seLinuxOptions.user == opts.user) : !has(c.securityContext.seLinuxOptions.user))
)
) : false)
: false)
)
validations:
- expression: '(has(request.operation) && request.operation == "UPDATE") || variables.usingAllowedSELinuxOptions'
messageExpression: '"SELinux options is not allowed, pod: " + variables.anyObject.metadata.name + ". Allowed options: [" + variables.params.allowedSELinuxOptions.map(opts, "{ level: " + opts.level + ", role: " + opts.role + ", type: " + opts.type + ", user: " + opts.user + "}").join(", ") + "]"'
- expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0'
messageExpression: '"SELinux options is not allowed, pod: " + variables.anyObject.metadata.name + ", container: " + variables.badContainers.map(c, c.name).join(", ") + ". Allowed options: [" + variables.params.allowedSELinuxOptions.map(opts, "{ level: " + opts.level + ", role: " + opts.role + ", type: " + opts.type + ", user: " + opts.user + "}").join(", ") + "]"'
- engine: Rego
source:
rego: |
package k8spspselinux

import data.lib.exclude_update.is_update
import data.lib.exempt_container.is_exempt

# Disallow top level custom SELinux options
violation[{"msg": msg, "details": {}}] {
# spec.securityContext.seLinuxOptions field is immutable.
not is_update(input.review)

has_field(input.review.object.spec.securityContext, "seLinuxOptions")
not input_seLinuxOptions_allowed(input.review.object.spec.securityContext.seLinuxOptions)
msg := sprintf("SELinux options is not allowed, pod: %v. Allowed options: %v", [input.review.object.metadata.name, input.parameters.allowedSELinuxOptions])
}
# Disallow container level custom SELinux options
violation[{"msg": msg, "details": {}}] {
# spec.containers.securityContext.seLinuxOptions field is immutable.
not is_update(input.review)

c := input_security_context[_]
not is_exempt(c)
has_field(c.securityContext, "seLinuxOptions")
not input_seLinuxOptions_allowed(c.securityContext.seLinuxOptions)
msg := sprintf("SELinux options is not allowed, pod: %v, container: %v. Allowed options: %v", [input.review.object.metadata.name, c.name, input.parameters.allowedSELinuxOptions])
}

input_seLinuxOptions_allowed(options) {
params := input.parameters.allowedSELinuxOptions[_]
field_allowed("level", options, params)
field_allowed("role", options, params)
field_allowed("type", options, params)
field_allowed("user", options, params)
}

field_allowed(field, options, params) {
params[field] == options[field]
}
field_allowed(field, options, _) {
not has_field(options, field)
}

input_security_context[c] {
c := input.review.object.spec.containers[_]
has_field(c.securityContext, "seLinuxOptions")
}
input_security_context[c] {
c := input.review.object.spec.initContainers[_]
has_field(c.securityContext, "seLinuxOptions")
}
input_security_context[c] {
c := input.review.object.spec.ephemeralContainers[_]
has_field(c.securityContext, "seLinuxOptions")
}

# has_field returns whether an object has a field
has_field(object, field) = true {
object[field]
}
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,22 @@
version: 1.1.0
name: k8spspvolumetypes
displayName: Volume Types
createdAt: "2024-05-14T00:55:44Z"
description: Restricts mountable volume types to those specified by the user. Corresponds to the `volumes` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#volumes-and-file-systems
digest: cf81297bf562f15dc11e9c56a6e78c7e0345934cea0ae6285735d9bc66a366f1
license: Apache-2.0
homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/volumes
keywords:
- gatekeeper
- open-policy-agent
- policies
readme: |-
# Volume Types
Restricts mountable volume types to those specified by the user. Corresponds to the `volumes` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#volumes-and-file-systems
install: |-
### Usage
```shell
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/volumes/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
Loading
Loading