1
16
17 package debug
18
19 import (
20 "fmt"
21
22 corev1 "k8s.io/api/core/v1"
23 "k8s.io/apimachinery/pkg/runtime"
24 "k8s.io/kubectl/pkg/util/podutils"
25 "k8s.io/utils/pointer"
26 )
27
28 type debugStyle int
29
30 const (
31
32 ephemeral debugStyle = iota
33
34 podCopy
35
36 node
37
38 unsupported
39 )
40
41 const (
42
43
44
45
46 ProfileLegacy = "legacy"
47
48 ProfileGeneral = "general"
49
50
51 ProfileBaseline = "baseline"
52
53
54 ProfileRestricted = "restricted"
55
56 ProfileNetadmin = "netadmin"
57
58 ProfileSysadmin = "sysadmin"
59 )
60
61 type ProfileApplier interface {
62
63 Apply(pod *corev1.Pod, containerName string, target runtime.Object) error
64 }
65
66
67 func NewProfileApplier(profile string) (ProfileApplier, error) {
68 switch profile {
69 case ProfileLegacy:
70 return &legacyProfile{}, nil
71 case ProfileGeneral:
72 return &generalProfile{}, nil
73 case ProfileBaseline:
74 return &baselineProfile{}, nil
75 case ProfileRestricted:
76 return &restrictedProfile{}, nil
77 case ProfileNetadmin:
78 return &netadminProfile{}, nil
79 case ProfileSysadmin:
80 return &sysadminProfile{}, nil
81 }
82
83 return nil, fmt.Errorf("unknown profile: %s", profile)
84 }
85
86 type legacyProfile struct {
87 }
88
89 type generalProfile struct {
90 }
91
92 type baselineProfile struct {
93 }
94
95 type restrictedProfile struct {
96 }
97
98 type netadminProfile struct {
99 }
100
101 type sysadminProfile struct {
102 }
103
104 func (p *legacyProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error {
105 switch target.(type) {
106 case *corev1.Pod:
107
108 return nil
109 case *corev1.Node:
110 mountRootPartition(pod, containerName)
111 useHostNamespaces(pod)
112 return nil
113 default:
114 return fmt.Errorf("the %s profile doesn't support objects of type %T", ProfileLegacy, target)
115 }
116 }
117
118 func getDebugStyle(pod *corev1.Pod, target runtime.Object) (debugStyle, error) {
119 switch target.(type) {
120 case *corev1.Pod:
121 if asserted, ok := target.(*corev1.Pod); ok {
122 if pod != asserted {
123 return podCopy, nil
124 }
125 }
126 return ephemeral, nil
127 case *corev1.Node:
128 return node, nil
129 }
130 return unsupported, fmt.Errorf("objects of type %T are not supported", target)
131 }
132
133 func (p *generalProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error {
134 style, err := getDebugStyle(pod, target)
135 if err != nil {
136 return fmt.Errorf("general profile: %s", err)
137 }
138
139 switch style {
140 case node:
141 mountRootPartition(pod, containerName)
142 clearSecurityContext(pod, containerName)
143 useHostNamespaces(pod)
144
145 case podCopy:
146 removeLabelsAndProbes(pod)
147 allowProcessTracing(pod, containerName)
148 shareProcessNamespace(pod)
149
150 case ephemeral:
151 allowProcessTracing(pod, containerName)
152 }
153
154 return nil
155 }
156
157 func (p *baselineProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error {
158 style, err := getDebugStyle(pod, target)
159 if err != nil {
160 return fmt.Errorf("baseline profile: %s", err)
161 }
162
163 clearSecurityContext(pod, containerName)
164
165 switch style {
166 case podCopy:
167 removeLabelsAndProbes(pod)
168 shareProcessNamespace(pod)
169
170 case ephemeral, node:
171
172 }
173
174 return nil
175 }
176
177 func (p *restrictedProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error {
178 style, err := getDebugStyle(pod, target)
179 if err != nil {
180 return fmt.Errorf("restricted profile: %s", err)
181 }
182
183 clearSecurityContext(pod, containerName)
184 disallowRoot(pod, containerName)
185 dropCapabilities(pod, containerName)
186 disallowPrivilegeEscalation(pod, containerName)
187 setSeccompProfile(pod, containerName)
188
189 switch style {
190 case podCopy:
191 shareProcessNamespace(pod)
192
193 case ephemeral, node:
194
195 }
196
197 return nil
198 }
199
200 func (p *netadminProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error {
201 style, err := getDebugStyle(pod, target)
202 if err != nil {
203 return fmt.Errorf("netadmin profile: %s", err)
204 }
205
206 allowNetadminCapability(pod, containerName)
207
208 switch style {
209 case node:
210 useHostNamespaces(pod)
211
212 case podCopy:
213 shareProcessNamespace(pod)
214
215 case ephemeral:
216
217 }
218
219 return nil
220 }
221
222 func (p *sysadminProfile) Apply(pod *corev1.Pod, containerName string, target runtime.Object) error {
223 style, err := getDebugStyle(pod, target)
224 if err != nil {
225 return fmt.Errorf("sysadmin profile: %s", err)
226 }
227
228 setPrivileged(pod, containerName)
229
230 switch style {
231 case node:
232 useHostNamespaces(pod)
233 mountRootPartition(pod, containerName)
234
235 case podCopy:
236
237 shareProcessNamespace(pod)
238 case ephemeral:
239
240 }
241
242 return nil
243 }
244
245
246
247 func removeLabelsAndProbes(p *corev1.Pod) {
248 p.Labels = nil
249 for i := range p.Spec.Containers {
250 p.Spec.Containers[i].LivenessProbe = nil
251 p.Spec.Containers[i].ReadinessProbe = nil
252 p.Spec.Containers[i].StartupProbe = nil
253 }
254 }
255
256
257 func mountRootPartition(p *corev1.Pod, containerName string) {
258 const volumeName = "host-root"
259 p.Spec.Volumes = append(p.Spec.Volumes, corev1.Volume{
260 Name: volumeName,
261 VolumeSource: corev1.VolumeSource{
262 HostPath: &corev1.HostPathVolumeSource{Path: "/"},
263 },
264 })
265 podutils.VisitContainers(&p.Spec, podutils.Containers, func(c *corev1.Container, _ podutils.ContainerType) bool {
266 if c.Name != containerName {
267 return true
268 }
269 c.VolumeMounts = append(c.VolumeMounts, corev1.VolumeMount{
270 MountPath: "/host",
271 Name: volumeName,
272 })
273 return false
274 })
275 }
276
277
278
279 func useHostNamespaces(p *corev1.Pod) {
280 p.Spec.HostNetwork = true
281 p.Spec.HostPID = true
282 p.Spec.HostIPC = true
283 }
284
285
286
287 func shareProcessNamespace(p *corev1.Pod) {
288 if p.Spec.ShareProcessNamespace == nil {
289 p.Spec.ShareProcessNamespace = pointer.Bool(true)
290 }
291 }
292
293
294 func clearSecurityContext(p *corev1.Pod, containerName string) {
295 podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool {
296 if c.Name != containerName {
297 return true
298 }
299 c.SecurityContext = nil
300 return false
301 })
302 }
303
304
305 func setPrivileged(p *corev1.Pod, containerName string) {
306 podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool {
307 if c.Name != containerName {
308 return true
309 }
310 if c.SecurityContext == nil {
311 c.SecurityContext = &corev1.SecurityContext{}
312 }
313 c.SecurityContext.Privileged = pointer.Bool(true)
314 return false
315 })
316 }
317
318
319 func disallowRoot(p *corev1.Pod, containerName string) {
320 podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool {
321 if c.Name != containerName {
322 return true
323 }
324 if c.SecurityContext == nil {
325 c.SecurityContext = &corev1.SecurityContext{}
326 }
327 c.SecurityContext.RunAsNonRoot = pointer.Bool(true)
328 return false
329 })
330 }
331
332
333 func dropCapabilities(p *corev1.Pod, containerName string) {
334 podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool {
335 if c.Name != containerName {
336 return true
337 }
338 if c.SecurityContext == nil {
339 c.SecurityContext = &corev1.SecurityContext{}
340 }
341 if c.SecurityContext.Capabilities == nil {
342 c.SecurityContext.Capabilities = &corev1.Capabilities{}
343 }
344 c.SecurityContext.Capabilities.Drop = []corev1.Capability{"ALL"}
345 c.SecurityContext.Capabilities.Add = nil
346 return false
347 })
348 }
349
350
351 func allowProcessTracing(p *corev1.Pod, containerName string) {
352 podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool {
353 if c.Name != containerName {
354 return true
355 }
356 addCapability(c, "SYS_PTRACE")
357 return false
358 })
359 }
360
361
362 func allowNetadminCapability(p *corev1.Pod, containerName string) {
363 podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool {
364 if c.Name != containerName {
365 return true
366 }
367 addCapability(c, "NET_ADMIN")
368 addCapability(c, "NET_RAW")
369 return false
370 })
371 }
372
373 func addCapability(c *corev1.Container, capability corev1.Capability) {
374 if c.SecurityContext == nil {
375 c.SecurityContext = &corev1.SecurityContext{}
376 }
377 if c.SecurityContext.Capabilities == nil {
378 c.SecurityContext.Capabilities = &corev1.Capabilities{}
379 }
380 c.SecurityContext.Capabilities.Add = append(c.SecurityContext.Capabilities.Add, capability)
381 }
382
383
384 func disallowPrivilegeEscalation(p *corev1.Pod, containerName string) {
385 podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool {
386 if c.Name != containerName {
387 return true
388 }
389 if c.SecurityContext == nil {
390 c.SecurityContext = &corev1.SecurityContext{}
391 }
392 c.SecurityContext.AllowPrivilegeEscalation = pointer.Bool(false)
393 return false
394 })
395 }
396
397
398 func setSeccompProfile(p *corev1.Pod, containerName string) {
399 podutils.VisitContainers(&p.Spec, podutils.AllContainers, func(c *corev1.Container, _ podutils.ContainerType) bool {
400 if c.Name != containerName {
401 return true
402 }
403 if c.SecurityContext == nil {
404 c.SecurityContext = &corev1.SecurityContext{}
405 }
406 c.SecurityContext.SeccompProfile = &corev1.SeccompProfile{Type: "RuntimeDefault"}
407 return false
408 })
409 }
410
View as plain text