Add debug logging to tsh when there is a SAN mismatch connecting to Auth via Proxy (#61059)

* Add debug logging to `tsh login` when there is a DNS SAN mismatch

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

* Fix formatting so it is consistent

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

* Filter for only proxy env vars. Improve message.

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

* Add unit tests for formatCertError.

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

* Revert copyright update

* Pretty print proxy env and certificate

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

* Print NotBefore

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

* Add case where there is a network intermediary

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

* Revert general HostnameError case. Add message for just the case of connecting to Auth.

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

* Apply suggestions from code review

Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>

* Fix missing deref. Only build in the special case.

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

* Remove internal cluster domain and move hostname to debug info.

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

* Add vertical space.

Signed-off-by: Chris Thach <chris.thach@goteleport.com>

---------

Signed-off-by: Chris Thach <chris.thach@goteleport.com>
Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>
This commit is contained in:
Chris Thach
2025-11-10 18:36:40 +00:00
committed by GitHub
co-authored by Zac Bergquist
parent f31407ca75
commit e16a7ad8ef
2 changed files with 88 additions and 0 deletions
+45
View File
@@ -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(),
+43
View File
@@ -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)
})
}