// 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. // // NOTE: this method works on a copy of the SC of the container. It is up to the caller to apply // the SC if validation passes. func (s *simpleProvider) CreateContainerSecurityContext(pod *api.Pod, container *api.Container) (*api.SecurityContext, map[string]string, error) { var sc *api.SecurityContext = nil if container.SecurityContext != nil { // work with a copy of the original copy := *container.SecurityContext sc = © } else { sc = &api.SecurityContext{} } annotations := maps.CopySS(pod.Annotations) if sc.RunAsUser == nil { uid, err := s.strategies.RunAsUserStrategy.Generate(pod, container) if err != nil { return nil, nil, err } sc.RunAsUser = uid } if sc.SELinuxOptions == nil { seLinux, err := s.strategies.SELinuxStrategy.Generate(pod, container) if err != nil { return nil, nil, err } sc.SELinuxOptions = seLinux } annotations, err := s.strategies.AppArmorStrategy.Generate(annotations, container) if err != nil { return nil, nil, err } if sc.Privileged == nil { priv := false sc.Privileged = &priv } // 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. if s.psp.Spec.RunAsUser.Rule == extensions.RunAsUserStrategyMustRunAsNonRoot { nonRoot := true sc.RunAsNonRoot = &nonRoot } caps, err := s.strategies.CapabilitiesStrategy.Generate(pod, container) if err != nil { return nil, nil, err } sc.Capabilities = caps // if the PSP requires a read only root filesystem and the container has not made a specific // request then default ReadOnlyRootFilesystem to true. if s.psp.Spec.ReadOnlyRootFilesystem && sc.ReadOnlyRootFilesystem == nil { readOnlyRootFS := true sc.ReadOnlyRootFilesystem = &readOnlyRootFS } return sc, annotations, nil }
func makeTestPod(annotations map[string]string) (*api.Pod, *api.Container) { return &api.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", Annotations: maps.CopySS(annotations), }, Spec: api.PodSpec{ Containers: []api.Container{container}, }, }, &container }
// Create a PodSecurityContext based on the given constraints. If a setting is already set // on the PodSecurityContext it will not be changed. Validate should be used after the context // is created to ensure it complies with the required restrictions. // // NOTE: this method works on a copy of the PodSecurityContext. It is up to the caller to // apply the PSC if validation passes. func (s *simpleProvider) CreatePodSecurityContext(pod *api.Pod) (*api.PodSecurityContext, map[string]string, error) { var sc *api.PodSecurityContext = nil if pod.Spec.SecurityContext != nil { // work with a copy copy := *pod.Spec.SecurityContext sc = © } else { sc = &api.PodSecurityContext{} } annotations := maps.CopySS(pod.Annotations) if len(sc.SupplementalGroups) == 0 { supGroups, err := s.strategies.SupplementalGroupStrategy.Generate(pod) if err != nil { return nil, nil, err } sc.SupplementalGroups = supGroups } if sc.FSGroup == nil { fsGroup, err := s.strategies.FSGroupStrategy.GenerateSingle(pod) if err != nil { return nil, nil, err } sc.FSGroup = fsGroup } if sc.SELinuxOptions == nil { seLinux, err := s.strategies.SELinuxStrategy.Generate(pod, nil) if err != nil { return nil, nil, err } sc.SELinuxOptions = seLinux } // 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. seccompProfile, err := s.strategies.SeccompStrategy.Generate(annotations, pod) if err != nil { return nil, nil, err } if seccompProfile != "" { if annotations == nil { annotations = map[string]string{} } annotations[api.SeccompPodAnnotationKey] = seccompProfile } return sc, annotations, nil }
func (s *strategy) Generate(annotations map[string]string, container *api.Container) (map[string]string, error) { copy := maps.CopySS(annotations) if annotations[apparmor.ContainerAnnotationKeyPrefix+container.Name] != "" { // Profile already set, nothing to do. return copy, nil } if s.defaultProfile == "" { // No default set. return copy, nil } if copy == nil { copy = map[string]string{} } // Add the default profile. copy[apparmor.ContainerAnnotationKeyPrefix+container.Name] = s.defaultProfile return copy, nil }
// Create a PodSecurityContext based on the given constraints. If a setting is already set // on the PodSecurityContext it will not be changed. Validate should be used after the context // is created to ensure it complies with the required restrictions. // // NOTE: this method works on a copy of the PodSecurityContext. It is up to the caller to // apply the PSC if validation passes. func (s *simpleProvider) CreatePodSecurityContext(pod *api.Pod) (*api.PodSecurityContext, map[string]string, error) { var sc *api.PodSecurityContext = nil if pod.Spec.SecurityContext != nil { // work with a copy copy := *pod.Spec.SecurityContext sc = © } else { sc = &api.PodSecurityContext{} } annotations := maps.CopySS(pod.Annotations) if len(sc.SupplementalGroups) == 0 { supGroups, err := s.strategies.SupplementalGroupStrategy.Generate(pod) if err != nil { return nil, nil, err } sc.SupplementalGroups = supGroups } if sc.FSGroup == nil { fsGroup, err := s.strategies.FSGroupStrategy.GenerateSingle(pod) if err != nil { return nil, nil, err } sc.FSGroup = fsGroup } if sc.SELinuxOptions == nil { seLinux, err := s.strategies.SELinuxStrategy.Generate(pod, nil) if err != nil { return nil, nil, err } sc.SELinuxOptions = seLinux } return sc, annotations, nil }
// assignSecurityContext creates a security context for each container in the pod // and validates that the sc falls within the psp constraints. All containers must validate against // the same psp or is not considered valid. func assignSecurityContext(provider psp.Provider, pod *api.Pod, fldPath *field.Path) field.ErrorList { generatedSCs := make([]*api.SecurityContext, len(pod.Spec.Containers)) var generatedInitSCs []*api.SecurityContext errs := field.ErrorList{} psc, pscAnnotations, err := provider.CreatePodSecurityContext(pod) if err != nil { errs = append(errs, field.Invalid(field.NewPath("spec", "securityContext"), pod.Spec.SecurityContext, err.Error())) } // save the original PSC and validate the generated PSC. Leave the generated PSC // set for container generation/validation. We will reset to original post container // validation. originalPSC := pod.Spec.SecurityContext pod.Spec.SecurityContext = psc originalAnnotations := maps.CopySS(pod.Annotations) pod.Annotations = pscAnnotations errs = append(errs, provider.ValidatePodSecurityContext(pod, field.NewPath("spec", "securityContext"))...) // Note: this is not changing the original container, we will set container SCs later so long // as all containers validated under the same PSP. for i, containerCopy := range pod.Spec.InitContainers { // We will determine the effective security context for the container and validate against that // since that is how the sc provider will eventually apply settings in the runtime. // This results in an SC that is based on the Pod's PSC with the set fields from the container // overriding pod level settings. containerCopy.SecurityContext = sc.InternalDetermineEffectiveSecurityContext(pod, &containerCopy) sc, scAnnotations, err := provider.CreateContainerSecurityContext(pod, &containerCopy) if err != nil { errs = append(errs, field.Invalid(field.NewPath("spec", "initContainers").Index(i).Child("securityContext"), "", err.Error())) continue } generatedInitSCs = append(generatedInitSCs, sc) containerCopy.SecurityContext = sc pod.Annotations = scAnnotations errs = append(errs, provider.ValidateContainerSecurityContext(pod, &containerCopy, field.NewPath("spec", "initContainers").Index(i).Child("securityContext"))...) } // Note: this is not changing the original container, we will set container SCs later so long // as all containers validated under the same PSP. for i, containerCopy := range pod.Spec.Containers { // We will determine the effective security context for the container and validate against that // since that is how the sc provider will eventually apply settings in the runtime. // This results in an SC that is based on the Pod's PSC with the set fields from the container // overriding pod level settings. containerCopy.SecurityContext = sc.InternalDetermineEffectiveSecurityContext(pod, &containerCopy) sc, scAnnotations, err := provider.CreateContainerSecurityContext(pod, &containerCopy) if err != nil { errs = append(errs, field.Invalid(field.NewPath("spec", "containers").Index(i).Child("securityContext"), "", err.Error())) continue } generatedSCs[i] = sc containerCopy.SecurityContext = sc pod.Annotations = scAnnotations errs = append(errs, provider.ValidateContainerSecurityContext(pod, &containerCopy, field.NewPath("spec", "containers").Index(i).Child("securityContext"))...) } if len(errs) > 0 { // ensure psc is not mutated if there are errors pod.Spec.SecurityContext = originalPSC pod.Annotations = originalAnnotations return errs } // if we've reached this code then we've generated and validated an SC for every container in the // pod so let's apply what we generated. Note: the psc is already applied. for i, sc := range generatedInitSCs { pod.Spec.InitContainers[i].SecurityContext = sc } for i, sc := range generatedSCs { pod.Spec.Containers[i].SecurityContext = sc } return nil }