Browse code

scc volumes support

Paul Weil authored on 2016/03/25 03:45:51
Showing 6 changed files
... ...
@@ -18956,6 +18956,7 @@
18956 18956
      "requiredDropCapabilities",
18957 18957
      "allowedCapabilities",
18958 18958
      "allowHostDirVolumePlugin",
18959
+     "volumes",
18959 18960
      "allowHostNetwork",
18960 18961
      "allowHostPorts",
18961 18962
      "allowHostPID",
... ...
@@ -19008,6 +19009,12 @@
19008 19008
       "type": "boolean",
19009 19009
       "description": "AllowHostDirVolumePlugin determines if the policy allow containers to use the HostDir volume plugin"
19010 19010
      },
19011
+     "volumes": {
19012
+      "type": "array",
19013
+      "items": {
19014
+       "$ref": "v1.FSType"
19015
+      }
19016
+     },
19011 19017
      "allowHostNetwork": {
19012 19018
       "type": "boolean",
19013 19019
       "description": "AllowHostNetwork determines if the policy allows the use of HostNetwork in the pod spec."
... ...
@@ -19060,6 +19067,10 @@
19060 19060
      }
19061 19061
     }
19062 19062
    },
19063
+   "v1.FSType": {
19064
+    "id": "v1.FSType",
19065
+    "properties": {}
19066
+   },
19063 19067
    "v1.SELinuxContextStrategyOptions": {
19064 19068
     "id": "v1.SELinuxContextStrategyOptions",
19065 19069
     "description": "SELinuxContextStrategyOptions defines the strategy type and any options used to create the strategy.",
... ...
@@ -15,7 +15,7 @@ func TestComputeDefinitions(t *testing.T) {
15 15
 	diffCaps.AllowedCapabilities = []kapi.Capability{"foo"}
16 16
 
17 17
 	diffHostDir := goodSCC()
18
-	diffHostDir.AllowHostDirVolumePlugin = true
18
+	diffHostDir.Volumes = []kapi.FSType{kapi.FSTypeHostPath}
19 19
 
20 20
 	diffHostNetwork := goodSCC()
21 21
 	diffHostNetwork.AllowHostNetwork = true
... ...
@@ -64,7 +64,7 @@ func GetBootstrapSecurityContextConstraints(sccNameToAdditionalGroups map[string
64 64
 				},
65 65
 			},
66 66
 			AllowPrivilegedContainer: true,
67
-			AllowHostDirVolumePlugin: true,
67
+			Volumes:                  []kapi.FSType{kapi.FSTypeAll},
68 68
 			AllowHostNetwork:         true,
69 69
 			AllowHostPorts:           true,
70 70
 			AllowHostPID:             true,
... ...
@@ -91,6 +91,7 @@ func GetBootstrapSecurityContextConstraints(sccNameToAdditionalGroups map[string
91 91
 					DescriptionAnnotation: SecurityContextConstraintNonRootDesc,
92 92
 				},
93 93
 			},
94
+			Volumes: []kapi.FSType{kapi.FSTypeEmptyDir, kapi.FSTypeSecret, kapi.FSTypeDownwardAPI, kapi.FSTypeConfigMap},
94 95
 			SELinuxContext: kapi.SELinuxContextStrategyOptions{
95 96
 				// This strategy requires that annotations on the namespace which will be populated
96 97
 				// by the admission controller.  If namespaces are not annotated creating the strategy
... ...
@@ -118,7 +119,7 @@ func GetBootstrapSecurityContextConstraints(sccNameToAdditionalGroups map[string
118 118
 					DescriptionAnnotation: SecurityContextConstraintHostMountAndAnyUIDDesc,
119 119
 				},
120 120
 			},
121
-			AllowHostDirVolumePlugin: true,
121
+			Volumes: []kapi.FSType{kapi.FSTypeHostPath, kapi.FSTypeEmptyDir, kapi.FSTypeSecret, kapi.FSTypeDownwardAPI, kapi.FSTypeConfigMap},
122 122
 			SELinuxContext: kapi.SELinuxContextStrategyOptions{
123 123
 				// This strategy requires that annotations on the namespace which will be populated
124 124
 				// by the admission controller.  If namespaces are not annotated creating the strategy
... ...
@@ -147,11 +148,11 @@ func GetBootstrapSecurityContextConstraints(sccNameToAdditionalGroups map[string
147 147
 					DescriptionAnnotation: SecurityContextConstraintHostNSDesc,
148 148
 				},
149 149
 			},
150
-			AllowHostDirVolumePlugin: true,
151
-			AllowHostNetwork:         true,
152
-			AllowHostPorts:           true,
153
-			AllowHostPID:             true,
154
-			AllowHostIPC:             true,
150
+			Volumes:          []kapi.FSType{kapi.FSTypeHostPath, kapi.FSTypeEmptyDir, kapi.FSTypeSecret, kapi.FSTypeDownwardAPI, kapi.FSTypeConfigMap},
151
+			AllowHostNetwork: true,
152
+			AllowHostPorts:   true,
153
+			AllowHostPID:     true,
154
+			AllowHostIPC:     true,
155 155
 			SELinuxContext: kapi.SELinuxContextStrategyOptions{
156 156
 				// This strategy requires that annotations on the namespace which will be populated
157 157
 				// by the admission controller.  If namespaces are not annotated creating the strategy
... ...
@@ -179,6 +180,7 @@ func GetBootstrapSecurityContextConstraints(sccNameToAdditionalGroups map[string
179 179
 					DescriptionAnnotation: SecurityContextConstraintRestrictedDesc,
180 180
 				},
181 181
 			},
182
+			Volumes: []kapi.FSType{kapi.FSTypeEmptyDir, kapi.FSTypeSecret, kapi.FSTypeDownwardAPI, kapi.FSTypeConfigMap},
182 183
 			SELinuxContext: kapi.SELinuxContextStrategyOptions{
183 184
 				// This strategy requires that annotations on the namespace which will be populated
184 185
 				// by the admission controller.  If namespaces are not annotated creating the strategy
... ...
@@ -208,6 +210,7 @@ func GetBootstrapSecurityContextConstraints(sccNameToAdditionalGroups map[string
208 208
 					DescriptionAnnotation: SecurityContextConstraintsAnyUIDDesc,
209 209
 				},
210 210
 			},
211
+			Volumes: []kapi.FSType{kapi.FSTypeEmptyDir, kapi.FSTypeSecret, kapi.FSTypeDownwardAPI, kapi.FSTypeConfigMap},
211 212
 			SELinuxContext: kapi.SELinuxContextStrategyOptions{
212 213
 				// This strategy requires that annotations on the namespace which will be populated
213 214
 				// by the admission controller.  If namespaces are not annotated creating the strategy
... ...
@@ -238,6 +241,7 @@ func GetBootstrapSecurityContextConstraints(sccNameToAdditionalGroups map[string
238 238
 			},
239 239
 			AllowHostNetwork: true,
240 240
 			AllowHostPorts:   true,
241
+			Volumes:          []kapi.FSType{kapi.FSTypeEmptyDir, kapi.FSTypeSecret, kapi.FSTypeDownwardAPI, kapi.FSTypeConfigMap},
241 242
 			SELinuxContext: kapi.SELinuxContextStrategyOptions{
242 243
 				// This strategy requires that annotations on the namespace which will be populated
243 244
 				// by the admission controller.  If namespaces are not annotated creating the strategy
... ...
@@ -27,7 +27,7 @@ func TestByPrioritiesScore(t *testing.T) {
27 27
 	nonPriviledSCC := testSCC("nonprivileged", 1)
28 28
 
29 29
 	hostDirSCC := testSCC("hostdir", 1)
30
-	hostDirSCC.AllowHostDirVolumePlugin = true
30
+	hostDirSCC.Volumes = []kapi.FSType{kapi.FSTypeHostPath}
31 31
 
32 32
 	sccs := []*kapi.SecurityContextConstraints{nonPriviledSCC, privilegedSCC, hostDirSCC}
33 33
 	// with equal priorities expect that the SCCs will be sorted with hold behavior based on their score,
... ...
@@ -24,11 +24,9 @@ func pointValue(constraint *kapi.SecurityContextConstraints) int {
24 24
 	if constraint.AllowPrivilegedContainer {
25 25
 		points += 20
26 26
 	}
27
-	// 9 gives us a value slightly higher than an SCC that allows run as any in both strategies since
28
-	// we're allowing access to the host system
29
-	if constraint.AllowHostDirVolumePlugin {
30
-		points += 10
31
-	}
27
+
28
+	// add points based on volume requests
29
+	points += volumePointValue(constraint)
32 30
 
33 31
 	// strategies in order of least restrictive to most restrictive
34 32
 	switch constraint.SELinuxContext.Type {
... ...
@@ -50,3 +48,37 @@ func pointValue(constraint *kapi.SecurityContextConstraints) int {
50 50
 	}
51 51
 	return points
52 52
 }
53
+
54
+// allowsHostPathVolume returns a score based on the volumes allowed by the SCC.
55
+// Allowing a host volume wil return a score of 10.  Allowance of anything other
56
+// than kapi.FSTypeSecret, kapi.FSTypeConfigMap, kapi.FSTypeConfigMap, kapi.FSTypeDownwardAPI
57
+// will result in a score of 5.  If the SCC only allows kapi.FSTypeSecret, kapi.FSTypeConfigMap,
58
+// kapi.FSTypeEmptyDir, kapi.FSTypeDownwardAPI it will have a score of 0.
59
+func volumePointValue(scc *kapi.SecurityContextConstraints) int {
60
+	hasHostVolume := false
61
+	hasNonTrivialVolume := false
62
+	for _, v := range scc.Volumes {
63
+		switch v {
64
+		case kapi.FSTypeHostPath, kapi.FSTypeAll:
65
+			hasHostVolume = true
66
+			// nothing more to do, this is the max point value
67
+			break
68
+		// it is easier to specifically list the trivial volumes and allow the
69
+		// default case to be non-trivial so we don't have to worry about adding
70
+		// volumes in the future unless they're trivial.
71
+		case kapi.FSTypeSecret, kapi.FSTypeConfigMap,
72
+			kapi.FSTypeEmptyDir, kapi.FSTypeDownwardAPI:
73
+			// do nothing
74
+		default:
75
+			hasNonTrivialVolume = true
76
+		}
77
+	}
78
+
79
+	if hasHostVolume {
80
+		return 10
81
+	}
82
+	if hasNonTrivialVolume {
83
+		return 5
84
+	}
85
+	return 0
86
+}
53 87
new file mode 100644
... ...
@@ -0,0 +1,162 @@
0
+package admission
1
+
2
+import (
3
+	"testing"
4
+
5
+	kapi "k8s.io/kubernetes/pkg/api"
6
+)
7
+
8
+func TestPointValue(t *testing.T) {
9
+	newSCC := func(priv bool, seLinuxStrategy kapi.SELinuxContextStrategyType, userStrategy kapi.RunAsUserStrategyType) *kapi.SecurityContextConstraints {
10
+		scc := &kapi.SecurityContextConstraints{
11
+			SELinuxContext: kapi.SELinuxContextStrategyOptions{
12
+				Type: seLinuxStrategy,
13
+			},
14
+			RunAsUser: kapi.RunAsUserStrategyOptions{
15
+				Type: userStrategy,
16
+			},
17
+		}
18
+		if priv {
19
+			scc.AllowPrivilegedContainer = true
20
+		}
21
+
22
+		return scc
23
+	}
24
+
25
+	seLinuxStrategies := map[kapi.SELinuxContextStrategyType]int{
26
+		kapi.SELinuxStrategyRunAsAny:  4,
27
+		kapi.SELinuxStrategyMustRunAs: 1,
28
+	}
29
+	userStrategies := map[kapi.RunAsUserStrategyType]int{
30
+		kapi.RunAsUserStrategyRunAsAny:         4,
31
+		kapi.RunAsUserStrategyMustRunAsNonRoot: 3,
32
+		kapi.RunAsUserStrategyMustRunAsRange:   2,
33
+		kapi.RunAsUserStrategyMustRunAs:        1,
34
+	}
35
+
36
+	privilegedPoints := 20
37
+
38
+	// run through all combos of user strategy + seLinux strategy + priv
39
+	for userStrategy, userStrategyPoints := range userStrategies {
40
+		for seLinuxStrategy, seLinuxStrategyPoints := range seLinuxStrategies {
41
+			expectedPoints := privilegedPoints + userStrategyPoints + seLinuxStrategyPoints
42
+			scc := newSCC(true, seLinuxStrategy, userStrategy)
43
+			actualPoints := pointValue(scc)
44
+
45
+			if actualPoints != expectedPoints {
46
+				t.Errorf("privileged, user: %v, seLinux %v expected %d score but got %d", userStrategy, seLinuxStrategy, expectedPoints, actualPoints)
47
+			}
48
+
49
+			expectedPoints = userStrategyPoints + seLinuxStrategyPoints
50
+			scc = newSCC(false, seLinuxStrategy, userStrategy)
51
+			actualPoints = pointValue(scc)
52
+
53
+			if actualPoints != expectedPoints {
54
+				t.Errorf("non privileged, user: %v, seLinux %v expected %d score but got %d", userStrategy, seLinuxStrategy, expectedPoints, actualPoints)
55
+			}
56
+		}
57
+	}
58
+
59
+	// sanity check to ensure volume score is added (specific volumes scores are tested below
60
+	scc := newSCC(false, kapi.SELinuxStrategyMustRunAs, kapi.RunAsUserStrategyMustRunAs)
61
+	scc.Volumes = []kapi.FSType{kapi.FSTypeHostPath}
62
+	actualPoints := pointValue(scc)
63
+	if actualPoints != 12 { //1 (SELinux) + 1 (User) + 10 (host path volume)
64
+		t.Errorf("volume score was not added to the scc point value correctly!")
65
+	}
66
+}
67
+
68
+func TestVolumePointValue(t *testing.T) {
69
+	newSCC := func(host, nonTrivial, trivial bool) *kapi.SecurityContextConstraints {
70
+		volumes := []kapi.FSType{}
71
+		if host {
72
+			volumes = append(volumes, kapi.FSTypeHostPath)
73
+		}
74
+		if nonTrivial {
75
+			volumes = append(volumes, kapi.FSTypeAWSElasticBlockStore)
76
+		}
77
+		if trivial {
78
+			volumes = append(volumes, kapi.FSTypeSecret)
79
+		}
80
+		return &kapi.SecurityContextConstraints{
81
+			Volumes: volumes,
82
+		}
83
+	}
84
+
85
+	allowAllSCC := &kapi.SecurityContextConstraints{
86
+		Volumes: []kapi.FSType{kapi.FSTypeAll},
87
+	}
88
+	nilVolumeSCC := &kapi.SecurityContextConstraints{}
89
+
90
+	tests := map[string]struct {
91
+		scc            *kapi.SecurityContextConstraints
92
+		expectedPoints int
93
+	}{
94
+		"all volumes": {
95
+			scc:            allowAllSCC,
96
+			expectedPoints: 10,
97
+		},
98
+		"host volume": {
99
+			scc:            newSCC(true, false, false),
100
+			expectedPoints: 10,
101
+		},
102
+		"host volume and non trivial volumes": {
103
+			scc:            newSCC(true, true, false),
104
+			expectedPoints: 10,
105
+		},
106
+		"host volume, non trivial, and trivial": {
107
+			scc:            newSCC(true, true, true),
108
+			expectedPoints: 10,
109
+		},
110
+		"non trivial": {
111
+			scc:            newSCC(false, true, false),
112
+			expectedPoints: 5,
113
+		},
114
+		"non trivial and trivial": {
115
+			scc:            newSCC(false, true, true),
116
+			expectedPoints: 5,
117
+		},
118
+		"trivial": {
119
+			scc:            newSCC(false, false, true),
120
+			expectedPoints: 0,
121
+		},
122
+		"trivial - secret": {
123
+			scc: &kapi.SecurityContextConstraints{
124
+				Volumes: []kapi.FSType{kapi.FSTypeSecret},
125
+			},
126
+			expectedPoints: 0,
127
+		},
128
+		"trivial - configMap": {
129
+			scc: &kapi.SecurityContextConstraints{
130
+				Volumes: []kapi.FSType{kapi.FSTypeConfigMap},
131
+			},
132
+			expectedPoints: 0,
133
+		},
134
+		"trivial - emptyDir": {
135
+			scc: &kapi.SecurityContextConstraints{
136
+				Volumes: []kapi.FSType{kapi.FSTypeEmptyDir},
137
+			},
138
+			expectedPoints: 0,
139
+		},
140
+		"trivial - downwardAPI": {
141
+			scc: &kapi.SecurityContextConstraints{
142
+				Volumes: []kapi.FSType{kapi.FSTypeDownwardAPI},
143
+			},
144
+			expectedPoints: 0,
145
+		},
146
+		"no volumes allowed": {
147
+			scc:            newSCC(false, false, false),
148
+			expectedPoints: 0,
149
+		},
150
+		"nil volumes": {
151
+			scc:            nilVolumeSCC,
152
+			expectedPoints: 0,
153
+		},
154
+	}
155
+	for k, v := range tests {
156
+		actualPoints := volumePointValue(v.scc)
157
+		if actualPoints != v.expectedPoints {
158
+			t.Errorf("%s expected %d volume score but got %d", k, v.expectedPoints, actualPoints)
159
+		}
160
+	}
161
+}