Skip to content

Commit

Permalink
new SCCs
Browse files Browse the repository at this point in the history
  • Loading branch information
pweil- committed Oct 30, 2015
1 parent 27c0ca8 commit 87bf28a
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 10 deletions.
211 changes: 202 additions & 9 deletions pkg/cmd/server/bootstrappolicy/securitycontextconstraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,47 @@ 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 restricted 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 the restricted SCC 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 restricted SCC but allows users to run with any UID and any GID. 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 {
// for system bootstrapping. This method takes additional users and groups that should be added
// to the strategies. Use GetBoostrapSCCAccess to produce the default set of mappings.
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,
Expand All @@ -37,12 +66,133 @@ func GetBootstrapSecurityContextConstraints(buildControllerUsername string) []ka
SupplementalGroups: kapi.SupplementalGroupsStrategyOptions{
Type: kapi.SupplementalGroupsStrategyRunAsAny,
},
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,
},
FSGroup: kapi.FSGroupStrategyOptions{
// This strategy requires that annotations on the namespace which will be populated
// by the admission controller. Admission will first look for the SupplementalGroupsAnnotation
// on the namespace and if it is unable to find that annotation it will attempt
// to use the UIDRangeAnnotation. If neither annotation exists then creation
// of the strategy will fail.
Type: kapi.FSGroupStrategyMustRunAs,
},
SupplementalGroups: kapi.SupplementalGroupsStrategyOptions{
// This strategy requires that annotations on the namespace which will be populated
// by the admission controller. Admission will first look for the SupplementalGroupsAnnotation
// on the namespace and if it is unable to find that annotation it will attempt
// to use the UIDRangeAnnotation. If neither annotation exists then creation
// of the strategy will fail.
Type: kapi.SupplementalGroupsStrategyMustRunAs,
},
},
// 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,
},
FSGroup: kapi.FSGroupStrategyOptions{
// This strategy requires that annotations on the namespace which will be populated
// by the admission controller. Admission will first look for the SupplementalGroupsAnnotation
// on the namespace and if it is unable to find that annotation it will attempt
// to use the UIDRangeAnnotation. If neither annotation exists then creation
// of the strategy will fail.
Type: kapi.FSGroupStrategyMustRunAs,
},
SupplementalGroups: kapi.SupplementalGroupsStrategyOptions{
// This strategy requires that annotations on the namespace which will be populated
// by the admission controller. Admission will first look for the SupplementalGroupsAnnotation
// on the namespace and if it is unable to find that annotation it will attempt
// to use the UIDRangeAnnotation. If neither annotation exists then creation
// of the strategy will fail.
Type: kapi.SupplementalGroupsStrategyMustRunAs,
},
},
// 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,
},
FSGroup: kapi.FSGroupStrategyOptions{
// This strategy requires that annotations on the namespace which will be populated
// by the admission controller. Admission will first look for the SupplementalGroupsAnnotation
// on the namespace and if it is unable to find that annotation it will attempt
// to use the UIDRangeAnnotation. If neither annotation exists then creation
// of the strategy will fail.
Type: kapi.FSGroupStrategyMustRunAs,
},
SupplementalGroups: kapi.SupplementalGroupsStrategyOptions{
// This strategy requires that annotations on the namespace which will be populated
// by the admission controller. Admission will first look for the SupplementalGroupsAnnotation
// on the namespace and if it is unable to find that annotation it will attempt
// to use the UIDRangeAnnotation. If neither annotation exists then creation
// of the strategy will fail.
Type: kapi.SupplementalGroupsStrategyMustRunAs,
},
},
// 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
Expand All @@ -61,19 +211,62 @@ func GetBootstrapSecurityContextConstraints(buildControllerUsername string) []ka
// by the admission controller. Admission will first look for the SupplementalGroupsAnnotation
// on the namespace and if it is unable to find that annotation it will attempt
// to use the UIDRangeAnnotation. If neither annotation exists then creation
// of the SCC will fail.
// of the strategy will fail.
Type: kapi.FSGroupStrategyMustRunAs,
},
SupplementalGroups: kapi.SupplementalGroupsStrategyOptions{
// This strategy requires that annotations on the namespace which will be populated
// by the admission controller. Admission will first look for the SupplementalGroupsAnnotation
// on the namespace and if it is unable to find that annotation it will attempt
// to use the UIDRangeAnnotation. If neither annotation exists then creation
// of the SCC will fail.
// of the strategy will fail.
Type: kapi.SupplementalGroupsStrategyMustRunAs,
},
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,
},
FSGroup: kapi.FSGroupStrategyOptions{
Type: kapi.FSGroupStrategyRunAsAny,
},
SupplementalGroups: kapi.SupplementalGroupsStrategyOptions{
Type: kapi.SupplementalGroupsStrategyRunAsAny,
},
},
}

// 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
}
70 changes: 70 additions & 0 deletions pkg/cmd/server/bootstrappolicy/securitycontextconstraints_test.go
Original file line number Diff line number Diff line change
@@ -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
}
7 changes: 6 additions & 1 deletion pkg/cmd/server/origin/ensure.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 87bf28a

Please sign in to comment.