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