From 2252a464fdede913fe5f8c3feee8fe1890c4a371 Mon Sep 17 00:00:00 2001 From: Paul Weil Date: Thu, 29 Oct 2015 13:29:57 -0400 Subject: [PATCH] new SCCs --- .../securitycontextconstraints.go | 150 +++++++++++++++++- .../securitycontextconstraints_test.go | 70 ++++++++ pkg/cmd/server/origin/ensure.go | 7 +- 3 files changed, 220 insertions(+), 7 deletions(-) create mode 100644 pkg/cmd/server/bootstrappolicy/securitycontextconstraints_test.go diff --git a/pkg/cmd/server/bootstrappolicy/securitycontextconstraints.go b/pkg/cmd/server/bootstrappolicy/securitycontextconstraints.go index 3ecd21b35392..bd43e21e1027 100644 --- a/pkg/cmd/server/bootstrappolicy/securitycontextconstraints.go +++ b/pkg/cmd/server/bootstrappolicy/securitycontextconstraints.go @@ -6,18 +6,46 @@ import ( const ( // SecurityContextConstraintPrivileged is used as the name for the system default privileged scc. - SecurityContextConstraintPrivileged = "privileged" + SecurityContextConstraintPrivileged = "privileged" + SecurityContextConstraintPrivilegedDesc = "privileged allows access to all privileged and host features and the ability to run as any user, any group, any fsGroup, and with any SELinux context. WARNING: this is the most relaxed SCC and should be used only for cluster administration. Grant with caution." + // SecurityContextConstraintRestricted is used as the name for the system default restricted scc. - SecurityContextConstraintRestricted = "restricted" + SecurityContextConstraintRestricted = "restricted" + SecurityContextConstraintRestrictedDesc = "restricted denys access to all host features and requires pods to be run with a UID, SELinux context, fs group, and supplemental groups that are allocated to the namespace. This is the most restrictive SCC." + + // SecurityContextConstraintNonRoot is used as the name for the system default non-root scc. + SecurityContextConstraintNonRoot = "nonroot" + SecurityContextConstraintNonRootDesc = "nonroot provides all features of the SecurityContextConstraintRestricted SCC but allows users to run with any non-root UID. The user must specify the UID or it must be specified on the by the manifest of the container runtime." + + // SecurityContextConstraintHostMount is used as the name for the system default host mount only scc. + SecurityContextConstraintHostMount = "hostmount" + SecurityContextConstraintHostMountDesc = "hostmount provides all the features of SecurityContextConstraintRestricted but allows host mounts by a pod. This is primarily used by the persistent volume recycler. WARNING: this SCC allows host file system access. Grant with caution." + + // SecurityContextConstraintHostNS is used as the name for the system default scc + // that grants access to all host ns features. + SecurityContextConstraintHostNS = "hostaccess" + SecurityContextConstraintHostNSDesc = "hostaccess allows access to all host namespaces but still requires pods to be run with a UID, SELinux context, fs group, and supplemental groups that are allocated to the namespace. WARNING: this SCC allows host access to namespaces, file systems, and PIDS. It should only be used by trusted pods. Grant with caution." + + // SecurityContextConstraintsAnyUID is used as the name for the system default scc that + // grants access to run as any uid but is still restricted to specific SELinux contexts. + SecurityContextConstraintsAnyUID = "anyuid" + SecurityContextConstraintsAnyUIDDesc = "anyuid provides all features of the SecurityContextConstraintRestricted SCC but allows users to run with any UID. This is the default SCC for authenticated users." + + // DescriptionAnnotation is the annotation used for attaching descriptions. + DescriptionAnnotation = "kubernetes.io/description" ) // GetBootstrapSecurityContextConstraints returns the slice of default SecurityContextConstraints // for system bootstrapping. -func GetBootstrapSecurityContextConstraints(buildControllerUsername string) []kapi.SecurityContextConstraints { +func GetBootstrapSecurityContextConstraints(sccNameToAdditionalGroups map[string][]string, sccNameToAdditionalUsers map[string][]string) []kapi.SecurityContextConstraints { constraints := []kapi.SecurityContextConstraints{ + // SecurityContextConstraintPrivileged allows all access for every field { ObjectMeta: kapi.ObjectMeta{ Name: SecurityContextConstraintPrivileged, + Annotations: map[string]string{ + DescriptionAnnotation: SecurityContextConstraintPrivilegedDesc, + }, }, AllowPrivilegedContainer: true, AllowHostDirVolumePlugin: true, @@ -31,12 +59,85 @@ func GetBootstrapSecurityContextConstraints(buildControllerUsername string) []ka RunAsUser: kapi.RunAsUserStrategyOptions{ Type: kapi.RunAsUserStrategyRunAsAny, }, - Users: []string{buildControllerUsername}, - Groups: []string{ClusterAdminGroup, NodesGroup}, }, + // SecurityContextConstraintNonRoot does not allow host access, allocates SELinux labels + // and allows the user to request a specific UID or provide the default in the dockerfile. + { + ObjectMeta: kapi.ObjectMeta{ + Name: SecurityContextConstraintNonRoot, + Annotations: map[string]string{ + DescriptionAnnotation: SecurityContextConstraintNonRootDesc, + }, + }, + SELinuxContext: kapi.SELinuxContextStrategyOptions{ + // This strategy requires that annotations on the namespace which will be populated + // by the admission controller. If namespaces are not annotated creating the strategy + // will fail. + Type: kapi.SELinuxStrategyMustRunAs, + }, + RunAsUser: kapi.RunAsUserStrategyOptions{ + // This strategy requires that the user request to run as a specific UID or that + // the docker file contain a USER directive. + Type: kapi.RunAsUserStrategyMustRunAsNonRoot, + }, + }, + // SecurityContextConstraintHostMount is the same as the restricted scc but allows host mounts. + // Used by the PV recycler. + { + ObjectMeta: kapi.ObjectMeta{ + Name: SecurityContextConstraintHostMount, + Annotations: map[string]string{ + DescriptionAnnotation: SecurityContextConstraintHostMountDesc, + }, + }, + AllowHostDirVolumePlugin: true, + SELinuxContext: kapi.SELinuxContextStrategyOptions{ + // This strategy requires that annotations on the namespace which will be populated + // by the admission controller. If namespaces are not annotated creating the strategy + // will fail. + Type: kapi.SELinuxStrategyMustRunAs, + }, + RunAsUser: kapi.RunAsUserStrategyOptions{ + // This strategy requires that annotations on the namespace which will be populated + // by the admission controller. If namespaces are not annotated creating the strategy + // will fail. + Type: kapi.RunAsUserStrategyMustRunAsRange, + }, + }, + // SecurityContextConstraintHostNS allows access to everything except privileged on the host + // but still allocates UIDs and SELinux. + { + ObjectMeta: kapi.ObjectMeta{ + Name: SecurityContextConstraintHostNS, + Annotations: map[string]string{ + DescriptionAnnotation: SecurityContextConstraintHostNSDesc, + }, + }, + AllowHostDirVolumePlugin: true, + AllowHostNetwork: true, + AllowHostPorts: true, + AllowHostPID: true, + AllowHostIPC: true, + SELinuxContext: kapi.SELinuxContextStrategyOptions{ + // This strategy requires that annotations on the namespace which will be populated + // by the admission controller. If namespaces are not annotated creating the strategy + // will fail. + Type: kapi.SELinuxStrategyMustRunAs, + }, + RunAsUser: kapi.RunAsUserStrategyOptions{ + // This strategy requires that annotations on the namespace which will be populated + // by the admission controller. If namespaces are not annotated creating the strategy + // will fail. + Type: kapi.RunAsUserStrategyMustRunAsRange, + }, + }, + // SecurityContextConstraintRestricted allows no host access and allocates UIDs and SELinux. { ObjectMeta: kapi.ObjectMeta{ Name: SecurityContextConstraintRestricted, + Annotations: map[string]string{ + DescriptionAnnotation: SecurityContextConstraintRestrictedDesc, + }, }, SELinuxContext: kapi.SELinuxContextStrategyOptions{ // This strategy requires that annotations on the namespace which will be populated @@ -50,8 +151,45 @@ func GetBootstrapSecurityContextConstraints(buildControllerUsername string) []ka // will fail. Type: kapi.RunAsUserStrategyMustRunAsRange, }, - Groups: []string{AuthenticatedGroup}, + }, + // SecurityContextConstraintsAnyUID allows no host access and allocates SELinux. + { + ObjectMeta: kapi.ObjectMeta{ + Name: SecurityContextConstraintsAnyUID, + Annotations: map[string]string{ + DescriptionAnnotation: SecurityContextConstraintsAnyUIDDesc, + }, + }, + SELinuxContext: kapi.SELinuxContextStrategyOptions{ + // This strategy requires that annotations on the namespace which will be populated + // by the admission controller. If namespaces are not annotated creating the strategy + // will fail. + Type: kapi.SELinuxStrategyMustRunAs, + }, + RunAsUser: kapi.RunAsUserStrategyOptions{ + Type: kapi.RunAsUserStrategyRunAsAny, + }, }, } + + // add default access + for i, constraint := range constraints { + if usersToAdd, ok := sccNameToAdditionalUsers[constraint.Name]; ok { + constraints[i].Users = append(constraints[i].Users, usersToAdd...) + } + if groupsToAdd, ok := sccNameToAdditionalGroups[constraint.Name]; ok { + constraints[i].Groups = append(constraints[i].Groups, groupsToAdd...) + } + } return constraints } + +// GetBoostrapSCCAccess provides the default set of access that should be passed to GetBootstrapSecurityContextConstraints. +func GetBoostrapSCCAccess() (map[string][]string, map[string][]string) { + groups := map[string][]string{ + SecurityContextConstraintPrivileged: {ClusterAdminGroup, NodesGroup}, + SecurityContextConstraintsAnyUID: {AuthenticatedGroup}, + } + users := map[string][]string{} + return groups, users +} diff --git a/pkg/cmd/server/bootstrappolicy/securitycontextconstraints_test.go b/pkg/cmd/server/bootstrappolicy/securitycontextconstraints_test.go new file mode 100644 index 000000000000..e5af8eb6420e --- /dev/null +++ b/pkg/cmd/server/bootstrappolicy/securitycontextconstraints_test.go @@ -0,0 +1,70 @@ +package bootstrappolicy + +import ( + "reflect" + "testing" +) + +func TestBootstrappedConstraints(t *testing.T) { + expectedConstraints := []string{ + SecurityContextConstraintPrivileged, + SecurityContextConstraintRestricted, + SecurityContextConstraintNonRoot, + SecurityContextConstraintHostMount, + SecurityContextConstraintHostNS, + SecurityContextConstraintsAnyUID, + } + expectedGroups, expectedUsers := getExpectedAccess() + + groups, users := GetBoostrapSCCAccess() + bootstrappedConstraints := GetBootstrapSecurityContextConstraints(groups, users) + + if len(expectedConstraints) != len(bootstrappedConstraints) { + t.Errorf("unexpected number of constraints: found %d, wanted %d", len(bootstrappedConstraints), len(expectedConstraints)) + } + + for _, constraint := range bootstrappedConstraints { + g := expectedGroups[constraint.Name] + if !reflect.DeepEqual(g, constraint.Groups) { + t.Errorf("unexpected group access for %s. Found %v, wanted %v", constraint.Name, constraint.Groups, g) + } + + u := expectedUsers[constraint.Name] + if !reflect.DeepEqual(u, constraint.Users) { + t.Errorf("unexpected user access for %s. Found %v, wanted %v", constraint.Name, constraint.Users, u) + } + } +} + +func TestBootstrappedConstraintsWithAddedUser(t *testing.T) { + expectedGroups, expectedUsers := getExpectedAccess() + + // get default access and add our own user to it + groups, users := GetBoostrapSCCAccess() + users[SecurityContextConstraintPrivileged] = append(users[SecurityContextConstraintPrivileged], "foo") + bootstrappedConstraints := GetBootstrapSecurityContextConstraints(groups, users) + + // add it to expected + expectedUsers[SecurityContextConstraintPrivileged] = append(expectedUsers[SecurityContextConstraintPrivileged], "foo") + + for _, constraint := range bootstrappedConstraints { + g := expectedGroups[constraint.Name] + if !reflect.DeepEqual(g, constraint.Groups) { + t.Errorf("unexpected group access for %s. Found %v, wanted %v", constraint.Name, constraint.Groups, g) + } + + u := expectedUsers[constraint.Name] + if !reflect.DeepEqual(u, constraint.Users) { + t.Errorf("unexpected user access for %s. Found %v, wanted %v", constraint.Name, constraint.Users, u) + } + } +} + +func getExpectedAccess() (map[string][]string, map[string][]string) { + groups := map[string][]string{ + SecurityContextConstraintPrivileged: {ClusterAdminGroup, NodesGroup}, + SecurityContextConstraintsAnyUID: {AuthenticatedGroup}, + } + users := map[string][]string{} + return groups, users +} diff --git a/pkg/cmd/server/origin/ensure.go b/pkg/cmd/server/origin/ensure.go index 710a1952d32e..dab6306daedc 100644 --- a/pkg/cmd/server/origin/ensure.go +++ b/pkg/cmd/server/origin/ensure.go @@ -159,9 +159,14 @@ func (c *MasterConfig) ensureDefaultSecurityContextConstraints() { } glog.Infof("No security context constraints detected, adding defaults") + + // add the build user to the privileged SCC access ns := c.Options.PolicyConfig.OpenShiftInfrastructureNamespace buildControllerUsername := serviceaccount.MakeUsername(ns, c.BuildControllerServiceAccount) - for _, scc := range bootstrappolicy.GetBootstrapSecurityContextConstraints(buildControllerUsername) { + bootstrapSCCGroups, bootstrapSCCUsers := bootstrappolicy.GetBoostrapSCCAccess() + bootstrapSCCUsers[bootstrappolicy.SecurityContextConstraintPrivileged] = append(bootstrapSCCUsers[bootstrappolicy.SecurityContextConstraintPrivileged], buildControllerUsername) + + for _, scc := range bootstrappolicy.GetBootstrapSecurityContextConstraints(bootstrapSCCGroups, bootstrapSCCUsers) { _, err = c.KubeClient().SecurityContextConstraints().Create(&scc) if err != nil { glog.Errorf("Unable to create default security context constraint %s. Got error: %v", scc.Name, err)