Mensaje de error
tls: first record does not look like a TLS handshake
gRPC, los controladores de bases de datos y otras bibliotecas lo envuelven en sus propios mensajes, así que busca en los registros el texto central. Ante respuestas HTTP, net/http dice en cambio «http: server gave HTTP response to HTTPS client».
Go crypto/tls — the peer's first bytes are not a TLS record (tls.Dial, gRPC, database drivers, http.Client)
Qué significa
El cliente TLS de Go envía un ClientHello y el primer registro TLS que recibe de vuelta no tiene la estructura de un registro de handshake, así que abandona de inmediato. El otro extremo habla otra cosa en ese puerto: HTTP plano (cuando Go no puede reconocerlo como tal), un servidor gRPC en texto plano o el saludo inicial de un protocolo de base de datos o de correo. En los servidores Go, la situación inversa (un cliente que habla texto plano a un puerto TLS) queda registrada con otro mensaje.
Por qué ocurre
Cliente TLS contra un puerto en texto plano
frecuenteUn puerto de servicio sin TLS, a menudo un sidecar o un listener interno, al que un cliente se conecta con tls.Dial o https://.
Cliente gRPC con credenciales TLS contra un servidor en texto plano
frecuenteLos servidores de desarrollo suelen funcionar sin TLS mientras el cliente se crea con credenciales TLS (o al revés, lo que falla en el servidor).
TLS implícito contra un protocolo STARTTLS
ocasionalSMTP en el puerto 587, PostgreSQL, MySQL, Redis con TLS en otro puerto: el servidor saluda primero en texto plano.
Cómo solucionarlo
- 1.
Comprobar qué habla el puerto
Un handshake TLS con openssl, o un vistazo rápido al banner, muestra si el puerto usa TLS o no.
bash # Does the port speak TLS? openssl s_client -connect api.example.com:8080 -servername api.example.com -brief </dev/null timeout 3 nc api.example.com 8080 </dev/null | head -1 # a text banner = plain protocol - 2.
gRPC: usar las credenciales que espera el servidor
Credenciales TLS para servidores TLS; credenciales inseguras solo para servidores en texto plano en redes de confianza.
go // gRPC: match the server. Plaintext server -> insecure credentials (only on trusted networks); // TLS server -> TLS credentials. conn, err := grpc.NewClient("api.example.com:9090", grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ServerName: "api.example.com"}))) - 3.
Usar STARTTLS donde el protocolo lo exija
StartTLS de net/smtp (y sus equivalentes en los controladores de bases de datos, por ejemplo sslmode en PostgreSQL) pasa la conexión a TLS dentro del mismo canal.
go // SMTP on 587 starts in plain text: use STARTTLS, not tls.Dial c, err := smtp.Dial("mail.example.com:587") if err == nil { err = c.StartTLS(&tls.Config{ServerName: "mail.example.com"}) }