Skip to content

Commit

Permalink
Make SCC with less capabilities more restrictive.
Browse files Browse the repository at this point in the history
  • Loading branch information
adelton committed Jun 23, 2017
1 parent 64fa9d6 commit 03b8ccf
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 5 deletions.
39 changes: 39 additions & 0 deletions pkg/security/scc/byrestrictions.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ func pointValue(constraint *kapi.SecurityContextConstraints) int {
// add points based on volume requests
points += volumePointValue(constraint)

// add points based on capabilities
points += capabilitiesPointValue(constraint)

// strategies in order of least restrictive to most restrictive
switch constraint.SELinuxContext.Type {
case kapi.SELinuxStrategyRunAsAny:
Expand Down Expand Up @@ -82,3 +85,39 @@ func volumePointValue(scc *kapi.SecurityContextConstraints) int {
}
return 0
}

// hasCap checks for needle in haystack.
func hasCap(needle kapi.Capability, haystack []kapi.Capability) bool {
for _, c := range haystack {
if needle == c {
return true
}
}
return false
}

// capabilitiesPointValue returns a score based on the capabilities allowed,
// added, or removed by the SCC. This allow us to prefer the more restrictive
// SCC.
func capabilitiesPointValue(scc *kapi.SecurityContextConstraints) int {
points := 5000
points += 300 * len(scc.DefaultAddCapabilities)
if hasCap(kapi.CapabilityAll, scc.AllowedCapabilities) {
points += 4000
} else if hasCap("ALL", scc.AllowedCapabilities) {
points += 4000
} else {
points += 10 * len(scc.AllowedCapabilities)
}
if hasCap("ALL", scc.RequiredDropCapabilities) {
points -= 3000
} else {
points -= 50 * len(scc.RequiredDropCapabilities)
}
if (points > 10000) {
return 10000
} else if (points < 0) {
return 0
}
return points
}
75 changes: 70 additions & 5 deletions pkg/security/scc/byrestrictions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ func TestPointValue(t *testing.T) {
// run through all combos of user strategy + seLinux strategy + priv
for userStrategy, userStrategyPoints := range userStrategies {
for seLinuxStrategy, seLinuxStrategyPoints := range seLinuxStrategies {
expectedPoints := privilegedPoints + userStrategyPoints + seLinuxStrategyPoints
expectedPoints := 5000 + privilegedPoints + userStrategyPoints + seLinuxStrategyPoints
scc := newSCC(true, seLinuxStrategy, userStrategy)
actualPoints := pointValue(scc)

if actualPoints != expectedPoints {
t.Errorf("privileged, user: %v, seLinux %v expected %d score but got %d", userStrategy, seLinuxStrategy, expectedPoints, actualPoints)
}

expectedPoints = userStrategyPoints + seLinuxStrategyPoints
expectedPoints = 5000 + userStrategyPoints + seLinuxStrategyPoints
scc = newSCC(false, seLinuxStrategy, userStrategy)
actualPoints = pointValue(scc)

Expand All @@ -57,12 +57,13 @@ func TestPointValue(t *testing.T) {
}
}

// sanity check to ensure volume score is added (specific volumes scores are tested below
// sanity check to ensure volume and capabilities scores are added (specific volumes
// and capabilities scores are tested below
scc := newSCC(false, kapi.SELinuxStrategyMustRunAs, kapi.RunAsUserStrategyMustRunAs)
scc.Volumes = []kapi.FSType{kapi.FSTypeHostPath}
actualPoints := pointValue(scc)
if actualPoints != 120000 { //10000 (SELinux) + 10000 (User) + 100000 (host path volume)
t.Errorf("volume score was not added to the scc point value correctly!")
if actualPoints != 125000 { //10000 (SELinux) + 10000 (User) + 100000 (host path volume) + 5000 capabilities
t.Errorf("volume score was not added to the scc point value correctly, got %d!", actualPoints)
}
}

Expand Down Expand Up @@ -172,3 +173,67 @@ func TestVolumePointValue(t *testing.T) {
}
}
}

func TestCapabilitiesPointValue(t *testing.T) {
newSCC := func(def []kapi.Capability, allow []kapi.Capability, drop []kapi.Capability) *kapi.SecurityContextConstraints {
return &kapi.SecurityContextConstraints{
DefaultAddCapabilities: def,
AllowedCapabilities: allow,
RequiredDropCapabilities: drop,
}
}

tests := map[string]struct {
scc *kapi.SecurityContextConstraints
expectedPoints int
}{
"nothing specified": {
scc: newSCC([]kapi.Capability{}, []kapi.Capability{}, []kapi.Capability{}),
expectedPoints: 5000,
},
"default": {
scc: newSCC([]kapi.Capability{"KILL", "MKNOD"},
[]kapi.Capability{},
[]kapi.Capability{}),
expectedPoints: 5600,
},
"allow": {
scc: newSCC([]kapi.Capability{},
[]kapi.Capability{"KILL", "MKNOD"},
[]kapi.Capability{}),
expectedPoints: 5020,
},
"allow star": {
scc: newSCC([]kapi.Capability{}, []kapi.Capability{"*"}, []kapi.Capability{}),
expectedPoints: 9000,
},
"allow all": {
scc: newSCC([]kapi.Capability{}, []kapi.Capability{"ALL"}, []kapi.Capability{}),
expectedPoints: 9000,
},
"drop": {
scc: newSCC([]kapi.Capability{},
[]kapi.Capability{},
[]kapi.Capability{"KILL", "MKNOD"}),
expectedPoints: 4900,
},
"drop all": {
scc: newSCC([]kapi.Capability{},
[]kapi.Capability{},
[]kapi.Capability{"ALL"}),
expectedPoints: 2000,
},
"mixture": {
scc: newSCC([]kapi.Capability{"SETUID", "SETGID"},
[]kapi.Capability{"*"},
[]kapi.Capability{"SYS_CHROOT"}),
expectedPoints: 9550,

This comment has been minimized.

Copy link
@php-coder

php-coder Jun 23, 2017

Contributor

The function call like newSCC([]kapi.Capability{"KILL", "MKNOD"}, []kapi.Capability{}, []kapi.Capability{}) isn't obvious, because it's not clear from the first sight what parameter maps to what caps.

I'd suggest to make these caps the members of the struct and create SCC in the loop instead of here:

"mixture": {
    defaultAdd:     []kapi.Capability{"SETUID", "SETGID"},
    allowed:        []kapi.Capability{"*"},
    requiredDrop:   []kapi.Capability{"SYS_CHROOT"}),
    expectedPoints: 9550,
},
},
}
for k, v := range tests {
actualPoints := capabilitiesPointValue(v.scc)
if actualPoints != v.expectedPoints {
t.Errorf("%s expected %d capability score but got %d", k, v.expectedPoints, actualPoints)
}
}
}

0 comments on commit 03b8ccf

Please sign in to comment.