Avatar ·

Ошибка "SSL is not enabled on the server" при подключении к PostgreSQL из Go

Всем привет! Я новичок в Go и пытаюсь подключиться к PostgreSQL, который запущен на сервере в облаке. Использую драйвер `lib/pq`. Сервер требует SSL-соединение, но я никак не могу его настроить. Вот мой код: ```go package main import ( "database/sql" "fmt" _ "github.com/lib/pq" ) func main() { connStr := "host=mycloudserver.com port=5432 user=myuser password=mypass dbname=mydb sslmode=disable" db, err := sql.Open("postgres", connStr) if err != nil { panic(err) } defer db.Close() err = db.Ping() if err != nil { fmt.Println("Ошибка подключения:", err) } else { fmt.Println("Подключение успешно!") } } ``` При запуске получаю ошибку: `pq: SSL is not enabled on the server`. Я понимаю, что проблема в `sslmode=disable`, но когда меняю на `sslmode=require` или `sslmode=verify-full`, программа падает с другой ошибкой: `pq: SSL is not supported` или `tls: first record does not look like a TLS handshake`. Подскажите, пожалуйста, как правильно настроить SSL-подключение? Нужно ли скачивать какие-то сертификаты? Или, может, проблема в самом драйвере? Буду благодарен за подробное объяснение с примерами. <<>>

Понимание проблемы: почему возникает ошибка

Ваша ошибка — классический случай, когда сервер PostgreSQL настроен на обязательное SSL-соединение, а клиент пытается подключиться без шифрования (sslmode=disable). Сервер отклоняет такое подключение. При попытке включить SSL без правильных сертификатов (sslmode=require) возникает вторая ошибка, потому что Go не может проверить сертификат сервера.

Решение: правильная настройка SSL-соединения

Вам нужно настроить три вещи:

  • Правильный режим SSL (sslmode)
  • Путь к корневому сертификату CA (если используете verify-full)
  • Проверить, что сервер PostgreSQL поддерживает TLS (версии 1.2 или 1.3)

Шаг 1. Определите режим SSL

Для большинства облачных баз данных (AWS RDS, DigitalOcean, Heroku) достаточно использовать sslmode=require. Этот режим шифрует соединение, но не проверяет сертификат сервера. Если вам нужна максимальная безопасность — используйте sslmode=verify-full, но тогда потребуется CA-сертификат.

Шаг 2. Получите сертификат CA

Для verify-full скачайте корневой сертификат вашего провайдера. Например, для AWS RDS:

wget https://s3.amazonaws.com/rds-downloads/rds-ca-2019-root.pem -O ~/certs/rds-ca.pem

Шаг 3. Исправьте код

Вот рабочий пример для sslmode=require (без проверки сертификата):

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/lib/pq"
)

func main() {
    // sslmode=require — шифрование без проверки сертификата
    connStr := "host=mycloudserver.com port=5432 user=myuser password=mypass dbname=mydb sslmode=require"
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        panic(err)
    }
    defer db.Close()

    err = db.Ping()
    if err != nil {
        fmt.Println("Ошибка:", err)
    } else {
        fmt.Println("Подключение успешно!")
    }
}

Если хотите проверять сертификат (рекомендуется для продакшена):

// sslmode=verify-full требует CA-сертификат
connStr := "host=mycloudserver.com port=5432 user=myuser password=mypass dbname=mydb sslmode=verify-full sslrootcert=/home/user/certs/rds-ca.pem"

Шаг 4. Альтернатива: настройка через SSL-конфигурацию Go

Если у вас нестандартные требования (например, самоподписанный сертификат), используйте программную настройку TLS:

import (
    "crypto/tls"
    "crypto/x509"
    "database/sql"
    "fmt"
    "io/ioutil"
    "log"
    _ "github.com/lib/pq"
)

func main() {
    // Загружаем CA-сертификат
    caCert, err := ioutil.ReadFile("/home/user/certs/ca.pem")
    if err != nil {
        log.Fatal(err)
    }
    caCertPool := x509.NewCertPool()
    caCertPool.AppendCertsFromPEM(caCert)

    // Настраиваем TLS
    tlsConfig := &tls.Config{
        RootCAs: caCertPool,
        // Для самоподписанных сертификатов: InsecureSkipVerify: true (не для продакшена!)
    }

    // Строка подключения без sslmode
    connStr := "host=mycloudserver.com port=5432 user=myuser password=mypass dbname=mydb"
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        log.Fatal(err)
    }

    // Устанавливаем TLS-конфигурацию через драйвер
    // (lib/pq поддерживает это через параметр sslmode=require + настройку)
    // Альтернатива: используйте pgx — он лучше работает с кастомным TLS
    fmt.Println("Подключение через lib/pq с кастомным TLS настроено")
    _ = db
}

Важные советы

  • Проверьте версию Go — старая версия может не поддерживать современные TLS-протоколы.
  • Используйте pgx — более современный драйвер, который проще настраивать под SSL: go get github.com/jackc/pgx/v5.
  • Не используйте sslmode=disable в продакшене — это угроза безопасности.
  • Если сервер использует самоподписанный сертификат, добавьте его в доверенные или используйте sslmode=require.
<<>>

Альтернативный подход: использование драйвера pgx

Драйвер lib/pq хорош, но у него ограниченная поддержка кастомных TLS-настроек. Рекомендую перейти на pgx — он официально поддерживается, быстрее и гибче в настройке SSL.

Пример с pgx

package main

import (
    "context"
    "fmt"
    "github.com/jackc/pgx/v5/pgxpool"
)

func main() {
    // Строка подключения с SSL-настройками
    connStr := "postgres://myuser:mypass@mycloudserver.com:5432/mydb?sslmode=require"

    pool, err := pgxpool.New(context.Background(), connStr)
    if err != nil {
        panic(err)
    }
    defer pool.Close()

    var greeting string
    err = pool.QueryRow(context.Background(), "select 'Hello, world!'").Scan(&greeting)
    if err != nil {
        fmt.Println("Ошибка:", err)
    } else {
        fmt.Println(greeting)
    }
}

Преимущества pgx

  • Поддерживает все режимы SSL: disable, require, verify-ca, verify-full.
  • Позволяет передавать TLS-конфигурацию через pgxpool.Config.
  • Лучшая производительность и поддержка Go-контекстов.
  • Меньше проблем с сертификатами — pgx автоматически ищет системные CA.

Если pgx не подходит

Попробуйте установить переменную окружения PGSSLMODE=require перед запуском программы — это сработает для lib/pq без изменения кода:

export PGSSLMODE=require
go run main.go

Но помните: это временное решение. Для продакшена всегда настраивайте SSL явно.