... | ... |
@@ -81,15 +81,6 @@ func fuzzInternalObject(t *testing.T, forVersion unversioned.GroupVersion, item |
81 | 81 |
obj.PodEvictionTimeout = "5m" |
82 | 82 |
} |
83 | 83 |
}, |
84 |
- func(obj *configapi.LegacyClientPolicyConfig, c fuzz.Continue) { |
|
85 |
- c.FuzzNoCustom(obj) |
|
86 |
- if len(obj.LegacyClientPolicy) == 0 { |
|
87 |
- obj.LegacyClientPolicy = configapi.AllowAll |
|
88 |
- } |
|
89 |
- if len(obj.RestrictedHTTPVerbs) == 0 { |
|
90 |
- obj.RestrictedHTTPVerbs = []string{"PUT", "POST"} |
|
91 |
- } |
|
92 |
- }, |
|
93 | 84 |
func(obj *configapi.NodeConfig, c fuzz.Continue) { |
94 | 85 |
c.FuzzNoCustom(obj) |
95 | 86 |
// Defaults/migrations for NetworkConfig |
... | ... |
@@ -318,28 +318,38 @@ type PolicyConfig struct { |
318 | 318 |
// OpenShiftInfrastructureNamespace is the namespace where OpenShift infrastructure resources live (like controller service accounts) |
319 | 319 |
OpenShiftInfrastructureNamespace string |
320 | 320 |
|
321 |
- // LegacyClientPolicyConfig controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS! |
|
322 |
- LegacyClientPolicyConfig LegacyClientPolicyConfig |
|
321 |
+ // UserAgentMatchingConfig controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS! |
|
322 |
+ UserAgentMatchingConfig UserAgentMatchingConfig |
|
323 | 323 |
} |
324 | 324 |
|
325 |
-type LegacyClientPolicyConfig struct { |
|
326 |
- // LegacyClientPolicy controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS! |
|
327 |
- // The default is AllowAll |
|
328 |
- LegacyClientPolicy LegacyClientPolicy |
|
329 |
- // RestrictedHTTPVerbs specifies which HTTP verbs are restricted. By default this is PUT and POST |
|
330 |
- RestrictedHTTPVerbs []string |
|
325 |
+// UserAgentMatchingConfig controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS! |
|
326 |
+type UserAgentMatchingConfig struct { |
|
327 |
+ // If this list is non-empty, then a User-Agent must match one of the UserAgentRegexes to be allowed |
|
328 |
+ RequiredClients []UserAgentMatchRule |
|
329 |
+ |
|
330 |
+ // If this list is non-empty, then a User-Agent must not match any of the UserAgentRegexes |
|
331 |
+ DeniedClients []UserAgentDenyRule |
|
332 |
+ |
|
333 |
+ // DefaultRejectionMessage is the message shown when rejecting a client. If it is not a set, a generic message is given. |
|
334 |
+ DefaultRejectionMessage string |
|
331 | 335 |
} |
332 | 336 |
|
333 |
-type LegacyClientPolicy string |
|
337 |
+// UserAgentMatchRule describes how to match a given request based on User-Agent and HTTPVerb |
|
338 |
+type UserAgentMatchRule struct { |
|
339 |
+ // UserAgentRegex is a regex that is checked against the User-Agent. |
|
340 |
+ Regex string |
|
334 | 341 |
|
335 |
-var ( |
|
336 |
- // AllowAll does not prevent any kinds of client version skew |
|
337 |
- AllowAll LegacyClientPolicy = "allow-all" |
|
338 |
- // DenyOldClients prevents older clients (but not newer ones) from issuing stomping requests |
|
339 |
- DenyOldClients LegacyClientPolicy = "deny-old-clients" |
|
340 |
- // DenySkewedClients prevents any non-matching client from issuing stomping requests |
|
341 |
- DenySkewedClients LegacyClientPolicy = "deny-skewed-clients" |
|
342 |
-) |
|
342 |
+ // HTTPVerbs specifies which HTTP verbs should be matched. An empty list means "match all verbs". |
|
343 |
+ HTTPVerbs []string |
|
344 |
+} |
|
345 |
+ |
|
346 |
+// UserAgentDenyRule adds a rejection message that can be used to help a user figure out how to get an approved client |
|
347 |
+type UserAgentDenyRule struct { |
|
348 |
+ UserAgentMatchRule |
|
349 |
+ |
|
350 |
+ // RejectionMessage is the message shown when rejecting a client. If it is not a set, the default message is used. |
|
351 |
+ RejectionMessage string |
|
352 |
+} |
|
343 | 353 |
|
344 | 354 |
// MasterNetworkConfig to be passed to the compiled in network plugin |
345 | 355 |
type MasterNetworkConfig struct { |
... | ... |
@@ -65,14 +65,6 @@ func addDefaultingFuncs(scheme *runtime.Scheme) { |
65 | 65 |
obj.PodEvictionTimeout = "5m" |
66 | 66 |
} |
67 | 67 |
}, |
68 |
- func(obj *LegacyClientPolicyConfig) { |
|
69 |
- if len(obj.LegacyClientPolicy) == 0 { |
|
70 |
- obj.LegacyClientPolicy = AllowAll |
|
71 |
- } |
|
72 |
- if obj.LegacyClientPolicy != AllowAll && len(obj.RestrictedHTTPVerbs) == 0 { |
|
73 |
- obj.RestrictedHTTPVerbs = []string{"PUT", "POST"} |
|
74 |
- } |
|
75 |
- }, |
|
76 | 68 |
func(obj *NodeConfig) { |
77 | 69 |
// Defaults/migrations for NetworkConfig |
78 | 70 |
if len(obj.NetworkConfig.NetworkPluginName) == 0 { |
... | ... |
@@ -362,16 +362,6 @@ func (LDAPSyncConfig) SwaggerDoc() map[string]string { |
362 | 362 |
return map_LDAPSyncConfig |
363 | 363 |
} |
364 | 364 |
|
365 |
-var map_LegacyClientPolicyConfig = map[string]string{ |
|
366 |
- "": "LegacyClientPolicyConfig holds configuration options for preventing *opt-in* clients using some HTTP verbs when talking to the API", |
|
367 |
- "legacyClientPolicy": "LegacyClientPolicy controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS! The default is AllowAll", |
|
368 |
- "restrictedHTTPVerbs": "RestrictedHTTPVerbs specifies which HTTP verbs are restricted. By default this is PUT and POST", |
|
369 |
-} |
|
370 |
- |
|
371 |
-func (LegacyClientPolicyConfig) SwaggerDoc() map[string]string { |
|
372 |
- return map_LegacyClientPolicyConfig |
|
373 |
-} |
|
374 |
- |
|
375 | 365 |
var map_MasterClients = map[string]string{ |
376 | 366 |
"": "MasterClients holds references to `.kubeconfig` files that qualify master clients for OpenShift and Kubernetes", |
377 | 367 |
"openshiftLoopbackKubeConfig": "OpenShiftLoopbackKubeConfig is a .kubeconfig filename for system components to loopback to this master", |
... | ... |
@@ -565,7 +555,7 @@ var map_PolicyConfig = map[string]string{ |
565 | 565 |
"bootstrapPolicyFile": "BootstrapPolicyFile points to a template that contains roles and rolebindings that will be created if no policy object exists in the master namespace", |
566 | 566 |
"openshiftSharedResourcesNamespace": "OpenShiftSharedResourcesNamespace is the namespace where shared OpenShift resources live (like shared templates)", |
567 | 567 |
"openshiftInfrastructureNamespace": "OpenShiftInfrastructureNamespace is the namespace where OpenShift infrastructure resources live (like controller service accounts)", |
568 |
- "legacyClientPolicyConfig": "LegacyClientPolicyConfig controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS!", |
|
568 |
+ "userAgentMatchingConfig": "UserAgentMatchingConfig controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS!", |
|
569 | 569 |
} |
570 | 570 |
|
571 | 571 |
func (PolicyConfig) SwaggerDoc() map[string]string { |
... | ... |
@@ -730,3 +720,33 @@ var map_TokenConfig = map[string]string{ |
730 | 730 |
func (TokenConfig) SwaggerDoc() map[string]string { |
731 | 731 |
return map_TokenConfig |
732 | 732 |
} |
733 |
+ |
|
734 |
+var map_UserAgentDenyRule = map[string]string{ |
|
735 |
+ "": "UserAgentDenyRule adds a rejection message that can be used to help a user figure out how to get an approved client", |
|
736 |
+ "rejectionMessage": "RejectionMessage is the message shown when rejecting a client. If it is not a set, the default message is used.", |
|
737 |
+} |
|
738 |
+ |
|
739 |
+func (UserAgentDenyRule) SwaggerDoc() map[string]string { |
|
740 |
+ return map_UserAgentDenyRule |
|
741 |
+} |
|
742 |
+ |
|
743 |
+var map_UserAgentMatchRule = map[string]string{ |
|
744 |
+ "": "UserAgentMatchRule describes how to match a given request based on User-Agent and HTTPVerb", |
|
745 |
+ "regex": "UserAgentRegex is a regex that is checked against the User-Agent. Known variants of oc clients 1. oc accessing kube resources: oc/v1.2.0 (linux/amd64) kubernetes/bc4550d 2. oc accessing openshift resources: oc/v1.1.3 (linux/amd64) openshift/b348c2f 3. openshift kubectl accessing kube resources: openshift/v1.2.0 (linux/amd64) kubernetes/bc4550d 4. openshit kubectl accessing openshift resources: openshift/v1.1.3 (linux/amd64) openshift/b348c2f 5. oadm accessing kube resources: oadm/v1.2.0 (linux/amd64) kubernetes/bc4550d 6. oadm accessing openshift resources: oadm/v1.1.3 (linux/amd64) openshift/b348c2f 7. openshift cli accessing kube resources: openshift/v1.2.0 (linux/amd64) kubernetes/bc4550d 8. openshift cli accessing openshift resources: openshift/v1.1.3 (linux/amd64) openshift/b348c2f", |
|
746 |
+ "httpVerbs": "HTTPVerbs specifies which HTTP verbs should be matched. An empty list means \"match all verbs\".", |
|
747 |
+} |
|
748 |
+ |
|
749 |
+func (UserAgentMatchRule) SwaggerDoc() map[string]string { |
|
750 |
+ return map_UserAgentMatchRule |
|
751 |
+} |
|
752 |
+ |
|
753 |
+var map_UserAgentMatchingConfig = map[string]string{ |
|
754 |
+ "": "UserAgentMatchingConfig controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS!", |
|
755 |
+ "requiredClients": "If this list is non-empty, then a User-Agent must match one of the UserAgentRegexes to be allowed", |
|
756 |
+ "deniedClients": "If this list is non-empty, then a User-Agent must not match any of the UserAgentRegexes", |
|
757 |
+ "defaultRejectionMessage": "DefaultRejectionMessage is the message shown when rejecting a client. If it is not a set, a generic message is given.", |
|
758 |
+} |
|
759 |
+ |
|
760 |
+func (UserAgentMatchingConfig) SwaggerDoc() map[string]string { |
|
761 |
+ return map_UserAgentMatchingConfig |
|
762 |
+} |
... | ... |
@@ -270,29 +270,47 @@ type PolicyConfig struct { |
270 | 270 |
// OpenShiftInfrastructureNamespace is the namespace where OpenShift infrastructure resources live (like controller service accounts) |
271 | 271 |
OpenShiftInfrastructureNamespace string `json:"openshiftInfrastructureNamespace"` |
272 | 272 |
|
273 |
- // LegacyClientPolicyConfig controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS! |
|
274 |
- LegacyClientPolicyConfig LegacyClientPolicyConfig `json:"legacyClientPolicyConfig"` |
|
273 |
+ // UserAgentMatchingConfig controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS! |
|
274 |
+ UserAgentMatchingConfig UserAgentMatchingConfig `json:"userAgentMatchingConfig"` |
|
275 | 275 |
} |
276 | 276 |
|
277 |
-// LegacyClientPolicyConfig holds configuration options for preventing *opt-in* clients using some HTTP verbs when talking to the API |
|
278 |
-type LegacyClientPolicyConfig struct { |
|
279 |
- // LegacyClientPolicy controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS! |
|
280 |
- // The default is AllowAll |
|
281 |
- LegacyClientPolicy LegacyClientPolicy `json:"legacyClientPolicy"` |
|
282 |
- // RestrictedHTTPVerbs specifies which HTTP verbs are restricted. By default this is PUT and POST |
|
283 |
- RestrictedHTTPVerbs []string `json:"restrictedHTTPVerbs"` |
|
277 |
+// UserAgentMatchingConfig controls how API calls from *voluntarily* identifying clients will be handled. THIS DOES NOT DEFEND AGAINST MALICIOUS CLIENTS! |
|
278 |
+type UserAgentMatchingConfig struct { |
|
279 |
+ // If this list is non-empty, then a User-Agent must match one of the UserAgentRegexes to be allowed |
|
280 |
+ RequiredClients []UserAgentMatchRule `json:"requiredClients"` |
|
281 |
+ |
|
282 |
+ // If this list is non-empty, then a User-Agent must not match any of the UserAgentRegexes |
|
283 |
+ DeniedClients []UserAgentDenyRule `json:"deniedClients"` |
|
284 |
+ |
|
285 |
+ // DefaultRejectionMessage is the message shown when rejecting a client. If it is not a set, a generic message is given. |
|
286 |
+ DefaultRejectionMessage string `json:"defaultRejectionMessage"` |
|
287 |
+} |
|
288 |
+ |
|
289 |
+// UserAgentMatchRule describes how to match a given request based on User-Agent and HTTPVerb |
|
290 |
+type UserAgentMatchRule struct { |
|
291 |
+ // UserAgentRegex is a regex that is checked against the User-Agent. |
|
292 |
+ // Known variants of oc clients |
|
293 |
+ // 1. oc accessing kube resources: oc/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
294 |
+ // 2. oc accessing openshift resources: oc/v1.1.3 (linux/amd64) openshift/b348c2f |
|
295 |
+ // 3. openshift kubectl accessing kube resources: openshift/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
296 |
+ // 4. openshit kubectl accessing openshift resources: openshift/v1.1.3 (linux/amd64) openshift/b348c2f |
|
297 |
+ // 5. oadm accessing kube resources: oadm/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
298 |
+ // 6. oadm accessing openshift resources: oadm/v1.1.3 (linux/amd64) openshift/b348c2f |
|
299 |
+ // 7. openshift cli accessing kube resources: openshift/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
300 |
+ // 8. openshift cli accessing openshift resources: openshift/v1.1.3 (linux/amd64) openshift/b348c2f |
|
301 |
+ Regex string `json:"regex"` |
|
302 |
+ |
|
303 |
+ // HTTPVerbs specifies which HTTP verbs should be matched. An empty list means "match all verbs". |
|
304 |
+ HTTPVerbs []string `json:"httpVerbs"` |
|
284 | 305 |
} |
285 | 306 |
|
286 |
-type LegacyClientPolicy string |
|
307 |
+// UserAgentDenyRule adds a rejection message that can be used to help a user figure out how to get an approved client |
|
308 |
+type UserAgentDenyRule struct { |
|
309 |
+ UserAgentMatchRule `json:", inline"` |
|
287 | 310 |
|
288 |
-var ( |
|
289 |
- // AllowAll does not prevent any kinds of client version skew |
|
290 |
- AllowAll LegacyClientPolicy = "allow-all" |
|
291 |
- // DenyOldClients prevents older clients (but not newer ones) from issuing stomping requests |
|
292 |
- DenyOldClients LegacyClientPolicy = "deny-old-clients" |
|
293 |
- // DenySkewedClients prevents any non-matching client from issuing stomping requests |
|
294 |
- DenySkewedClients LegacyClientPolicy = "deny-skewed-clients" |
|
295 |
-) |
|
311 |
+ // RejectionMessage is the message shown when rejecting a client. If it is not a set, the default message is used. |
|
312 |
+ RejectionMessage string `json:"rejectionMessage"` |
|
313 |
+} |
|
296 | 314 |
|
297 | 315 |
// RoutingConfig holds the necessary configuration options for routing to subdomains |
298 | 316 |
type RoutingConfig struct { |
... | ... |
@@ -421,11 +421,12 @@ oauthConfig: |
421 | 421 |
pauseControllers: false |
422 | 422 |
policyConfig: |
423 | 423 |
bootstrapPolicyFile: "" |
424 |
- legacyClientPolicyConfig: |
|
425 |
- legacyClientPolicy: "" |
|
426 |
- restrictedHTTPVerbs: null |
|
427 | 424 |
openshiftInfrastructureNamespace: "" |
428 | 425 |
openshiftSharedResourcesNamespace: "" |
426 |
+ userAgentMatchingConfig: |
|
427 |
+ defaultRejectionMessage: "" |
|
428 |
+ deniedClients: null |
|
429 |
+ requiredClients: null |
|
429 | 430 |
projectConfig: |
430 | 431 |
defaultNodeSelector: "" |
431 | 432 |
projectRequestMessage: "" |
... | ... |
@@ -497,6 +497,19 @@ func ValidatePolicyConfig(config api.PolicyConfig, fldPath *field.Path) field.Er |
497 | 497 |
allErrs = append(allErrs, ValidateNamespace(config.OpenShiftSharedResourcesNamespace, fldPath.Child("openShiftSharedResourcesNamespace"))...) |
498 | 498 |
allErrs = append(allErrs, ValidateNamespace(config.OpenShiftInfrastructureNamespace, fldPath.Child("openShiftInfrastructureNamespace"))...) |
499 | 499 |
|
500 |
+ for i, matchingRule := range config.UserAgentMatchingConfig.DeniedClients { |
|
501 |
+ _, err := regexp.Compile(matchingRule.Regex) |
|
502 |
+ if err != nil { |
|
503 |
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("userAgentMatchingConfig", "deniedClients").Index(i), matchingRule.Regex, err.Error())) |
|
504 |
+ } |
|
505 |
+ } |
|
506 |
+ for i, matchingRule := range config.UserAgentMatchingConfig.RequiredClients { |
|
507 |
+ _, err := regexp.Compile(matchingRule.Regex) |
|
508 |
+ if err != nil { |
|
509 |
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("userAgentMatchingConfig", "requiredClients").Index(i), matchingRule.Regex, err.Error())) |
|
510 |
+ } |
|
511 |
+ } |
|
512 |
+ |
|
500 | 513 |
return allErrs |
501 | 514 |
} |
502 | 515 |
|
... | ... |
@@ -8,10 +8,9 @@ import ( |
8 | 8 |
"net/http" |
9 | 9 |
"regexp" |
10 | 10 |
"sort" |
11 |
- "strings" |
|
12 | 11 |
|
13 |
- "github.com/coreos/go-semver/semver" |
|
14 | 12 |
restful "github.com/emicklei/go-restful" |
13 |
+ "github.com/golang/glog" |
|
15 | 14 |
|
16 | 15 |
kapi "k8s.io/kubernetes/pkg/api" |
17 | 16 |
kapierrors "k8s.io/kubernetes/pkg/api/errors" |
... | ... |
@@ -19,12 +18,10 @@ import ( |
19 | 19 |
"k8s.io/kubernetes/pkg/apiserver" |
20 | 20 |
"k8s.io/kubernetes/pkg/runtime" |
21 | 21 |
"k8s.io/kubernetes/pkg/util/sets" |
22 |
- kversion "k8s.io/kubernetes/pkg/version" |
|
23 | 22 |
|
24 | 23 |
"github.com/openshift/origin/pkg/authorization/authorizer" |
25 | 24 |
configapi "github.com/openshift/origin/pkg/cmd/server/api" |
26 | 25 |
"github.com/openshift/origin/pkg/util/httprequest" |
27 |
- "github.com/openshift/origin/pkg/version" |
|
28 | 26 |
) |
29 | 27 |
|
30 | 28 |
// TODO We would like to use the IndexHandler from k8s but we do not yet have a |
... | ... |
@@ -178,85 +175,100 @@ func namespacingFilter(handler http.Handler, contextMapper kapi.RequestContextMa |
178 | 178 |
}) |
179 | 179 |
} |
180 | 180 |
|
181 |
-// variants I know I have to worry about |
|
182 |
-// 1. oc kube resources: oc/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
183 |
-// 2. oc openshift resources: oc/v1.1.3 (linux/amd64) openshift/b348c2f |
|
184 |
-// 3. openshift kubectl kube resources: openshift/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
185 |
-// 4. openshit kubectl openshift resources: openshift/v1.1.3 (linux/amd64) openshift/b348c2f |
|
186 |
-// 5. oadm kube resources: oadm/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
187 |
-// 6. oadm openshift resources: oadm/v1.1.3 (linux/amd64) openshift/b348c2f |
|
188 |
-// 7. openshift cli kube resources: openshift/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
189 |
-// 8. openshift cli openshift resources: openshift/v1.1.3 (linux/amd64) openshift/b348c2f |
|
190 |
-var ( |
|
191 |
- kubeStyleUserAgent = regexp.MustCompile(`\w+/v([\w\.]+) \(.+/.+\) kubernetes/\w{7}`) |
|
192 |
- openshiftStyleUserAgent = regexp.MustCompile(`\w+/v([\w\.]+) \(.+/.+\) openshift/\w{7}`) |
|
193 |
-) |
|
181 |
+type userAgentFilter struct { |
|
182 |
+ regex *regexp.Regexp |
|
183 |
+ message string |
|
184 |
+ verbs sets.String |
|
185 |
+} |
|
186 |
+ |
|
187 |
+func newUserAgentFilter(config configapi.UserAgentMatchRule) (userAgentFilter, error) { |
|
188 |
+ regex, err := regexp.Compile(config.Regex) |
|
189 |
+ if err != nil { |
|
190 |
+ return userAgentFilter{}, err |
|
191 |
+ } |
|
192 |
+ userAgentFilter := userAgentFilter{regex: regex, verbs: sets.NewString(config.HTTPVerbs...)} |
|
193 |
+ |
|
194 |
+ return userAgentFilter, nil |
|
195 |
+} |
|
196 |
+ |
|
197 |
+func (f *userAgentFilter) matches(verb, userAgent string) bool { |
|
198 |
+ if len(f.verbs) > 0 && !f.verbs.Has(verb) { |
|
199 |
+ return false |
|
200 |
+ } |
|
201 |
+ |
|
202 |
+ return f.regex.MatchString(userAgent) |
|
203 |
+} |
|
194 | 204 |
|
195 | 205 |
// versionSkewFilter adds a filter that may deny requests from skewed |
196 | 206 |
// oc clients, since we know that those clients will strip unknown fields which can lead to unexpected outcomes |
197 |
-func (c *MasterConfig) versionSkewFilter(openshiftBinaryInfo version.Info, kubeBinaryInfo kversion.Info, handler http.Handler) http.Handler { |
|
198 |
- skewedClientPolicy := c.Options.PolicyConfig.LegacyClientPolicyConfig.LegacyClientPolicy |
|
199 |
- if skewedClientPolicy == configapi.AllowAll { |
|
207 |
+func (c *MasterConfig) versionSkewFilter(handler http.Handler) http.Handler { |
|
208 |
+ infoResolver := &apiserver.RequestInfoResolver{APIPrefixes: sets.NewString("api", "osapi", "oapi", "apis"), GrouplessAPIPrefixes: sets.NewString("api", "osapi", "oapi")} |
|
209 |
+ |
|
210 |
+ filterConfig := c.Options.PolicyConfig.UserAgentMatchingConfig |
|
211 |
+ if len(filterConfig.RequiredClients) == 0 && len(filterConfig.DeniedClients) == 0 { |
|
200 | 212 |
return handler |
201 | 213 |
} |
202 |
- seg := strings.SplitN(openshiftBinaryInfo.GitVersion, "-", 2) |
|
203 |
- openshiftServerVersion := seg[0][1:] |
|
204 |
- seg = strings.SplitN(kubeBinaryInfo.GitVersion, "-", 2) |
|
205 |
- kubeServerVersion := seg[0][1:] |
|
206 | 214 |
|
207 |
- restrictedVerbs := sets.NewString(c.Options.PolicyConfig.LegacyClientPolicyConfig.RestrictedHTTPVerbs...) |
|
208 |
- |
|
209 |
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { |
|
210 |
- if !restrictedVerbs.Has(req.Method) { |
|
211 |
- handler.ServeHTTP(w, req) |
|
212 |
- return |
|
213 |
- } |
|
215 |
+ defaultMessage := filterConfig.DefaultRejectionMessage |
|
216 |
+ if len(defaultMessage) == 0 { |
|
217 |
+ defaultMessage = "the cluster administrator has disabled access for this client, please upgrade or consult your administrator" |
|
218 |
+ } |
|
214 | 219 |
|
215 |
- userAgent := req.Header.Get("User-Agent") |
|
216 |
- if len(userAgent) == 0 { |
|
217 |
- handler.ServeHTTP(w, req) |
|
218 |
- return |
|
220 |
+ // the structure of the legacyClientPolicyConfig is pretty easy to write, but its inefficient to use at runtime |
|
221 |
+ // pre-process the config elements to make a more efficicent structure. |
|
222 |
+ allowedFilters := []userAgentFilter{} |
|
223 |
+ deniedFilters := []userAgentFilter{} |
|
224 |
+ for _, config := range filterConfig.RequiredClients { |
|
225 |
+ userAgentFilter, err := newUserAgentFilter(config) |
|
226 |
+ if err != nil { |
|
227 |
+ glog.Errorf("Failure to compile User-Agent regex %v: %v", config.Regex, err) |
|
228 |
+ continue |
|
219 | 229 |
} |
220 | 230 |
|
221 |
- clientVersion := "" |
|
222 |
- serverVersion := "" |
|
223 |
- if submatches := kubeStyleUserAgent.FindStringSubmatch(userAgent); len(submatches) == 2 { |
|
224 |
- clientVersion = submatches[1] |
|
225 |
- serverVersion = kubeServerVersion |
|
231 |
+ allowedFilters = append(allowedFilters, userAgentFilter) |
|
232 |
+ } |
|
233 |
+ for _, config := range filterConfig.DeniedClients { |
|
234 |
+ userAgentFilter, err := newUserAgentFilter(config.UserAgentMatchRule) |
|
235 |
+ if err != nil { |
|
236 |
+ glog.Errorf("Failure to compile User-Agent regex %v: %v", config.Regex, err) |
|
237 |
+ continue |
|
226 | 238 |
} |
227 |
- if submatches := openshiftStyleUserAgent.FindStringSubmatch(userAgent); len(submatches) == 2 { |
|
228 |
- clientVersion = submatches[1] |
|
229 |
- serverVersion = openshiftServerVersion |
|
239 |
+ userAgentFilter.message = config.RejectionMessage |
|
240 |
+ if len(userAgentFilter.message) == 0 { |
|
241 |
+ userAgentFilter.message = defaultMessage |
|
230 | 242 |
} |
231 |
- if len(clientVersion) == 0 { |
|
243 |
+ |
|
244 |
+ deniedFilters = append(deniedFilters, userAgentFilter) |
|
245 |
+ } |
|
246 |
+ |
|
247 |
+ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { |
|
248 |
+ if requestInfo, err := infoResolver.GetRequestInfo(req); err == nil && !requestInfo.IsResourceRequest { |
|
232 | 249 |
handler.ServeHTTP(w, req) |
233 | 250 |
return |
234 | 251 |
} |
235 | 252 |
|
236 |
- switch skewedClientPolicy { |
|
237 |
- case configapi.DenyOldClients: |
|
238 |
- serverSemVer, err := semver.NewVersion(serverVersion) |
|
239 |
- if err != nil { |
|
240 |
- handler.ServeHTTP(w, req) |
|
241 |
- return |
|
242 |
- } |
|
243 |
- clientSemVer, err := semver.NewVersion(clientVersion) |
|
244 |
- if err != nil { |
|
245 |
- handler.ServeHTTP(w, req) |
|
246 |
- return |
|
253 |
+ userAgent := req.Header.Get("User-Agent") |
|
254 |
+ |
|
255 |
+ if len(allowedFilters) > 0 { |
|
256 |
+ foundMatch := false |
|
257 |
+ for _, filter := range allowedFilters { |
|
258 |
+ if filter.matches(req.Method, userAgent) { |
|
259 |
+ foundMatch = true |
|
260 |
+ break |
|
261 |
+ } |
|
247 | 262 |
} |
248 | 263 |
|
249 |
- if clientSemVer.LessThan(*serverSemVer) { |
|
250 |
- forbidden(fmt.Sprintf("userVersion %v is older than the server version %v; mutation is denied", clientVersion, serverVersion), nil, w, req) |
|
264 |
+ if !foundMatch { |
|
265 |
+ forbidden(defaultMessage, nil, w, req) |
|
251 | 266 |
return |
252 | 267 |
} |
268 |
+ } |
|
253 | 269 |
|
254 |
- case configapi.DenySkewedClients: |
|
255 |
- if clientVersion != serverVersion { |
|
256 |
- forbidden(fmt.Sprintf("userVersion %v is different than the server version %v; mutation is denied", clientVersion, serverVersion), nil, w, req) |
|
270 |
+ for _, filter := range deniedFilters { |
|
271 |
+ if filter.matches(req.Method, userAgent) { |
|
272 |
+ forbidden(filter.message, nil, w, req) |
|
257 | 273 |
return |
258 | 274 |
} |
259 |
- |
|
260 | 275 |
} |
261 | 276 |
|
262 | 277 |
handler.ServeHTTP(w, req) |
... | ... |
@@ -7,10 +7,7 @@ import ( |
7 | 7 |
"strings" |
8 | 8 |
"testing" |
9 | 9 |
|
10 |
- kversion "k8s.io/kubernetes/pkg/version" |
|
11 |
- |
|
12 | 10 |
configapi "github.com/openshift/origin/pkg/cmd/server/api" |
13 |
- "github.com/openshift/origin/pkg/version" |
|
14 | 11 |
) |
15 | 12 |
|
16 | 13 |
var ( |
... | ... |
@@ -25,12 +22,13 @@ var ( |
25 | 25 |
|
26 | 26 |
olderOCKubeResources = "oc/v1.1.10 (linux/amd64) kubernetes/bc4550d" |
27 | 27 |
olderOCOriginResources = "oc/v1.1.1 (linux/amd64) openshift/b348c2f" |
28 |
+ oldestOCOriginResources = "oc/v1.0.1 (linux/amd64) openshift/b348c2f" |
|
28 | 29 |
olderOpenshiftKubectlKubeResources = "openshift/v1.1.10 (linux/amd64) kubernetes/bc4550d" |
29 | 30 |
olderOpenshiftKubectlOriginResources = "openshift/v1.1.1 (linux/amd64) openshift/b348c2f" |
30 | 31 |
olderOADMKubeResources = "oadm/v1.1.10 (linux/amd64) kubernetes/bc4550d" |
31 | 32 |
olderOADMOriginResources = "oadm/v1.1.1 (linux/amd64) openshift/b348c2f" |
32 | 33 |
olderVersionUserAgents = []string{ |
33 |
- olderOCKubeResources, olderOCOriginResources, olderOpenshiftKubectlKubeResources, olderOpenshiftKubectlOriginResources, olderOADMKubeResources, olderOADMOriginResources} |
|
34 |
+ olderOCKubeResources, olderOCOriginResources, oldestOCOriginResources, olderOpenshiftKubectlKubeResources, olderOpenshiftKubectlOriginResources, olderOADMKubeResources, olderOADMOriginResources} |
|
34 | 35 |
|
35 | 36 |
newerOCKubeResources = "oc/v1.2.1 (linux/amd64) kubernetes/bc4550d" |
36 | 37 |
newerOCOriginResources = "oc/v1.1.4 (linux/amd64) openshift/b348c2f" |
... | ... |
@@ -43,10 +41,24 @@ var ( |
43 | 43 |
|
44 | 44 |
notOCVersion = "something else" |
45 | 45 |
|
46 |
- openshiftServerVersion = version.Info{GitVersion: "v1.1.3"} |
|
47 |
- kubeServerVersion = kversion.Info{GitVersion: "v1.2.0"} |
|
46 |
+ openshiftServerVersion = `v1\.1\.3` |
|
47 |
+ kubeServerVersion = `v1\.2\.0` |
|
48 | 48 |
) |
49 | 49 |
|
50 |
+// variants I know I have to worry about |
|
51 |
+// 1. oc kube resources: oc/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
52 |
+// 2. oc openshift resources: oc/v1.1.3 (linux/amd64) openshift/b348c2f |
|
53 |
+// 3. openshift kubectl kube resources: openshift/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
54 |
+// 4. openshit kubectl openshift resources: openshift/v1.1.3 (linux/amd64) openshift/b348c2f |
|
55 |
+// 5. oadm kube resources: oadm/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
56 |
+// 6. oadm openshift resources: oadm/v1.1.3 (linux/amd64) openshift/b348c2f |
|
57 |
+// 7. openshift cli kube resources: openshift/v1.2.0 (linux/amd64) kubernetes/bc4550d |
|
58 |
+// 8. openshift cli openshift resources: openshift/v1.1.3 (linux/amd64) openshift/b348c2f |
|
59 |
+// var ( |
|
60 |
+// kubeStyleUserAgent = regexp.MustCompile(`\w+/v([\w\.]+) \(.+/.+\) kubernetes/\w{7}`) |
|
61 |
+// openshiftStyleUserAgent = regexp.MustCompile(`\w+/v([\w\.]+) \(.+/.+\) openshift/\w{7}`) |
|
62 |
+// ) |
|
63 |
+ |
|
50 | 64 |
type versionSkewTestCase struct { |
51 | 65 |
name string |
52 | 66 |
userAgents []string |
... | ... |
@@ -55,25 +67,6 @@ type versionSkewTestCase struct { |
55 | 55 |
} |
56 | 56 |
|
57 | 57 |
func (tc versionSkewTestCase) Run(url string, t *testing.T) { |
58 |
- // gets always succeed |
|
59 |
- for _, userAgent := range tc.userAgents { |
|
60 |
- req, err := http.NewRequest("GET", url, nil) |
|
61 |
- if err != nil { |
|
62 |
- t.Errorf("%s: unexpected error: %v", tc.name, err) |
|
63 |
- return |
|
64 |
- } |
|
65 |
- req.Header.Add("User-Agent", userAgent) |
|
66 |
- resp, err := http.DefaultClient.Do(req) |
|
67 |
- if err != nil { |
|
68 |
- t.Errorf("%s: unexpected error: %v", tc.name, err) |
|
69 |
- return |
|
70 |
- } |
|
71 |
- if resp.StatusCode != http.StatusOK { |
|
72 |
- t.Errorf("%s: unexpected status: %v", tc.name, resp.StatusCode) |
|
73 |
- return |
|
74 |
- } |
|
75 |
- } |
|
76 |
- |
|
77 | 58 |
for _, method := range tc.methods { |
78 | 59 |
for _, userAgent := range tc.userAgents { |
79 | 60 |
req, err := http.NewRequest(method, url, nil) |
... | ... |
@@ -89,13 +82,13 @@ func (tc versionSkewTestCase) Run(url string, t *testing.T) { |
89 | 89 |
} |
90 | 90 |
if len(tc.failureMessage) == 0 { |
91 | 91 |
if resp.StatusCode != http.StatusOK { |
92 |
- t.Errorf("%s: unexpected status: %v", tc.name, resp.StatusCode) |
|
92 |
+ t.Errorf("%s: %s: unexpected status: %v", tc.name, userAgent, resp.StatusCode) |
|
93 | 93 |
return |
94 | 94 |
} |
95 | 95 |
|
96 | 96 |
} else { |
97 | 97 |
if resp.StatusCode != http.StatusForbidden { |
98 |
- t.Errorf("%s: unexpected status: %v", tc.name, resp.StatusCode) |
|
98 |
+ t.Errorf("%s: %s: unexpected status: %v", tc.name, userAgent, resp.StatusCode) |
|
99 | 99 |
return |
100 | 100 |
} |
101 | 101 |
|
... | ... |
@@ -115,14 +108,16 @@ func (tc versionSkewTestCase) Run(url string, t *testing.T) { |
115 | 115 |
|
116 | 116 |
} |
117 | 117 |
|
118 |
-func TestVersionSkewFilterAllowAll(t *testing.T) { |
|
119 |
- verbs := []string{"PUT", "POST"} |
|
118 |
+func TestVersionSkewFilterDenyOld(t *testing.T) { |
|
119 |
+ verbs := []string{"PATCH", "POST"} |
|
120 | 120 |
doNothingHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { |
121 | 121 |
}) |
122 | 122 |
config := MasterConfig{} |
123 |
- config.Options.PolicyConfig.LegacyClientPolicyConfig.LegacyClientPolicy = configapi.AllowAll |
|
124 |
- config.Options.PolicyConfig.LegacyClientPolicyConfig.RestrictedHTTPVerbs = verbs |
|
125 |
- server := httptest.NewServer(config.versionSkewFilter(openshiftServerVersion, kubeServerVersion, doNothingHandler)) |
|
123 |
+ config.Options.PolicyConfig.UserAgentMatchingConfig.DeniedClients = []configapi.UserAgentDenyRule{ |
|
124 |
+ {UserAgentMatchRule: configapi.UserAgentMatchRule{Regex: `\w+/v1\.1\.10 \(.+/.+\) kubernetes/\w{7}`, HTTPVerbs: verbs}, RejectionMessage: "rejected for reasons!"}, |
|
125 |
+ {UserAgentMatchRule: configapi.UserAgentMatchRule{Regex: `\w+/v(?:(?:1\.1\.1)|(?:1\.0\.1)) \(.+/.+\) openshift/\w{7}`, HTTPVerbs: verbs}, RejectionMessage: "rejected for reasons!"}, |
|
126 |
+ } |
|
127 |
+ server := httptest.NewServer(config.versionSkewFilter(doNothingHandler)) |
|
126 | 128 |
defer server.Close() |
127 | 129 |
|
128 | 130 |
testCases := []versionSkewTestCase{ |
... | ... |
@@ -137,9 +132,10 @@ func TestVersionSkewFilterAllowAll(t *testing.T) { |
137 | 137 |
methods: verbs, |
138 | 138 |
}, |
139 | 139 |
{ |
140 |
- name: "older", |
|
141 |
- userAgents: olderVersionUserAgents, |
|
142 |
- methods: verbs, |
|
140 |
+ name: "older", |
|
141 |
+ userAgents: olderVersionUserAgents, |
|
142 |
+ failureMessage: "rejected for reasons!", |
|
143 |
+ methods: verbs, |
|
143 | 144 |
}, |
144 | 145 |
{ |
145 | 146 |
name: "newer", |
... | ... |
@@ -154,62 +150,71 @@ func TestVersionSkewFilterAllowAll(t *testing.T) { |
154 | 154 |
} |
155 | 155 |
|
156 | 156 |
for _, tc := range testCases { |
157 |
- tc.Run(server.URL, t) |
|
157 |
+ tc.Run(server.URL+"/api/v1/namespaces", t) |
|
158 | 158 |
} |
159 | 159 |
} |
160 | 160 |
|
161 |
-func TestVersionSkewFilterDenyOld(t *testing.T) { |
|
162 |
- verbs := []string{"PATCH", "POST"} |
|
161 |
+func TestVersionSkewFilterDenySkewed(t *testing.T) { |
|
162 |
+ verbs := []string{"PUT", "DELETE"} |
|
163 | 163 |
doNothingHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { |
164 | 164 |
}) |
165 | 165 |
config := MasterConfig{} |
166 |
- config.Options.PolicyConfig.LegacyClientPolicyConfig.LegacyClientPolicy = configapi.DenyOldClients |
|
167 |
- config.Options.PolicyConfig.LegacyClientPolicyConfig.RestrictedHTTPVerbs = verbs |
|
168 |
- server := httptest.NewServer(config.versionSkewFilter(openshiftServerVersion, kubeServerVersion, doNothingHandler)) |
|
166 |
+ config.Options.PolicyConfig.UserAgentMatchingConfig.RequiredClients = []configapi.UserAgentMatchRule{ |
|
167 |
+ {Regex: `\w+/` + kubeServerVersion + ` \(.+/.+\) kubernetes/\w{7}`, HTTPVerbs: verbs}, |
|
168 |
+ {Regex: `\w+/` + openshiftServerVersion + ` \(.+/.+\) openshift/\w{7}`, HTTPVerbs: verbs}, |
|
169 |
+ } |
|
170 |
+ config.Options.PolicyConfig.UserAgentMatchingConfig.DefaultRejectionMessage = "rejected for reasons!" |
|
171 |
+ server := httptest.NewServer(config.versionSkewFilter(doNothingHandler)) |
|
169 | 172 |
defer server.Close() |
170 | 173 |
|
171 | 174 |
testCases := []versionSkewTestCase{ |
172 | 175 |
{ |
173 |
- name: "missing", |
|
174 |
- userAgents: []string{""}, |
|
175 |
- methods: verbs, |
|
176 |
+ name: "missing", |
|
177 |
+ userAgents: []string{""}, |
|
178 |
+ failureMessage: "rejected for reasons!", |
|
179 |
+ methods: verbs, |
|
176 | 180 |
}, |
177 | 181 |
{ |
178 |
- name: "not oc", |
|
179 |
- userAgents: []string{notOCVersion}, |
|
180 |
- methods: verbs, |
|
182 |
+ name: "not oc", |
|
183 |
+ userAgents: []string{notOCVersion}, |
|
184 |
+ failureMessage: "rejected for reasons!", |
|
185 |
+ methods: verbs, |
|
181 | 186 |
}, |
182 | 187 |
{ |
183 | 188 |
name: "older", |
184 | 189 |
userAgents: olderVersionUserAgents, |
185 |
- failureMessage: " is older than the server version", |
|
190 |
+ failureMessage: "rejected for reasons!", |
|
186 | 191 |
methods: verbs, |
187 | 192 |
}, |
188 | 193 |
{ |
189 |
- name: "newer", |
|
190 |
- userAgents: newerVersionUserAgents, |
|
191 |
- methods: verbs, |
|
194 |
+ name: "newer", |
|
195 |
+ userAgents: newerVersionUserAgents, |
|
196 |
+ failureMessage: "rejected for reasons!", |
|
197 |
+ methods: verbs, |
|
192 | 198 |
}, |
193 | 199 |
{ |
194 |
- name: "exact", |
|
200 |
+ name: "current", |
|
195 | 201 |
userAgents: currentVersionUserAgents, |
196 | 202 |
methods: verbs, |
197 | 203 |
}, |
198 | 204 |
} |
199 | 205 |
|
200 | 206 |
for _, tc := range testCases { |
201 |
- tc.Run(server.URL, t) |
|
207 |
+ tc.Run(server.URL+"/api/v1/namespaces", t) |
|
202 | 208 |
} |
203 | 209 |
} |
204 | 210 |
|
205 |
-func TestVersionSkewFilterDenySkewed(t *testing.T) { |
|
211 |
+func TestVersionSkewFilterSkippedOnNonAPIRequest(t *testing.T) { |
|
206 | 212 |
verbs := []string{"PUT", "DELETE"} |
207 | 213 |
doNothingHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { |
208 | 214 |
}) |
209 | 215 |
config := MasterConfig{} |
210 |
- config.Options.PolicyConfig.LegacyClientPolicyConfig.LegacyClientPolicy = configapi.DenySkewedClients |
|
211 |
- config.Options.PolicyConfig.LegacyClientPolicyConfig.RestrictedHTTPVerbs = verbs |
|
212 |
- server := httptest.NewServer(config.versionSkewFilter(openshiftServerVersion, kubeServerVersion, doNothingHandler)) |
|
216 |
+ config.Options.PolicyConfig.UserAgentMatchingConfig.RequiredClients = []configapi.UserAgentMatchRule{ |
|
217 |
+ {Regex: `\w+/` + kubeServerVersion + ` \(.+/.+\) kubernetes/\w{7}`, HTTPVerbs: verbs}, |
|
218 |
+ {Regex: `\w+/` + openshiftServerVersion + ` \(.+/.+\) openshift/\w{7}`, HTTPVerbs: verbs}, |
|
219 |
+ } |
|
220 |
+ config.Options.PolicyConfig.UserAgentMatchingConfig.DefaultRejectionMessage = "rejected for reasons!" |
|
221 |
+ server := httptest.NewServer(config.versionSkewFilter(doNothingHandler)) |
|
213 | 222 |
defer server.Close() |
214 | 223 |
|
215 | 224 |
testCases := []versionSkewTestCase{ |
... | ... |
@@ -224,16 +229,14 @@ func TestVersionSkewFilterDenySkewed(t *testing.T) { |
224 | 224 |
methods: verbs, |
225 | 225 |
}, |
226 | 226 |
{ |
227 |
- name: "older", |
|
228 |
- userAgents: olderVersionUserAgents, |
|
229 |
- failureMessage: "is different than the server version", |
|
230 |
- methods: verbs, |
|
227 |
+ name: "older", |
|
228 |
+ userAgents: olderVersionUserAgents, |
|
229 |
+ methods: verbs, |
|
231 | 230 |
}, |
232 | 231 |
{ |
233 |
- name: "newer", |
|
234 |
- userAgents: newerVersionUserAgents, |
|
235 |
- failureMessage: "is different than the server version", |
|
236 |
- methods: verbs, |
|
232 |
+ name: "newer", |
|
233 |
+ userAgents: newerVersionUserAgents, |
|
234 |
+ methods: verbs, |
|
237 | 235 |
}, |
238 | 236 |
{ |
239 | 237 |
name: "current", |
... | ... |
@@ -243,6 +246,6 @@ func TestVersionSkewFilterDenySkewed(t *testing.T) { |
243 | 243 |
} |
244 | 244 |
|
245 | 245 |
for _, tc := range testCases { |
246 |
- tc.Run(server.URL, t) |
|
246 |
+ tc.Run(server.URL+"/api/v1", t) |
|
247 | 247 |
} |
248 | 248 |
} |
... | ... |
@@ -29,7 +29,6 @@ import ( |
29 | 29 |
"k8s.io/kubernetes/pkg/util" |
30 | 30 |
"k8s.io/kubernetes/pkg/util/sets" |
31 | 31 |
utilwait "k8s.io/kubernetes/pkg/util/wait" |
32 |
- kversion "k8s.io/kubernetes/pkg/version" |
|
33 | 32 |
|
34 | 33 |
"github.com/openshift/origin/pkg/api/v1" |
35 | 34 |
"github.com/openshift/origin/pkg/api/v1beta3" |
... | ... |
@@ -105,7 +104,6 @@ import ( |
105 | 105 |
"github.com/openshift/origin/pkg/authorization/rulevalidation" |
106 | 106 |
configapi "github.com/openshift/origin/pkg/cmd/server/api" |
107 | 107 |
routeplugin "github.com/openshift/origin/pkg/route/allocation/simple" |
108 |
- "github.com/openshift/origin/pkg/version" |
|
109 | 108 |
) |
110 | 109 |
|
111 | 110 |
const ( |
... | ... |
@@ -161,7 +159,7 @@ func (c *MasterConfig) Run(protected []APIInstaller, unprotected []APIInstaller) |
161 | 161 |
} |
162 | 162 |
extra = append(extra, msgs...) |
163 | 163 |
} |
164 |
- handler := c.versionSkewFilter(version.Get(), kversion.Get(), safe) |
|
164 |
+ handler := c.versionSkewFilter(safe) |
|
165 | 165 |
handler = c.authorizationFilter(handler) |
166 | 166 |
handler = authenticationHandlerFilter(handler, c.Authenticator, c.getRequestContextMapper()) |
167 | 167 |
handler = namespacingFilter(handler, c.getRequestContextMapper()) |