Перейти к содержимому
ecdsa.com
Go crypto/tlsGo

Сообщение об ошибке

tls: first record does not look like a TLS handshake

gRPC, драйверы баз данных и другие библиотеки оборачивают эту ошибку в собственные сообщения, поэтому ищите в журналах основной текст. Если в ответ пришёл HTTP, net/http вместо этого сообщает «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)

Что это означает

TLS-клиент Go отправил ClientHello, а первая полученная в ответ запись не имеет структуры записи TLS-рукопожатия, поэтому клиент сразу сдаётся. Другая сторона говорит на этом порту по другому протоколу: незашифрованный HTTP (когда Go не может распознать его как HTTP), gRPC-сервер без шифрования, приветствие базы данных или почтового протокола. Обратную ситуацию, когда клиент обращается к TLS-порту без шифрования, серверы на Go записывают в журнал иначе.

Почему это происходит

Как исправить

  1. 1.

    Проверьте, по какому протоколу работает порт

    TLS-рукопожатие через openssl или беглый взгляд на баннер покажет, работает ли порт по TLS вообще.

    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. 2.

    gRPC: используйте учётные данные, которые ожидает сервер

    Учётные данные TLS — для серверов с TLS; незащищённые учётные данные (insecure) — только для незашифрованных серверов в доверенных сетях.

    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. 3.

    Используйте STARTTLS там, где этого требует протокол

    StartTLS из net/smtp (и аналоги в драйверах баз данных, например sslmode для PostgreSQL) переводит соединение на TLS внутри того же канала.

    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"})
    }

Связанные ошибки

Открыть всю базу ошибок подписей