func TestOAuthDisabled(t *testing.T) { // Build master config masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } // Disable OAuth masterOptions.OAuthConfig = nil // Start server clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } client, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Make sure cert auth still works namespaces, err := client.Namespaces().List(labels.Everything(), fields.Everything()) if err != nil { t.Fatalf("Unexpected error %v", err) } if len(namespaces.Items) == 0 { t.Errorf("Expected namespaces, got none") } // Use the server and CA info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Make sure we can't authenticate using OAuth if _, err := tokencmd.RequestToken(&anonConfig, nil, "username", "password"); err == nil { t.Error("Expected error, got none") } }
func TestUnprivilegedNewProjectFromTemplate(t *testing.T) { namespace := "foo" templateName := "bar" masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.ProjectConfig.ProjectRequestTemplate = namespace + "/" + templateName clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := clusterAdminClient.Projects().Create(&projectapi.Project{ObjectMeta: kapi.ObjectMeta{Name: namespace}}); err != nil { t.Fatalf("unexpected error: %v", err) } template := projectrequeststorage.DefaultTemplate() template.Name = templateName template.Namespace = namespace template.Objects[0].(*projectapi.Project).Annotations["extra"] = "here" _, err = clusterAdminClient.Templates(namespace).Create(template) if err != nil { t.Fatalf("unexpected error: %v", err) } requestProject := oc.NewProjectOptions{ ProjectName: "new-project", DisplayName: "display name here", Description: "the special description", Client: valerieOpenshiftClient, Out: ioutil.Discard, } if err := requestProject.Run(); err != nil { t.Fatalf("unexpected error: %v", err) } waitForProject(t, valerieOpenshiftClient, "new-project", 5*time.Second, 10) project, err := valerieOpenshiftClient.Projects().Get("new-project") if err != nil { t.Fatalf("unexpected error: %v", err) } if project.Annotations["extra"] != "here" { t.Errorf("unexpected project %#v", project) } if err := clusterAdminClient.Templates(namespace).Delete(templateName); err != nil { t.Fatalf("unexpected error: %v", err) } requestProject.ProjectName = "different" // This should fail during the template retrieve if err := requestProject.Run(); !kapierrors.IsNotFound(err) { t.Fatalf("expected a not found error, but got %v", err) } }
func TestOAuthRequestHeader(t *testing.T) { // Write cert we're going to use to verify OAuth requestheader requests caFile, err := ioutil.TempFile("", "test.crt") if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(caFile.Name()) if err := ioutil.WriteFile(caFile.Name(), rootCACert, os.FileMode(0600)); err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "requestheader", UseAsChallenger: false, UseAsLogin: false, Provider: runtime.EmbeddedObject{ &configapi.RequestHeaderIdentityProvider{ ClientCA: caFile.Name(), Headers: []string{"My-Remote-User", "SSO-User"}, }, }, } // Start server clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Use the server and CA info, but no client cert info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Build the authorize request with the My-Remote-User header authorizeURL := clientConfig.Host + "/oauth/authorize?client_id=openshift-challenging-client&response_type=token" req, err := http.NewRequest("GET", authorizeURL, nil) req.Header.Set("My-Remote-User", "myuser") // Make the request without cert auth transport, err := kclient.TransportFor(&anonConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } resp, err := transport.RoundTrip(req) if err != nil { t.Fatalf("unexpected error: %v", err) } redirect, err := resp.Location() if err != nil { t.Fatalf("expected 302 redirect, got error: %v", err) } if redirect.Query().Get("error") == "" { t.Fatalf("expected unsuccessful token request, got redirected to %v", redirect.String()) } // Use the server and CA info, with cert info authProxyConfig := anonConfig authProxyConfig.CertData = clientCert authProxyConfig.KeyData = clientKey // Make the request with cert info transport, err = kclient.TransportFor(&authProxyConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } resp, err = transport.RoundTrip(req) if err != nil { t.Fatalf("unexpected error: %v", err) } redirect, err = resp.Location() if err != nil { t.Fatalf("expected 302 redirect, got error: %v", err) } if redirect.Query().Get("error") != "" { t.Fatalf("expected successful token request, got error %v", redirect.String()) } // Extract the access_token // group #0 is everything. #1 #2 #3 accessTokenRedirectRegex := regexp.MustCompile(`(^|&)access_token=([^&]+)($|&)`) accessToken := "" if matches := accessTokenRedirectRegex.FindStringSubmatch(redirect.Fragment); matches != nil { accessToken = matches[2] } if accessToken == "" { t.Fatalf("Expected access token, got %s", redirect.String()) } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != "myuser" { t.Fatalf("Expected myuser as the user, got %v", user) } }
func TestOAuthBasicAuthPassword(t *testing.T) { remotePrefix := "remote" expectedLogin := "******" expectedPassword := "******" expectedAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(expectedLogin+":"+expectedPassword)) expectedUsername := remotePrefix + expectedLogin // Create tempfiles with certs and keys we're going to use certNames := map[string]string{} for certName, certContents := range basicAuthCerts { f, err := ioutil.TempFile("", certName) if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(f.Name()) if err := ioutil.WriteFile(f.Name(), certContents, os.FileMode(0600)); err != nil { t.Fatalf("unexpected error: %v", err) } certNames[certName] = f.Name() } // Build client cert pool clientCAs, err := util.CertPoolFromFile(certNames[basicAuthRemoteCACert]) if err != nil { t.Fatalf("unexpected error: %v", err) } // Build remote handler remoteHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { if req.TLS == nil { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected TLS") } if len(req.TLS.VerifiedChains) != 1 { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected peer cert verified by server") } if req.Header.Get("Authorization") != expectedAuthHeader { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Unexpected auth header: %s", req.Header.Get("Authorization")) } w.Header().Set("Content-Type", "application/json") w.Write([]byte(fmt.Sprintf(`{"sub":"%s"}`, expectedUsername))) }) // Start remote server remoteAddr, err := testutil.FindAvailableBindAddress(9443, 9999) if err != nil { t.Fatalf("Couldn't get free address for test server: %v", err) } remoteServer := &http.Server{ Addr: remoteAddr, Handler: remoteHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, TLSConfig: &tls.Config{ // Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability) MinVersion: tls.VersionTLS10, // RequireAndVerifyClientCert lets us limit requests to ones with a valid client certificate ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: clientCAs, }, } go func() { if err := remoteServer.ListenAndServeTLS(certNames[basicAuthRemoteServerCert], certNames[basicAuthRemoteServerKey]); err != nil { t.Fatalf("unexpected error: %v", err) } }() // Build master config masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "basicauth", UseAsChallenger: true, UseAsLogin: true, Provider: runtime.EmbeddedObject{ &configapi.BasicAuthPasswordIdentityProvider{ RemoteConnectionInfo: configapi.RemoteConnectionInfo{ URL: fmt.Sprintf("https://%s", remoteAddr), CA: certNames[basicAuthRemoteCACert], ClientCert: configapi.CertInfo{ CertFile: certNames[basicAuthClientCert], KeyFile: certNames[basicAuthClientKey], }, }, }, }, } // Start server clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Use the server and CA info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Make sure we can get a token accessToken, err := tokencmd.RequestToken(&anonConfig, nil, expectedLogin, expectedPassword) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(accessToken) == 0 { t.Errorf("Expected access token, got none") } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != expectedUsername { t.Fatalf("Expected username as the user, got %v", user) } }
func TestHTPasswd(t *testing.T) { htpasswdFile, err := ioutil.TempFile("", "test.htpasswd") if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(htpasswdFile.Name()) masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "htpasswd", UseAsChallenger: true, UseAsLogin: true, Provider: runtime.EmbeddedObject{ &configapi.HTPasswdPasswordIdentityProvider{ File: htpasswdFile.Name(), }, }, } clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } // Use the server and CA info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Make sure we can't authenticate if _, err := tokencmd.RequestToken(&anonConfig, nil, "username", "password"); err == nil { t.Error("Expected error, got none") } // Update the htpasswd file with output of `htpasswd -n -b username password` userpass := "******" ioutil.WriteFile(htpasswdFile.Name(), []byte(userpass), os.FileMode(0600)) // Make sure we can get a token accessToken, err := tokencmd.RequestToken(&anonConfig, nil, "username", "password") if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(accessToken) == 0 { t.Errorf("Expected access token, got none") } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != "username" { t.Fatalf("Expected username as the user, got %v", user) } }