func TestAssignSecurityContext(t *testing.T) { // set up test data // scc that will deny privileged container requests and has a default value for a field (uid) var uid int64 = 9999 fsGroup := int64(1) scc := &kapi.SecurityContextConstraints{ ObjectMeta: kapi.ObjectMeta{ Name: "test scc", }, SELinuxContext: kapi.SELinuxContextStrategyOptions{ Type: kapi.SELinuxStrategyRunAsAny, }, RunAsUser: kapi.RunAsUserStrategyOptions{ Type: kapi.RunAsUserStrategyMustRunAs, UID: &uid, }, // require allocation for a field in the psc as well to test changes/no changes FSGroup: kapi.FSGroupStrategyOptions{ Type: kapi.FSGroupStrategyMustRunAs, Ranges: []kapi.IDRange{ {Min: fsGroup, Max: fsGroup}, }, }, SupplementalGroups: kapi.SupplementalGroupsStrategyOptions{ Type: kapi.SupplementalGroupsStrategyRunAsAny, }, } provider, err := kscc.NewSimpleProvider(scc) if err != nil { t.Fatalf("failed to create provider: %v", err) } createContainer := func(priv bool) kapi.Container { return kapi.Container{ SecurityContext: &kapi.SecurityContext{ Privileged: &priv, }, } } // these are set up such that the containers always have a nil uid. If the case should not // validate then the uids should not have been updated by the strategy. If the case should // validate then uids should be set. This is ensuring that we're hanging on to the old SC // as we generate/validate and only updating the original container if the entire pod validates testCases := map[string]struct { pod *kapi.Pod shouldValidate bool expectedUID *int64 }{ "pod and container SC is not changed when invalid": { pod: &kapi.Pod{ Spec: kapi.PodSpec{ SecurityContext: &kapi.PodSecurityContext{}, Containers: []kapi.Container{createContainer(true)}, }, }, shouldValidate: false, }, "must validate all containers": { pod: &kapi.Pod{ Spec: kapi.PodSpec{ // good pod and bad pod SecurityContext: &kapi.PodSecurityContext{}, Containers: []kapi.Container{createContainer(false), createContainer(true)}, }, }, shouldValidate: false, }, "pod validates": { pod: &kapi.Pod{ Spec: kapi.PodSpec{ SecurityContext: &kapi.PodSecurityContext{}, Containers: []kapi.Container{createContainer(false)}, }, }, shouldValidate: true, }, } for k, v := range testCases { errs := assignSecurityContext(provider, v.pod, nil) if v.shouldValidate && len(errs) > 0 { t.Errorf("%s expected to validate but received errors %v", k, errs) continue } if !v.shouldValidate && len(errs) == 0 { t.Errorf("%s expected validation errors but received none", k) continue } // if we shouldn't have validated ensure that uid is not set on the containers // and ensure the psc does not have fsgroup set if !v.shouldValidate { if v.pod.Spec.SecurityContext.FSGroup != nil { t.Errorf("%s had a non-nil FSGroup %d. FSGroup should not be set on test cases that don't validate", k, *v.pod.Spec.SecurityContext.FSGroup) } for _, c := range v.pod.Spec.Containers { if c.SecurityContext.RunAsUser != nil { t.Errorf("%s had non-nil UID %d. UID should not be set on test cases that don't validate", k, *c.SecurityContext.RunAsUser) } } } // if we validated then the pod sc should be updated now with the defaults from the SCC if v.shouldValidate { if *v.pod.Spec.SecurityContext.FSGroup != fsGroup { t.Errorf("%s expected fsgroup to be defaulted but found %v", k, v.pod.Spec.SecurityContext.FSGroup) } for _, c := range v.pod.Spec.Containers { if *c.SecurityContext.RunAsUser != uid { t.Errorf("%s expected uid to be defaulted to %d but found %v", k, uid, c.SecurityContext.RunAsUser) } } } } }
// createProvidersFromConstraints creates providers from the constraints supplied, including // looking up pre-allocated values if necessary using the pod's namespace. func (c *constraint) createProvidersFromConstraints(ns string, sccs []*kapi.SecurityContextConstraints) ([]scc.SecurityContextConstraintsProvider, []error) { var ( // namespace is declared here for reuse but we will not fetch it unless required by the matched constraints namespace *kapi.Namespace // collected providers providers []scc.SecurityContextConstraintsProvider // collected errors to return errs []error ) // set pre-allocated values on constraints for _, constraint := range sccs { var err error resolveUIDRange := requiresPreAllocatedUIDRange(constraint) resolveSELinuxLevel := requiresPreAllocatedSELinuxLevel(constraint) if resolveUIDRange || resolveSELinuxLevel { var min, max *int64 var level string // Ensure we have the namespace if namespace, err = c.getNamespace(ns, namespace); err != nil { errs = append(errs, fmt.Errorf("error fetching namespace %s required to preallocate values for %s: %v", ns, constraint.Name, err)) continue } // Resolve the values from the namespace if resolveUIDRange { if min, max, err = getPreallocatedUIDRange(namespace); err != nil { errs = append(errs, fmt.Errorf("unable to find pre-allocated uid annotation for namespace %s while trying to configure SCC %s: %v", namespace.Name, constraint.Name, err)) continue } } if resolveSELinuxLevel { if level, err = getPreallocatedLevel(namespace); err != nil { errs = append(errs, fmt.Errorf("unable to find pre-allocated mcs annotation for namespace %s while trying to configure SCC %s: %v", namespace.Name, constraint.Name, err)) continue } } // Make a copy of the constraint so we don't mutate the store's cache var constraintCopy kapi.SecurityContextConstraints = *constraint constraint = &constraintCopy if resolveSELinuxLevel && constraint.SELinuxContext.SELinuxOptions != nil { // Make a copy of the SELinuxOptions so we don't mutate the store's cache var seLinuxOptionsCopy kapi.SELinuxOptions = *constraint.SELinuxContext.SELinuxOptions constraint.SELinuxContext.SELinuxOptions = &seLinuxOptionsCopy } // Set the resolved values if resolveUIDRange { constraint.RunAsUser.UIDRangeMin = min constraint.RunAsUser.UIDRangeMax = max } if resolveSELinuxLevel { if constraint.SELinuxContext.SELinuxOptions == nil { constraint.SELinuxContext.SELinuxOptions = &kapi.SELinuxOptions{} } constraint.SELinuxContext.SELinuxOptions.Level = level } } // Create the provider provider, err := scc.NewSimpleProvider(constraint) if err != nil { errs = append(errs, fmt.Errorf("error creating provider for SCC %s in namespace %s: %v", constraint.Name, ns, err)) continue } providers = append(providers, provider) } return providers, errs }
// createProvidersFromConstraints creates providers from the constraints supplied, including // looking up pre-allocated values if necessary using the pod's namespace. func (c *constraint) createProvidersFromConstraints(ns string, sccs []*kapi.SecurityContextConstraints) ([]scc.SecurityContextConstraintsProvider, []error) { var ( // namespace is declared here for reuse but we will not fetch it unless required by the matched constraints namespace *kapi.Namespace // collected providers providers []scc.SecurityContextConstraintsProvider // collected errors to return errs []error ) // set pre-allocated values on constraints for _, constraint := range sccs { var err error resolveUIDRange := requiresPreAllocatedUIDRange(constraint) resolveSELinuxLevel := requiresPreAllocatedSELinuxLevel(constraint) resolveFSGroup := requiresPreallocatedFSGroup(constraint) resolveSupplementalGroups := requiresPreallocatedSupplementalGroups(constraint) requiresNamespaceAllocations := resolveUIDRange || resolveSELinuxLevel || resolveFSGroup || resolveSupplementalGroups if requiresNamespaceAllocations { // Ensure we have the namespace namespace, err = c.getNamespace(ns, namespace) if err != nil { errs = append(errs, fmt.Errorf("error fetching namespace %s required to preallocate values for %s: %v", ns, constraint.Name, err)) continue } } // Make a copy of the constraint so we don't mutate the store's cache var constraintCopy kapi.SecurityContextConstraints = *constraint constraint = &constraintCopy // Resolve the values from the namespace if resolveUIDRange { constraint.RunAsUser.UIDRangeMin, constraint.RunAsUser.UIDRangeMax, err = getPreallocatedUIDRange(namespace) if err != nil { errs = append(errs, fmt.Errorf("unable to find pre-allocated uid annotation for namespace %s while trying to configure SCC %s: %v", namespace.Name, constraint.Name, err)) continue } } if resolveSELinuxLevel { var level string if level, err = getPreallocatedLevel(namespace); err != nil { errs = append(errs, fmt.Errorf("unable to find pre-allocated mcs annotation for namespace %s while trying to configure SCC %s: %v", namespace.Name, constraint.Name, err)) continue } // SELinuxOptions is a pointer, if we are resolving and it is already initialized // we need to make a copy of it so we don't manipulate the store's cache. if constraint.SELinuxContext.SELinuxOptions != nil { var seLinuxOptionsCopy kapi.SELinuxOptions = *constraint.SELinuxContext.SELinuxOptions constraint.SELinuxContext.SELinuxOptions = &seLinuxOptionsCopy } else { constraint.SELinuxContext.SELinuxOptions = &kapi.SELinuxOptions{} } constraint.SELinuxContext.SELinuxOptions.Level = level } if resolveFSGroup { fsGroup, err := getPreallocatedFSGroup(namespace) if err != nil { errs = append(errs, fmt.Errorf("unable to find pre-allocated group annotation for namespace %s while trying to configure SCC %s: %v", namespace.Name, constraint.Name, err)) continue } constraint.FSGroup.Ranges = fsGroup } if resolveSupplementalGroups { supplementalGroups, err := getPreallocatedSupplementalGroups(namespace) if err != nil { errs = append(errs, fmt.Errorf("unable to find pre-allocated group annotation for namespace %s while trying to configure SCC %s: %v", namespace.Name, constraint.Name, err)) continue } constraint.SupplementalGroups.Ranges = supplementalGroups } // Create the provider provider, err := scc.NewSimpleProvider(constraint) if err != nil { errs = append(errs, fmt.Errorf("error creating provider for SCC %s in namespace %s: %v", constraint.Name, ns, err)) continue } providers = append(providers, provider) } return providers, errs }
// CreateProviderFromConstraint creates a SecurityContextConstraintProvider from a SecurityContextConstraint func CreateProviderFromConstraint(ns string, namespace *kapi.Namespace, constraint *kapi.SecurityContextConstraints, client clientset.Interface) (kscc.SecurityContextConstraintsProvider, *kapi.Namespace, error) { var err error resolveUIDRange := requiresPreAllocatedUIDRange(constraint) resolveSELinuxLevel := requiresPreAllocatedSELinuxLevel(constraint) resolveFSGroup := requiresPreallocatedFSGroup(constraint) resolveSupplementalGroups := requiresPreallocatedSupplementalGroups(constraint) requiresNamespaceAllocations := resolveUIDRange || resolveSELinuxLevel || resolveFSGroup || resolveSupplementalGroups if requiresNamespaceAllocations { // Ensure we have the namespace namespace, err = getNamespaceByName(ns, namespace, client) if err != nil { return nil, namespace, fmt.Errorf("error fetching namespace %s required to preallocate values for %s: %v", ns, constraint.Name, err) } } // Make a copy of the constraint so we don't mutate the store's cache var constraintCopy kapi.SecurityContextConstraints = *constraint constraint = &constraintCopy // Resolve the values from the namespace if resolveUIDRange { constraint.RunAsUser.UIDRangeMin, constraint.RunAsUser.UIDRangeMax, err = getPreallocatedUIDRange(namespace) if err != nil { return nil, namespace, fmt.Errorf("unable to find pre-allocated uid annotation for namespace %s while trying to configure SCC %s: %v", namespace.Name, constraint.Name, err) } } if resolveSELinuxLevel { var level string if level, err = getPreallocatedLevel(namespace); err != nil { return nil, namespace, fmt.Errorf("unable to find pre-allocated mcs annotation for namespace %s while trying to configure SCC %s: %v", namespace.Name, constraint.Name, err) } // SELinuxOptions is a pointer, if we are resolving and it is already initialized // we need to make a copy of it so we don't manipulate the store's cache. if constraint.SELinuxContext.SELinuxOptions != nil { var seLinuxOptionsCopy kapi.SELinuxOptions = *constraint.SELinuxContext.SELinuxOptions constraint.SELinuxContext.SELinuxOptions = &seLinuxOptionsCopy } else { constraint.SELinuxContext.SELinuxOptions = &kapi.SELinuxOptions{} } constraint.SELinuxContext.SELinuxOptions.Level = level } if resolveFSGroup { fsGroup, err := getPreallocatedFSGroup(namespace) if err != nil { return nil, namespace, fmt.Errorf("unable to find pre-allocated group annotation for namespace %s while trying to configure SCC %s: %v", namespace.Name, constraint.Name, err) } constraint.FSGroup.Ranges = fsGroup } if resolveSupplementalGroups { supplementalGroups, err := getPreallocatedSupplementalGroups(namespace) if err != nil { return nil, namespace, fmt.Errorf("unable to find pre-allocated group annotation for namespace %s while trying to configure SCC %s: %v", namespace.Name, constraint.Name, err) } constraint.SupplementalGroups.Ranges = supplementalGroups } // Create the provider provider, err := kscc.NewSimpleProvider(constraint) if err != nil { return nil, namespace, fmt.Errorf("error creating provider for SCC %s in namespace %s: %v", constraint.Name, ns, err) } return provider, namespace, nil }