Выберите продукт

PostgreSQL SSL/TLS: sslmode require и verify-full, root.crt, SNI и типовые ошибки проверки сертификата

Пошагово настраиваем SSL/TLS в PostgreSQL и клиентах: разница между sslmode=require и verify-full, где хранится root.crt, как устроена цепочка CA, когда всплывает SNI и как быстро найти причину certificate verify failed.
PostgreSQL SSL/TLS: sslmode require и verify-full, root.crt, SNI и типовые ошибки проверки сертификата

Зачем вообще разбираться в SSL/TLS для PostgreSQL

SSL/TLS в PostgreSQL решает две разные задачи: шифрует канал (чтобы логины и данные не утекали по сети) и, при строгой проверке, подтверждает, что клиент подключился именно к вашему серверу (а не к подмене). В продакшене важно не останавливаться на «просто включили TLS», а добиться проверяемой аутентичности.

Ниже разберём ключевые элементы: клиентский sslmode (в первую очередь require и verify-full), файл root.crt и цепочку CA, серверные параметры ssl_cert_file/ssl_key_file, правила pg_hba.conf с hostssl, нюансы SNI и типовую ошибку certificate verify failed.

Карта режимов sslmode: что реально означает require и verify-full

Параметр sslmode задаётся на стороне клиента (libpq: psql, а также многие драйверы, совместимые по смыслу). Это главный рычаг, который определяет: «просто шифруем» или «шифруем и проверяем, что сервер настоящий».

sslmode=require

require означает: «подключайся только по TLS». При этом сертификат сервера может не проверяться так строго, как в браузере: канал шифруется, но теоретический риск MITM (подмена сервера) остаётся, если вы не проверяете CA и имя хоста.

require подходит как минимальный уровень защиты (например, внутри доверенной сети) или как промежуточный этап миграции к строгой проверке.

sslmode=verify-full

verify-full включает полноценную проверку: клиент строит цепочку доверия до CA (обычно через root.crt или системное хранилище) и сверяет имя сервера с тем, что указано в сертификате (SAN/CN). Это режим «как в нормальном TLS» и обычно именно он нужен в бою.

Для verify-full критично, чтобы host в строке подключения совпал с DNS-именем в SAN (или CN, если SAN отсутствует). Подключение по IP при сертификате на домен почти всегда ломается проверкой.

Коротко: когда какой режим

  • require — быстро включить шифрование, но без строгой аутентификации сервера.

  • verify-full — шифрование + защита от подмены сервера (рекомендуемый вариант для продакшена).

Если вы только планируете выпуск сертификатов под нужные DNS-имена, начните с require, а затем переведите клиентов на verify-full после подготовки цепочки и DNS.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Какие сертификаты нужны: server certificate, root.crt и client certificate

Чтобы TLS в PostgreSQL работал предсказуемо, полезно разделять роли файлов:

  • server certificate (сертификат сервера) и ключ — лежат на сервере PostgreSQL и участвуют в установлении TLS-сессии.

  • root.crt на клиенте — корневой сертификат CA (или набор CA), которому клиент доверяет при проверке сертификата сервера.

  • client certificate (клиентский сертификат) и ключ — нужны, если вы включаете mTLS (взаимную аутентификацию), когда сервер проверяет сертификат клиента.

Где обычно лежит root.crt у клиента

Для libpq-утилит (например, psql) путь по умолчанию зависит от ОС:

  • Linux/macOS: ~/.postgresql/root.crt

  • Windows: %APPDATA%\postgresql\root.crt

Если не хотите полагаться на дефолты, задавайте путь явно параметром sslrootcert в строке подключения.

Цепочка сертификатов и частая причина certificate verify failed

Одна из самых типовых причин certificate verify failed — клиенту не хватает промежуточных сертификатов CA или он доверяет «не тому» корню. На сервере это часто решается тем, что ssl_cert_file содержит сертификат сервера и, при необходимости, промежуточные сертификаты (bundle) в одном файле. На клиенте root.crt должен соответствовать корню, который подписал серверный сертификат.

Схема цепочки сертификатов для PostgreSQL: корневой и промежуточные CA

Настройка PostgreSQL на стороне сервера: ssl=on, сертификаты и hostssl

Ниже — практический минимум, который обычно требуется на сервере. Точные имена файлов и пути зависят от вашей раскладки каталогов и дистрибутива.

1) Включаем SSL и указываем файлы

В postgresql.conf (или подключаемых include-конфигах) задайте параметры:

ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
# опционально: если используете свой CA и хотите проверять клиентские сертификаты
# ssl_ca_file = 'root-ca.crt'

Практика из эксплуатации:

  • Файл ключа сервера должен быть доступен только пользователю postgres (жёсткие права), иначе PostgreSQL откажется стартовать.

  • Если цепочка требует промежуточные CA, добавьте их в конец файла сертификата (bundle) и убедитесь, что клиенты строят цепочку до доверенного корня.

2) Принуждаем подключения по TLS: pg_hba.conf hostssl

sslmode определяет поведение клиента, а pg_hba.conf — политику сервера. Чтобы сервер принимал подключения только по TLS для нужных сетей/пользователей, используйте hostssl и явно запрещайте hostnossl там, где нельзя без шифрования.

# только TLS для конкретной подсети
hostssl  all  all  10.0.0.0/24  scram-sha-256

# запретить нешифрованные подключения с той же подсети
hostnossl  all  all  10.0.0.0/24  reject

После изменения примените конфигурацию (перечитайте HBA/конфиг) и проверьте логи PostgreSQL.

3) Если нужен client certificate (mTLS)

mTLS имеет смысл, когда вы хотите, чтобы доступ определялся не только паролем/ролью, но и наличием клиентского сертификата. Это удобно для сервисных аккаунтов и автоматизации (выпуск/отзыв сертификатов как отдельный контур управления доступом).

На стороне PostgreSQL задают CA, которым будут проверяться клиентские сертификаты:

ssl = on
ssl_ca_file = 'client-ca.crt'

А в pg_hba.conf выбирают методы аутентификации, где сертификат обязателен (например, cert), либо комбинируют TLS и парольную аутентификацию по вашей политике.

Клиентская сторона: строки подключения, root.crt и реальные проверки

Дальше — команды, которые помогают быстро подтвердить, что TLS включён, и понять, на каком этапе ломается проверка.

Проверяем, что TLS реально используется

Быстрый способ — посмотреть запись своего backend’а в pg_stat_ssl:

psql "host=db.example.com dbname=app user=app sslmode=require" -c "select * from pg_stat_ssl where pid = pg_backend_pid();"

В колонке ssl должно быть t. Дополнительно полезно смотреть version, cipher, client_dn (если используете клиентские сертификаты).

Пример с verify-full и явным root.crt

Если root.crt лежит не в дефолтном пути, задайте sslrootcert:

psql "host=db.example.com dbname=app user=app sslmode=verify-full sslrootcert=/etc/ssl/my-ca/root.crt"

В этом режиме клиент должен успешно провалидировать цепочку и имя сервера.

Подключение по IP и verify-full: почему ломается

Если сделать так:

psql "host=203.0.113.10 dbname=app user=app sslmode=verify-full"

а сертификат выписан на db.example.com, проверка имени не пройдёт. Рабочие варианты:

  • Подключаться по доменному имени, которое указано в SAN сертификата.

  • Выпустить сертификат, где SAN включает IP (редко удобно и подходит не всем).

Если у вас используется пулер соединений, обратите внимание, где именно завершается TLS: на приложении, на пулере или на PostgreSQL. Для практики по пулерам пригодится отдельный разбор: как устроить пул соединений PgBouncer и не потерять безопасность.

SNI в PostgreSQL: где он появляется и когда ломает verify-full

SNI (Server Name Indication) — расширение TLS, которое позволяет клиенту передать имя хоста во время рукопожатия. Оно критично, если один IP-адрес и порт обслуживают несколько TLS-сертификатов (обычно это мир HTTPS, но вокруг PostgreSQL такое тоже встречается: TLS-терминация на прокси, балансировщики, shared IP).

Как понять, что SNI влияет на ваш кейс

SNI важно, если вы подключаетесь не напрямую к PostgreSQL, а через компонент, который выбирает сертификат по имени:

  • TCP-proxy с TLS-терминацией и выбором сертификата по SNI;

  • балансировщик, где один адрес обслуживает несколько окружений;

  • маршрутизация по имени (разные бэкенды в зависимости от SNI).

При подключении по IP клиент часто не отправляет ожидаемое имя, прокси может выдать «не тот» сертификат, и вы получите certificate verify failed или ошибку несоответствия имени.

Практическая проверка сертификата и SNI через openssl

Для первичной диагностики посмотрите, какой сертификат реально отдаётся на рукопожатии:

openssl s_client -connect db.example.com:5432 -servername db.example.com -showcerts

Проверьте CN/SAN, наличие промежуточных сертификатов и строку verify return code.

Пример диагностики SNI и сертификата через openssl s_client

Разбор ошибки: certificate verify failed (частые причины и быстрые действия)

Ошибка встречается и в psql, и в приложениях. В реальной эксплуатации почти всегда виновато одно из трёх: доверие (CA), цепочка (intermediate) или имя (SAN/host). Ниже — чек «что проверять в первую очередь».

1) На клиенте нет root.crt или он «не тот»

Проверьте, что клиент использует нужный CA: либо корректный root.crt, либо системное хранилище, либо параметр sslrootcert. Важно: это должен быть именно тот корень, который подписал (или является корнем цепочки для) серверный сертификат.

2) Сервер не отдает цепочку (intermediate CA)

Если сертификат выдан через промежуточный CA, клиенту может не хватить intermediate. Решение: сформировать правильный bundle в ssl_cert_file (сертификат сервера + промежуточные) и применить конфиг.

3) Несовпадение имени: verify-full и неправильный host

Проверьте host в строке подключения: он должен совпадать с DNS-именем из SAN. Подключение по IP при сертификате на DNS — ожидаемо падает. Этот «слом» и есть цель verify-full.

4) Прокси/балансировщик выдает не тот сертификат (включая SNI)

Если в цепочке есть TLS-терминация на прокси, убедитесь, что клиент отправляет правильное имя (SNI) и что на прокси настроен нужный сертификат именно для этого имени. Проверяйте через openssl s_client с -servername.

5) Права на ключи/файлы и ошибки старта PostgreSQL

При неверных правах на ssl_key_file PostgreSQL часто отказывается запускаться. Если вы выкатывали изменения автоматизацией, всегда смотрите логи сервиса и убеждайтесь, что сервер стартовал именно с ожидаемыми параметрами TLS.

Чек-лист перед включением verify-full в проде

  1. У серверного сертификата есть SAN с DNS-именем, по которому подключаются клиенты.

  2. На клиентах есть корректный CA (root.crt или настроенное системное доверие).

  3. Сервер отдает корректную цепочку (server + intermediate при необходимости).

  4. В pg_hba.conf есть hostssl для нужных сетей и hostnossl ... reject там, где нельзя без TLS.

  5. Проверка через openssl s_client с -servername возвращает ожидаемый сертификат и успешную валидацию.

Практика: как мигрировать с require на verify-full без простоя

Если у вас много приложений и окружений, мигрируйте поэтапно:

  • Сначала включите TLS на сервере и настройте hostssl, но временно допускайте клиентов с sslmode=require.

  • Параллельно раскатайте на клиентов CA (root.crt) и приведите строки подключения к доменному имени из SAN.

  • Переводите клиентов на sslmode=verify-full постепенно, начиная с некритичных сервисов.

  • Когда все перешли — запретите не-TLS через hostnossl ... reject.

Если вы строите отказоустойчивую схему (репликация, PITR, бэкапы), сразу учитывайте, что валидный TLS нужен не только приложениям, но и служебным компонентам. По резервному копированию PostgreSQL пригодится: практическая настройка pgBackRest: бэкапы и восстановление.

Итоги

sslmode — это не «галочка про шифрование», а политика доверия. require даёт шифрование, но не гарантирует, что сервер не подменили. verify-full добавляет валидацию цепочки CA через root.crt и сверку имени хоста по SAN, поэтому требует аккуратной подготовки сертификатов и правильного DNS-имени в подключении.

Если вы видите certificate verify failed, чаще всего виновато одно из трёх: неверный/отсутствующий root.crt, неполная цепочка на сервере или несовпадение имени (особенно при verify-full). А если между клиентом и PostgreSQL есть прокси/балансировщик, держите в голове SNI: он напрямую влияет на то, какой сертификат будет показан на TLS-рукопожатии.

Поделиться статьей

Вам будет интересно

LVM snapshot и I/O: почему растёт задержка и как делать backup без сюрпризов OpenAI Статья написана AI (GPT 5)

LVM snapshot и I/O: почему растёт задержка и как делать backup без сюрпризов

LVM snapshot удобен для горячего backup, но почти всегда увеличивает нагрузку на диск. Объясняю, откуда берётся рост I/O latency, ...
DNS TTL: как работает время жизни записей, кэш резолверов и «распространение» изменений OpenAI Статья написана AI (GPT 5)

DNS TTL: как работает время жизни записей, кэш резолверов и «распространение» изменений

TTL в DNS — не «время распространения», а срок хранения ответа в кэше. Разберём, где живут кэши резолверов, ОС и приложений, почем ...
Nginx access.log: почему real_user_agent пустой и как логировать реальный User-Agent OpenAI Статья написана AI (GPT 5)

Nginx access.log: почему real_user_agent пустой и как логировать реальный User-Agent

Если в access.log поле real_user_agent пустое, причина обычно в цепочке прокси/CDN или в том, что логируется не та переменная. Пок ...