1/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package securitycontext
18
19import (
20	v1 "k8s.io/api/core/v1"
21)
22
23// HasPrivilegedRequest returns the value of SecurityContext.Privileged, taking into account
24// the possibility of nils
25func HasPrivilegedRequest(container *v1.Container) bool {
26	if container.SecurityContext == nil {
27		return false
28	}
29	if container.SecurityContext.Privileged == nil {
30		return false
31	}
32	return *container.SecurityContext.Privileged
33}
34
35// HasCapabilitiesRequest returns true if Adds or Drops are defined in the security context
36// capabilities, taking into account nils
37func HasCapabilitiesRequest(container *v1.Container) bool {
38	if container.SecurityContext == nil {
39		return false
40	}
41	if container.SecurityContext.Capabilities == nil {
42		return false
43	}
44	return len(container.SecurityContext.Capabilities.Add) > 0 || len(container.SecurityContext.Capabilities.Drop) > 0
45}
46
47// HasWindowsHostProcessRequest returns true if container should run as HostProcess container,
48// taking into account nils
49func HasWindowsHostProcessRequest(pod *v1.Pod, container *v1.Container) bool {
50	effectiveSc := DetermineEffectiveSecurityContext(pod, container)
51
52	if effectiveSc.WindowsOptions == nil {
53		return false
54	}
55	if effectiveSc.WindowsOptions.HostProcess == nil {
56		return false
57	}
58	return *effectiveSc.WindowsOptions.HostProcess
59}
60
61// DetermineEffectiveSecurityContext returns a synthesized SecurityContext for reading effective configurations
62// from the provided pod's and container's security context. Container's fields take precedence in cases where both
63// are set
64func DetermineEffectiveSecurityContext(pod *v1.Pod, container *v1.Container) *v1.SecurityContext {
65	effectiveSc := securityContextFromPodSecurityContext(pod)
66	containerSc := container.SecurityContext
67
68	if effectiveSc == nil && containerSc == nil {
69		return &v1.SecurityContext{}
70	}
71	if effectiveSc != nil && containerSc == nil {
72		return effectiveSc
73	}
74	if effectiveSc == nil && containerSc != nil {
75		return containerSc
76	}
77
78	if containerSc.SELinuxOptions != nil {
79		effectiveSc.SELinuxOptions = new(v1.SELinuxOptions)
80		*effectiveSc.SELinuxOptions = *containerSc.SELinuxOptions
81	}
82
83	if containerSc.WindowsOptions != nil {
84		// only override fields that are set at the container level, not the whole thing
85		if effectiveSc.WindowsOptions == nil {
86			effectiveSc.WindowsOptions = &v1.WindowsSecurityContextOptions{}
87		}
88		if containerSc.WindowsOptions.GMSACredentialSpecName != nil || containerSc.WindowsOptions.GMSACredentialSpec != nil {
89			// both GMSA fields go hand in hand
90			effectiveSc.WindowsOptions.GMSACredentialSpecName = containerSc.WindowsOptions.GMSACredentialSpecName
91			effectiveSc.WindowsOptions.GMSACredentialSpec = containerSc.WindowsOptions.GMSACredentialSpec
92		}
93		if containerSc.WindowsOptions.RunAsUserName != nil {
94			effectiveSc.WindowsOptions.RunAsUserName = containerSc.WindowsOptions.RunAsUserName
95		}
96		if containerSc.WindowsOptions.HostProcess != nil {
97			effectiveSc.WindowsOptions.HostProcess = containerSc.WindowsOptions.HostProcess
98		}
99	}
100
101	if containerSc.Capabilities != nil {
102		effectiveSc.Capabilities = new(v1.Capabilities)
103		*effectiveSc.Capabilities = *containerSc.Capabilities
104	}
105
106	if containerSc.Privileged != nil {
107		effectiveSc.Privileged = new(bool)
108		*effectiveSc.Privileged = *containerSc.Privileged
109	}
110
111	if containerSc.RunAsUser != nil {
112		effectiveSc.RunAsUser = new(int64)
113		*effectiveSc.RunAsUser = *containerSc.RunAsUser
114	}
115
116	if containerSc.RunAsGroup != nil {
117		effectiveSc.RunAsGroup = new(int64)
118		*effectiveSc.RunAsGroup = *containerSc.RunAsGroup
119	}
120
121	if containerSc.RunAsNonRoot != nil {
122		effectiveSc.RunAsNonRoot = new(bool)
123		*effectiveSc.RunAsNonRoot = *containerSc.RunAsNonRoot
124	}
125
126	if containerSc.ReadOnlyRootFilesystem != nil {
127		effectiveSc.ReadOnlyRootFilesystem = new(bool)
128		*effectiveSc.ReadOnlyRootFilesystem = *containerSc.ReadOnlyRootFilesystem
129	}
130
131	if containerSc.AllowPrivilegeEscalation != nil {
132		effectiveSc.AllowPrivilegeEscalation = new(bool)
133		*effectiveSc.AllowPrivilegeEscalation = *containerSc.AllowPrivilegeEscalation
134	}
135
136	if containerSc.ProcMount != nil {
137		effectiveSc.ProcMount = new(v1.ProcMountType)
138		*effectiveSc.ProcMount = *containerSc.ProcMount
139	}
140
141	return effectiveSc
142}
143
144// DetermineEffectiveRunAsUser returns a pointer of UID from the provided pod's
145// and container's security context and a bool value to indicate if it is absent.
146// Container's runAsUser take precedence in cases where both are set.
147func DetermineEffectiveRunAsUser(pod *v1.Pod, container *v1.Container) (*int64, bool) {
148	var runAsUser *int64
149	if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.RunAsUser != nil {
150		runAsUser = new(int64)
151		*runAsUser = *pod.Spec.SecurityContext.RunAsUser
152	}
153	if container.SecurityContext != nil && container.SecurityContext.RunAsUser != nil {
154		runAsUser = new(int64)
155		*runAsUser = *container.SecurityContext.RunAsUser
156	}
157	if runAsUser == nil {
158		return nil, false
159	}
160	return runAsUser, true
161}
162
163func securityContextFromPodSecurityContext(pod *v1.Pod) *v1.SecurityContext {
164	if pod.Spec.SecurityContext == nil {
165		return nil
166	}
167
168	synthesized := &v1.SecurityContext{}
169
170	if pod.Spec.SecurityContext.SELinuxOptions != nil {
171		synthesized.SELinuxOptions = &v1.SELinuxOptions{}
172		*synthesized.SELinuxOptions = *pod.Spec.SecurityContext.SELinuxOptions
173	}
174
175	if pod.Spec.SecurityContext.WindowsOptions != nil {
176		synthesized.WindowsOptions = &v1.WindowsSecurityContextOptions{}
177		*synthesized.WindowsOptions = *pod.Spec.SecurityContext.WindowsOptions
178	}
179
180	if pod.Spec.SecurityContext.RunAsUser != nil {
181		synthesized.RunAsUser = new(int64)
182		*synthesized.RunAsUser = *pod.Spec.SecurityContext.RunAsUser
183	}
184
185	if pod.Spec.SecurityContext.RunAsGroup != nil {
186		synthesized.RunAsGroup = new(int64)
187		*synthesized.RunAsGroup = *pod.Spec.SecurityContext.RunAsGroup
188	}
189
190	if pod.Spec.SecurityContext.RunAsNonRoot != nil {
191		synthesized.RunAsNonRoot = new(bool)
192		*synthesized.RunAsNonRoot = *pod.Spec.SecurityContext.RunAsNonRoot
193	}
194
195	return synthesized
196}
197
198// AddNoNewPrivileges returns if we should add the no_new_privs option.
199func AddNoNewPrivileges(sc *v1.SecurityContext) bool {
200	if sc == nil {
201		return false
202	}
203
204	// handle the case where the user did not set the default and did not explicitly set allowPrivilegeEscalation
205	if sc.AllowPrivilegeEscalation == nil {
206		return false
207	}
208
209	// handle the case where defaultAllowPrivilegeEscalation is false or the user explicitly set allowPrivilegeEscalation to true/false
210	return !*sc.AllowPrivilegeEscalation
211}
212
213var (
214	// These *must* be kept in sync with moby/moby.
215	// https://github.com/moby/moby/blob/master/oci/defaults.go#L116-L134
216	// @jessfraz will watch changes to those files upstream.
217	defaultMaskedPaths = []string{
218		"/proc/acpi",
219		"/proc/kcore",
220		"/proc/keys",
221		"/proc/latency_stats",
222		"/proc/timer_list",
223		"/proc/timer_stats",
224		"/proc/sched_debug",
225		"/proc/scsi",
226		"/sys/firmware",
227	}
228	defaultReadonlyPaths = []string{
229		"/proc/asound",
230		"/proc/bus",
231		"/proc/fs",
232		"/proc/irq",
233		"/proc/sys",
234		"/proc/sysrq-trigger",
235	}
236)
237
238// ConvertToRuntimeMaskedPaths converts the ProcMountType to the specified or default
239// masked paths.
240func ConvertToRuntimeMaskedPaths(opt *v1.ProcMountType) []string {
241	if opt != nil && *opt == v1.UnmaskedProcMount {
242		// Unmasked proc mount should have no paths set as masked.
243		return []string{}
244	}
245
246	// Otherwise, add the default masked paths to the runtime security context.
247	return defaultMaskedPaths
248}
249
250// ConvertToRuntimeReadonlyPaths converts the ProcMountType to the specified or default
251// readonly paths.
252func ConvertToRuntimeReadonlyPaths(opt *v1.ProcMountType) []string {
253	if opt != nil && *opt == v1.UnmaskedProcMount {
254		// Unmasked proc mount should have no paths set as readonly.
255		return []string{}
256	}
257
258	// Otherwise, add the default readonly paths to the runtime security context.
259	return defaultReadonlyPaths
260}
261