// TestValidateAllowedVolumes will test that for every field of VolumeSource we can create // a pod with that type of volume and deny it, accept it explicitly, or accept it with // the FSTypeAll wildcard. func TestValidateAllowedVolumes(t *testing.T) { val := reflect.ValueOf(api.VolumeSource{}) for i := 0; i < val.NumField(); i++ { // reflectively create the volume source fieldVal := val.Type().Field(i) volumeSource := api.VolumeSource{} volumeSourceVolume := reflect.New(fieldVal.Type.Elem()) reflect.ValueOf(&volumeSource).Elem().FieldByName(fieldVal.Name).Set(volumeSourceVolume) volume := api.Volume{VolumeSource: volumeSource} // sanity check before moving on fsType, err := sccutil.GetVolumeFSType(volume) if err != nil { t.Errorf("error getting FSType for %s: %s", fieldVal.Name, err.Error()) continue } // add the volume to the pod pod := defaultPod() pod.Spec.Volumes = []api.Volume{volume} // create an SCC that allows no volumes scc := defaultSCC() provider, err := NewSimpleProvider(scc) if err != nil { t.Errorf("error creating provider for %s: %s", fieldVal.Name, err.Error()) continue } // expect a denial for this SCC and test the error message to ensure it's related to the volumesource errs := provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath("")) if len(errs) != 1 { t.Errorf("expected exactly 1 error for %s but got %v", fieldVal.Name, errs) } else { if !strings.Contains(errs.ToAggregate().Error(), fmt.Sprintf("%s volumes are not allowed to be used", fsType)) { t.Errorf("did not find the expected error, received: %v", errs) } } // now add the fstype directly to the scc and it should validate scc.Volumes = []api.FSType{fsType} errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath("")) if len(errs) != 0 { t.Errorf("directly allowing volume expected no errors for %s but got %v", fieldVal.Name, errs) } // now change the scc to allow any volumes and the pod should still validate scc.Volumes = []api.FSType{api.FSTypeAll} errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath("")) if len(errs) != 0 { t.Errorf("wildcard volume expected no errors for %s but got %v", fieldVal.Name, errs) } } }
// 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{} if container.SecurityContext == nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("securityContext"), container.SecurityContext, "No security context is set")) return allErrs } sc := container.SecurityContext allErrs = append(allErrs, s.runAsUserStrategy.Validate(pod, container)...) allErrs = append(allErrs, s.seLinuxStrategy.Validate(pod, container)...) if !s.scc.AllowPrivilegedContainer && *sc.Privileged { allErrs = append(allErrs, field.Invalid(fldPath.Child("privileged"), *sc.Privileged, "Privileged containers are not allowed")) } allErrs = append(allErrs, s.capabilitiesStrategy.Validate(pod, container)...) if len(pod.Spec.Volumes) > 0 && !sccutil.SCCAllowsAllVolumes(s.scc) { allowedVolumes := sccutil.FSTypeToStringSet(s.scc.Volumes) for i, v := range pod.Spec.Volumes { fsType, err := sccutil.GetVolumeFSType(v) if err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("volumes").Index(i), string(fsType), err.Error())) continue } if !allowedVolumes.Has(string(fsType)) { allErrs = append(allErrs, field.Invalid( fldPath.Child("volumes").Index(i), string(fsType), fmt.Sprintf("%s volumes are not allowed to be used", string(fsType)))) } } } if !s.scc.AllowHostNetwork && pod.Spec.SecurityContext.HostNetwork { allErrs = append(allErrs, field.Invalid(fldPath.Child("hostNetwork"), pod.Spec.SecurityContext.HostNetwork, "Host network is not allowed to be used")) } if !s.scc.AllowHostPorts { containersPath := fldPath.Child("containers") for idx, c := range pod.Spec.Containers { idxPath := containersPath.Index(idx) allErrs = append(allErrs, s.hasHostPort(&c, idxPath)...) } } if !s.scc.AllowHostPID && pod.Spec.SecurityContext.HostPID { allErrs = append(allErrs, field.Invalid(fldPath.Child("hostPID"), pod.Spec.SecurityContext.HostPID, "Host PID is not allowed to be used")) } if !s.scc.AllowHostIPC && pod.Spec.SecurityContext.HostIPC { allErrs = append(allErrs, field.Invalid(fldPath.Child("hostIPC"), pod.Spec.SecurityContext.HostIPC, "Host IPC is not allowed to be used")) } if s.scc.ReadOnlyRootFilesystem { if sc.ReadOnlyRootFilesystem == nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("readOnlyRootFilesystem"), sc.ReadOnlyRootFilesystem, "ReadOnlyRootFilesystem may not be nil and must be set to true")) } else if !*sc.ReadOnlyRootFilesystem { allErrs = append(allErrs, field.Invalid(fldPath.Child("readOnlyRootFilesystem"), *sc.ReadOnlyRootFilesystem, "ReadOnlyRootFilesystem must be set to true")) } } return allErrs }