Skip to content

Commit 4ad966c

Browse files
authored
chore: adding cel for psp-privileged-containers (#543)
Fixes #541 Signed-off-by: Jaydip Gabani <gabanijaydip@gmail.com>
1 parent ba29c0d commit 4ad966c

File tree

14 files changed

+457
-110
lines changed

14 files changed

+457
-110
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: k8spspprivilegedcontainer
3+
displayName: Privileged Container
4+
createdAt: "2024-06-03T23:35:27Z"
5+
description: Controls the ability of any container to enable privileged mode. Corresponds to the `privileged` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privileged
6+
digest: 967e9d9abf48b1686b497dd3e2f3a870b9f5b51c5ab1d837ea0ff680fbfaa7c6
7+
license: Apache-2.0
8+
homeURL: https://open-policy-agent.github.io/gatekeeper-library/website/privileged-containers
9+
keywords:
10+
- gatekeeper
11+
- open-policy-agent
12+
- policies
13+
readme: |-
14+
# Privileged Container
15+
Controls the ability of any container to enable privileged mode. Corresponds to the `privileged` field in a PodSecurityPolicy. For more information, see https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privileged
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/privileged-containers/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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: constraints.gatekeeper.sh/v1beta1
2+
kind: K8sPSPPrivilegedContainer
3+
metadata:
4+
name: psp-privileged-container
5+
spec:
6+
match:
7+
kinds:
8+
- apiGroups: [""]
9+
kinds: ["Pod"]
10+
excludedNamespaces: ["kube-system"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: nginx-privileged-disallowed
5+
labels:
6+
app: nginx-privileged
7+
spec:
8+
ephemeralContainers:
9+
- name: nginx
10+
image: nginx
11+
securityContext:
12+
privileged: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: nginx-privileged-allowed
5+
labels:
6+
app: nginx-privileged
7+
spec:
8+
containers:
9+
- name: nginx
10+
image: nginx
11+
securityContext:
12+
privileged: false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: nginx-privileged-disallowed
5+
labels:
6+
app: nginx-privileged
7+
spec:
8+
containers:
9+
- name: nginx
10+
image: nginx
11+
securityContext:
12+
privileged: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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-privileged-disallowed
10+
labels:
11+
app: nginx-privileged
12+
spec:
13+
containers:
14+
- name: nginx
15+
image: nginx
16+
securityContext:
17+
privileged: true
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: privileged-containers
5+
tests:
6+
- name: privileged-containers-disallowed
7+
template: template.yaml
8+
constraint: samples/psp-privileged-container/constraint.yaml
9+
cases:
10+
- name: example-disallowed
11+
object: samples/psp-privileged-container/example_disallowed.yaml
12+
assertions:
13+
- violations: yes
14+
- name: example-allowed
15+
object: samples/psp-privileged-container/example_allowed.yaml
16+
assertions:
17+
- violations: no
18+
- name: disallowed-ephemeral
19+
object: samples/psp-privileged-container/disallowed_ephemeral.yaml
20+
assertions:
21+
- violations: yes
22+
- name: update
23+
object: samples/psp-privileged-container/update.yaml
24+
assertions:
25+
- violations: no
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
apiVersion: templates.gatekeeper.sh/v1
2+
kind: ConstraintTemplate
3+
metadata:
4+
name: k8spspprivilegedcontainer
5+
annotations:
6+
metadata.gatekeeper.sh/title: "Privileged Container"
7+
metadata.gatekeeper.sh/version: 1.1.0
8+
description: >-
9+
Controls the ability of any container to enable privileged mode.
10+
Corresponds to the `privileged` field in a PodSecurityPolicy. For more
11+
information, see
12+
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privileged
13+
spec:
14+
crd:
15+
spec:
16+
names:
17+
kind: K8sPSPPrivilegedContainer
18+
validation:
19+
openAPIV3Schema:
20+
type: object
21+
description: >-
22+
Controls the ability of any container to enable privileged mode.
23+
Corresponds to the `privileged` field in a PodSecurityPolicy. For more
24+
information, see
25+
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#privileged
26+
properties:
27+
exemptImages:
28+
description: >-
29+
Any container that uses an image that matches an entry in this list will be excluded
30+
from enforcement. Prefix-matching can be signified with `*`. For example: `my-image-*`.
31+
32+
It is recommended that users use the fully-qualified Docker image name (e.g. start with a domain name)
33+
in order to avoid unexpectedly exempting images from an untrusted repository.
34+
type: array
35+
items:
36+
type: string
37+
targets:
38+
- target: admission.k8s.gatekeeper.sh
39+
code:
40+
- engine: K8sNativeValidation
41+
source:
42+
variables:
43+
- name: containers
44+
expression: 'has(variables.anyObject.spec.containers) ? variables.anyObject.spec.containers : []'
45+
- name: initContainers
46+
expression: 'has(variables.anyObject.spec.initContainers) ? variables.anyObject.spec.initContainers : []'
47+
- name: ephemeralContainers
48+
expression: 'has(variables.anyObject.spec.ephemeralContainers) ? variables.anyObject.spec.ephemeralContainers : []'
49+
- name: exemptImagePrefixes
50+
expression: |
51+
!has(variables.params.exemptImages) ? [] :
52+
variables.params.exemptImages.filter(image, image.endsWith("*")).map(image, string(image).replace("*", ""))
53+
- name: exemptImageExplicit
54+
expression: |
55+
!has(variables.params.exemptImages) ? [] :
56+
variables.params.exemptImages.filter(image, !image.endsWith("*"))
57+
- name: exemptImages
58+
expression: |
59+
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,
60+
container.image in variables.exemptImageExplicit ||
61+
variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption)))
62+
- name: badContainers
63+
expression: |
64+
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,
65+
!(container.image in variables.exemptImages) &&
66+
(has(container.securityContext) && has(container.securityContext.privileged) && container.securityContext.privileged == true)
67+
).map(container, "Privileged container is not allowed: " + container.name +", securityContext: " + container.securityContext)
68+
validations:
69+
- expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0'
70+
messageExpression: 'variables.badContainers.join("\n")'
71+
- engine: Rego
72+
source:
73+
rego: |
74+
package k8spspprivileged
75+
76+
import data.lib.exclude_update.is_update
77+
import data.lib.exempt_container.is_exempt
78+
79+
violation[{"msg": msg, "details": {}}] {
80+
# spec.containers.privileged field is immutable.
81+
not is_update(input.review)
82+
83+
c := input_containers[_]
84+
not is_exempt(c)
85+
c.securityContext.privileged
86+
msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
87+
}
88+
89+
input_containers[c] {
90+
c := input.review.object.spec.containers[_]
91+
}
92+
93+
input_containers[c] {
94+
c := input.review.object.spec.initContainers[_]
95+
}
96+
97+
input_containers[c] {
98+
c := input.review.object.spec.ephemeralContainers[_]
99+
}
100+
libs:
101+
- |
102+
package lib.exclude_update
103+
104+
is_update(review) {
105+
review.operation == "UPDATE"
106+
}
107+
- |
108+
package lib.exempt_container
109+
110+
is_exempt(container) {
111+
exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", [])
112+
img := container.image
113+
exemption := exempt_images[_]
114+
_matches_exemption(img, exemption)
115+
}
116+
117+
_matches_exemption(img, exemption) {
118+
not endswith(exemption, "*")
119+
exemption == img
120+
}
121+
122+
_matches_exemption(img, exemption) {
123+
endswith(exemption, "*")
124+
prefix := trim_suffix(exemption, "*")
125+
startswith(img, prefix)
126+
}

library/pod-security-policy/privileged-containers/template.yaml

Lines changed: 79 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ metadata:
44
name: k8spspprivilegedcontainer
55
annotations:
66
metadata.gatekeeper.sh/title: "Privileged Container"
7-
metadata.gatekeeper.sh/version: 1.0.1
7+
metadata.gatekeeper.sh/version: 1.1.0
88
description: >-
99
Controls the ability of any container to enable privileged mode.
1010
Corresponds to the `privileged` field in a PodSecurityPolicy. For more
@@ -36,57 +36,91 @@ spec:
3636
type: string
3737
targets:
3838
- target: admission.k8s.gatekeeper.sh
39-
rego: |
40-
package k8spspprivileged
39+
code:
40+
- engine: K8sNativeValidation
41+
source:
42+
variables:
43+
- name: containers
44+
expression: 'has(variables.anyObject.spec.containers) ? variables.anyObject.spec.containers : []'
45+
- name: initContainers
46+
expression: 'has(variables.anyObject.spec.initContainers) ? variables.anyObject.spec.initContainers : []'
47+
- name: ephemeralContainers
48+
expression: 'has(variables.anyObject.spec.ephemeralContainers) ? variables.anyObject.spec.ephemeralContainers : []'
49+
- name: exemptImagePrefixes
50+
expression: |
51+
!has(variables.params.exemptImages) ? [] :
52+
variables.params.exemptImages.filter(image, image.endsWith("*")).map(image, string(image).replace("*", ""))
53+
- name: exemptImageExplicit
54+
expression: |
55+
!has(variables.params.exemptImages) ? [] :
56+
variables.params.exemptImages.filter(image, !image.endsWith("*"))
57+
- name: exemptImages
58+
expression: |
59+
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,
60+
container.image in variables.exemptImageExplicit ||
61+
variables.exemptImagePrefixes.exists(exemption, string(container.image).startsWith(exemption)))
62+
- name: badContainers
63+
expression: |
64+
(variables.containers + variables.initContainers + variables.ephemeralContainers).filter(container,
65+
!(container.image in variables.exemptImages) &&
66+
(has(container.securityContext) && has(container.securityContext.privileged) && container.securityContext.privileged == true)
67+
).map(container, "Privileged container is not allowed: " + container.name +", securityContext: " + container.securityContext)
68+
validations:
69+
- expression: '(has(request.operation) && request.operation == "UPDATE") || size(variables.badContainers) == 0'
70+
messageExpression: 'variables.badContainers.join("\n")'
71+
- engine: Rego
72+
source:
73+
rego: |
74+
package k8spspprivileged
4175
42-
import data.lib.exclude_update.is_update
43-
import data.lib.exempt_container.is_exempt
76+
import data.lib.exclude_update.is_update
77+
import data.lib.exempt_container.is_exempt
4478
45-
violation[{"msg": msg, "details": {}}] {
46-
# spec.containers.privileged field is immutable.
47-
not is_update(input.review)
79+
violation[{"msg": msg, "details": {}}] {
80+
# spec.containers.privileged field is immutable.
81+
not is_update(input.review)
4882
49-
c := input_containers[_]
50-
not is_exempt(c)
51-
c.securityContext.privileged
52-
msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
53-
}
83+
c := input_containers[_]
84+
not is_exempt(c)
85+
c.securityContext.privileged
86+
msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
87+
}
5488
55-
input_containers[c] {
56-
c := input.review.object.spec.containers[_]
57-
}
89+
input_containers[c] {
90+
c := input.review.object.spec.containers[_]
91+
}
5892
59-
input_containers[c] {
60-
c := input.review.object.spec.initContainers[_]
61-
}
93+
input_containers[c] {
94+
c := input.review.object.spec.initContainers[_]
95+
}
6296
63-
input_containers[c] {
64-
c := input.review.object.spec.ephemeralContainers[_]
65-
}
66-
libs:
67-
- |
68-
package lib.exclude_update
97+
input_containers[c] {
98+
c := input.review.object.spec.ephemeralContainers[_]
99+
}
100+
libs:
101+
- |
102+
package lib.exclude_update
69103
70-
is_update(review) {
71-
review.operation == "UPDATE"
72-
}
73-
- |
74-
package lib.exempt_container
104+
is_update(review) {
105+
review.operation == "UPDATE"
106+
}
107+
- |
108+
package lib.exempt_container
75109
76-
is_exempt(container) {
77-
exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", [])
78-
img := container.image
79-
exemption := exempt_images[_]
80-
_matches_exemption(img, exemption)
81-
}
110+
is_exempt(container) {
111+
exempt_images := object.get(object.get(input, "parameters", {}), "exemptImages", [])
112+
img := container.image
113+
exemption := exempt_images[_]
114+
_matches_exemption(img, exemption)
115+
}
82116
83-
_matches_exemption(img, exemption) {
84-
not endswith(exemption, "*")
85-
exemption == img
86-
}
117+
_matches_exemption(img, exemption) {
118+
not endswith(exemption, "*")
119+
exemption == img
120+
}
87121
88-
_matches_exemption(img, exemption) {
89-
endswith(exemption, "*")
90-
prefix := trim_suffix(exemption, "*")
91-
startswith(img, prefix)
92-
}
122+
_matches_exemption(img, exemption) {
123+
endswith(exemption, "*")
124+
prefix := trim_suffix(exemption, "*")
125+
startswith(img, prefix)
126+
}

0 commit comments

Comments
 (0)