// GetAndTestEtcdClient creates an etcd client based on the provided config. It will attempt to // connect to the etcd server and block until the server responds at least once, or return an // error if the server never responded. func GetAndTestEtcdClient(etcdClientInfo configapi.EtcdConnectionInfo) (*etcdclient.Client, error) { // etcd does a poor job of setting up the transport - use the Kube client stack transport, err := client.TransportFor(&client.Config{ TLSClientConfig: client.TLSClientConfig{ CertFile: etcdClientInfo.ClientCert.CertFile, KeyFile: etcdClientInfo.ClientCert.KeyFile, CAFile: etcdClientInfo.CA, }, WrapTransport: DefaultEtcdClientTransport, }) if err != nil { return nil, err } etcdClient := etcdclient.NewClient(etcdClientInfo.URLs) etcdClient.SetTransport(transport.(*http.Transport)) for i := 0; ; i++ { _, err := etcdClient.Get("/", false, false) if err == nil || tools.IsEtcdNotFound(err) { break } if i > 100 { return nil, fmt.Errorf("could not reach etcd: %v", err) } time.Sleep(50 * time.Millisecond) } return etcdClient, nil }
// NewProxyServer creates and installs a new ProxyServer. // It automatically registers the created ProxyServer to http.DefaultServeMux. // 'filter', if non-nil, protects requests to the api only. func NewProxyServer(filebase string, apiProxyPrefix string, staticPrefix string, filter *FilterServer, cfg *client.Config) (*ProxyServer, error) { host := cfg.Host if !strings.HasSuffix(host, "/") { host = host + "/" } target, err := url.Parse(host) if err != nil { return nil, err } proxy := newProxy(target) if proxy.Transport, err = client.TransportFor(cfg); err != nil { return nil, err } proxyServer := http.Handler(proxy) if filter != nil { proxyServer = filter.HandlerFor(proxyServer) } if !strings.HasPrefix(apiProxyPrefix, "/api") { proxyServer = stripLeaveSlash(apiProxyPrefix, proxyServer) } mux := http.NewServeMux() mux.Handle(apiProxyPrefix, proxyServer) if filebase != "" { // Require user to explicitly request this behavior rather than // serving their working directory by default. mux.Handle(staticPrefix, newFileHandler(staticPrefix, filebase)) } return &ProxyServer{handler: mux}, nil }
// NewProxyServer creates and installs a new ProxyServer. // It automatically registers the created ProxyServer to http.DefaultServeMux. func NewProxyServer(filebase string, apiProxyPrefix string, staticPrefix string, filter *FilterServer, cfg *client.Config) (*ProxyServer, error) { host := cfg.Host if !strings.HasSuffix(host, "/") { host = host + "/" } target, err := url.Parse(host) if err != nil { return nil, err } proxy := newProxyServer(target) if proxy.Transport, err = client.TransportFor(cfg); err != nil { return nil, err } var server http.Handler if strings.HasPrefix(apiProxyPrefix, "/api") { server = proxy } else { server = http.StripPrefix(apiProxyPrefix, proxy) } if filter != nil { filter.delegate = server server = filter } proxy.mux.Handle(apiProxyPrefix, server) proxy.mux.Handle(staticPrefix, newFileHandler(staticPrefix, filebase)) return proxy, nil }
// NewUpgradeAwareSingleHostReverseProxy creates a new UpgradeAwareSingleHostReverseProxy. func NewUpgradeAwareSingleHostReverseProxy(clientConfig *kclient.Config, backendAddr *url.URL) (*UpgradeAwareSingleHostReverseProxy, error) { transport, err := kclient.TransportFor(clientConfig) if err != nil { return nil, err } reverseProxy := httputil.NewSingleHostReverseProxy(backendAddr) reverseProxy.FlushInterval = 200 * time.Millisecond p := &UpgradeAwareSingleHostReverseProxy{ clientConfig: clientConfig, backendAddr: backendAddr, transport: transport, reverseProxy: reverseProxy, } p.reverseProxy.Transport = p return p, nil }
// NewProxyServer creates and installs a new ProxyServer. // It automatically registers the created ProxyServer to http.DefaultServeMux. func NewProxyServer(filebase string, apiProxyPrefix string, staticPrefix string, cfg *client.Config) (*ProxyServer, error) { prefix := cfg.Prefix if prefix == "" { prefix = "/api" } target, err := url.Parse(singleJoiningSlash(cfg.Host, prefix)) if err != nil { return nil, err } proxy := newProxyServer(target) if proxy.Transport, err = client.TransportFor(cfg); err != nil { return nil, err } http.Handle(apiProxyPrefix, http.StripPrefix(apiProxyPrefix, proxy)) http.Handle(staticPrefix, newFileHandler(staticPrefix, filebase)) return proxy, nil }
// RunStartBuildWebHook tries to trigger the provided webhook. It will attempt to utilize the current client // configuration if the webhook has the same URL. func RunStartBuildWebHook(f *clientcmd.Factory, out io.Writer, webhook string, path, postReceivePath string, repo git.Repository) error { hook, err := url.Parse(webhook) if err != nil { return err } event, err := hookEventFromPostReceive(repo, path, postReceivePath) if err != nil { return err } // TODO: should be a versioned struct data, err := json.Marshal(event) if err != nil { return err } httpClient := http.DefaultClient // when using HTTPS, try to reuse the local config transport if possible to get a client cert // TODO: search all configs if hook.Scheme == "https" { config, err := f.OpenShiftClientConfig.ClientConfig() if err == nil { if url, err := client.DefaultServerURL(config.Host, "", "test", true); err == nil { if url.Host == hook.Host && url.Scheme == hook.Scheme { if rt, err := client.TransportFor(config); err == nil { httpClient = &http.Client{Transport: rt} } } } } } glog.V(4).Infof("Triggering hook %s\n%s", hook, string(data)) resp, err := httpClient.Post(hook.String(), "application/json", bytes.NewBuffer(data)) if err != nil { return err } switch { case resp.StatusCode == 301 || resp.StatusCode == 302: // TODO: follow redirect and display output case resp.StatusCode < 200 || resp.StatusCode >= 300: body, _ := ioutil.ReadAll(resp.Body) return fmt.Errorf("server rejected our request %d\nremote: %s", resp.StatusCode, string(body)) } return nil }
// RequestToken uses the cmd arguments to locate an openshift oauth server and attempts to authenticate // it returns the access token if it gets one. An error if it does not func RequestToken(clientCfg *kclient.Config, reader io.Reader, defaultUsername string, defaultPassword string) (string, error) { tokenGetter := &tokenGetterInfo{} osClient, err := client.New(clientCfg) if err != nil { return "", err } // get the transport, so that we can use it to build our own client that wraps it // our client understands certain challenges and can respond to them clientTransport, err := kclient.TransportFor(clientCfg) if err != nil { return "", err } httpClient := &http.Client{ Transport: clientTransport, CheckRedirect: tokenGetter.checkRedirect, } osClient.Client = &challengingClient{httpClient, reader, defaultUsername, defaultPassword} result := osClient.Get().AbsPath("/oauth", osinserver.AuthorizePath). Param("response_type", "token"). Param("client_id", "openshift-challenging-client"). Do() if err := result.Error(); err != nil && !isRedirectError(err) { return "", err } if len(tokenGetter.accessToken) == 0 { r, _ := result.Raw() if description, ok := rawOAuthJSONErrorDescription(r); ok { return "", fmt.Errorf("cannot retrieve a token: %s", description) } glog.V(4).Infof("A request token could not be created, server returned: %s", string(r)) return "", fmt.Errorf("the server did not return a token (possible server error)") } return tokenGetter.accessToken, nil }
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) } }