diff --git a/pkg/printers/internalversion/describe.go b/pkg/printers/internalversion/describe.go index 45bb2d4ddf1..0d64b65e979 100644 --- a/pkg/printers/internalversion/describe.go +++ b/pkg/printers/internalversion/describe.go @@ -138,6 +138,7 @@ func describerMap(c clientset.Interface) map[schema.GroupKind]printers.Describer extensions.Kind("ReplicaSet"): &ReplicaSetDescriber{c}, extensions.Kind("NetworkPolicy"): &ExtensionsNetworkPolicyDescriber{c}, + extensions.Kind("PodSecurityPolicy"): &PodSecurityPolicyDescriber{c}, autoscaling.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c}, extensions.Kind("DaemonSet"): &DaemonSetDescriber{c}, extensions.Kind("Deployment"): &DeploymentDescriber{c, versionedClientsetForDeployment(c)}, @@ -3177,6 +3178,135 @@ func describePriorityClass(pc *scheduling.PriorityClass, events *api.EventList) }) } +// PodSecurityPolicyDescriber generates information about a PodSecurityPolicy. +type PodSecurityPolicyDescriber struct { + clientset.Interface +} + +func (d *PodSecurityPolicyDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { + psp, err := d.Extensions().PodSecurityPolicies().Get(name, metav1.GetOptions{}) + if err != nil { + return "", err + } + + return describePodSecurityPolicy(psp) +} + +func describePodSecurityPolicy(psp *extensions.PodSecurityPolicy) (string, error) { + return tabbedString(func(out io.Writer) error { + w := NewPrefixWriter(out) + w.Write(LEVEL_0, "Name:\t%s\n", psp.Name) + + w.Write(LEVEL_0, "\nSettings:\n") + + w.Write(LEVEL_1, "Allow Privileged:\t%t\n", psp.Spec.Privileged) + w.Write(LEVEL_1, "Default Add Capabilities:\t%v\n", capsToString(psp.Spec.DefaultAddCapabilities)) + w.Write(LEVEL_1, "Required Drop Capabilities:\t%s\n", capsToString(psp.Spec.RequiredDropCapabilities)) + w.Write(LEVEL_1, "Allowed Capabilities:\t%s\n", capsToString(psp.Spec.AllowedCapabilities)) + w.Write(LEVEL_1, "Allowed Volume Types:\t%s\n", fsTypeToString(psp.Spec.Volumes)) + + w.Write(LEVEL_1, "Allow Host Network:\t%t\n", psp.Spec.HostNetwork) + w.Write(LEVEL_1, "Allow Host Ports:\t%s\n", hostPortRangeToString(psp.Spec.HostPorts)) + w.Write(LEVEL_1, "Allow Host PID:\t%t\n", psp.Spec.HostPID) + w.Write(LEVEL_1, "Allow Host IPC:\t%t\n", psp.Spec.HostIPC) + w.Write(LEVEL_1, "Read Only Root Filesystem:\t%v\n", psp.Spec.ReadOnlyRootFilesystem) + + w.Write(LEVEL_1, "SELinux Context Strategy: %s\t\n", string(psp.Spec.SELinux.Rule)) + var user, role, seLinuxType, level string + if psp.Spec.SELinux.SELinuxOptions != nil { + user = psp.Spec.SELinux.SELinuxOptions.User + role = psp.Spec.SELinux.SELinuxOptions.Role + seLinuxType = psp.Spec.SELinux.SELinuxOptions.Type + level = psp.Spec.SELinux.SELinuxOptions.Level + } + w.Write(LEVEL_2, "User:\t%s\n", stringOrNone(user)) + w.Write(LEVEL_2, "Role:\t%s\n", stringOrNone(role)) + w.Write(LEVEL_2, "Type:\t%s\n", stringOrNone(seLinuxType)) + w.Write(LEVEL_2, "Level:\t%s\n", stringOrNone(level)) + + w.Write(LEVEL_1, "Run As User Strategy: %s\t\n", string(psp.Spec.RunAsUser.Rule)) + w.Write(LEVEL_2, "Ranges:\t%s\n", userIDRangeToString(psp.Spec.RunAsUser.Ranges)) + + w.Write(LEVEL_1, "FSGroup Strategy: %s\t\n", string(psp.Spec.FSGroup.Rule)) + w.Write(LEVEL_2, "Ranges:\t%s\n", groupIDRangeToString(psp.Spec.FSGroup.Ranges)) + + w.Write(LEVEL_1, "Supplemental Groups Strategy: %s\t\n", string(psp.Spec.SupplementalGroups.Rule)) + w.Write(LEVEL_2, "Ranges:\t%s\n", groupIDRangeToString(psp.Spec.SupplementalGroups.Ranges)) + + return nil + }) +} + +func stringOrAll(s string) string { + if len(s) > 0 { + return s + } + return "*" +} + +func stringOrNone(s string) string { + if len(s) > 0 { + return s + } + return "" +} + +func fsTypeToString(volumes []extensions.FSType) string { + strVolumes := []string{} + for _, v := range volumes { + strVolumes = append(strVolumes, string(v)) + } + return stringOrNone(strings.Join(strVolumes, ",")) +} + +func hostPortRangeToString(ranges []extensions.HostPortRange) string { + formattedString := "" + if ranges != nil { + strRanges := []string{} + for _, r := range ranges { + strRanges = append(strRanges, fmt.Sprintf("%d-%d", r.Min, r.Max)) + } + formattedString = strings.Join(strRanges, ",") + } + return stringOrNone(formattedString) +} + +func userIDRangeToString(ranges []extensions.UserIDRange) string { + formattedString := "" + if ranges != nil { + strRanges := []string{} + for _, r := range ranges { + strRanges = append(strRanges, fmt.Sprintf("%d-%d", r.Min, r.Max)) + } + formattedString = strings.Join(strRanges, ",") + } + return stringOrNone(formattedString) +} + +func groupIDRangeToString(ranges []extensions.GroupIDRange) string { + formattedString := "" + if ranges != nil { + strRanges := []string{} + for _, r := range ranges { + strRanges = append(strRanges, fmt.Sprintf("%d-%d", r.Min, r.Max)) + } + formattedString = strings.Join(strRanges, ",") + } + return stringOrNone(formattedString) +} + +func capsToString(caps []api.Capability) string { + formattedString := "" + if caps != nil { + strCaps := []string{} + for _, c := range caps { + strCaps = append(strCaps, string(c)) + } + formattedString = strings.Join(strCaps, ",") + } + return stringOrNone(formattedString) +} + // newErrNoDescriber creates a new ErrNoDescriber with the names of the provided types. func newErrNoDescriber(types ...reflect.Type) error { names := make([]string, 0, len(types)) diff --git a/pkg/printers/internalversion/describe_test.go b/pkg/printers/internalversion/describe_test.go index 24d10763a5a..a541c1b5c6b 100644 --- a/pkg/printers/internalversion/describe_test.go +++ b/pkg/printers/internalversion/describe_test.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "reflect" + "regexp" "strings" "testing" "time" @@ -1462,6 +1463,63 @@ URL: http://localhost } } +func TestDescribePodSecurityPolicy(t *testing.T) { + expected := []string{ + "Name:\t*mypsp", + "Allow Privileged:\t*false", + "Default Add Capabilities:\t*", + "Required Drop Capabilities:\t*", + "Allowed Capabilities:\t*", + "Allowed Volume Types:\t*", + "Allow Host Network:\t*false", + "Allow Host Ports:\t*", + "Allow Host PID:\t*false", + "Allow Host IPC:\t*false", + "Read Only Root Filesystem:\t*false", + "SELinux Context Strategy: RunAsAny", + "User:\t*", + "Role:\t*", + "Type:\t*", + "Level:\t*", + "Run As User Strategy: RunAsAny", + "FSGroup Strategy: RunAsAny", + "Supplemental Groups Strategy: RunAsAny", + } + + fake := fake.NewSimpleClientset(&extensions.PodSecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mypsp", + }, + Spec: extensions.PodSecurityPolicySpec{ + SELinux: extensions.SELinuxStrategyOptions{ + Rule: extensions.SELinuxStrategyRunAsAny, + }, + RunAsUser: extensions.RunAsUserStrategyOptions{ + Rule: extensions.RunAsUserStrategyRunAsAny, + }, + FSGroup: extensions.FSGroupStrategyOptions{ + Rule: extensions.FSGroupStrategyRunAsAny, + }, + SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{ + Rule: extensions.SupplementalGroupsStrategyRunAsAny, + }, + }, + }) + + c := &describeClient{T: t, Namespace: "", Interface: fake} + d := PodSecurityPolicyDescriber{c} + out, err := d.Describe("", "mypsp", printers.DescriberSettings{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + for _, item := range expected { + if matched, _ := regexp.MatchString(item, out); !matched { + t.Errorf("Expected to find %q in: %q", item, out) + } + } +} + func TestDescribeResourceQuota(t *testing.T) { fake := fake.NewSimpleClientset(&api.ResourceQuota{ ObjectMeta: metav1.ObjectMeta{