pspmigrator/pspmutating.go
2022-06-24 10:13:51 -07:00

134 lines
4.6 KiB
Go

package pspmigrator
import (
"context"
"fmt"
"strings"
"github.com/go-test/deep"
v1 "k8s.io/api/core/v1"
"k8s.io/api/policy/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
func GetContainerSecurityContexts(podSpec v1.PodSpec) []*v1.SecurityContext {
scs := make([]*v1.SecurityContext, 0)
for _, c := range podSpec.Containers {
scs = append(scs, c.SecurityContext)
}
return scs
}
func GetPSPAnnotations(annotations map[string]string) map[string]string {
pspAnnotations := make(map[string]string)
for ann, val := range annotations {
if strings.Contains(ann, "seccomp.security") || strings.Contains(ann, "apparmor.security") {
pspAnnotations[ann] = val
}
}
return pspAnnotations
}
// IsPodBeingMutatedByPSP returns whether a pod is likely mutated by a PSP object. It also returns the difference
// of the securityContext attribute between the parent controller (e.g. Deployment) and the running pod.
func IsPodBeingMutatedByPSP(pod *v1.Pod, clientset *kubernetes.Clientset) (mutating bool, diff []string, err error) {
diff = make([]string, 0)
if len(pod.ObjectMeta.OwnerReferences) > 0 {
var owner metav1.OwnerReference
for _, reference := range pod.ObjectMeta.OwnerReferences {
if reference.Controller != nil && *reference.Controller == true {
owner = reference
break
}
}
var parentPod v1.PodTemplateSpec
if owner.Kind == "ReplicaSet" {
rs, err := clientset.AppsV1().ReplicaSets(pod.Namespace).Get(context.TODO(), owner.Name, metav1.GetOptions{})
if err != nil {
return false, diff, err
}
parentPod = rs.Spec.Template
}
if owner.Kind == "DaemonSet" {
ds, err := clientset.AppsV1().DaemonSets(pod.Namespace).Get(context.TODO(), owner.Name, metav1.GetOptions{})
if err != nil {
return false, diff, err
}
parentPod = ds.Spec.Template
}
if owner.Kind == "Node" {
return false, diff, fmt.Errorf("Pod with ownerReference of kind Node is not supported. OwnerReference of pod %v was %#v", pod.Name, owner)
}
if diffNew := deep.Equal(GetContainerSecurityContexts(parentPod.Spec), GetContainerSecurityContexts(pod.Spec)); diffNew != nil {
diff = append(diff, diffNew...)
}
if diffNew := deep.Equal(parentPod.Spec.SecurityContext, pod.Spec.SecurityContext); diffNew != nil {
diff = append(diff, diffNew...)
}
if diffNew := deep.Equal(GetPSPAnnotations(parentPod.ObjectMeta.Annotations), GetPSPAnnotations(pod.ObjectMeta.Annotations)); diffNew != nil {
diff = append(diff, diffNew...)
}
}
if len(diff) > 0 {
return true, diff, nil
}
return false, diff, nil
}
// IsPSPMutating checks wheter a PodSecurityPolicy is potentially mutating
// pods. It returns true if one of the fields or annotations used in the
// PodSecurityPolicy is suspected to be mutating pods. The field or annotations
// that are suspected to be mutating are returned as well.
func IsPSPMutating(pspObj *v1beta1.PodSecurityPolicy) (mutating bool, fields, annotations []string) {
fields = make([]string, 0)
annotations = make([]string, 0)
if len(pspObj.Spec.DefaultAddCapabilities) > 0 {
fields = append(fields, "DefaultAddCapabilities")
}
if len(pspObj.Spec.RequiredDropCapabilities) > 0 {
fields = append(fields, "RequiredDropCapabilities")
}
if pspObj.Spec.SELinux.Rule != v1beta1.SELinuxStrategyRunAsAny {
fields = append(fields, "SELinux")
}
if pspObj.Spec.RunAsUser.Rule != v1beta1.RunAsUserStrategyRunAsAny {
fields = append(fields, "RunAsUser")
}
if pspObj.Spec.RunAsGroup != nil && pspObj.Spec.RunAsGroup.Rule == v1beta1.RunAsGroupStrategyMustRunAs {
fields = append(fields, "RunAsGroup")
}
if pspObj.Spec.SupplementalGroups.Rule != v1beta1.SupplementalGroupsStrategyRunAsAny {
fields = append(fields, "SupplementalGroups")
}
if pspObj.Spec.FSGroup.Rule != v1beta1.FSGroupStrategyRunAsAny {
fields = append(fields, "FSGroup")
}
if pspObj.Spec.ReadOnlyRootFilesystem != false {
fields = append(fields, "ReadOnlyRootFilesystem")
}
if pspObj.Spec.DefaultAllowPrivilegeEscalation != nil {
fields = append(fields, "DefaultAllowPrivilegeEscalation")
}
if pspObj.Spec.AllowPrivilegeEscalation != nil && *pspObj.Spec.AllowPrivilegeEscalation != true {
fields = append(fields, "AllowPrivilegeEscalation")
}
mutatingAnnotations := make(map[string]bool)
mutatingAnnotations["seccomp.security.alpha.kubernetes.io/defaultProfileName"] = true
mutatingAnnotations["apparmor.security.beta.kubernetes.io/defaultProfileName"] = true
for k, _ := range pspObj.Annotations {
if _, ok := mutatingAnnotations[k]; ok {
annotations = append(annotations, k)
}
}
if len(fields) > 0 || len(annotations) > 0 {
return true, fields, annotations
}
return false, fields, annotations
}