Mensagem de erro
tls: first record does not look like a TLS handshake
O gRPC, drivers de banco de dados e outras bibliotecas encapsulam o erro nas próprias mensagens, então procure nos logs o trecho central do texto. Para respostas HTTP, o net/http diz “http: server gave HTTP response to HTTPS client” em vez disso.
Go crypto/tls — the peer's first bytes are not a TLS record (tls.Dial, gRPC, database drivers, http.Client)
O que significa
O cliente TLS do Go enviou um ClientHello, e o primeiro registro que recebeu de volta não tem a estrutura de um registro de handshake TLS, então ele desiste imediatamente. O outro lado fala outra coisa nessa porta: HTTP não criptografado (quando o Go não consegue reconhecê-lo como tal), um servidor gRPC não criptografado, a saudação de um protocolo de banco de dados ou de e-mail. Servidores Go registram de outra forma a situação inversa, um cliente que fala texto claro com uma porta TLS.
Por que acontece
Cliente TLS em uma porta de texto claro
comumUma porta de serviço sem TLS, muitas vezes de um sidecar ou de um listener interno, acessada com tls.Dial ou https://.
Cliente gRPC com credenciais TLS contra um servidor não criptografado
comumServidores de desenvolvimento muitas vezes rodam sem TLS enquanto o cliente é criado com credenciais TLS (ou o contrário, que falha no servidor).
TLS implícito em um protocolo STARTTLS
ocasionalSMTP na porta 587, PostgreSQL, MySQL, Redis com TLS em outra porta: o servidor envia primeiro uma saudação em texto claro.
Como corrigir
- 1.
Verificar o que a porta fala
Um handshake TLS com o openssl, ou uma olhada rápida no banner, mostra se a porta usa TLS ou não.
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 as credenciais que o servidor espera
Credenciais TLS para servidores TLS; credenciais inseguras só para servidores não criptografados em redes confiáveis.
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 onde o protocolo exige
O StartTLS do net/smtp (e os equivalentes nos drivers de banco de dados, por exemplo, o sslmode do PostgreSQL) passa a conexão para TLS dentro do próprio 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"}) }