From 1668c6f10701c9ee1bec9a952cfbf58e07679a6d Mon Sep 17 00:00:00 2001 From: Eric Tune Date: Sat, 1 Nov 2014 23:50:00 -0700 Subject: [PATCH] Authorization based on namespace, kind, readonly. Also, pass Authorizer into master.Config. --- cmd/apiserver/apiserver.go | 8 +- cmd/integration/integration.go | 3 +- pkg/apiserver/apiserver.go | 1 + pkg/apiserver/authz.go | 12 +- pkg/apiserver/handlers.go | 52 ++++- pkg/auth/authorizer/interfaces.go | 30 ++- pkg/master/master.go | 20 +- test/integration/auth_test.go | 305 ++++++++++++++++++++++++++---- test/integration/client_test.go | 3 +- 9 files changed, 374 insertions(+), 60 deletions(-) diff --git a/cmd/apiserver/apiserver.go b/cmd/apiserver/apiserver.go index 75b45d0232c..e2f84de5440 100644 --- a/cmd/apiserver/apiserver.go +++ b/cmd/apiserver/apiserver.go @@ -145,6 +145,12 @@ func main() { } n := net.IPNet(portalNet) + + authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(*authorizationMode) + if err != nil { + glog.Fatalf("Invalid Authorization Config: %v", err) + } + config := &master.Config{ Client: client, Cloud: cloud, @@ -161,7 +167,7 @@ func main() { ReadOnlyPort: *readOnlyPort, ReadWritePort: *port, PublicAddress: *publicAddressOverride, - AuthorizationMode: *authorizationMode, + Authorizer: authorizer, } m := master.New(config) diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index 3a12bd45483..22a2bc220d3 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -36,6 +36,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi" + "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" minionControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller" replicationControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/controller" @@ -146,7 +147,7 @@ func startComponents(manifestURL string) (apiServerURL string) { KubeletClient: fakeKubeletClient{}, EnableLogsSupport: false, APIPrefix: "/api", - AuthorizationMode: "AlwaysAllow", + Authorizer: apiserver.NewAlwaysAllowAuthorizer(), ReadWritePort: portNumber, ReadOnlyPort: portNumber, diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 1af55671bd6..47c27ed6871 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -107,6 +107,7 @@ func (g *APIGroup) InstallREST(mux Mux, paths ...string) { prefix = strings.TrimRight(prefix, "/") proxyHandler := &ProxyHandler{prefix + "/proxy/", g.handler.storage, g.handler.codec} mux.Handle(prefix+"/", http.StripPrefix(prefix, restHandler)) + // Note: update GetAttribs() when adding a handler. mux.Handle(prefix+"/watch/", http.StripPrefix(prefix+"/watch/", watchHandler)) mux.Handle(prefix+"/proxy/", http.StripPrefix(prefix+"/proxy/", proxyHandler)) mux.Handle(prefix+"/redirect/", http.StripPrefix(prefix+"/redirect/", redirectHandler)) diff --git a/pkg/apiserver/authz.go b/pkg/apiserver/authz.go index 09470f51543..882af6739e9 100644 --- a/pkg/apiserver/authz.go +++ b/pkg/apiserver/authz.go @@ -36,6 +36,10 @@ func (alwaysAllowAuthorizer) Authorize(a authorizer.Attributes) (err error) { return nil } +func NewAlwaysAllowAuthorizer() authorizer.Authorizer { + return new(alwaysAllowAuthorizer) +} + // alwaysDenyAuthorizer is an implementation of authorizer.Attributes // which always says no to an authorization request. // It is useful in unit tests to force an operation to be forbidden. @@ -45,6 +49,10 @@ func (alwaysDenyAuthorizer) Authorize(a authorizer.Attributes) (err error) { return errors.New("Everything is forbidden.") } +func NewAlwaysDenyAuthorizer() authorizer.Authorizer { + return new(alwaysDenyAuthorizer) +} + const ( ModeAlwaysAllow string = "AlwaysAllow" ModeAlwaysDeny string = "AlwaysDeny" @@ -59,9 +67,9 @@ func NewAuthorizerFromAuthorizationConfig(authorizationMode string) (authorizer. // Keep cases in sync with constant list above. switch authorizationMode { case ModeAlwaysAllow: - return new(alwaysAllowAuthorizer), nil + return NewAlwaysAllowAuthorizer(), nil case ModeAlwaysDeny: - return new(alwaysDenyAuthorizer), nil + return NewAlwaysDenyAuthorizer(), nil default: return nil, errors.New("Unknown authorization mode") } diff --git a/pkg/apiserver/handlers.go b/pkg/apiserver/handlers.go index da9a2daeb26..b82f9384db9 100644 --- a/pkg/apiserver/handlers.go +++ b/pkg/apiserver/handlers.go @@ -30,10 +30,49 @@ import ( "github.com/golang/glog" ) +// specialVerbs contains just strings which are used in REST paths for special actions that don't fall under the normal +// CRUDdy GET/POST/PUT/DELETE actions on REST objects. +// TODO: find a way to keep this up to date automatically. Maybe dynamically populate list as handlers added to +// master's Mux. +var specialVerbs = map[string]bool{ + "proxy": true, + "redirect": true, + "watch": true, +} + +// KindFromRequest returns Kind if Kind can be extracted from the request. Otherwise, the empty string. +func KindFromRequest(req http.Request) string { + // TODO: find a way to keep this code's assumptions about paths up to date with changes in the code. Maybe instead + // of directly adding handler's code to the master's Mux, have a function which forces the structure when adding + // them. + parts := splitPath(req.URL.Path) + if len(parts) > 2 && parts[0] == "api" { + if _, ok := specialVerbs[parts[2]]; ok { + if len(parts) > 3 { + return parts[3] + } + } else { + return parts[2] + } + } + return "" +} + +// IsReadOnlyReq() is true for any (or at least many) request which has no observable +// side effects on state of apiserver (though there may be internal side effects like +// caching and logging). +func IsReadOnlyReq(req http.Request) bool { + if req.Method == "GET" { + // TODO: add OPTIONS and HEAD if we ever support those. + return true + } + return false +} + // ReadOnly passes all GET requests on to handler, and returns an error on all other requests. func ReadOnly(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - if req.Method == "GET" { + if IsReadOnlyReq(*req) { handler.ServeHTTP(w, req) return } @@ -143,6 +182,17 @@ func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attrib attribs.User = user } + attribs.ReadOnly = IsReadOnlyReq(*req) + + // If a path follows the conventions of the REST object store, then + // we can extract the object Kind. Otherwise, not. + attribs.Kind = KindFromRequest(*req) + + // If the request specifies a namespace, then the namespace is filled in. + // Assumes there is no empty string namespace. Unspecified results + // in empty (does not understand defaulting rules.) + attribs.Namespace = req.URL.Query().Get("namespace") + return &attribs } diff --git a/pkg/auth/authorizer/interfaces.go b/pkg/auth/authorizer/interfaces.go index 61de4c4259c..dd5ad05fcc9 100644 --- a/pkg/auth/authorizer/interfaces.go +++ b/pkg/auth/authorizer/interfaces.go @@ -23,7 +23,20 @@ import ( // Attributes is an interface used by an Authorizer to get information about a request // that is used to make an authorization decision. type Attributes interface { + // The user string which the request was authenticated as, or empty if + // no authentication occured and the request was allowed to proceed. GetUserName() string + // TODO: add groups, e.g. GetGroups() []string + + // When IsReadOnly() == true, the request has no side effects, other than + // caching, logging, and other incidentals. + IsReadOnly() bool + + // The namespace of the object, if a request is for a REST object. + GetNamespace() string + + // The kind of object, if a request is for a REST object. + GetKind() string } // Authorizer makes an authorization decision based on information gained by making @@ -35,9 +48,24 @@ type Authorizer interface { // AttributesRecord implements Attributes interface. type AttributesRecord struct { - User user.Info + User user.Info + ReadOnly bool + Namespace string + Kind string } func (a *AttributesRecord) GetUserName() string { return a.User.GetName() } + +func (a *AttributesRecord) IsReadOnly() bool { + return a.ReadOnly +} + +func (a *AttributesRecord) GetNamespace() string { + return a.Namespace +} + +func (a *AttributesRecord) GetKind() string { + return a.Kind +} diff --git a/pkg/master/master.go b/pkg/master/master.go index 5d3ee34759d..9aa931a9bd2 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -67,8 +67,7 @@ type Config struct { APIPrefix string CorsAllowedOriginList util.StringList TokenAuthFile string - AuthorizationMode string - AuthorizerForTesting authorizer.Authorizer + Authorizer authorizer.Authorizer // Number of masters running; all masters must be started with the // same value for this field. (Numbers > 1 currently untested.) @@ -104,7 +103,7 @@ type Master struct { apiPrefix string corsAllowedOriginList util.StringList tokenAuthFile string - authorizationzMode string + authorizer authorizer.Authorizer masterCount int // "Outputs" @@ -227,7 +226,7 @@ func New(c *Config) *Master { apiPrefix: c.APIPrefix, corsAllowedOriginList: c.CorsAllowedOriginList, tokenAuthFile: c.TokenAuthFile, - authorizationzMode: c.AuthorizationMode, + authorizer: c.Authorizer, masterCount: c.MasterCount, readOnlyServer: net.JoinHostPort(c.PublicAddress, strconv.Itoa(int(c.ReadOnlyPort))), @@ -319,19 +318,8 @@ func (m *Master) init(c *Config) { handler = apiserver.CORS(handler, allowedOriginRegexps, nil, nil, "true") } - // Install Authorizer - var authorizer authorizer.Authorizer - if c.AuthorizerForTesting != nil { - authorizer = c.AuthorizerForTesting - } else { - var err error - authorizer, err = apiserver.NewAuthorizerFromAuthorizationConfig(m.authorizationzMode) - if err != nil { - glog.Fatal(err) - } - } attributeGetter := apiserver.NewRequestAttributeGetter(userContexts) - handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, authorizer) + handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, m.authorizer) // Install Authenticator if authenticator != nil { diff --git a/test/integration/auth_test.go b/test/integration/auth_test.go index df60f1c5468..ce2386a3f0d 100644 --- a/test/integration/auth_test.go +++ b/test/integration/auth_test.go @@ -32,6 +32,7 @@ import ( "os" "testing" + "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/master" @@ -88,7 +89,7 @@ func TestWhoAmI(t *testing.T) { EnableUISupport: false, APIPrefix: "/api", TokenAuthFile: tokenFilename, - AuthorizationMode: "AlwaysAllow", + Authorizer: apiserver.NewAlwaysAllowAuthorizer(), }) s := httptest.NewServer(m.Handler) @@ -237,6 +238,7 @@ var aEndpoints string = ` var code200or202 = map[int]bool{200: true, 202: true} // Unpredicatable which will be returned. var code400 = map[int]bool{400: true} +var code403 = map[int]bool{403: true} var code404 = map[int]bool{404: true} var code409 = map[int]bool{409: true} var code422 = map[int]bool{422: true} @@ -372,7 +374,7 @@ func TestAuthModeAlwaysAllow(t *testing.T) { EnableLogsSupport: false, EnableUISupport: false, APIPrefix: "/api", - AuthorizationMode: "AlwaysAllow", + Authorizer: apiserver.NewAlwaysAllowAuthorizer(), }) s := httptest.NewServer(m.Handler) @@ -417,7 +419,7 @@ func TestAuthModeAlwaysDeny(t *testing.T) { EnableLogsSupport: false, EnableUISupport: false, APIPrefix: "/api", - AuthorizationMode: "AlwaysDeny", + Authorizer: apiserver.NewAlwaysDenyAuthorizer(), }) s := httptest.NewServer(m.Handler) @@ -465,8 +467,6 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) { defer os.Remove(tokenFilename) // This file has alice and bob in it. - aaa := allowAliceAuthorizer{} - // Set up a master helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") @@ -475,22 +475,19 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) { } m := master.New(&master.Config{ - EtcdHelper: helper, - KubeletClient: client.FakeKubeletClient{}, - EnableLogsSupport: false, - EnableUISupport: false, - APIPrefix: "/api", - TokenAuthFile: tokenFilename, - AuthorizerForTesting: aaa, + EtcdHelper: helper, + KubeletClient: client.FakeKubeletClient{}, + EnableLogsSupport: false, + EnableUISupport: false, + APIPrefix: "/api", + TokenAuthFile: tokenFilename, + Authorizer: allowAliceAuthorizer{}, }) s := httptest.NewServer(m.Handler) defer s.Close() transport := http.DefaultTransport - // Alice is authorized. - - // for _, r := range getTestRequests() { token := AliceToken t.Logf("case %v", r) @@ -524,8 +521,6 @@ func TestBobIsForbidden(t *testing.T) { defer os.Remove(tokenFilename) // This file has alice and bob in it. - aaa := allowAliceAuthorizer{} - // Set up a master helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") @@ -534,22 +529,19 @@ func TestBobIsForbidden(t *testing.T) { } m := master.New(&master.Config{ - EtcdHelper: helper, - KubeletClient: client.FakeKubeletClient{}, - EnableLogsSupport: false, - EnableUISupport: false, - APIPrefix: "/api", - TokenAuthFile: tokenFilename, - AuthorizerForTesting: aaa, + EtcdHelper: helper, + KubeletClient: client.FakeKubeletClient{}, + EnableLogsSupport: false, + EnableUISupport: false, + APIPrefix: "/api", + TokenAuthFile: tokenFilename, + Authorizer: allowAliceAuthorizer{}, }) s := httptest.NewServer(m.Handler) defer s.Close() transport := http.DefaultTransport - // Alice is authorized. - - // for _, r := range getTestRequests() { token := BobToken t.Logf("case %v", r) @@ -585,8 +577,6 @@ func TestUnknownUserIsUnauthorized(t *testing.T) { defer os.Remove(tokenFilename) // This file has alice and bob in it. - aaa := allowAliceAuthorizer{} - // Set up a master helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") @@ -595,13 +585,13 @@ func TestUnknownUserIsUnauthorized(t *testing.T) { } m := master.New(&master.Config{ - EtcdHelper: helper, - KubeletClient: client.FakeKubeletClient{}, - EnableLogsSupport: false, - EnableUISupport: false, - APIPrefix: "/api", - TokenAuthFile: tokenFilename, - AuthorizerForTesting: aaa, + EtcdHelper: helper, + KubeletClient: client.FakeKubeletClient{}, + EnableLogsSupport: false, + EnableUISupport: false, + APIPrefix: "/api", + TokenAuthFile: tokenFilename, + Authorizer: allowAliceAuthorizer{}, }) s := httptest.NewServer(m.Handler) @@ -625,7 +615,248 @@ func TestUnknownUserIsUnauthorized(t *testing.T) { } // Expect all of unauthenticated user's request to be "Unauthorized" if resp.StatusCode != http.StatusUnauthorized { - t.Errorf("Expected status Unauthorized, but got %s", resp.Status) + t.Errorf("Expected status %v, but got %v", http.StatusUnauthorized, resp.StatusCode) + b, _ := ioutil.ReadAll(resp.Body) + t.Errorf("Body: %v", string(b)) + } + } + } +} + +// Inject into master an authorizer that uses namespace information. +// TODO(etune): remove this test once a more comprehensive built-in authorizer is implemented. +type allowFooNamespaceAuthorizer struct{} + +func (allowFooNamespaceAuthorizer) Authorize(a authorizer.Attributes) error { + if a.GetNamespace() == "foo" { + return nil + } + return errors.New("I can't allow that. Try another namespace, buddy.") +} + +// TestNamespaceAuthorization tests that authorization can be controlled +// by namespace. +func TestNamespaceAuthorization(t *testing.T) { + deleteAllEtcdKeys() + + tokenFilename := writeTestTokenFile() + defer os.Remove(tokenFilename) + // This file has alice and bob in it. + + // Set up a master + + helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + m := master.New(&master.Config{ + EtcdHelper: helper, + KubeletClient: client.FakeKubeletClient{}, + EnableLogsSupport: false, + EnableUISupport: false, + APIPrefix: "/api", + TokenAuthFile: tokenFilename, + Authorizer: allowFooNamespaceAuthorizer{}, + }) + + s := httptest.NewServer(m.Handler) + defer s.Close() + transport := http.DefaultTransport + + requests := []struct { + verb string + URL string + body string + statusCodes map[int]bool // allowed status codes. + }{ + {"POST", "/api/v1beta1/pods?namespace=foo", aPod, code200or202}, + {"GET", "/api/v1beta1/pods?namespace=foo", "", code200or202}, + {"GET", "/api/v1beta1/pods/a?namespace=foo", "", code200or202}, + {"DELETE", "/api/v1beta1/pods/a?namespace=foo", "", code200or202}, + + {"POST", "/api/v1beta1/pods?namespace=bar", aPod, code403}, + {"GET", "/api/v1beta1/pods?namespace=bar", "", code403}, + {"GET", "/api/v1beta1/pods/a?namespace=bar", "", code403}, + {"DELETE", "/api/v1beta1/pods/a?namespace=bar", "", code403}, + + {"POST", "/api/v1beta1/pods", aPod, code403}, + {"GET", "/api/v1beta1/pods", "", code403}, + {"GET", "/api/v1beta1/pods/a", "", code403}, + {"DELETE", "/api/v1beta1/pods/a", "", code403}, + } + + for _, r := range requests { + token := BobToken + t.Logf("case %v", r) + bodyBytes := bytes.NewReader([]byte(r.body)) + req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) + { + resp, err := transport.RoundTrip(req) + defer resp.Body.Close() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := r.statusCodes[resp.StatusCode]; !ok { + t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode) + } + } + } +} + +// Inject into master an authorizer that uses kind information. +// TODO(etune): remove this test once a more comprehensive built-in authorizer is implemented. +type allowServicesAuthorizer struct{} + +func (allowServicesAuthorizer) Authorize(a authorizer.Attributes) error { + if a.GetKind() == "services" { + return nil + } + return errors.New("I can't allow that. Hint: try services.") +} + +// TestKindAuthorization tests that authorization can be controlled +// by namespace. +func TestKindAuthorization(t *testing.T) { + deleteAllEtcdKeys() + + tokenFilename := writeTestTokenFile() + defer os.Remove(tokenFilename) + // This file has alice and bob in it. + + // Set up a master + + helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + m := master.New(&master.Config{ + EtcdHelper: helper, + KubeletClient: client.FakeKubeletClient{}, + EnableLogsSupport: false, + EnableUISupport: false, + APIPrefix: "/api", + TokenAuthFile: tokenFilename, + Authorizer: allowServicesAuthorizer{}, + }) + + s := httptest.NewServer(m.Handler) + defer s.Close() + transport := http.DefaultTransport + + requests := []struct { + verb string + URL string + body string + statusCodes map[int]bool // allowed status codes. + }{ + {"POST", "/api/v1beta1/services", aService, code200or202}, + {"GET", "/api/v1beta1/services", "", code200or202}, + {"GET", "/api/v1beta1/services/a", "", code200or202}, + {"DELETE", "/api/v1beta1/services/a", "", code200or202}, + + {"POST", "/api/v1beta1/pods", aPod, code403}, + {"GET", "/api/v1beta1/pods", "", code403}, + {"GET", "/api/v1beta1/pods/a", "", code403}, + {"DELETE", "/api/v1beta1/pods/a", "", code403}, + } + + for _, r := range requests { + token := BobToken + t.Logf("case %v", r) + bodyBytes := bytes.NewReader([]byte(r.body)) + req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) + { + resp, err := transport.RoundTrip(req) + defer resp.Body.Close() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := r.statusCodes[resp.StatusCode]; !ok { + t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode) + } + } + } +} + +// Inject into master an authorizer that uses ReadOnly information. +// TODO(etune): remove this test once a more comprehensive built-in authorizer is implemented. +type allowReadAuthorizer struct{} + +func (allowReadAuthorizer) Authorize(a authorizer.Attributes) error { + if a.IsReadOnly() { + return nil + } + return errors.New("I'm afraid I can't let you do that.") +} + +// TestReadOnlyAuthorization tests that authorization can be controlled +// by namespace. +func TestReadOnlyAuthorization(t *testing.T) { + deleteAllEtcdKeys() + + tokenFilename := writeTestTokenFile() + defer os.Remove(tokenFilename) + // This file has alice and bob in it. + + // Set up a master + + helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + m := master.New(&master.Config{ + EtcdHelper: helper, + KubeletClient: client.FakeKubeletClient{}, + EnableLogsSupport: false, + EnableUISupport: false, + APIPrefix: "/api", + TokenAuthFile: tokenFilename, + Authorizer: allowReadAuthorizer{}, + }) + + s := httptest.NewServer(m.Handler) + defer s.Close() + transport := http.DefaultTransport + + requests := []struct { + verb string + URL string + body string + statusCodes map[int]bool // allowed status codes. + }{ + {"POST", "/api/v1beta1/pods", aPod, code403}, + {"GET", "/api/v1beta1/pods", "", code200or202}, + {"GET", "/api/v1beta1/pods/a", "", code404}, + } + + for _, r := range requests { + token := BobToken + t.Logf("case %v", r) + bodyBytes := bytes.NewReader([]byte(r.body)) + req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) + { + resp, err := transport.RoundTrip(req) + defer resp.Body.Close() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := r.statusCodes[resp.StatusCode]; !ok { + t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode) } } } diff --git a/test/integration/client_test.go b/test/integration/client_test.go index 181c5512f0c..44df24ff45f 100644 --- a/test/integration/client_test.go +++ b/test/integration/client_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/master" @@ -45,7 +46,7 @@ func TestClient(t *testing.T) { EnableLogsSupport: false, EnableUISupport: false, APIPrefix: "/api", - AuthorizationMode: "AlwaysAllow", + Authorizer: apiserver.NewAlwaysAllowAuthorizer(), }) s := httptest.NewServer(m.Handler)