diff --git a/lib/utils/cli.go b/lib/utils/cli.go index 6821764c73196..2b1c7fd20412d 100644 --- a/lib/utils/cli.go +++ b/lib/utils/cli.go @@ -238,6 +238,51 @@ func formatCertError(err error) string { var hostnameErr x509.HostnameError if errors.As(err, &hostnameErr) { + // Special case for connecting to Auth via Proxy using internal cluster domain. + if strings.HasSuffix(hostnameErr.Host, ".teleport.cluster.local") { + var proxyEnvBuilder strings.Builder + for _, key := range []string{ + "https_proxy", "http_proxy", "no_proxy", + "HTTPS_PROXY", "HTTP_PROXY", "NO_PROXY", + } { + if val, ok := os.LookupEnv(key); ok { + fmt.Fprintf(&proxyEnvBuilder, " %s: %s\n", key, val) + } + } + + return fmt.Sprintf(`Cannot connect to the Auth service via the Teleport Proxy. + + There might be one or more network intermediaries (like a proxy or VPN) that are modifying your connection before it + reaches the Teleport Proxy. These intermediaries can alter how your connection is seen by the Teleport Proxy and + routed, leading to certificate mismatches. + + To fix this, ensure that any network intermediaries are properly configured and not interfering with your connection. + +DEBUG INFO: + Host: %s + + Proxy Environment Variables: +%s + Server Certificate Details: + Subject: %s + Issuer: %s + Serial Number: %s + Not Before: %s + Not After: %s + DNS Names: %v + IP Addresses: %v`, + hostnameErr.Host, + proxyEnvBuilder.String(), + hostnameErr.Certificate.Subject, + hostnameErr.Certificate.Issuer, + hostnameErr.Certificate.SerialNumber, + hostnameErr.Certificate.NotBefore, + hostnameErr.Certificate.NotAfter, + hostnameErr.Certificate.DNSNames, + hostnameErr.Certificate.IPAddresses, + ) + } + return fmt.Sprintf("Cannot establish https connection to %s:\n%s\n%s\n", hostnameErr.Host, hostnameErr.Error(), diff --git a/lib/utils/cli_test.go b/lib/utils/cli_test.go index 7e299c410d2e4..d9b2b04609a12 100644 --- a/lib/utils/cli_test.go +++ b/lib/utils/cli_test.go @@ -20,6 +20,7 @@ package utils import ( "crypto/x509" + "errors" "fmt" "log/slog" "testing" @@ -212,3 +213,45 @@ func TestFilterArguments(t *testing.T) { require.Equal(t, tt.expected, FilterArguments(tt.args, app.Model()), fmt.Sprintf("test case %v", i)) } } + +// TestFormatCertError tests the formatCertError function for various x509 error types and messages. +func TestFormatCertError(t *testing.T) { + t.Run("UnknownAuthorityError", func(t *testing.T) { + err := x509.UnknownAuthorityError{} + msg := formatCertError(err) + require.Contains(t, msg, "The proxy you are connecting to has presented a certificate signed by a") + }) + + t.Run("HostnameErrorConnectingToAuth", func(t *testing.T) { + cert := &x509.Certificate{Raw: []byte("dummy")} + err := x509.HostnameError{Certificate: cert, Host: "99999999999999999999999999999999.teleport.cluster.local"} + msg := formatCertError(err) + require.Contains(t, msg, "Cannot connect to the Auth service via the Teleport Proxy.") + require.Contains(t, msg, "Host: 99999999999999999999999999999999.teleport.cluster.local") + }) + + t.Run("HostnameError", func(t *testing.T) { + cert := &x509.Certificate{Raw: []byte("dummy")} + err := x509.HostnameError{Certificate: cert, Host: "example.com"} + msg := formatCertError(err) + require.Contains(t, msg, "Cannot establish https connection to example.com") + }) + + t.Run("CertificateInvalidError", func(t *testing.T) { + err := x509.CertificateInvalidError{Reason: x509.Expired, Cert: &x509.Certificate{}} + msg := formatCertError(err) + require.Contains(t, msg, "The certificate presented by the proxy is invalid") + }) + + t.Run("CertificateNotTrustedError", func(t *testing.T) { + err := errors.New("certificate is not trusted") + msg := formatCertError(err) + require.Contains(t, msg, "The proxy you are connecting to has presented a certificate signed by") + }) + + t.Run("NoMatch", func(t *testing.T) { + err := errors.New("some other error") + msg := formatCertError(err) + require.Empty(t, msg) + }) +}