Skip to content

Commit 3c86f25

Browse files
committed
adding cel for psp/volume and pss/selinux
Signed-off-by: Jaydip Gabani <[email protected]>
1 parent 192d220 commit 3c86f25

File tree

25 files changed

+988
-232
lines changed

25 files changed

+988
-232
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
version: 1.1.0
2+
name: k8spspselinuxv2
3+
displayName: SELinux V2
4+
createdAt: "2024-05-20T18:10:16Z"
5+
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
6+
digest: eba35fa7bc8ccf1110732549d7eeabaee17bc8d3f100aac7cba42913578285a2
7+
license: Apache-2.0
8+
homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/selinux
9+
keywords:
10+
- gatekeeper
11+
- open-policy-agent
12+
- policies
13+
readme: |-
14+
# SELinux V2
15+
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
16+
install: |-
17+
### Usage
18+
```shell
19+
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/selinux/1.1.0/template.yaml
20+
```
21+
provider:
22+
name: Gatekeeper Library
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
resources:
2+
- template.yaml
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: constraints.gatekeeper.sh/v1beta1
2+
kind: K8sPSPSELinuxV2
3+
metadata:
4+
name: psp-selinux-v2
5+
spec:
6+
match:
7+
kinds:
8+
- apiGroups: [""]
9+
kinds: ["Pod"]
10+
parameters:
11+
allowedSELinuxOptions:
12+
- level: s0:c123,c456
13+
role: object_r
14+
type: svirt_sandbox_file_t
15+
user: system_u
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: nginx-selinux-disallowed
5+
labels:
6+
app: nginx-selinux
7+
spec:
8+
ephemeralContainers:
9+
- name: nginx
10+
image: nginx
11+
securityContext:
12+
seLinuxOptions:
13+
level: s1:c234,c567
14+
user: sysadm_u
15+
role: sysadm_r
16+
type: svirt_lxc_net_t
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: nginx-selinux-allowed
5+
labels:
6+
app: nginx-selinux
7+
spec:
8+
containers:
9+
- name: nginx
10+
image: nginx
11+
securityContext:
12+
seLinuxOptions:
13+
level: s0:c123,c456
14+
role: object_r
15+
type: svirt_sandbox_file_t
16+
user: system_u
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: nginx-selinux-disallowed
5+
labels:
6+
app: nginx-selinux
7+
spec:
8+
containers:
9+
- name: nginx
10+
image: nginx
11+
securityContext:
12+
seLinuxOptions:
13+
level: s1:c234,c567
14+
user: sysadm_u
15+
role: sysadm_r
16+
type: svirt_lxc_net_t
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
kind: AdmissionReview
2+
apiVersion: admission.k8s.io/v1beta1
3+
request:
4+
operation: "UPDATE"
5+
object:
6+
apiVersion: v1
7+
kind: Pod
8+
metadata:
9+
name: nginx-selinux-disallowed
10+
labels:
11+
app: nginx-selinux
12+
spec:
13+
containers:
14+
- name: nginx
15+
image: nginx
16+
securityContext:
17+
seLinuxOptions:
18+
level: s1:c234,c567
19+
user: sysadm_u
20+
role: sysadm_r
21+
type: svirt_lxc_net_t
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
kind: Suite
2+
apiVersion: test.gatekeeper.sh/v1alpha1
3+
metadata:
4+
name: selinux
5+
tests:
6+
- name: require-matching-selinux-options
7+
template: template.yaml
8+
constraint: samples/psp-selinux-v2/constraint.yaml
9+
cases:
10+
- name: example-disallowed
11+
object: samples/psp-selinux-v2/example_disallowed.yaml
12+
assertions:
13+
- violations: yes
14+
- name: example-allowed
15+
object: samples/psp-selinux-v2/example_allowed.yaml
16+
assertions:
17+
- violations: no
18+
- name: disallowed-ephemeral
19+
object: samples/psp-selinux-v2/disallowed_ephemeral.yaml
20+
assertions:
21+
- violations: yes
22+
- name: update
23+
object: samples/psp-selinux-v2/update.yaml
24+
assertions:
25+
- violations: no
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
apiVersion: templates.gatekeeper.sh/v1
2+
kind: ConstraintTemplate
3+
metadata:
4+
name: k8spspselinuxv2
5+
annotations:
6+
metadata.gatekeeper.sh/title: "SELinux V2"
7+
metadata.gatekeeper.sh/version: 1.1.0
8+
description: >-
9+
Defines an allow-list of seLinuxOptions configurations for pod
10+
containers. Corresponds to a PodSecurityPolicy requiring SELinux configs.
11+
For more information, see
12+
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#selinux
13+
spec:
14+
crd:
15+
spec:
16+
names:
17+
kind: K8sPSPSELinuxV2
18+
validation:
19+
# Schema for the `parameters` field
20+
openAPIV3Schema:
21+
type: object
22+
description: >-
23+
Defines an allow-list of seLinuxOptions configurations for pod
24+
containers. Corresponds to a PodSecurityPolicy requiring SELinux configs.
25+
For more information, see
26+
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#selinux
27+
properties:
28+
exemptImages:
29+
description: >-
30+
Any container that uses an image that matches an entry in this list will be excluded
31+
from enforcement. Prefix-matching can be signified with `*`. For example: `my-image-*`.
32+
33+
It is recommended that users use the fully-qualified Docker image name (e.g. start with a domain name)
34+
in order to avoid unexpectedly exempting images from an untrusted repository.
35+
type: array
36+
items:
37+
type: string
38+
allowedSELinuxOptions:
39+
type: array
40+
description: "An allow-list of SELinux options configurations."
41+
items:
42+
type: object
43+
description: "An allowed configuration of SELinux options for a pod container."
44+
properties:
45+
level:
46+
type: string
47+
description: "An SELinux level."
48+
role:
49+
type: string
50+
description: "An SELinux role."
51+
type:
52+
type: string
53+
description: "An SELinux type."
54+
user:
55+
type: string
56+
description: "An SELinux user."
57+
targets:
58+
- target: admission.k8s.gatekeeper.sh
59+
code:
60+
- engine: K8sNativeValidation
61+
source:
62+
variables:
63+
- name: notViolatingSELinuxOptions
64+
expression: |
65+
has(object.spec.securityContext) && has(object.spec.securityContext.seLinuxOptions) ?
66+
(has(variables.params.allowedSELinuxOptions) ?
67+
(
68+
(has(variables.params.allowedSELinuxOptions.level) && has(object.spec.securityContext.seLinuxOptions.level) && (object.spec.securityContext.seLinuxOptions.level == variables.params.allowedSELinuxOptions.level)) &&
69+
(has(variables.params.allowedSELinuxOptions.role) && has(object.spec.securityContext.seLinuxOptions.role) && (object.spec.securityContext.seLinuxOptions.role == variables.params.allowedSELinuxOptions.role)) &&
70+
(has(variables.params.allowedSELinuxOptions.type) && has(object.spec.securityContext.seLinuxOptions.type) && (object.spec.securityContext.seLinuxOptions.type == variables.params.allowedSELinuxOptions.type)) &&
71+
(has(variables.params.allowedSELinuxOptions.user) && has(object.spec.securityContext.seLinuxOptions.user) && (object.spec.securityContext.seLinuxOptions.user == variables.params.allowedSELinuxOptions.user))
72+
) :
73+
(!has(object.spec.securityContext.seLinuxOptions.level) && !has(object.spec.securityContext.seLinuxOptions.role) && !has(object.spec.securityContext.seLinuxOptions.type) && !has(object.spec.securityContext.seLinuxOptions.user)))
74+
: true
75+
- name: containers
76+
expression: 'has(object.spec.containers) ? object.spec.containers.filter(c, has(c.securityContext) && has(c.securityContext.seLinuxOptions)) : []'
77+
- name: initContainers
78+
expression: 'has(object.spec.initContainers) ? object.spec.initContainers.filter(c, has(c.securityContext) && has(c.securityContext.seLinuxOptions)) : []'
79+
- name: ephemeralContainers
80+
expression: 'has(object.spec.ephemeralContainers) ? object.spec.ephemeralContainers.filter(c, has(c.securityContext) && has(c.securityContext.seLinuxOptions)) : []'
81+
- name: exemptImagePrefixes
82+
expression: |
83+
!has(variables.params.exemptImages) ? [] :
84+
variables.params.exemptImages.filter(image, image.endsWith("*")).map(image, string(image).replace("*", ""))
85+
- name: exemptImageExplicit
86+
expression: |
87+
!has(variables.params.exemptImages) ? [] :
88+
variables.params.exemptImages.filter(image, !image.endsWith("*"))
89+
- name: exemptImages
90+
expression: |
91+
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,
92+
container.image in variables.exemptImageExplicit ||
93+
variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption)))
94+
- name: badContainers
95+
expression: |
96+
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(c, !(c.image in variables.exemptImages) && (has(c.securityContext.seLinuxOptions) ? (
97+
has(variables.params.allowedSELinuxOptions) ?
98+
(
99+
!(has(variables.params.allowedSELinuxOptions.level) && has(c.securityContext.seLinuxOptions.level) && (c.securityContext.seLinuxOptions.level == variables.params.allowedSELinuxOptions.level)) ||
100+
!(has(variables.params.allowedSELinuxOptions.role) && has(c.securityContext.seLinuxOptions.role) && (c.securityContext.seLinuxOptions.role == variables.params.allowedSELinuxOptions.role)) ||
101+
!(has(variables.params.allowedSELinuxOptions.type) && has(c.securityContext.seLinuxOptions.type) && (c.securityContext.seLinuxOptions.type == variables.params.allowedSELinuxOptions.type)) ||
102+
!(has(variables.params.allowedSELinuxOptions.user) && has(c.securityContext.seLinuxOptions.user) && (c.securityContext.seLinuxOptions.user == variables.params.allowedSELinuxOptions.user))
103+
) : (has(c.securityContext.seLinuxOptions.level) || has(c.securityContext.seLinuxOptions.role) || has(c.securityContext.seLinuxOptions.type) || has(c.securityContext.seLinuxOptions.user))
104+
) : false
105+
))
106+
validations:
107+
- expression: '(has(request.operation) && request.operation == "UPDATE") || variables.notViolatingSELinuxOptions'
108+
messageExpression: '"SELinux options is not allowed, pod: " + object.metadata.name + ". Allowed options: " + variables.params.allowedSELinuxOptions'
109+
- expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0'
110+
messageExpression: '"SELinux options is not allowed, pod: " + object.metadata.name + ", container: " + variables.badContainers.map(c, c.name).join(", ")'
111+
- engine: Rego
112+
source:
113+
rego: |
114+
package k8spspselinux
115+
116+
import data.lib.exclude_update.is_update
117+
import data.lib.exempt_container.is_exempt
118+
119+
# Disallow top level custom SELinux options
120+
violation[{"msg": msg, "details": {}}] {
121+
# spec.securityContext.seLinuxOptions field is immutable.
122+
not is_update(input.review)
123+
124+
has_field(input.review.object.spec.securityContext, "seLinuxOptions")
125+
not input_seLinuxOptions_allowed(input.review.object.spec.securityContext.seLinuxOptions)
126+
msg := sprintf("SELinux options is not allowed, pod: %v. Allowed options: %v", [input.review.object.metadata.name, input.parameters.allowedSELinuxOptions])
127+
}
128+
# Disallow container level custom SELinux options
129+
violation[{"msg": msg, "details": {}}] {
130+
# spec.containers.securityContext.seLinuxOptions field is immutable.
131+
not is_update(input.review)
132+
133+
c := input_security_context[_]
134+
not is_exempt(c)
135+
has_field(c.securityContext, "seLinuxOptions")
136+
not input_seLinuxOptions_allowed(c.securityContext.seLinuxOptions)
137+
msg := sprintf("SELinux options is not allowed, pod: %v, container: %v. Allowed options: %v", [input.review.object.metadata.name, c.name, input.parameters.allowedSELinuxOptions])
138+
}
139+
140+
input_seLinuxOptions_allowed(options) {
141+
params := input.parameters.allowedSELinuxOptions[_]
142+
field_allowed("level", options, params)
143+
field_allowed("role", options, params)
144+
field_allowed("type", options, params)
145+
field_allowed("user", options, params)
146+
}
147+
148+
field_allowed(field, options, params) {
149+
params[field] == options[field]
150+
}
151+
field_allowed(field, options, _) {
152+
not has_field(options, field)
153+
}
154+
155+
input_security_context[c] {
156+
c := input.review.object.spec.containers[_]
157+
has_field(c.securityContext, "seLinuxOptions")
158+
}
159+
input_security_context[c] {
160+
c := input.review.object.spec.initContainers[_]
161+
has_field(c.securityContext, "seLinuxOptions")
162+
}
163+
input_security_context[c] {
164+
c := input.review.object.spec.ephemeralContainers[_]
165+
has_field(c.securityContext, "seLinuxOptions")
166+
}
167+
168+
# has_field returns whether an object has a field
169+
has_field(object, field) = true {
170+
object[field]
171+
}
172+
libs:
173+
- |
174+
package lib.exclude_update
175+
176+
is_update(review) {
177+
review.operation == "UPDATE"
178+
}
179+
- |
180+
package lib.exempt_container
181+
182+
is_exempt(container) {
183+
exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", [])
184+
img := container.image
185+
exemption := exempt_images[_]
186+
_matches_exemption(img, exemption)
187+
}
188+
189+
_matches_exemption(img, exemption) {
190+
not endswith(exemption, "*")
191+
exemption == img
192+
}
193+
194+
_matches_exemption(img, exemption) {
195+
endswith(exemption, "*")
196+
prefix := trim_suffix(exemption, "*")
197+
startswith(img, prefix)
198+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
version: 1.1.0
2+
name: k8spspvolumetypes
3+
displayName: Volume Types
4+
createdAt: "2024-05-14T00:55:44Z"
5+
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
6+
digest: f27f6759e3fc2969cddea91ad3b1b0d06554d63c6b9137e35e89a5e8dbd458d9
7+
license: Apache-2.0
8+
homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/volumes
9+
keywords:
10+
- gatekeeper
11+
- open-policy-agent
12+
- policies
13+
readme: |-
14+
# Volume Types
15+
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
16+
install: |-
17+
### Usage
18+
```shell
19+
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/artifacthub/library/pod-security-policy/volumes/1.1.0/template.yaml
20+
```
21+
provider:
22+
name: Gatekeeper Library

0 commit comments

Comments
 (0)