diff --git a/cliclient/cliclient.go b/cliclient/cliclient.go index 9f6206f4..d2d4cee6 100644 --- a/cliclient/cliclient.go +++ b/cliclient/cliclient.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "strings" - "time" errorsPkg "github.com/pkg/errors" "github.com/rancher/cli/config" @@ -181,19 +180,14 @@ func createClientOpts(config *config.ServerConfig) *clientbase.ClientOpts { serverURL = config.URL + "/v3" } - options := &clientbase.ClientOpts{ + return &clientbase.ClientOpts{ URL: serverURL, AccessKey: config.AccessKey, SecretKey: config.SecretKey, CACerts: config.CACerts, ProxyURL: config.ProxyURL, + Timeout: config.GetHTTPTimeout(), } - - if config.HTTPTimeoutSeconds > 0 { - options.Timeout = time.Duration(config.HTTPTimeoutSeconds) * time.Second - } - - return options } func SplitOnColon(s string) []string { diff --git a/cmd/common.go b/cmd/common.go index 0dadb8fd..9ab51be1 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -3,11 +3,13 @@ package cmd import ( "bufio" "bytes" + "crypto/tls" "crypto/x509" "encoding/pem" "fmt" "io" "math/rand" + "net/http" "net/url" "os" "os/exec" @@ -39,6 +41,7 @@ const ( letters = "abcdefghijklmnopqrstuvwxyz0123456789" cfgFile = "cli2.json" kubeConfigKeyFormat = "%s-%s" + defaultHTTPTimeout = time.Minute // Matches the default timeout of the Norman Api Client. ) var ( @@ -624,3 +627,33 @@ func ConfigDir() (string, error) { } return filepath.Join(homeDir, ".rancher"), nil } + +func newHTTPClient(serverConfig *config.ServerConfig, tlsConfig *tls.Config) (*http.Client, error) { + var proxy func(*http.Request) (*url.URL, error) + if serverConfig.ProxyURL != "" { + proxyURL, err := url.Parse(serverConfig.ProxyURL) + if err != nil { + return nil, fmt.Errorf("invalid proxy address %s: %w", serverConfig.ProxyURL, err) + } + proxy = http.ProxyURL(proxyURL) + } else { + proxy = http.ProxyFromEnvironment + } + + tr := &http.Transport{ + Proxy: proxy, + } + if tlsConfig != nil { + tr.TLSClientConfig = tlsConfig + } + + timeout := serverConfig.GetHTTPTimeout() + if timeout == 0 { + timeout = defaultHTTPTimeout + } + + return &http.Client{ + Transport: tr, + Timeout: timeout, + }, nil +} diff --git a/cmd/login.go b/cmd/login.go index e30aa6a4..9c4b2077 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -12,7 +12,6 @@ import ( "os" "strconv" "strings" - "time" "github.com/sirupsen/logrus" @@ -253,32 +252,18 @@ func getProjectContext(ctx *cli.Context, c *cliclient.MasterClient) (string, err return projectCollection.Data[selection].ID, nil } -func getCertFromServer(ctx *cli.Context, cf *config.ServerConfig) (*cliclient.MasterClient, error) { - req, err := http.NewRequest("GET", cf.URL+"/v3/settings/cacerts", nil) +func getCertFromServer(ctx *cli.Context, serverConfig *config.ServerConfig) (*cliclient.MasterClient, error) { + req, err := http.NewRequest("GET", serverConfig.URL+"/v3/settings/cacerts", nil) if err != nil { return nil, err } - req.SetBasicAuth(cf.AccessKey, cf.SecretKey) + req.SetBasicAuth(serverConfig.AccessKey, serverConfig.SecretKey) - var proxy func(*http.Request) (*url.URL, error) - if cf.ProxyURL != "" { - proxyURL, err := url.Parse(cf.ProxyURL) - if err != nil { - return nil, fmt.Errorf("invalid proxy address %s: %w", cf.ProxyURL, err) - } - proxy = http.ProxyURL(proxyURL) - } else { - proxy = http.ProxyFromEnvironment - } - - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - Proxy: proxy, - } - client := &http.Client{Transport: tr} - if cf.HTTPTimeoutSeconds > 0 { - client.Timeout = time.Duration(cf.HTTPTimeoutSeconds) * time.Second + tlsConfig := &tls.Config{InsecureSkipVerify: true} + client, err := newHTTPClient(serverConfig, tlsConfig) + if err != nil { + return nil, err } res, err := client.Do(req) @@ -296,7 +281,7 @@ func getCertFromServer(ctx *cli.Context, cf *config.ServerConfig) (*cliclient.Ma var certReponse *CACertResponse err = json.Unmarshal(content, &certReponse) if err != nil { - return nil, fmt.Errorf("Unable to parse response from %s/v3/settings/cacerts\nError: %s\nResponse:\n%s", cf.URL, err, content) + return nil, fmt.Errorf("Unable to parse response from %s/v3/settings/cacerts\nError: %s\nResponse:\n%s", serverConfig.URL, err, content) } cert, err := verifyCert([]byte(certReponse.Value)) @@ -311,14 +296,14 @@ func getCertFromServer(ctx *cli.Context, cf *config.ServerConfig) (*cliclient.Ma } if !ctx.Bool("skip-verify") { - if ok := verifyUserAcceptsCert(serverCerts, cf.URL); !ok { + if ok := verifyUserAcceptsCert(serverCerts, serverConfig.URL); !ok { return nil, errors.New("CACert of server was not accepted, unable to login") } } - cf.CACerts = cert + serverConfig.CACerts = cert - return cliclient.NewManagementClient(cf) + return cliclient.NewManagementClient(serverConfig) } func verifyUserAcceptsCert(certs []string, url string) bool { diff --git a/cmd/ssh.go b/cmd/ssh.go index 04faa58f..e2e96f69 100644 --- a/cmd/ssh.go +++ b/cmd/ssh.go @@ -9,12 +9,10 @@ import ( "fmt" "io" "net/http" - "net/url" "os" "os/exec" "path" "strings" - "time" "github.com/pkg/errors" "github.com/rancher/cli/cliclient" @@ -172,35 +170,21 @@ func getSSHKey(c *cliclient.MasterClient, link, nodeName string) ([]byte, string req.SetBasicAuth(c.UserConfig.AccessKey, c.UserConfig.SecretKey) req.Header.Add("Accept-Encoding", "zip") - var proxy func(*http.Request) (*url.URL, error) - if c.UserConfig.ProxyURL != "" { - proxyURL, err := url.Parse(c.UserConfig.ProxyURL) - if err != nil { - return nil, "", fmt.Errorf("invalid proxy address %s: %w", c.UserConfig.ProxyURL, err) - } - proxy = http.ProxyURL(proxyURL) - } else { - proxy = http.ProxyFromEnvironment - } - - tr := &http.Transport{ - Proxy: proxy, - } - + var tlsConfig *tls.Config if c.UserConfig.CACerts != "" { roots := x509.NewCertPool() ok := roots.AppendCertsFromPEM([]byte(c.UserConfig.CACerts)) if !ok { return []byte{}, "", err } - tr.TLSClientConfig = &tls.Config{ + tlsConfig = &tls.Config{ RootCAs: roots, } } - client := &http.Client{Transport: tr} - if c.UserConfig.HTTPTimeoutSeconds > 0 { - client.Timeout = time.Duration(c.UserConfig.HTTPTimeoutSeconds) * time.Second + client, err := newHTTPClient(c.UserConfig, tlsConfig) + if err != nil { + return nil, "", err } resp, err := client.Do(req) diff --git a/config/config.go b/config/config.go index 747dcb02..e1ad2278 100644 --- a/config/config.go +++ b/config/config.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/sirupsen/logrus" "k8s.io/client-go/tools/clientcmd/api" @@ -38,6 +39,10 @@ type ServerConfig struct { HTTPTimeoutSeconds int `json:"httpTimeoutSeconds"` } +func (c *ServerConfig) GetHTTPTimeout() time.Duration { + return time.Duration(c.HTTPTimeoutSeconds) * time.Second +} + // LoadFromPath attempts to load a config from the given file path. If the file // doesn't exist, an empty config is returned. func LoadFromPath(path string) (Config, error) {