Fehlermeldung
tls: first record does not look like a TLS handshake
gRPC, Datenbanktreiber und andere Bibliotheken verpacken den Fehler in eigene Meldungen, suche in den Logs also nach dem Kerntext. Bei HTTP-Antworten meldet net/http stattdessen „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)
Was der Fehler bedeutet
Der TLS-Client von Go hat ein ClientHello gesendet, und der erste Record, den er zurückbekommen hat, hat nicht die Struktur eines TLS-Handshake-Records, deshalb gibt er sofort auf. Die Gegenseite spricht auf diesem Port etwas anderes: einfaches HTTP (wenn Go es nicht als solches erkennt), ein gRPC-Server im Klartext, die Begrüßung eines Datenbank- oder Mailprotokolls. Den umgekehrten Fall – ein Client, der Klartext an einen TLS-Port sendet – protokollieren Go-Server anders.
Warum der Fehler auftritt
TLS-Client gegen einen Klartext-Port
häufigEin Dienstport ohne TLS, oft ein Sidecar oder ein interner Listener, der mit tls.Dial oder https:// angesprochen wird.
gRPC-Client mit TLS-Credentials gegen einen Klartext-Server
häufigEntwicklungsserver laufen häufig ohne TLS, während der Client mit TLS-Credentials gebaut ist (oder umgekehrt, was dann auf dem Server scheitert).
Implizites TLS gegen ein STARTTLS-Protokoll
gelegentlichSMTP auf Port 587, PostgreSQL, MySQL, Redis mit TLS auf einem anderen Port: Der Server begrüßt den Client zuerst im Klartext.
So behebst du den Fehler
- 1.
Prüfen, was der Port spricht
Ein TLS-Handshake mit openssl oder ein kurzer Blick auf das Banner zeigt, ob der Port überhaupt TLS spricht.
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: die Credentials verwenden, die der Server erwartet
TLS-Credentials für TLS-Server; unsichere Credentials nur für Klartext-Server in vertrauenswürdigen Netzen.
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.
STARTTLS verwenden, wo das Protokoll es verlangt
StartTLS aus net/smtp (und die Entsprechungen in Datenbanktreibern, etwa sslmode bei PostgreSQL) stellen die bestehende Verbindung in-band auf TLS um.
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"}) }