func newFakeAuthenticator() authenticator.Request { return bearertoken.New(authenticator.TokenFunc(func(token string) (user.Info, bool, error) { if token == "" { return nil, false, errors.New("no bearer token found") } // Set the bearer token as the user name. return &user.DefaultInfo{Name: token, UID: token}, true, nil })) }
func TestAuthenticateRequestTokenError(t *testing.T) { auth := New(authenticator.TokenFunc(func(token string) (user.Info, bool, error) { return nil, false, errors.New("error") })) user, ok, err := auth.AuthenticateRequest(&http.Request{ Header: http.Header{"Authorization": []string{"Bearer token"}}, }) if ok || user != nil || err == nil { t.Errorf("expected error") } }
func TestAuthenticateRequest(t *testing.T) { auth := New(authenticator.TokenFunc(func(token string) (user.Info, bool, error) { if token != "token" { t.Errorf("unexpected token: %s", token) } return &user.DefaultInfo{Name: "user"}, true, nil })) user, ok, err := auth.AuthenticateRequest(&http.Request{ Header: http.Header{"Authorization": []string{"Bearer token"}}, }) if !ok || user == nil || err != nil { t.Errorf("expected valid user") } }
func TestAuthenticateRequestBadValue(t *testing.T) { testCases := []struct { Req *http.Request }{ {Req: &http.Request{}}, {Req: &http.Request{Header: http.Header{"Authorization": []string{"Bearer"}}}}, {Req: &http.Request{Header: http.Header{"Authorization": []string{"bear token"}}}}, {Req: &http.Request{Header: http.Header{"Authorization": []string{"Bearer: token"}}}}, } for i, testCase := range testCases { auth := New(authenticator.TokenFunc(func(token string) (user.Info, bool, error) { t.Errorf("authentication should not have been called") return nil, false, nil })) user, ok, err := auth.AuthenticateRequest(testCase.Req) if ok || user != nil || err != nil { t.Errorf("%d: expected not authenticated (no token)", i) } } }
// startServiceAccountTestServer returns a started server // It is the responsibility of the caller to ensure the returned stopFunc is called func startServiceAccountTestServer(t *testing.T) (*clientset.Clientset, restclient.Config, func()) { deleteAllEtcdKeys() // Listener var m *master.Master apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) // Anonymous client config clientConfig := restclient.Config{Host: apiServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}} // Root client // TODO: remove rootClient after we refactor pkg/admission to use the clientset. rootClientset := clientset.NewForConfigOrDie(&restclient.Config{Host: apiServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}, BearerToken: rootToken}) // Set up two authenticators: // 1. A token authenticator that maps the rootToken to the "root" user // 2. A ServiceAccountToken authenticator that validates ServiceAccount tokens rootTokenAuth := authenticator.TokenFunc(func(token string) (user.Info, bool, error) { if token == rootToken { return &user.DefaultInfo{rootUserName, "", []string{}}, true, nil } return nil, false, nil }) serviceAccountKey, _ := rsa.GenerateKey(rand.Reader, 2048) serviceAccountTokenGetter := serviceaccountcontroller.NewGetterFromClient(rootClientset) serviceAccountTokenAuth := serviceaccount.JWTTokenAuthenticator([]*rsa.PublicKey{&serviceAccountKey.PublicKey}, true, serviceAccountTokenGetter) authenticator := union.New( bearertoken.New(rootTokenAuth), bearertoken.New(serviceAccountTokenAuth), ) // Set up a stub authorizer: // 1. The "root" user is allowed to do anything // 2. ServiceAccounts named "ro" are allowed read-only operations in their namespace // 3. ServiceAccounts named "rw" are allowed any operation in their namespace authorizer := authorizer.AuthorizerFunc(func(attrs authorizer.Attributes) error { username := attrs.GetUserName() ns := attrs.GetNamespace() // If the user is "root"... if username == rootUserName { // allow them to do anything return nil } // If the user is a service account... if serviceAccountNamespace, serviceAccountName, err := serviceaccount.SplitUsername(username); err == nil { // Limit them to their own namespace if serviceAccountNamespace == ns { switch serviceAccountName { case readOnlyServiceAccountName: if attrs.IsReadOnly() { return nil } case readWriteServiceAccountName: return nil } } } return fmt.Errorf("User %s is denied (ns=%s, readonly=%v, resource=%s)", username, ns, attrs.IsReadOnly(), attrs.GetResource()) }) // Set up admission plugin to auto-assign serviceaccounts to pods serviceAccountAdmission := serviceaccountadmission.NewServiceAccount(rootClientset) masterConfig := framework.NewMasterConfig() masterConfig.EnableIndex = true masterConfig.Authenticator = authenticator masterConfig.Authorizer = authorizer masterConfig.AdmissionControl = serviceAccountAdmission // Create a master and install handlers into mux. m, err := master.New(masterConfig) if err != nil { t.Fatalf("Error in bringing up the master: %v", err) } // Start the service account and service account token controllers tokenController := serviceaccountcontroller.NewTokensController(rootClientset, serviceaccountcontroller.TokensControllerOptions{TokenGenerator: serviceaccount.JWTTokenGenerator(serviceAccountKey)}) tokenController.Run() serviceAccountController := serviceaccountcontroller.NewServiceAccountsController(rootClientset, serviceaccountcontroller.DefaultServiceAccountsControllerOptions()) serviceAccountController.Run() // Start the admission plugin reflectors serviceAccountAdmission.Run() stop := func() { tokenController.Stop() serviceAccountController.Stop() serviceAccountAdmission.Stop() // TODO: Uncomment when fix #19254 // apiServer.Close() } return rootClientset, clientConfig, stop }
// startServiceAccountTestServer returns a started server // It is the responsibility of the caller to ensure the returned stopFunc is called func startServiceAccountTestServer(t *testing.T) (*client.Client, client.Config, func()) { deleteAllEtcdKeys() // Etcd etcdStorage, err := framework.NewEtcdStorage() if err != nil { t.Fatalf("unexpected error: %v", err) } expEtcdStorage, err := framework.NewExtensionsEtcdStorage(nil) if err != nil { t.Fatalf("unexpected error: %v", err) } storageDestinations := master.NewStorageDestinations() storageDestinations.AddAPIGroup("", etcdStorage) storageDestinations.AddAPIGroup("extensions", expEtcdStorage) storageVersions := make(map[string]string) storageVersions[""] = testapi.Default.Version() storageVersions["extensions"] = testapi.Extensions.GroupAndVersion() // Listener var m *master.Master apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) // Anonymous client config clientConfig := client.Config{Host: apiServer.URL, Version: testapi.Default.Version()} // Root client rootClient := client.NewOrDie(&client.Config{Host: apiServer.URL, Version: testapi.Default.Version(), BearerToken: rootToken}) // Set up two authenticators: // 1. A token authenticator that maps the rootToken to the "root" user // 2. A ServiceAccountToken authenticator that validates ServiceAccount tokens rootTokenAuth := authenticator.TokenFunc(func(token string) (user.Info, bool, error) { if token == rootToken { return &user.DefaultInfo{rootUserName, "", "", "", []string{}, ""}, true, nil } return nil, false, nil }) serviceAccountKey, err := rsa.GenerateKey(rand.Reader, 2048) serviceAccountTokenGetter := serviceaccount.NewGetterFromClient(rootClient) serviceAccountTokenAuth := serviceaccount.JWTTokenAuthenticator([]*rsa.PublicKey{&serviceAccountKey.PublicKey}, true, serviceAccountTokenGetter) authenticator := union.New( bearertoken.New(rootTokenAuth), bearertoken.New(serviceAccountTokenAuth), ) // Set up a stub authorizer: // 1. The "root" user is allowed to do anything // 2. ServiceAccounts named "ro" are allowed read-only operations in their namespace // 3. ServiceAccounts named "rw" are allowed any operation in their namespace authorizer := authorizer.AuthorizerFunc(func(attrs authorizer.Attributes) (string, error) { username := attrs.GetUserName() ns := attrs.GetNamespace() // If the user is "root"... if username == rootUserName { // allow them to do anything return "", nil } // If the user is a service account... if serviceAccountNamespace, serviceAccountName, err := serviceaccount.SplitUsername(username); err == nil { // Limit them to their own namespace if serviceAccountNamespace == ns { switch serviceAccountName { case readOnlyServiceAccountName: if attrs.IsReadOnly() { return "", nil } case readWriteServiceAccountName: return "", nil } } } return "", fmt.Errorf("User %s is denied (ns=%s, readonly=%v, resource=%s)", username, ns, attrs.IsReadOnly(), attrs.GetResource()) }) // Set up admission plugin to auto-assign serviceaccounts to pods serviceAccountAdmission := serviceaccountadmission.NewServiceAccount(rootClient) // Create a master and install handlers into mux. m = master.New(&master.Config{ StorageDestinations: storageDestinations, KubeletClient: client.FakeKubeletClient{}, EnableLogsSupport: false, EnableUISupport: false, EnableIndex: true, APIPrefix: "/api", Authenticator: authenticator, Authorizer: authorizer, AdmissionControl: serviceAccountAdmission, StorageVersions: storageVersions, }) // Start the service account and service account token controllers tokenController := serviceaccount.NewTokensController(rootClient, serviceaccount.TokensControllerOptions{TokenGenerator: serviceaccount.JWTTokenGenerator(serviceAccountKey)}) tokenController.Run() serviceAccountController := serviceaccount.NewServiceAccountsController(rootClient, serviceaccount.DefaultServiceAccountsControllerOptions()) serviceAccountController.Run() // Start the admission plugin reflectors serviceAccountAdmission.Run() stop := func() { tokenController.Stop() serviceAccountController.Stop() serviceAccountAdmission.Stop() apiServer.Close() } return rootClient, clientConfig, stop }
// startServiceAccountTestServer returns a started server // It is the responsibility of the caller to ensure the returned stopFunc is called func startServiceAccountTestServer(t *testing.T) (*clientset.Clientset, restclient.Config, func()) { // Listener h := &framework.MasterHolder{Initialized: make(chan struct{})} apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { <-h.Initialized h.M.GenericAPIServer.Handler.ServeHTTP(w, req) })) // Anonymous client config clientConfig := restclient.Config{Host: apiServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(v1.GroupName).GroupVersion}} // Root client // TODO: remove rootClient after we refactor pkg/admission to use the clientset. rootClientset := clientset.NewForConfigOrDie(&restclient.Config{Host: apiServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(v1.GroupName).GroupVersion}, BearerToken: rootToken}) internalRootClientset := internalclientset.NewForConfigOrDie(&restclient.Config{Host: apiServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(v1.GroupName).GroupVersion}, BearerToken: rootToken}) // Set up two authenticators: // 1. A token authenticator that maps the rootToken to the "root" user // 2. A ServiceAccountToken authenticator that validates ServiceAccount tokens rootTokenAuth := authenticator.TokenFunc(func(token string) (user.Info, bool, error) { if token == rootToken { return &user.DefaultInfo{Name: rootUserName}, true, nil } return nil, false, nil }) serviceAccountKey, _ := rsa.GenerateKey(rand.Reader, 2048) serviceAccountTokenGetter := serviceaccountcontroller.NewGetterFromClient(rootClientset) serviceAccountTokenAuth := serviceaccount.JWTTokenAuthenticator([]interface{}{&serviceAccountKey.PublicKey}, true, serviceAccountTokenGetter) authenticator := union.New( bearertoken.New(rootTokenAuth), bearertoken.New(serviceAccountTokenAuth), ) // Set up a stub authorizer: // 1. The "root" user is allowed to do anything // 2. ServiceAccounts named "ro" are allowed read-only operations in their namespace // 3. ServiceAccounts named "rw" are allowed any operation in their namespace authorizer := authorizer.AuthorizerFunc(func(attrs authorizer.Attributes) (bool, string, error) { username := "" if user := attrs.GetUser(); user != nil { username = user.GetName() } ns := attrs.GetNamespace() // If the user is "root"... if username == rootUserName { // allow them to do anything return true, "", nil } // If the user is a service account... if serviceAccountNamespace, serviceAccountName, err := serviceaccount.SplitUsername(username); err == nil { // Limit them to their own namespace if serviceAccountNamespace == ns { switch serviceAccountName { case readOnlyServiceAccountName: if attrs.IsReadOnly() { return true, "", nil } case readWriteServiceAccountName: return true, "", nil } } } return false, fmt.Sprintf("User %s is denied (ns=%s, readonly=%v, resource=%s)", username, ns, attrs.IsReadOnly(), attrs.GetResource()), nil }) // Set up admission plugin to auto-assign serviceaccounts to pods serviceAccountAdmission := serviceaccountadmission.NewServiceAccount(internalRootClientset) masterConfig := framework.NewMasterConfig() masterConfig.GenericConfig.EnableIndex = true masterConfig.GenericConfig.Authenticator = authenticator masterConfig.GenericConfig.Authorizer = authorizer masterConfig.GenericConfig.AdmissionControl = serviceAccountAdmission framework.RunAMasterUsingServer(masterConfig, apiServer, h) // Start the service account and service account token controllers stopCh := make(chan struct{}) tokenController := serviceaccountcontroller.NewTokensController(rootClientset, serviceaccountcontroller.TokensControllerOptions{TokenGenerator: serviceaccount.JWTTokenGenerator(serviceAccountKey)}) go tokenController.Run(1, stopCh) informers := informers.NewSharedInformerFactory(rootClientset, nil, controller.NoResyncPeriodFunc()) serviceAccountController := serviceaccountcontroller.NewServiceAccountsController(informers.ServiceAccounts(), informers.Namespaces(), rootClientset, serviceaccountcontroller.DefaultServiceAccountsControllerOptions()) informers.Start(stopCh) go serviceAccountController.Run(5, stopCh) // Start the admission plugin reflectors serviceAccountAdmission.Run() stop := func() { close(stopCh) serviceAccountAdmission.Stop() apiServer.Close() } return rootClientset, clientConfig, stop }
func TestBearerToken(t *testing.T) { tests := map[string]struct { AuthorizationHeaders []string TokenAuth authenticator.Token RemoveHeader bool ExpectedUserName string ExpectedOK bool ExpectedErr bool ExpectedAuthorizationHeaders []string }{ "no header": { AuthorizationHeaders: nil, RemoveHeader: true, ExpectedUserName: "", ExpectedOK: false, ExpectedErr: false, ExpectedAuthorizationHeaders: nil, }, "empty header": { AuthorizationHeaders: []string{""}, RemoveHeader: true, ExpectedUserName: "", ExpectedOK: false, ExpectedErr: false, ExpectedAuthorizationHeaders: []string{""}, }, "non-bearer header": { AuthorizationHeaders: []string{"Basic 123"}, RemoveHeader: true, ExpectedUserName: "", ExpectedOK: false, ExpectedErr: false, ExpectedAuthorizationHeaders: []string{"Basic 123"}, }, "empty bearer token": { AuthorizationHeaders: []string{"Bearer "}, RemoveHeader: true, ExpectedUserName: "", ExpectedOK: false, ExpectedErr: false, ExpectedAuthorizationHeaders: []string{"Bearer "}, }, "valid bearer token removing header": { AuthorizationHeaders: []string{"Bearer 123"}, TokenAuth: authenticator.TokenFunc(func(t string) (user.Info, bool, error) { return &user.DefaultInfo{Name: "myuser"}, true, nil }), RemoveHeader: true, ExpectedUserName: "******", ExpectedOK: true, ExpectedErr: false, ExpectedAuthorizationHeaders: nil, }, "valid bearer token leaving header": { AuthorizationHeaders: []string{"Bearer 123"}, TokenAuth: authenticator.TokenFunc(func(t string) (user.Info, bool, error) { return &user.DefaultInfo{Name: "myuser"}, true, nil }), RemoveHeader: false, ExpectedUserName: "******", ExpectedOK: true, ExpectedErr: false, ExpectedAuthorizationHeaders: []string{"Bearer 123"}, }, "invalid bearer token": { AuthorizationHeaders: []string{"Bearer 123"}, TokenAuth: authenticator.TokenFunc(func(t string) (user.Info, bool, error) { return nil, false, nil }), RemoveHeader: true, ExpectedUserName: "", ExpectedOK: false, ExpectedErr: false, ExpectedAuthorizationHeaders: []string{"Bearer 123"}, }, "error bearer token": { AuthorizationHeaders: []string{"Bearer 123"}, TokenAuth: authenticator.TokenFunc(func(t string) (user.Info, bool, error) { return nil, false, errors.New("error") }), RemoveHeader: true, ExpectedUserName: "", ExpectedOK: false, ExpectedErr: true, ExpectedAuthorizationHeaders: []string{"Bearer 123"}, }, } for k, tc := range tests { req, _ := http.NewRequest("GET", "/", nil) for _, h := range tc.AuthorizationHeaders { req.Header.Add("Authorization", h) } bearerAuth := New(tc.TokenAuth, tc.RemoveHeader) u, ok, err := bearerAuth.AuthenticateRequest(req) if tc.ExpectedErr != (err != nil) { t.Errorf("%s: Expected err=%v, got %v", k, tc.ExpectedErr, err) continue } if ok != tc.ExpectedOK { t.Errorf("%s: Expected ok=%v, got %v", k, tc.ExpectedOK, ok) continue } if ok && u.GetName() != tc.ExpectedUserName { t.Errorf("%s: Expected username=%v, got %v", k, tc.ExpectedUserName, u.GetName()) continue } if !reflect.DeepEqual(req.Header["Authorization"], tc.ExpectedAuthorizationHeaders) { t.Errorf("%s: Expected headers=%#v, got %#v", k, tc.ExpectedAuthorizationHeaders, req.Header["Authorization"]) continue } } }
func TestCache(t *testing.T) { tokenAuthInvocations := []string{} tokenAuth := authenticator.TokenFunc(func(token string) (user.Info, bool, error) { tokenAuthInvocations = append(tokenAuthInvocations, token) switch { case strings.HasPrefix(token, "user"): return &user.DefaultInfo{Name: token}, true, nil case strings.HasPrefix(token, "unauthorized"): return nil, false, kerrs.NewUnauthorized(token) case strings.HasPrefix(token, "error"): return nil, false, errors.New(token) default: return nil, false, nil } }) tests := map[string]struct { TTL time.Duration CacheSize int Requests []testRequest ExpectedInvocations []string ExpectedCacheSize int }{ "miss": { TTL: time.Minute, CacheSize: 1, Requests: []testRequest{ {Token: "user1", ExpectedUserName: "******", ExpectedOK: true}, }, ExpectedInvocations: []string{"user1"}, ExpectedCacheSize: 1, }, "cache hit user": { TTL: time.Minute, CacheSize: 1, Requests: []testRequest{ {Token: "user1", ExpectedUserName: "******", ExpectedOK: true}, {Token: "user1", ExpectedUserName: "******", ExpectedOK: true}, }, ExpectedInvocations: []string{"user1"}, ExpectedCacheSize: 1, }, "cache hit invalid": { TTL: time.Minute, CacheSize: 1, Requests: []testRequest{ {Token: "invalid1", ExpectedOK: false}, {Token: "invalid1", ExpectedOK: false}, }, ExpectedInvocations: []string{"invalid1"}, ExpectedCacheSize: 1, }, "cache hit unauthorized error": { TTL: time.Minute, CacheSize: 1, Requests: []testRequest{ {Token: "unauthorized1", ExpectedErr: true}, {Token: "unauthorized1", ExpectedErr: true}, }, ExpectedInvocations: []string{"unauthorized1"}, ExpectedCacheSize: 1, }, "uncacheable error": { TTL: time.Minute, CacheSize: 1, Requests: []testRequest{ {Token: "error1", ExpectedErr: true}, {Token: "error1", ExpectedErr: true}, }, ExpectedInvocations: []string{"error1", "error1"}, ExpectedCacheSize: 0, }, "expire": { TTL: time.Minute, CacheSize: 1, Requests: []testRequest{ {Token: "user1", ExpectedUserName: "******", ExpectedOK: true}, {Token: "user1", ExpectedUserName: "******", ExpectedOK: true, Offset: 2 * time.Minute}, }, ExpectedInvocations: []string{"user1", "user1"}, ExpectedCacheSize: 1, }, "evacuation": { TTL: time.Minute, CacheSize: 2, Requests: []testRequest{ // Request user1 {Token: "user1", ExpectedUserName: "******", ExpectedOK: true}, // Requests for user2 and user3 evacuate user1 {Token: "user2", ExpectedUserName: "******", ExpectedOK: true, Offset: 10 * time.Second}, {Token: "user3", ExpectedUserName: "******", ExpectedOK: true, Offset: 20 * time.Second}, {Token: "user2", ExpectedUserName: "******", ExpectedOK: true, Offset: 30 * time.Second}, {Token: "user3", ExpectedUserName: "******", ExpectedOK: true, Offset: 40 * time.Second}, {Token: "user2", ExpectedUserName: "******", ExpectedOK: true, Offset: 50 * time.Second}, {Token: "user3", ExpectedUserName: "******", ExpectedOK: true, Offset: 60 * time.Second}, // Request for user1 refetches {Token: "user1", ExpectedUserName: "******", ExpectedOK: true}, }, ExpectedInvocations: []string{"user1", "user2", "user3", "user1"}, ExpectedCacheSize: 2, }, } for k, tc := range tests { tokenAuthInvocations = []string{} start := time.Now() auth, err := NewAuthenticator(tokenAuth, tc.TTL, tc.CacheSize) if err != nil { t.Errorf("%s: Unexpected error: %v", k, err) } cacheAuth := auth.(*CacheAuthenticator) for i, r := range tc.Requests { cacheAuth.now = func() time.Time { return start.Add(r.Offset) } u, ok, err := cacheAuth.AuthenticateToken(r.Token) if r.ExpectedErr != (err != nil) { t.Errorf("%s: %d: Expected err=%v, got %v", k, i, r.ExpectedErr, err) continue } if ok != r.ExpectedOK { t.Errorf("%s: %d: Expected ok=%v, got %v", k, i, r.ExpectedOK, ok) continue } if ok && u.GetName() != r.ExpectedUserName { t.Errorf("%s: %d: Expected username=%v, got %v", k, i, r.ExpectedUserName, u.GetName()) continue } } if !reflect.DeepEqual(tc.ExpectedInvocations, tokenAuthInvocations) { t.Errorf("%s: Expected invocations=%v, got %v", k, tc.ExpectedInvocations, tokenAuthInvocations) } if cacheAuth.cache.Len() != tc.ExpectedCacheSize { t.Errorf("%s: Expected cache size %d, got %d", k, tc.ExpectedCacheSize, cacheAuth.cache.Len()) } } }