2016-05-05 19:43:54 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2016 The Kubernetes Authors .
2016-05-05 19:43:54 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package podsecuritypolicy
import (
"fmt"
2017-06-16 08:22:50 +00:00
"strings"
2016-05-05 19:43:54 +00:00
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/util/validation/field"
2017-11-08 22:34:54 +00:00
api "k8s.io/kubernetes/pkg/apis/core"
2016-05-05 19:43:54 +00:00
"k8s.io/kubernetes/pkg/apis/extensions"
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
2017-10-14 19:07:09 +00:00
"k8s.io/kubernetes/pkg/securitycontext"
2016-08-17 17:14:23 +00:00
"k8s.io/kubernetes/pkg/util/maps"
2016-05-05 19:43:54 +00:00
)
// used to pass in the field being validated for reusable group strategies so they
// can create informative error messages.
const (
fsGroupField = "fsGroup"
supplementalGroupsField = "supplementalGroups"
)
// simpleProvider is the default implementation of Provider.
type simpleProvider struct {
psp * extensions . PodSecurityPolicy
strategies * ProviderStrategies
}
// ensure we implement the interface correctly.
var _ Provider = & simpleProvider { }
// NewSimpleProvider creates a new Provider instance.
func NewSimpleProvider ( psp * extensions . PodSecurityPolicy , namespace string , strategyFactory StrategyFactory ) ( Provider , error ) {
if psp == nil {
return nil , fmt . Errorf ( "NewSimpleProvider requires a PodSecurityPolicy" )
}
if strategyFactory == nil {
return nil , fmt . Errorf ( "NewSimpleProvider requires a StrategyFactory" )
}
strategies , err := strategyFactory . CreateStrategies ( psp , namespace )
if err != nil {
return nil , err
}
return & simpleProvider {
psp : psp ,
strategies : strategies ,
} , nil
}
2017-11-10 17:22:34 +00:00
// DefaultPodSecurityContext sets the default values of the required but not filled fields.
// It modifies the SecurityContext and annotations of the provided pod. Validation should be
// used after the context is defaulted to ensure it complies with the required restrictions.
func ( s * simpleProvider ) DefaultPodSecurityContext ( pod * api . Pod ) error {
2017-10-14 19:07:09 +00:00
sc := securitycontext . NewPodSecurityContextMutator ( pod . Spec . SecurityContext )
2016-05-05 19:43:54 +00:00
2017-10-14 19:07:09 +00:00
if sc . SupplementalGroups ( ) == nil {
2016-05-05 19:43:54 +00:00
supGroups , err := s . strategies . SupplementalGroupStrategy . Generate ( pod )
if err != nil {
2017-11-10 17:22:34 +00:00
return err
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
sc . SetSupplementalGroups ( supGroups )
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
if sc . FSGroup ( ) == nil {
2016-05-05 19:43:54 +00:00
fsGroup , err := s . strategies . FSGroupStrategy . GenerateSingle ( pod )
if err != nil {
2017-11-10 17:22:34 +00:00
return err
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
sc . SetFSGroup ( fsGroup )
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
if sc . SELinuxOptions ( ) == nil {
2016-05-05 19:43:54 +00:00
seLinux , err := s . strategies . SELinuxStrategy . Generate ( pod , nil )
if err != nil {
2017-11-10 17:22:34 +00:00
return err
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
sc . SetSELinuxOptions ( seLinux )
2016-05-05 19:43:54 +00:00
}
2016-08-23 16:52:27 +00:00
// This is only generated on the pod level. Containers inherit the pod's profile. If the
// container has a specific profile set then it will be caught in the validation step.
2017-11-10 17:22:34 +00:00
seccompProfile , err := s . strategies . SeccompStrategy . Generate ( pod . Annotations , pod )
2016-08-23 16:52:27 +00:00
if err != nil {
2017-11-10 17:22:34 +00:00
return err
2016-08-23 16:52:27 +00:00
}
if seccompProfile != "" {
2017-11-10 17:22:34 +00:00
if pod . Annotations == nil {
pod . Annotations = map [ string ] string { }
2016-08-23 16:52:27 +00:00
}
2017-11-10 17:22:34 +00:00
pod . Annotations [ api . SeccompPodAnnotationKey ] = seccompProfile
2016-08-23 16:52:27 +00:00
}
2017-11-10 17:22:34 +00:00
pod . Spec . SecurityContext = sc . PodSecurityContext ( )
return nil
2016-05-05 19:43:54 +00:00
}
// Create a SecurityContext based on the given constraints. If a setting is already set on the
// container's security context then it will not be changed. Validation should be used after
// the context is created to ensure it complies with the required restrictions.
2016-08-17 17:14:23 +00:00
func ( s * simpleProvider ) CreateContainerSecurityContext ( pod * api . Pod , container * api . Container ) ( * api . SecurityContext , map [ string ] string , error ) {
2017-10-14 19:07:09 +00:00
sc := securitycontext . NewEffectiveContainerSecurityContextMutator (
securitycontext . NewPodSecurityContextAccessor ( pod . Spec . SecurityContext ) ,
securitycontext . NewContainerSecurityContextMutator ( container . SecurityContext ) ,
)
2016-08-17 17:14:23 +00:00
annotations := maps . CopySS ( pod . Annotations )
2017-10-14 19:07:09 +00:00
if sc . RunAsUser ( ) == nil {
2016-05-05 19:43:54 +00:00
uid , err := s . strategies . RunAsUserStrategy . Generate ( pod , container )
if err != nil {
2016-08-17 17:14:23 +00:00
return nil , nil , err
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
sc . SetRunAsUser ( uid )
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
if sc . SELinuxOptions ( ) == nil {
2016-05-05 19:43:54 +00:00
seLinux , err := s . strategies . SELinuxStrategy . Generate ( pod , container )
if err != nil {
2016-08-17 17:14:23 +00:00
return nil , nil , err
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
sc . SetSELinuxOptions ( seLinux )
2016-05-05 19:43:54 +00:00
}
2016-08-18 00:24:47 +00:00
annotations , err := s . strategies . AppArmorStrategy . Generate ( annotations , container )
if err != nil {
return nil , nil , err
}
2016-05-05 19:43:54 +00:00
// if we're using the non-root strategy set the marker that this container should not be
// run as root which will signal to the kubelet to do a final check either on the runAsUser
// or, if runAsUser is not set, the image UID will be checked.
2017-10-14 19:07:09 +00:00
if sc . RunAsNonRoot ( ) == nil && sc . RunAsUser ( ) == nil && s . psp . Spec . RunAsUser . Rule == extensions . RunAsUserStrategyMustRunAsNonRoot {
2016-05-05 19:43:54 +00:00
nonRoot := true
2017-10-14 19:07:09 +00:00
sc . SetRunAsNonRoot ( & nonRoot )
2016-05-05 19:43:54 +00:00
}
caps , err := s . strategies . CapabilitiesStrategy . Generate ( pod , container )
if err != nil {
2016-08-17 17:14:23 +00:00
return nil , nil , err
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
sc . SetCapabilities ( caps )
2016-05-05 19:43:54 +00:00
// if the PSP requires a read only root filesystem and the container has not made a specific
// request then default ReadOnlyRootFilesystem to true.
2017-10-14 19:07:09 +00:00
if s . psp . Spec . ReadOnlyRootFilesystem && sc . ReadOnlyRootFilesystem ( ) == nil {
2016-05-05 19:43:54 +00:00
readOnlyRootFS := true
2017-10-14 19:07:09 +00:00
sc . SetReadOnlyRootFilesystem ( & readOnlyRootFS )
2016-05-05 19:43:54 +00:00
}
2017-06-26 19:13:28 +00:00
// if the PSP sets DefaultAllowPrivilegeEscalation and the container security context
// allowPrivilegeEscalation is not set, then default to that set by the PSP.
2017-10-14 19:07:09 +00:00
if s . psp . Spec . DefaultAllowPrivilegeEscalation != nil && sc . AllowPrivilegeEscalation ( ) == nil {
sc . SetAllowPrivilegeEscalation ( s . psp . Spec . DefaultAllowPrivilegeEscalation )
2017-06-26 19:13:28 +00:00
}
// if the PSP sets psp.AllowPrivilegeEscalation to false set that as the default
2017-10-14 19:07:09 +00:00
if ! s . psp . Spec . AllowPrivilegeEscalation && sc . AllowPrivilegeEscalation ( ) == nil {
sc . SetAllowPrivilegeEscalation ( & s . psp . Spec . AllowPrivilegeEscalation )
2017-06-26 19:13:28 +00:00
}
2017-10-14 19:07:09 +00:00
return sc . ContainerSecurityContext ( ) , annotations , nil
2016-05-05 19:43:54 +00:00
}
// Ensure a pod's SecurityContext is in compliance with the given constraints.
func ( s * simpleProvider ) ValidatePodSecurityContext ( pod * api . Pod , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2017-10-14 19:07:09 +00:00
sc := securitycontext . NewPodSecurityContextAccessor ( pod . Spec . SecurityContext )
2016-05-05 19:43:54 +00:00
2017-06-21 07:13:36 +00:00
fsGroups := [ ] int64 { }
2017-10-14 19:07:09 +00:00
if fsGroup := sc . FSGroup ( ) ; fsGroup != nil {
fsGroups = append ( fsGroups , * fsGroup )
2016-05-05 19:43:54 +00:00
}
allErrs = append ( allErrs , s . strategies . FSGroupStrategy . Validate ( pod , fsGroups ) ... )
2017-10-14 19:07:09 +00:00
allErrs = append ( allErrs , s . strategies . SupplementalGroupStrategy . Validate ( pod , sc . SupplementalGroups ( ) ) ... )
2016-08-23 16:52:27 +00:00
allErrs = append ( allErrs , s . strategies . SeccompStrategy . ValidatePod ( pod ) ... )
2016-05-05 19:43:54 +00:00
2017-10-14 19:07:09 +00:00
allErrs = append ( allErrs , s . strategies . SELinuxStrategy . Validate ( fldPath . Child ( "seLinuxOptions" ) , pod , nil , sc . SELinuxOptions ( ) ) ... )
2016-05-05 19:43:54 +00:00
2017-10-14 19:07:09 +00:00
if ! s . psp . Spec . HostNetwork && sc . HostNetwork ( ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "hostNetwork" ) , sc . HostNetwork ( ) , "Host network is not allowed to be used" ) )
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
if ! s . psp . Spec . HostPID && sc . HostPID ( ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "hostPID" ) , sc . HostPID ( ) , "Host PID is not allowed to be used" ) )
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
if ! s . psp . Spec . HostIPC && sc . HostIPC ( ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "hostIPC" ) , sc . HostIPC ( ) , "Host IPC is not allowed to be used" ) )
2016-05-05 19:43:54 +00:00
}
2016-08-19 08:33:56 +00:00
allErrs = append ( allErrs , s . strategies . SysctlsStrategy . Validate ( pod ) ... )
2017-07-10 21:05:46 +00:00
// TODO(tallclair): ValidatePodSecurityContext should be renamed to ValidatePod since its scope
2016-09-01 22:55:34 +00:00
// is not limited to the PodSecurityContext.
2017-08-06 19:40:18 +00:00
if len ( pod . Spec . Volumes ) > 0 {
allowsAllVolumeTypes := psputil . PSPAllowsAllVolumes ( s . psp )
2016-09-01 22:55:34 +00:00
allowedVolumes := psputil . FSTypeToStringSet ( s . psp . Spec . Volumes )
for i , v := range pod . Spec . Volumes {
fsType , err := psputil . GetVolumeFSType ( v )
if err != nil {
allErrs = append ( allErrs , field . Invalid ( field . NewPath ( "spec" , "volumes" ) . Index ( i ) , string ( fsType ) , err . Error ( ) ) )
continue
}
2017-08-06 19:40:18 +00:00
if ! allowsAllVolumeTypes && ! allowedVolumes . Has ( string ( fsType ) ) {
2016-09-01 22:55:34 +00:00
allErrs = append ( allErrs , field . Invalid (
field . NewPath ( "spec" , "volumes" ) . Index ( i ) , string ( fsType ) ,
fmt . Sprintf ( "%s volumes are not allowed to be used" , string ( fsType ) ) ) )
2017-08-06 19:40:18 +00:00
continue
}
if fsType == extensions . HostPath {
if ! psputil . AllowsHostVolumePath ( s . psp , v . HostPath . Path ) {
allErrs = append ( allErrs , field . Invalid (
field . NewPath ( "spec" , "volumes" ) . Index ( i ) . Child ( "hostPath" , "pathPrefix" ) , v . HostPath . Path ,
fmt . Sprintf ( "is not allowed to be used" ) ) )
}
2016-09-01 22:55:34 +00:00
}
}
}
2016-05-05 19:43:54 +00:00
return allErrs
}
// Ensure a container's SecurityContext is in compliance with the given constraints
func ( s * simpleProvider ) ValidateContainerSecurityContext ( pod * api . Pod , container * api . Container , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
2017-10-14 19:07:09 +00:00
podSC := securitycontext . NewPodSecurityContextAccessor ( pod . Spec . SecurityContext )
sc := securitycontext . NewEffectiveContainerSecurityContextAccessor ( podSC , securitycontext . NewContainerSecurityContextMutator ( container . SecurityContext ) )
2016-05-05 19:43:54 +00:00
2017-10-14 19:07:09 +00:00
allErrs = append ( allErrs , s . strategies . RunAsUserStrategy . Validate ( fldPath . Child ( "securityContext" ) , pod , container , sc . RunAsNonRoot ( ) , sc . RunAsUser ( ) ) ... )
allErrs = append ( allErrs , s . strategies . SELinuxStrategy . Validate ( fldPath . Child ( "seLinuxOptions" ) , pod , container , sc . SELinuxOptions ( ) ) ... )
2016-08-18 00:24:47 +00:00
allErrs = append ( allErrs , s . strategies . AppArmorStrategy . Validate ( pod , container ) ... )
2016-08-23 16:52:27 +00:00
allErrs = append ( allErrs , s . strategies . SeccompStrategy . ValidateContainer ( pod , container ) ... )
2016-05-05 19:43:54 +00:00
2017-10-14 19:07:09 +00:00
privileged := sc . Privileged ( )
if ! s . psp . Spec . Privileged && privileged != nil && * privileged {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "privileged" ) , * privileged , "Privileged containers are not allowed" ) )
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
allErrs = append ( allErrs , s . strategies . CapabilitiesStrategy . Validate ( pod , container , sc . Capabilities ( ) ) ... )
2016-05-05 19:43:54 +00:00
2017-10-14 19:07:09 +00:00
if ! s . psp . Spec . HostNetwork && podSC . HostNetwork ( ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "hostNetwork" ) , podSC . HostNetwork ( ) , "Host network is not allowed to be used" ) )
2016-05-05 19:43:54 +00:00
}
containersPath := fldPath . Child ( "containers" )
for idx , c := range pod . Spec . Containers {
idxPath := containersPath . Index ( idx )
allErrs = append ( allErrs , s . hasInvalidHostPort ( & c , idxPath ) ... )
}
2016-07-21 02:20:42 +00:00
containersPath = fldPath . Child ( "initContainers" )
for idx , c := range pod . Spec . InitContainers {
idxPath := containersPath . Index ( idx )
allErrs = append ( allErrs , s . hasInvalidHostPort ( & c , idxPath ) ... )
}
2017-10-14 19:07:09 +00:00
if ! s . psp . Spec . HostPID && podSC . HostPID ( ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "hostPID" ) , podSC . HostPID ( ) , "Host PID is not allowed to be used" ) )
2016-05-05 19:43:54 +00:00
}
2017-10-14 19:07:09 +00:00
if ! s . psp . Spec . HostIPC && podSC . HostIPC ( ) {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "hostIPC" ) , podSC . HostIPC ( ) , "Host IPC is not allowed to be used" ) )
2016-05-05 19:43:54 +00:00
}
if s . psp . Spec . ReadOnlyRootFilesystem {
2017-10-14 19:07:09 +00:00
readOnly := sc . ReadOnlyRootFilesystem ( )
if readOnly == nil {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "readOnlyRootFilesystem" ) , readOnly , "ReadOnlyRootFilesystem may not be nil and must be set to true" ) )
} else if ! * readOnly {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "readOnlyRootFilesystem" ) , * readOnly , "ReadOnlyRootFilesystem must be set to true" ) )
2016-05-05 19:43:54 +00:00
}
}
2017-10-14 19:07:09 +00:00
allowEscalation := sc . AllowPrivilegeEscalation ( )
if ! s . psp . Spec . AllowPrivilegeEscalation && allowEscalation == nil {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "allowPrivilegeEscalation" ) , allowEscalation , "Allowing privilege escalation for containers is not allowed" ) )
2017-06-26 19:13:28 +00:00
}
2017-10-14 19:07:09 +00:00
if ! s . psp . Spec . AllowPrivilegeEscalation && allowEscalation != nil && * allowEscalation {
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "allowPrivilegeEscalation" ) , * allowEscalation , "Allowing privilege escalation for containers is not allowed" ) )
2017-06-26 19:13:28 +00:00
}
2016-05-05 19:43:54 +00:00
return allErrs
}
2017-08-09 15:50:04 +00:00
// hasInvalidHostPort checks whether the port definitions on the container fall outside of the ranges allowed by the PSP.
2016-05-05 19:43:54 +00:00
func ( s * simpleProvider ) hasInvalidHostPort ( container * api . Container , fldPath * field . Path ) field . ErrorList {
allErrs := field . ErrorList { }
for _ , cp := range container . Ports {
if cp . HostPort > 0 && ! s . isValidHostPort ( int ( cp . HostPort ) ) {
2017-06-16 08:22:50 +00:00
detail := fmt . Sprintf ( "Host port %d is not allowed to be used. Allowed ports: [%s]" , cp . HostPort , hostPortRangesToString ( s . psp . Spec . HostPorts ) )
2016-05-05 19:43:54 +00:00
allErrs = append ( allErrs , field . Invalid ( fldPath . Child ( "hostPort" ) , cp . HostPort , detail ) )
}
}
return allErrs
}
// isValidHostPort returns true if the port falls in any range allowed by the PSP.
func ( s * simpleProvider ) isValidHostPort ( port int ) bool {
for _ , hostPortRange := range s . psp . Spec . HostPorts {
if port >= hostPortRange . Min && port <= hostPortRange . Max {
return true
}
}
return false
}
// Get the name of the PSP that this provider was initialized with.
func ( s * simpleProvider ) GetPSPName ( ) string {
return s . psp . Name
}
2017-06-16 08:22:50 +00:00
func hostPortRangesToString ( ranges [ ] extensions . HostPortRange ) string {
formattedString := ""
if ranges != nil {
strRanges := [ ] string { }
for _ , r := range ranges {
if r . Min == r . Max {
strRanges = append ( strRanges , fmt . Sprintf ( "%d" , r . Min ) )
} else {
strRanges = append ( strRanges , fmt . Sprintf ( "%d-%d" , r . Min , r . Max ) )
}
}
formattedString = strings . Join ( strRanges , "," )
}
return formattedString
}