diff --git a/cmd/kube-apiserver/apiserver.go b/cmd/kube-apiserver/apiserver.go index cdca499d1eb..afddcb2acb3 100644 --- a/cmd/kube-apiserver/apiserver.go +++ b/cmd/kube-apiserver/apiserver.go @@ -150,6 +150,11 @@ func main() { n := net.IPNet(portalNet) + authenticator, err := apiserver.NewAuthenticatorFromTokenFile(*tokenAuthFile) + if err != nil { + glog.Fatalf("Invalid Authentication Config: %v", err) + } + authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(*authorizationMode, *authorizationPolicyFile) if err != nil { glog.Fatalf("Invalid Authorization Config: %v", err) @@ -167,10 +172,10 @@ func main() { EnableUISupport: true, APIPrefix: *apiPrefix, CorsAllowedOriginList: corsAllowedOriginList, - TokenAuthFile: *tokenAuthFile, ReadOnlyPort: *readOnlyPort, ReadWritePort: *port, PublicAddress: *publicAddressOverride, + Authenticator: authenticator, Authorizer: authorizer, } m := master.New(config) diff --git a/pkg/apiserver/authn.go b/pkg/apiserver/authn.go new file mode 100644 index 00000000000..bf1ebdac7d9 --- /dev/null +++ b/pkg/apiserver/authn.go @@ -0,0 +1,36 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apiserver + +import ( + "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator" + "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator/bearertoken" + "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/auth/authenticator/token/tokenfile" +) + +// NewAuthenticatorFromTokenFile returns an authenticator.Request or an error +func NewAuthenticatorFromTokenFile(tokenAuthFile string) (authenticator.Request, error) { + var authenticator authenticator.Request + if len(tokenAuthFile) != 0 { + tokenAuthenticator, err := tokenfile.NewCSV(tokenAuthFile) + if err != nil { + return nil, err + } + authenticator = bearertoken.New(tokenAuthenticator) + } + return authenticator, nil +} diff --git a/pkg/master/master.go b/pkg/master/master.go index d5aeaa80afc..42e05d9add6 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -34,8 +34,6 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator" - "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator/bearertoken" - "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator/tokenfile" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/handlers" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" @@ -73,7 +71,7 @@ type Config struct { EnableUISupport bool APIPrefix string CorsAllowedOriginList util.StringList - TokenAuthFile string + Authenticator authenticator.Request Authorizer authorizer.Authorizer // Number of masters running; all masters must be started with the @@ -111,7 +109,7 @@ type Master struct { enableUISupport bool apiPrefix string corsAllowedOriginList util.StringList - tokenAuthFile string + authenticator authenticator.Request authorizer authorizer.Authorizer masterCount int @@ -242,7 +240,7 @@ func New(c *Config) *Master { enableUISupport: c.EnableUISupport, apiPrefix: c.APIPrefix, corsAllowedOriginList: c.CorsAllowedOriginList, - tokenAuthFile: c.TokenAuthFile, + authenticator: c.Authenticator, authorizer: c.Authorizer, masterCount: c.MasterCount, @@ -309,14 +307,7 @@ func (m *Master) init(c *Config) { go util.Forever(func() { podCache.UpdateAllContainers() }, time.Second*30) var userContexts = handlers.NewUserRequestContext() - var authenticator authenticator.Request - if len(c.TokenAuthFile) != 0 { - tokenAuthenticator, err := tokenfile.New(c.TokenAuthFile) - if err != nil { - glog.Fatalf("Unable to load the token authentication file '%s': %v", c.TokenAuthFile, err) - } - authenticator = bearertoken.New(tokenAuthenticator) - } + var authenticator = c.Authenticator // TODO: Factor out the core API registration m.storage = map[string]apiserver.RESTStorage{ diff --git a/pkg/auth/authenticator/tokenfile/tokenfile.go b/plugin/pkg/auth/authenticator/token/tokenfile/tokenfile.go similarity index 88% rename from pkg/auth/authenticator/tokenfile/tokenfile.go rename to plugin/pkg/auth/authenticator/token/tokenfile/tokenfile.go index dcd6851111f..d8c15755eea 100644 --- a/pkg/auth/authenticator/tokenfile/tokenfile.go +++ b/plugin/pkg/auth/authenticator/token/tokenfile/tokenfile.go @@ -29,7 +29,9 @@ type TokenAuthenticator struct { tokens map[string]*user.DefaultInfo } -func New(path string) (*TokenAuthenticator, error) { +// NewCSV returns a TokenAuthenticator, populated from a CSV file. +// The CSV file must contain records in the format "token,username,useruid" +func NewCSV(path string) (*TokenAuthenticator, error) { file, err := os.Open(path) if err != nil { return nil, err diff --git a/pkg/auth/authenticator/tokenfile/tokenfile_test.go b/plugin/pkg/auth/authenticator/token/tokenfile/tokenfile_test.go similarity index 99% rename from pkg/auth/authenticator/tokenfile/tokenfile_test.go rename to plugin/pkg/auth/authenticator/token/tokenfile/tokenfile_test.go index d0f09f4edce..3b0d05ff983 100644 --- a/pkg/auth/authenticator/tokenfile/tokenfile_test.go +++ b/plugin/pkg/auth/authenticator/token/tokenfile/tokenfile_test.go @@ -109,5 +109,5 @@ func newWithContents(t *testing.T, contents string) (auth *TokenAuthenticator, e t.Fatalf("unexpected error writing tokenfile: %v", err) } - return New(f.Name()) + return NewCSV(f.Name()) } diff --git a/plugin/pkg/auth/authenticator/token/tokentest/tokentest.go b/plugin/pkg/auth/authenticator/token/tokentest/tokentest.go new file mode 100644 index 00000000000..6943c33f89d --- /dev/null +++ b/plugin/pkg/auth/authenticator/token/tokentest/tokentest.go @@ -0,0 +1,36 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tokentest + +import "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" + +type TokenAuthenticator struct { + Tokens map[string]*user.DefaultInfo +} + +func New() *TokenAuthenticator { + return &TokenAuthenticator{ + Tokens: make(map[string]*user.DefaultInfo), + } +} +func (a *TokenAuthenticator) AuthenticateToken(value string) (user.Info, bool, error) { + user, ok := a.Tokens[value] + if !ok { + return nil, false, nil + } + return user, true, nil +} diff --git a/test/integration/auth_test.go b/test/integration/auth_test.go index f25eacce0ac..858c1924890 100644 --- a/test/integration/auth_test.go +++ b/test/integration/auth_test.go @@ -33,10 +33,14 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" + "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator" + "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator/bearertoken" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer/abac" + "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/master" + "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/auth/authenticator/token/tokentest" ) func init() { @@ -47,24 +51,13 @@ const ( AliceToken string = "abc123" // username: alice. Present in token file. BobToken string = "xyz987" // username: bob. Present in token file. UnknownToken string = "qwerty" // Not present in token file. - // Keep file in sync with above constants. - TokenfileCSV string = ` -abc123,alice,1 -xyz987,bob,2 -` ) -func writeTestTokenFile(t *testing.T) string { - // Write a token file. - f, err := ioutil.TempFile("", "auth_integration_test") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - f.Close() - if err := ioutil.WriteFile(f.Name(), []byte(TokenfileCSV), 0700); err != nil { - t.Fatalf("unexpected error writing tokenfile: %v", err) - } - return f.Name() +func getTestTokenAuth() authenticator.Request { + tokenAuthenticator := tokentest.New() + tokenAuthenticator.Tokens[AliceToken] = &user.DefaultInfo{Name: "alice", UID: "1"} + tokenAuthenticator.Tokens[BobToken] = &user.DefaultInfo{Name: "bob", UID: "2"} + return bearertoken.New(tokenAuthenticator) } // TestWhoAmI passes a known Bearer Token to the master's /_whoami endpoint and checks that @@ -79,15 +72,13 @@ func TestWhoAmI(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - tokenFilename := writeTestTokenFile(t) - defer os.Remove(tokenFilename) m := master.New(&master.Config{ EtcdHelper: helper, KubeletClient: client.FakeKubeletClient{}, EnableLogsSupport: false, EnableUISupport: false, APIPrefix: "/api", - TokenAuthFile: tokenFilename, + Authenticator: getTestTokenAuth(), Authorizer: apiserver.NewAlwaysAllowAuthorizer(), }) @@ -467,8 +458,6 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) { deleteAllEtcdKeys() - tokenFilename := writeTestTokenFile(t) - defer os.Remove(tokenFilename) // This file has alice and bob in it. // Set up a master @@ -484,7 +473,7 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) { EnableLogsSupport: false, EnableUISupport: false, APIPrefix: "/api", - TokenAuthFile: tokenFilename, + Authenticator: getTestTokenAuth(), Authorizer: allowAliceAuthorizer{}, }) @@ -521,8 +510,6 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) { func TestBobIsForbidden(t *testing.T) { deleteAllEtcdKeys() - tokenFilename := writeTestTokenFile(t) - defer os.Remove(tokenFilename) // This file has alice and bob in it. // Set up a master @@ -538,7 +525,7 @@ func TestBobIsForbidden(t *testing.T) { EnableLogsSupport: false, EnableUISupport: false, APIPrefix: "/api", - TokenAuthFile: tokenFilename, + Authenticator: getTestTokenAuth(), Authorizer: allowAliceAuthorizer{}, }) @@ -577,8 +564,6 @@ func TestBobIsForbidden(t *testing.T) { func TestUnknownUserIsUnauthorized(t *testing.T) { deleteAllEtcdKeys() - tokenFilename := writeTestTokenFile(t) - defer os.Remove(tokenFilename) // This file has alice and bob in it. // Set up a master @@ -594,7 +579,7 @@ func TestUnknownUserIsUnauthorized(t *testing.T) { EnableLogsSupport: false, EnableUISupport: false, APIPrefix: "/api", - TokenAuthFile: tokenFilename, + Authenticator: getTestTokenAuth(), Authorizer: allowAliceAuthorizer{}, }) @@ -651,8 +636,6 @@ func newAuthorizerWithContents(t *testing.T, contents string) authorizer.Authori func TestNamespaceAuthorization(t *testing.T) { deleteAllEtcdKeys() - tokenFilename := writeTestTokenFile(t) - defer os.Remove(tokenFilename) // This file has alice and bob in it. helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") @@ -668,7 +651,7 @@ func TestNamespaceAuthorization(t *testing.T) { EnableLogsSupport: false, EnableUISupport: false, APIPrefix: "/api", - TokenAuthFile: tokenFilename, + Authenticator: getTestTokenAuth(), Authorizer: a, }) @@ -726,8 +709,6 @@ func TestNamespaceAuthorization(t *testing.T) { func TestKindAuthorization(t *testing.T) { deleteAllEtcdKeys() - tokenFilename := writeTestTokenFile(t) - defer os.Remove(tokenFilename) // This file has alice and bob in it. // Set up a master @@ -745,7 +726,7 @@ func TestKindAuthorization(t *testing.T) { EnableLogsSupport: false, EnableUISupport: false, APIPrefix: "/api", - TokenAuthFile: tokenFilename, + Authenticator: getTestTokenAuth(), Authorizer: a, }) @@ -797,8 +778,6 @@ func TestKindAuthorization(t *testing.T) { func TestReadOnlyAuthorization(t *testing.T) { deleteAllEtcdKeys() - tokenFilename := writeTestTokenFile(t) - defer os.Remove(tokenFilename) // This file has alice and bob in it. // Set up a master @@ -816,7 +795,7 @@ func TestReadOnlyAuthorization(t *testing.T) { EnableLogsSupport: false, EnableUISupport: false, APIPrefix: "/api", - TokenAuthFile: tokenFilename, + Authenticator: getTestTokenAuth(), Authorizer: a, })