Example #1
0
// 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 = &copy
	} 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
}
Example #2
0
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
}
Example #3
0
// 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 = &copy
	} 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
}
Example #4
0
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
}
Example #5
0
// 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 = &copy
	} 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
}
Example #6
0
// 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
}