-
Notifications
You must be signed in to change notification settings - Fork 320
/
template.yaml
185 lines (173 loc) Β· 7.81 KB
/
template.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8spspprocmount
annotations:
metadata.gatekeeper.sh/title: "Proc Mount"
metadata.gatekeeper.sh/version: 1.1.1
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.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)
}