// Create registers a given new PodSecurityPolicyReview instance to r.registry. func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { pspr, ok := obj.(*securityapi.PodSecurityPolicyReview) if !ok { return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a PodSecurityPolicyReview: %#v", obj)) } if errs := securityvalidation.ValidatePodSecurityPolicyReview(pspr); len(errs) > 0 { return nil, kapierrors.NewInvalid(kapi.Kind("PodSecurityPolicyReview"), "", errs) } ns, ok := kapi.NamespaceFrom(ctx) if !ok { return nil, kapierrors.NewBadRequest("namespace parameter required.") } serviceAccounts, err := getServiceAccounts(pspr.Spec, r.saCache, ns) if err != nil { return nil, kapierrors.NewBadRequest(err.Error()) } if len(serviceAccounts) == 0 { glog.Errorf("No service accounts for namespace %s", ns) return nil, kapierrors.NewBadRequest(fmt.Sprintf("unable to find ServiceAccount for namespace: %s", ns)) } errs := []error{} newStatus := securityapi.PodSecurityPolicyReviewStatus{} for _, sa := range serviceAccounts { userInfo := serviceaccount.UserInfo(ns, sa.Name, "") saConstraints, err := r.sccMatcher.FindApplicableSCCs(userInfo) if err != nil { errs = append(errs, fmt.Errorf("unable to find SecurityContextConstraints for ServiceAccount %s: %v", sa.Name, err)) continue } oscc.DeduplicateSecurityContextConstraints(saConstraints) sort.Sort(oscc.ByPriority(saConstraints)) var namespace *kapi.Namespace for _, constraint := range saConstraints { var ( provider kscc.SecurityContextConstraintsProvider err error ) pspsrs := securityapi.PodSecurityPolicySubjectReviewStatus{} if provider, namespace, err = oscc.CreateProviderFromConstraint(ns, namespace, constraint, r.client); err != nil { errs = append(errs, fmt.Errorf("unable to create provider for service account %s: %v", sa.Name, err)) continue } _, err = podsecuritypolicysubjectreview.FillPodSecurityPolicySubjectReviewStatus(&pspsrs, provider, pspr.Spec.Template.Spec, constraint) if err != nil { glog.Errorf("unable to fill PodSecurityPolicyReviewStatus from constraint %v", err) continue } sapsprs := securityapi.ServiceAccountPodSecurityPolicyReviewStatus{pspsrs, sa.Name} newStatus.AllowedServiceAccounts = append(newStatus.AllowedServiceAccounts, sapsprs) } } if len(errs) > 0 { return nil, kapierrors.NewBadRequest(fmt.Sprintf("%s", kerrors.NewAggregate(errs))) } pspr.Status = newStatus return pspr, nil }
// Create registers a given new PodSecurityPolicySubjectReview instance to r.registry. func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { pspsr, ok := obj.(*securityapi.PodSecurityPolicySubjectReview) if !ok { return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a PodSecurityPolicySubjectReview: %#v", obj)) } ns, ok := kapi.NamespaceFrom(ctx) if !ok { return nil, kapierrors.NewBadRequest("namespace parameter required.") } if errs := securityvalidation.ValidatePodSecurityPolicySubjectReview(pspsr); len(errs) > 0 { return nil, kapierrors.NewInvalid(kapi.Kind("PodSecurityPolicySubjectReview"), "", errs) } userInfo := &user.DefaultInfo{Name: pspsr.Spec.User, Groups: pspsr.Spec.Groups} matchedConstraints, err := r.sccMatcher.FindApplicableSCCs(userInfo) if err != nil { return nil, kapierrors.NewBadRequest(fmt.Sprintf("unable to find SecurityContextConstraints: %v", err)) } saName := pspsr.Spec.Template.Spec.ServiceAccountName if len(saName) > 0 { saUserInfo := serviceaccount.UserInfo(ns, saName, "") saConstraints, err := r.sccMatcher.FindApplicableSCCs(saUserInfo) if err != nil { return nil, kapierrors.NewBadRequest(fmt.Sprintf("unable to find SecurityContextConstraints: %v", err)) } matchedConstraints = append(matchedConstraints, saConstraints...) } oscc.DeduplicateSecurityContextConstraints(matchedConstraints) sort.Sort(oscc.ByPriority(matchedConstraints)) var namespace *kapi.Namespace for _, constraint := range matchedConstraints { var ( provider kscc.SecurityContextConstraintsProvider err error ) if provider, namespace, err = oscc.CreateProviderFromConstraint(ns, namespace, constraint, r.client); err != nil { glog.Errorf("Unable to create provider for constraint: %v", err) continue } filled, err := FillPodSecurityPolicySubjectReviewStatus(&pspsr.Status, provider, pspsr.Spec.Template.Spec, constraint) if err != nil { glog.Errorf("unable to fill PodSecurityPolicySubjectReviewStatus from constraint %v", err) continue } if filled { return pspsr, nil } } return pspsr, nil }
func TestAdmitWithPrioritizedSCC(t *testing.T) { // scc with high priority but very restrictive. restricted := restrictiveSCC() restrictedPriority := int32(100) restricted.Priority = &restrictedPriority // sccs with matching priorities but one will have a higher point score (by the run as user strategy) uidFive := int64(5) matchingPrioritySCCOne := laxSCC() matchingPrioritySCCOne.Name = "matchingPrioritySCCOne" matchingPrioritySCCOne.RunAsUser = kapi.RunAsUserStrategyOptions{ Type: kapi.RunAsUserStrategyMustRunAs, UID: &uidFive, } matchingPriority := int32(5) matchingPrioritySCCOne.Priority = &matchingPriority matchingPrioritySCCTwo := laxSCC() matchingPrioritySCCTwo.Name = "matchingPrioritySCCTwo" matchingPrioritySCCTwo.RunAsUser = kapi.RunAsUserStrategyOptions{ Type: kapi.RunAsUserStrategyMustRunAsRange, UIDRangeMin: &uidFive, UIDRangeMax: &uidFive, } matchingPrioritySCCTwo.Priority = &matchingPriority // sccs with matching priorities and scores so should be matched by sorted name uidSix := int64(6) matchingPriorityAndScoreSCCOne := laxSCC() matchingPriorityAndScoreSCCOne.Name = "matchingPriorityAndScoreSCCOne" matchingPriorityAndScoreSCCOne.RunAsUser = kapi.RunAsUserStrategyOptions{ Type: kapi.RunAsUserStrategyMustRunAs, UID: &uidSix, } matchingPriorityAndScorePriority := int32(1) matchingPriorityAndScoreSCCOne.Priority = &matchingPriorityAndScorePriority matchingPriorityAndScoreSCCTwo := laxSCC() matchingPriorityAndScoreSCCTwo.Name = "matchingPriorityAndScoreSCCTwo" matchingPriorityAndScoreSCCTwo.RunAsUser = kapi.RunAsUserStrategyOptions{ Type: kapi.RunAsUserStrategyMustRunAs, UID: &uidSix, } matchingPriorityAndScoreSCCTwo.Priority = &matchingPriorityAndScorePriority // we will expect these to sort as: expectedSort := []string{"restrictive", "matchingPrioritySCCOne", "matchingPrioritySCCTwo", "matchingPriorityAndScoreSCCOne", "matchingPriorityAndScoreSCCTwo"} sccsToSort := []*kapi.SecurityContextConstraints{matchingPriorityAndScoreSCCTwo, matchingPriorityAndScoreSCCOne, matchingPrioritySCCTwo, matchingPrioritySCCOne, restricted} sort.Sort(oscc.ByPriority(sccsToSort)) for i, scc := range sccsToSort { if scc.Name != expectedSort[i] { t.Fatalf("unexpected sort found %s at element %d but expected %s", scc.Name, i, expectedSort[i]) } } // sorting works as we're expecting // now, to test we will craft some requests that are targeted to validate against specific // SCCs and ensure that they come out with the right annotation. This means admission // is using the sort strategy we expect. namespace := createNamespaceForTest() serviceAccount := createSAForTest() tc := clientsetfake.NewSimpleClientset(namespace, serviceAccount) cache := &oscache.IndexerToSecurityContextConstraintsLister{ Indexer: cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}), } for _, scc := range sccsToSort { err := cache.Add(scc) if err != nil { t.Fatalf("error adding sccs to store: %v", err) } } // create the admission plugin plugin := NewTestAdmission(cache, tc) // match the restricted SCC testSCCAdmission(goodPod(), plugin, restricted.Name, t) // match matchingPrioritySCCOne by setting RunAsUser to 5 matchingPrioritySCCOnePod := goodPod() matchingPrioritySCCOnePod.Spec.Containers[0].SecurityContext.RunAsUser = &uidFive testSCCAdmission(matchingPrioritySCCOnePod, plugin, matchingPrioritySCCOne.Name, t) // match matchingPriorityAndScoreSCCOne by setting RunAsUser to 6 matchingPriorityAndScoreSCCOnePod := goodPod() matchingPriorityAndScoreSCCOnePod.Spec.Containers[0].SecurityContext.RunAsUser = &uidSix testSCCAdmission(matchingPriorityAndScoreSCCOnePod, plugin, matchingPriorityAndScoreSCCOne.Name, t) }
// Admit determines if the pod should be admitted based on the requested security context // and the available SCCs. // // 1. Find SCCs for the user. // 2. Find SCCs for the SA. If there is an error retrieving SA SCCs it is not fatal. // 3. Remove duplicates between the user/SA SCCs. // 4. Create the providers, includes setting pre-allocated values if necessary. // 5. Try to generate and validate an SCC with providers. If we find one then admit the pod // with the validated SCC. If we don't find any reject the pod and give all errors from the // failed attempts. // On updates, the BeforeUpdate of the pod strategy only zeroes out the status. That means that // any change that claims the pod is no longer privileged will be removed. That should hold until // we get a true old/new set of objects in. func (c *constraint) Admit(a kadmission.Attributes) error { if a.GetResource().GroupResource() != kapi.Resource("pods") { return nil } if len(a.GetSubresource()) != 0 { return nil } pod, ok := a.GetObject().(*kapi.Pod) // if we can't convert then we don't handle this object so just return if !ok { return nil } // get all constraints that are usable by the user glog.V(4).Infof("getting security context constraints for pod %s (generate: %s) in namespace %s with user info %v", pod.Name, pod.GenerateName, a.GetNamespace(), a.GetUserInfo()) sccMatcher := oscc.NewDefaultSCCMatcher(c.sccLister) matchedConstraints, err := sccMatcher.FindApplicableSCCs(a.GetUserInfo()) if err != nil { return kadmission.NewForbidden(a, err) } // get all constraints that are usable by the SA if len(pod.Spec.ServiceAccountName) > 0 { userInfo := serviceaccount.UserInfo(a.GetNamespace(), pod.Spec.ServiceAccountName, "") glog.V(4).Infof("getting security context constraints for pod %s (generate: %s) with service account info %v", pod.Name, pod.GenerateName, userInfo) saConstraints, err := sccMatcher.FindApplicableSCCs(userInfo) if err != nil { return kadmission.NewForbidden(a, err) } matchedConstraints = append(matchedConstraints, saConstraints...) } // remove duplicate constraints and sort matchedConstraints = oscc.DeduplicateSecurityContextConstraints(matchedConstraints) sort.Sort(oscc.ByPriority(matchedConstraints)) providers, errs := oscc.CreateProvidersFromConstraints(a.GetNamespace(), matchedConstraints, c.client) logProviders(pod, providers, errs) if len(providers) == 0 { return kadmission.NewForbidden(a, fmt.Errorf("no providers available to validate pod request")) } // all containers in a single pod must validate under a single provider or we will reject the request validationErrs := field.ErrorList{} for _, provider := range providers { if errs := oscc.AssignSecurityContext(provider, pod, field.NewPath(fmt.Sprintf("provider %s: ", provider.GetSCCName()))); len(errs) > 0 { validationErrs = append(validationErrs, errs...) continue } // the entire pod validated, annotate and accept the pod glog.V(4).Infof("pod %s (generate: %s) validated against provider %s", pod.Name, pod.GenerateName, provider.GetSCCName()) if pod.ObjectMeta.Annotations == nil { pod.ObjectMeta.Annotations = map[string]string{} } pod.ObjectMeta.Annotations[allocator.ValidatedSCCAnnotation] = provider.GetSCCName() return nil } // we didn't validate against any security context constraint provider, reject the pod and give the errors for each attempt glog.V(4).Infof("unable to validate pod %s (generate: %s) against any security context constraint: %v", pod.Name, pod.GenerateName, validationErrs) return kadmission.NewForbidden(a, fmt.Errorf("unable to validate against any security context constraint: %v", validationErrs)) }