// 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 }
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 scc := &kapi.SecurityContextConstraints{ ObjectMeta: kapi.ObjectMeta{ Name: "test scc", }, SELinuxContext: kapi.SELinuxContextStrategyOptions{ Type: kapi.SELinuxStrategyRunAsAny, }, RunAsUser: kapi.RunAsUserStrategyOptions{ Type: kapi.RunAsUserStrategyMustRunAs, UID: &uid, }, } 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 }{ "container SC is not changed when invalid": { pod: &kapi.Pod{ Spec: kapi.PodSpec{ Containers: []kapi.Container{createContainer(true)}, }, }, shouldValidate: false, }, "must validate all containers": { pod: &kapi.Pod{ Spec: kapi.PodSpec{ // good pod and bad pod Containers: []kapi.Container{createContainer(false), createContainer(true)}, }, }, shouldValidate: false, }, "pod validates": { pod: &kapi.Pod{ Spec: kapi.PodSpec{ Containers: []kapi.Container{createContainer(false)}, }, }, shouldValidate: true, }, } for k, v := range testCases { errs := assignSecurityContext(provider, v.pod) 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") continue } // if we shouldn't have validated ensure that uid is not set on the containers if !v.shouldValidate { 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 dont' 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 { 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) } } } } }