Esempio n. 1
0
func TestNormalizeServerURL(t *testing.T) {
	testCases := []struct {
		originalServerURL   string
		normalizedServerURL string
	}{
		{
			originalServerURL:   "localhost",
			normalizedServerURL: "https://localhost:443",
		},
		{
			originalServerURL:   "https://localhost",
			normalizedServerURL: "https://localhost:443",
		},
		{
			originalServerURL:   "localhost:443",
			normalizedServerURL: "https://localhost:443",
		},
		{
			originalServerURL:   "https://localhost:443",
			normalizedServerURL: "https://localhost:443",
		},
		{
			originalServerURL:   "http://localhost",
			normalizedServerURL: "http://localhost:80",
		},
		{
			originalServerURL:   "localhost:8443",
			normalizedServerURL: "https://localhost:8443",
		},
	}

	for _, test := range testCases {
		t.Logf("evaluating test: normalize %s -> %s", test.originalServerURL, test.normalizedServerURL)
		normalized, err := config.NormalizeServerURL(test.originalServerURL)
		if err != nil {
			t.Errorf("unexpected error normalizing %s: %s", test.originalServerURL, err)
		}
		if normalized != test.normalizedServerURL {
			t.Errorf("unexpected server URL normalization result for %s: expected %s, got %s", test.originalServerURL, test.normalizedServerURL, normalized)
		}
	}
}
Esempio n. 2
0
// getClientConfig returns back the current clientConfig as we know it.  If there is no clientConfig, it builds one with enough information
// to talk to a server.  This may involve user prompts.  This method is not threadsafe.
func (o *LoginOptions) getClientConfig() (*kclient.Config, error) {
	if o.Config != nil {
		return o.Config, nil
	}

	clientConfig := &kclient.Config{}

	if len(o.Server) == 0 {
		// we need to have a server to talk to
		if cmdutil.IsTerminal(o.Reader) {
			for !o.serverProvided() {
				defaultServer := defaultClusterURL
				promptMsg := fmt.Sprintf("Server [%s]: ", defaultServer)

				o.Server = cmdutil.PromptForStringWithDefault(o.Reader, defaultServer, promptMsg)
			}
		}
	}

	// normalize the provided server to a format expected by config
	serverNormalized, err := config.NormalizeServerURL(o.Server)
	if err != nil {
		return nil, err
	}
	o.Server = serverNormalized
	clientConfig.Host = o.Server

	if len(o.CAFile) > 0 {
		clientConfig.CAFile = o.CAFile

	} else {
		// check all cluster stanzas to see if we already have one with this URL that contains a client cert
		for _, cluster := range o.StartingKubeConfig.Clusters {
			if cluster.Server == clientConfig.Host {
				if len(cluster.CertificateAuthority) > 0 {
					clientConfig.CAFile = cluster.CertificateAuthority
					break
				}

				if len(cluster.CertificateAuthorityData) > 0 {
					clientConfig.CAData = cluster.CertificateAuthorityData
					break
				}
			}
		}
	}

	// ping to check if server is reachable
	osClient, err := client.New(clientConfig)
	if err != nil {
		return nil, err
	}

	result := osClient.Get().AbsPath("/osapi").Do()
	if result.Error() != nil {
		switch {
		case o.InsecureTLS:
			clientConfig.Insecure = true

		// certificate issue, prompt user for insecure connection
		case clientcmd.IsCertificateAuthorityUnknown(result.Error()):
			// check to see if we already have a cluster stanza that tells us to use --insecure for this particular server.  If we don't, then prompt
			clientConfigToTest := *clientConfig
			clientConfigToTest.Insecure = true
			matchingClusters := getMatchingClusters(clientConfigToTest, *o.StartingKubeConfig)

			if len(matchingClusters) > 0 {
				clientConfig.Insecure = true

			} else if cmdutil.IsTerminal(o.Reader) {
				fmt.Fprintln(o.Out, "The server uses a certificate signed by an unknown authority.")
				fmt.Fprintln(o.Out, "You can bypass the certificate check, but any data you send to the server could be intercepted by others.")

				clientConfig.Insecure = cmdutil.PromptForBool(os.Stdin, "Use insecure connections? (y/n): ")
				if !clientConfig.Insecure {
					return nil, fmt.Errorf(clientcmd.GetPrettyMessageFor(result.Error()))
				}
				fmt.Fprintln(o.Out)
			}

		default:
			return nil, result.Error()
		}
	}

	// check for matching api version
	if len(o.APIVersion) > 0 {
		clientConfig.Version = o.APIVersion
	}

	o.Config = clientConfig
	return o.Config, nil
}
Esempio n. 3
0
// getClientConfig returns back the current clientConfig as we know it.  If there is no clientConfig, it builds one with enough information
// to talk to a server.  This may involve user prompts.  This method is not threadsafe.
func (o *LoginOptions) getClientConfig() (*restclient.Config, error) {
	if o.Config != nil {
		return o.Config, nil
	}

	if len(o.Server) == 0 {
		// we need to have a server to talk to
		if kterm.IsTerminal(o.Reader) {
			for !o.serverProvided() {
				defaultServer := defaultClusterURL
				promptMsg := fmt.Sprintf("Server [%s]: ", defaultServer)
				o.Server = term.PromptForStringWithDefault(o.Reader, o.Out, defaultServer, promptMsg)
			}
		}
	}

	clientConfig := &restclient.Config{}

	// normalize the provided server to a format expected by config
	serverNormalized, err := config.NormalizeServerURL(o.Server)
	if err != nil {
		return nil, err
	}
	o.Server = serverNormalized
	clientConfig.Host = o.Server

	// use specified CA or find existing CA
	if len(o.CAFile) > 0 {
		clientConfig.CAFile = o.CAFile
		clientConfig.CAData = nil
	} else if caFile, caData, ok := findExistingClientCA(clientConfig.Host, *o.StartingKubeConfig); ok {
		clientConfig.CAFile = caFile
		clientConfig.CAData = caData
	}

	// try to TCP connect to the server to make sure it's reachable, and discover
	// about the need of certificates or insecure TLS
	if err := dialToServer(*clientConfig); err != nil {
		switch err.(type) {
		// certificate authority unknown, check or prompt if we want an insecure
		// connection or if we already have a cluster stanza that tells us to
		// connect to this particular server insecurely
		case x509.UnknownAuthorityError, x509.HostnameError, x509.CertificateInvalidError:
			if o.InsecureTLS ||
				hasExistingInsecureCluster(*clientConfig, *o.StartingKubeConfig) ||
				promptForInsecureTLS(o.Reader, o.Out, err) {
				clientConfig.Insecure = true
				clientConfig.CAFile = ""
				clientConfig.CAData = nil
			} else {
				return nil, clientcmd.GetPrettyErrorForServer(err, o.Server)
			}
		// TLS record header errors, like oversized record which usually means
		// the server only supports "http"
		case tls.RecordHeaderError:
			return nil, clientcmd.GetPrettyErrorForServer(err, o.Server)
		default:
			// suggest the port used in the cluster URL by default, in case we're not already using it
			host, port, parsed, err1 := getHostPort(o.Server)
			_, defaultClusterPort, _, err2 := getHostPort(defaultClusterURL)
			if err1 == nil && err2 == nil && port != defaultClusterPort {
				parsed.Host = net.JoinHostPort(host, defaultClusterPort)
				return nil, fmt.Errorf("%s\nYou may want to try using the default cluster port: %s", err.Error(), parsed.String())
			}
			return nil, err
		}
	}

	// check for matching api version
	if !o.APIVersion.Empty() {
		clientConfig.GroupVersion = &o.APIVersion
	}

	o.Config = clientConfig

	return o.Config, nil
}