func TestAuthenticateRequestError(t *testing.T) { failed := make(chan struct{}) contextMapper := api.NewRequestContextMapper() auth := WithAuthentication( http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) { t.Errorf("unexpected call to handler") }), contextMapper, authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) { return nil, false, errors.New("failure") }), http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { close(failed) }), ) auth.ServeHTTP(httptest.NewRecorder(), &http.Request{}) <-failed empty, err := api.IsEmpty(contextMapper) if err != nil { t.Fatalf("unexpected error: %v", err) } if !empty { t.Fatalf("contextMapper should have no stored requests: %v", contextMapper) } }
func TestAuthenticateRequest(t *testing.T) { success := make(chan struct{}) contextMapper := api.NewRequestContextMapper() auth, err := NewRequestAuthenticator( contextMapper, authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) { return &user.DefaultInfo{Name: "user"}, true, nil }), http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { t.Errorf("unexpected call to failed") }), http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) { ctx, ok := contextMapper.Get(req) if ctx == nil || !ok { t.Errorf("no context stored on contextMapper: %#v", contextMapper) } user, ok := api.UserFrom(ctx) if user == nil || !ok { t.Errorf("no user stored in context: %#v", ctx) } close(success) }), ) auth.ServeHTTP(httptest.NewRecorder(), &http.Request{}) <-success empty, err := api.IsEmpty(contextMapper) if err != nil { t.Fatalf("unexpected error: %v", err) } if !empty { t.Fatalf("contextMapper should have no stored requests: %v", contextMapper) } }
func TestGroupAdder(t *testing.T) { adder := authenticator.Request( NewGroupAdder( authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) { return &user.DefaultInfo{Name: "user", Groups: []string{"original"}}, true, nil }), []string{"added"}, ), ) user, _, _ := adder.AuthenticateRequest(nil) if !reflect.DeepEqual(user.GetGroups(), []string{"original", "added"}) { t.Errorf("Expected original,added groups, got %#v", user.GetGroups()) } }
func TestAuthenticateRequest(t *testing.T) { success := make(chan struct{}) contextMapper := api.NewRequestContextMapper() auth := WithAuthentication( http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) { ctx, ok := contextMapper.Get(req) if ctx == nil || !ok { t.Errorf("no context stored on contextMapper: %#v", contextMapper) } user, ok := api.UserFrom(ctx) if user == nil || !ok { t.Errorf("no user stored in context: %#v", ctx) } if req.Header.Get("Authorization") != "" { t.Errorf("Authorization header should be removed from request on success: %#v", req) } close(success) }), contextMapper, authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) { if req.Header.Get("Authorization") == "Something" { return &user.DefaultInfo{Name: "user"}, true, nil } return nil, false, errors.New("Authorization header is missing.") }), http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { t.Errorf("unexpected call to failed") }), ) auth.ServeHTTP(httptest.NewRecorder(), &http.Request{Header: map[string][]string{"Authorization": {"Something"}}}) <-success empty, err := api.IsEmpty(contextMapper) if err != nil { t.Fatalf("unexpected error: %v", err) } if !empty { t.Fatalf("contextMapper should have no stored requests: %v", contextMapper) } }
// startMasterOrDie starts a kubernetes master and an httpserver to handle api requests func startMasterOrDie(masterConfig *master.Config, incomingServer *httptest.Server, masterReceiver MasterReceiver) (*master.Master, *httptest.Server) { var m *master.Master var s *httptest.Server if incomingServer != nil { s = incomingServer } else { s = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.GenericAPIServer.Handler.ServeHTTP(w, req) })) } if masterConfig == nil { masterConfig = NewMasterConfig() masterConfig.GenericConfig.EnableProfiling = true masterConfig.GenericConfig.EnableSwaggerSupport = true masterConfig.GenericConfig.EnableOpenAPISupport = true masterConfig.GenericConfig.OpenAPIConfig.Info = &spec.Info{ InfoProps: spec.InfoProps{ Title: "Kubernetes", Version: "unversioned", }, } masterConfig.GenericConfig.OpenAPIConfig.DefaultResponse = &spec.Response{ ResponseProps: spec.ResponseProps{ Description: "Default Response.", }, } masterConfig.GenericConfig.OpenAPIConfig.Definitions = openapi.OpenAPIDefinitions } // set the loopback client config if masterConfig.GenericConfig.LoopbackClientConfig == nil { masterConfig.GenericConfig.LoopbackClientConfig = &restclient.Config{QPS: 50, Burst: 100, ContentConfig: restclient.ContentConfig{NegotiatedSerializer: api.Codecs}} } masterConfig.GenericConfig.LoopbackClientConfig.Host = s.URL privilegedLoopbackToken := uuid.NewRandom().String() // wrap any available authorizer tokens := make(map[string]*user.DefaultInfo) tokens[privilegedLoopbackToken] = &user.DefaultInfo{ Name: user.APIServerUser, UID: uuid.NewRandom().String(), Groups: []string{user.SystemPrivilegedGroup}, } tokenAuthenticator := authenticator.NewAuthenticatorFromTokens(tokens) if masterConfig.GenericConfig.Authenticator == nil { masterConfig.GenericConfig.Authenticator = authenticatorunion.New(tokenAuthenticator, authauthenticator.RequestFunc(alwaysEmpty)) } else { masterConfig.GenericConfig.Authenticator = authenticatorunion.New(tokenAuthenticator, masterConfig.GenericConfig.Authenticator) } if masterConfig.GenericConfig.Authorizer != nil { tokenAuthorizer := authorizer.NewPrivilegedGroups(user.SystemPrivilegedGroup) masterConfig.GenericConfig.Authorizer = authorizerunion.New(tokenAuthorizer, masterConfig.GenericConfig.Authorizer) } else { masterConfig.GenericConfig.Authorizer = alwaysAllow{} } masterConfig.GenericConfig.LoopbackClientConfig.BearerToken = privilegedLoopbackToken m, err := masterConfig.Complete().New() if err != nil { glog.Fatalf("error in bringing up the master: %v", err) } if masterReceiver != nil { masterReceiver.SetMaster(m) } cfg := *masterConfig.GenericConfig.LoopbackClientConfig cfg.ContentConfig.GroupVersion = &unversioned.GroupVersion{} privilegedClient, err := restclient.RESTClientFor(&cfg) if err != nil { glog.Fatal(err) } err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { result := privilegedClient.Get().AbsPath("/healthz").Do() status := 0 result.StatusCode(&status) if status == 200 { return true, nil } return false, nil }) if err != nil { glog.Fatal(err) } // TODO have this start method actually use the normal start sequence for the API server // this method never actually calls the `Run` method for the API server // fire the post hooks ourselves m.GenericAPIServer.RunPostStartHooks() // wait for services to be ready if masterConfig.EnableCoreControllers { // TODO Once /healthz is updated for posthooks, we'll wait for good health coreClient := coreclient.NewForConfigOrDie(&cfg) svcWatch, err := coreClient.Services(api.NamespaceDefault).Watch(v1.ListOptions{}) if err != nil { glog.Fatal(err) } _, err = watch.Until(30*time.Second, svcWatch, func(event watch.Event) (bool, error) { if event.Type != watch.Added { return false, nil } if event.Object.(*v1.Service).Name == "kubernetes" { return true, nil } return false, nil }) if err != nil { glog.Fatal(err) } } return m, s }
func TestSubjectAccessReview(t *testing.T) { var m *master.Master s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { m.Handler.ServeHTTP(w, req) })) defer s.Close() masterConfig := framework.NewIntegrationTestMasterConfig() masterConfig.GenericConfig.Authenticator = authenticator.RequestFunc(alwaysAlice) masterConfig.GenericConfig.Authorizer = sarAuthorizer{} masterConfig.GenericConfig.AdmissionControl = admit.NewAlwaysAdmit() m, err := masterConfig.Complete().New() if err != nil { t.Fatalf("error in bringing up the master: %v", err) } clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}) tests := []struct { name string sar *authorizationapi.SubjectAccessReview expectedError string expectedStatus authorizationapi.SubjectAccessReviewStatus }{ { name: "simple allow", sar: &authorizationapi.SubjectAccessReview{ Spec: authorizationapi.SubjectAccessReviewSpec{ ResourceAttributes: &authorizationapi.ResourceAttributes{ Verb: "list", Group: api.GroupName, Version: "v1", Resource: "pods", }, User: "******", }, }, expectedStatus: authorizationapi.SubjectAccessReviewStatus{ Allowed: true, Reason: "you're not dave", }, }, { name: "simple deny", sar: &authorizationapi.SubjectAccessReview{ Spec: authorizationapi.SubjectAccessReviewSpec{ ResourceAttributes: &authorizationapi.ResourceAttributes{ Verb: "list", Group: api.GroupName, Version: "v1", Resource: "pods", }, User: "******", }, }, expectedStatus: authorizationapi.SubjectAccessReviewStatus{ Allowed: false, Reason: "no", EvaluationError: "I'm sorry, Dave", }, }, { name: "simple error", sar: &authorizationapi.SubjectAccessReview{ Spec: authorizationapi.SubjectAccessReviewSpec{ ResourceAttributes: &authorizationapi.ResourceAttributes{ Verb: "list", Group: api.GroupName, Version: "v1", Resource: "pods", }, }, }, expectedError: "at least one of user or group must be specified", }, } for _, test := range tests { response, err := clientset.Authorization().SubjectAccessReviews().Create(test.sar) switch { case err == nil && len(test.expectedError) == 0: case err != nil && strings.Contains(err.Error(), test.expectedError): continue case err != nil && len(test.expectedError) != 0: t.Errorf("%s: unexpected error: %v", test.name, err) continue default: t.Errorf("%s: expected %v, got %v", test.name, test.expectedError, err) continue } if response.Status != test.expectedStatus { t.Errorf("%s: expected %v, got %v", test.name, test.expectedStatus, response.Status) continue } } }
func TestLocalSubjectAccessReview(t *testing.T) { masterConfig := framework.NewIntegrationTestMasterConfig() masterConfig.GenericConfig.Authenticator = authenticator.RequestFunc(alwaysAlice) masterConfig.GenericConfig.Authorizer = sarAuthorizer{} masterConfig.GenericConfig.AdmissionControl = admit.NewAlwaysAdmit() _, s := framework.RunAMaster(masterConfig) defer s.Close() clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}}) tests := []struct { name string namespace string sar *authorizationapi.LocalSubjectAccessReview expectedError string expectedStatus authorizationapi.SubjectAccessReviewStatus }{ { name: "simple allow", namespace: "foo", sar: &authorizationapi.LocalSubjectAccessReview{ ObjectMeta: api.ObjectMeta{Namespace: "foo"}, Spec: authorizationapi.SubjectAccessReviewSpec{ ResourceAttributes: &authorizationapi.ResourceAttributes{ Verb: "list", Group: api.GroupName, Version: "v1", Resource: "pods", Namespace: "foo", }, User: "******", }, }, expectedStatus: authorizationapi.SubjectAccessReviewStatus{ Allowed: true, Reason: "you're not dave", }, }, { name: "simple deny", namespace: "foo", sar: &authorizationapi.LocalSubjectAccessReview{ ObjectMeta: api.ObjectMeta{Namespace: "foo"}, Spec: authorizationapi.SubjectAccessReviewSpec{ ResourceAttributes: &authorizationapi.ResourceAttributes{ Verb: "list", Group: api.GroupName, Version: "v1", Resource: "pods", Namespace: "foo", }, User: "******", }, }, expectedStatus: authorizationapi.SubjectAccessReviewStatus{ Allowed: false, Reason: "no", EvaluationError: "I'm sorry, Dave", }, }, { name: "conflicting namespace", namespace: "foo", sar: &authorizationapi.LocalSubjectAccessReview{ ObjectMeta: api.ObjectMeta{Namespace: "foo"}, Spec: authorizationapi.SubjectAccessReviewSpec{ ResourceAttributes: &authorizationapi.ResourceAttributes{ Verb: "list", Group: api.GroupName, Version: "v1", Resource: "pods", Namespace: "bar", }, User: "******", }, }, expectedError: "must match metadata.namespace", }, { name: "missing namespace", namespace: "foo", sar: &authorizationapi.LocalSubjectAccessReview{ ObjectMeta: api.ObjectMeta{Namespace: "foo"}, Spec: authorizationapi.SubjectAccessReviewSpec{ ResourceAttributes: &authorizationapi.ResourceAttributes{ Verb: "list", Group: api.GroupName, Version: "v1", Resource: "pods", }, User: "******", }, }, expectedError: "must match metadata.namespace", }, } for _, test := range tests { response, err := clientset.Authorization().LocalSubjectAccessReviews(test.namespace).Create(test.sar) switch { case err == nil && len(test.expectedError) == 0: case err != nil && strings.Contains(err.Error(), test.expectedError): continue case err != nil && len(test.expectedError) != 0: t.Errorf("%s: unexpected error: %v", test.name, err) continue default: t.Errorf("%s: expected %v, got %v", test.name, test.expectedError, err) continue } if response.Status != test.expectedStatus { t.Errorf("%s: expected %#v, got %#v", test.name, test.expectedStatus, response.Status) continue } } }
func TestSelfSubjectAccessReview(t *testing.T) { username := "******" masterConfig := framework.NewIntegrationTestMasterConfig() masterConfig.GenericConfig.Authenticator = authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) { return &user.DefaultInfo{Name: username}, true, nil }) masterConfig.GenericConfig.Authorizer = sarAuthorizer{} masterConfig.GenericConfig.AdmissionControl = admit.NewAlwaysAdmit() _, s := framework.RunAMaster(masterConfig) defer s.Close() clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion}}) tests := []struct { name string username string sar *authorizationapi.SelfSubjectAccessReview expectedError string expectedStatus authorizationapi.SubjectAccessReviewStatus }{ { name: "simple allow", username: "******", sar: &authorizationapi.SelfSubjectAccessReview{ Spec: authorizationapi.SelfSubjectAccessReviewSpec{ ResourceAttributes: &authorizationapi.ResourceAttributes{ Verb: "list", Group: api.GroupName, Version: "v1", Resource: "pods", }, }, }, expectedStatus: authorizationapi.SubjectAccessReviewStatus{ Allowed: true, Reason: "you're not dave", }, }, { name: "simple deny", username: "******", sar: &authorizationapi.SelfSubjectAccessReview{ Spec: authorizationapi.SelfSubjectAccessReviewSpec{ ResourceAttributes: &authorizationapi.ResourceAttributes{ Verb: "list", Group: api.GroupName, Version: "v1", Resource: "pods", }, }, }, expectedStatus: authorizationapi.SubjectAccessReviewStatus{ Allowed: false, Reason: "no", EvaluationError: "I'm sorry, Dave", }, }, } for _, test := range tests { username = test.username response, err := clientset.Authorization().SelfSubjectAccessReviews().Create(test.sar) switch { case err == nil && len(test.expectedError) == 0: case err != nil && strings.Contains(err.Error(), test.expectedError): continue case err != nil && len(test.expectedError) != 0: t.Errorf("%s: unexpected error: %v", test.name, err) continue default: t.Errorf("%s: expected %v, got %v", test.name, test.expectedError, err) continue } if response.Status != test.expectedStatus { t.Errorf("%s: expected %v, got %v", test.name, test.expectedStatus, response.Status) continue } } }
func NewAuthenticator() authenticator.Request { return authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) { return &user.DefaultInfo{Name: bootstrappolicy.UnauthenticatedUsername, Groups: []string{bootstrappolicy.UnauthenticatedGroup}}, true, nil }) }
func TestX509Verifier(t *testing.T) { multilevelOpts := DefaultVerifyOptions() multilevelOpts.Roots = x509.NewCertPool() multilevelOpts.Roots.AddCert(getCertsFromFile(t, "root")[0]) testCases := map[string]struct { Insecure bool Certs []*x509.Certificate Opts x509.VerifyOptions AllowedCNs sets.String ExpectOK bool ExpectErr bool }{ "non-tls": { Insecure: true, ExpectOK: false, ExpectErr: false, }, "tls, no certs": { ExpectOK: false, ExpectErr: false, }, "self signed": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, selfSignedCert), ExpectErr: true, }, "server cert disallowed": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, serverCert), ExpectErr: true, }, "server cert allowing non-client cert usages": { Opts: x509.VerifyOptions{Roots: getRootCertPool(t)}, Certs: getCerts(t, serverCert), ExpectOK: true, ExpectErr: false, }, "valid client cert": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, clientCNCert), ExpectOK: true, ExpectErr: false, }, "valid client cert with wrong CN": { Opts: getDefaultVerifyOptions(t), AllowedCNs: sets.NewString("foo", "bar"), Certs: getCerts(t, clientCNCert), ExpectOK: false, ExpectErr: true, }, "valid client cert with right CN": { Opts: getDefaultVerifyOptions(t), AllowedCNs: sets.NewString("client_cn"), Certs: getCerts(t, clientCNCert), ExpectOK: true, ExpectErr: false, }, "future cert": { Opts: x509.VerifyOptions{ CurrentTime: time.Now().Add(-100 * time.Hour * 24 * 365), Roots: getRootCertPool(t), }, Certs: getCerts(t, clientCNCert), ExpectOK: false, ExpectErr: true, }, "expired cert": { Opts: x509.VerifyOptions{ CurrentTime: time.Now().Add(100 * time.Hour * 24 * 365), Roots: getRootCertPool(t), }, Certs: getCerts(t, clientCNCert), ExpectOK: false, ExpectErr: true, }, "multi-level, valid": { Opts: multilevelOpts, Certs: getCertsFromFile(t, "client-valid", "intermediate"), ExpectOK: true, ExpectErr: false, }, "multi-level, expired": { Opts: multilevelOpts, Certs: getCertsFromFile(t, "client-expired", "intermediate"), ExpectOK: false, ExpectErr: true, }, } for k, testCase := range testCases { req, _ := http.NewRequest("GET", "/", nil) if !testCase.Insecure { req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs} } authCall := false auth := authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) { authCall = true return &user.DefaultInfo{Name: "innerauth"}, true, nil }) a := NewVerifier(testCase.Opts, auth, testCase.AllowedCNs) user, ok, err := a.AuthenticateRequest(req) if testCase.ExpectErr && err == nil { t.Errorf("%s: Expected error, got none", k) continue } if !testCase.ExpectErr && err != nil { t.Errorf("%s: Got unexpected error: %v", k, err) continue } if testCase.ExpectOK != ok { t.Errorf("%s: Expected ok=%v, got %v", k, testCase.ExpectOK, ok) continue } if testCase.ExpectOK { if !authCall { t.Errorf("%s: Expected inner auth called, wasn't", k) continue } if "innerauth" != user.GetName() { t.Errorf("%s: Expected user.name=%v, got %v", k, "innerauth", user.GetName()) continue } } else { if authCall { t.Errorf("%s: Expected inner auth not to be called, was", k) continue } } } }
func NewAuthenticator() authenticator.Request { return authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) { return &user.DefaultInfo{Name: anonymousUser, Groups: []string{unauthenticatedGroup}}, true, nil }) }