TLS между Directum RX и PostgreSQL: режимы Npgsql, сертификаты, pg_hba и проверка, что шифрование работает
Как включить шифрование соединений от сервисов Directum RX до PostgreSQL и не сломать подключение. Настройки сервера, правила hostssl в pg_hba.conf, режимы SSL Mode в Npgsql и чем Require отличается от VerifyFull, доверие к удостоверяющему центру в контейнере, проверка через pg_stat_ssl и типовые ошибки с цепочками.
Коротко.
ssl = onтолько разрешает шифрование. Обязательным его делаетhostsslвpg_hba.conf, а защиту от подмены сервера дают режимVerifyFullв Npgsql и доверенный корень внутри контейнеров RX. Проверяйте поpg_stat_sslна сервере базы, цепочку по счётуBEGINиEND, срок сертификата держите под наблюдением.
Служба ИБ требует, чтобы трафик между серверами приложений и базой данных шёл в зашифрованном виде. На сервере
PostgreSQL включают ssl = on, перезапускают сервисы Directum RX, и всё вроде бы работает. Через месяц
проверка показывает, что половина соединений по-прежнему открытые, а остальные шифруются, но сертификат сервера
никто не проверяет. Разбираем, как включить TLS так, чтобы он действительно защищал, и как в этом убедиться.
Симптом
- После включения SSL на сервере сервисы RX не подключаются к базе, в журналах ошибки TLS или отказ по
pg_hba.confс концовкойno encryptionилиSSL encryption. - Всё работает, но проверка показывает соединения без шифрования.
- Сервисы на .NET пишут
The remote certificate is invalid according to the validation procedure, аpsqlс того же сервера подключается. - Java-приложения рядом с RX, например Keycloak, падают при старте с
CertificateException: Incomplete data. - После продления сертификата на сервере базы часть клиентов перестаёт подключаться.
Почему так
У TLS до базы три отдельных вопроса, и включение ssl = on отвечает только на первый.
- Умеет ли сервер шифровать. Это
ssl = onи файлы сертификата и ключа. - Обязан ли клиент шифровать. Это правила в
pg_hba.conf: правилоhostпускает и с шифрованием, и без,hostsslтолько с шифрованием. - Проверяет ли клиент, что говорит с настоящим сервером. Это режим SSL на стороне клиента. Шифрование без проверки сертификата защищает от того, кто просто слушает сеть, но не от того, кто встал посередине и предъявил свой сертификат.
Сервисы RX подключаются к PostgreSQL через драйвер Npgsql, и режим задаётся параметром SSL Mode
в строке подключения к базе в настройках RX.
| SSL Mode | Шифрует | Проверяет сертификат сервера | Проверяет имя сервера |
|---|---|---|---|
Disable |
нет | нет | нет |
Prefer |
если сервер умеет | нет | нет |
Require |
да, иначе ошибка | нет | нет |
VerifyCA |
да | да, по доверенному центру | нет |
VerifyFull |
да | да | да |
Значения по умолчанию и поведение Require менялись между версиями Npgsql. В части версий для Require
требовался дополнительный параметр Trust Server Certificate=true, в более новых этот параметр устарел.
Поэтому режим задаётся явно, а результат проверяется на сервере базы по pg_stat_ssl.
Настройка сервера
# postgresql.conf
ssl = on
ssl_cert_file = '/etc/postgresql/tls/server.crt' # сертификат сервера и промежуточные, по порядку
ssl_key_file = '/etc/postgresql/tls/server.key'
ssl_min_protocol_version = 'TLSv1.2'
- Ключ должен принадлежать пользователю
postgresс правами0600илиrootс группойpostgresи правами0640. При более широких правах сервер откажется его читать. - В сертификате в поле SAN должно быть имя, по которому подключаются клиенты. Если RX подключается по IP-адресу, этот адрес тоже должен быть в SAN.
- Настройки SSL перечитываются без перезапуска:
SELECT pg_reload_conf();. Уже открытые соединения продолжают работать со старым сертификатом, новые получают новый.
Затем правила доступа. Чтобы закрыть подключения без шифрования для сети приложений, разрешающие правила
переводятся на hostssl, а ниже ставится явный запрет:
# pg_hba.conf
hostssl rx rx_user 10.10.1.0/24 scram-sha-256
hostnossl all all 10.10.1.0/24 reject
Порядок важен: PostgreSQL применяет первое подходящее правило. Разбор отказов по pg_hba.conf есть
в справочнике: нет подходящего правила и
сработал запрет.
Настройка клиента
В строке подключения RX:
Host=db01.example.local;Port=5432;Database=rx;Username=rx_user;Password=...;SSL Mode=VerifyFull
Для VerifyCA и VerifyFull клиенту нужно доверять удостоверяющему центру, который выпустил сертификат базы.
Npgsql на Linux проверяет сертификат по системному хранилищу, а сервисы RX работают в контейнерах, и у контейнера
хранилище своё. Корневой сертификат на хосте контейнер не видит: его нужно добавить в образ или смонтировать
и обновить хранилище внутри. Как это сделать на разных дистрибутивах, описано в
справочнике ошибок.
Npgsql умеет брать корневой сертификат и из отдельного файла (параметр Root Certificate), но тогда файл
должен быть доступен внутри контейнера по указанному пути.
Если между RX и базой стоит пулер соединений (pgbouncer, Odyssey), TLS настраивается на двух участках: от клиента до пулера и от пулера до базы. Каждый проверяется отдельно.
Проверка
Главная проверка делается на сервере базы. Представление pg_stat_ssl показывает по каждому соединению,
зашифровано ли оно и чем:
SELECT a.usename, a.client_addr, a.application_name, s.ssl, s.version, s.cipher
FROM pg_stat_activity a JOIN pg_stat_ssl s USING (pid)
WHERE a.backend_type = 'client backend'
ORDER BY s.ssl, a.client_addr;
Строки с ssl = f это соединения без шифрования. По адресу и имени приложения видно, чьи они.
Проверить сертификат сервера с машины клиента можно без драйвера, OpenSSL умеет начинать TLS по протоколу PostgreSQL:
openssl s_client -starttls postgres -connect db01.example.local:5432 -showcerts </dev/null
В выводе смотрите Verify return code и сколько сертификатов отдал сервер. Если сервер отдаёт один сертификат,
а выпущен он промежуточным центром, клиенты, которые не достраивают цепочку сами, откажутся подключаться.
Проверка режима verify-full из psql:
psql "host=db01.example.local dbname=rx user=rx_user sslmode=verify-full sslrootcert=/path/ca.crt" -c 'select 1'
Типовые ошибки
| Что видно | Причина |
|---|---|
Отказ pg_hba.conf с no encryption |
клиент подключается без TLS, а разрешающее правило только hostssl |
RemoteCertificateNameMismatch |
имени или адреса, по которому подключается RX, нет в SAN сертификата |
UntrustedRoot в сервисе RX, а psql на хосте работает |
корневой сертификат добавлен на хост, но не в контейнер |
PartialChain |
в ssl_cert_file только сертификат сервера, без промежуточных |
CertificateException: Incomplete data в Java |
файл с цепочкой обрезан: потеряна строка END CERTIFICATE при копировании |
| После продления часть клиентов не подключается | новый сертификат выпущен другим центром или без нужного имени в SAN |
Последние две ошибки удобно ловить до выкладки. В файле цепочки число строк BEGIN CERTIFICATE должно
совпадать с числом END CERTIFICATE: openssl x509 читает только первый сертификат и об обрезанном хвосте
молчит.
grep -c 'BEGIN CERTIFICATE' server.crt; grep -c 'END CERTIFICATE' server.crt
openssl x509 -in server.crt -noout -subject -issuer -ext subjectAltName -enddate
Что сделать
- Включить
ssl = onс полной цепочкой и минимальной версией TLS 1.2, перечитать настройки. - Проверить, что клиенты могут подключиться с
VerifyFull: корень доверен в контейнерах, имя в SAN. - Перевести строки подключения RX и остальных клиентов на явный режим
VerifyFull. - По
pg_stat_sslубедиться, что соединений без шифрования не осталось. - Перевести правила в
pg_hba.confнаhostsslи добавить запретhostnossl. - Поставить срок действия сертификата базы под наблюдение. Продление делается заранее и проверяется теми же командами.
Чего не делать
- Не останавливаться на
ssl = on. Без правилhostsslклиент может подключиться и без шифрования. - Не ставить
Requireи считать, что сертификат проверяется. Он не проверяется. - Не закрывать
hostnosslдо того, как все клиенты переведены. Перестанет работать то, о чём забыли: резервное копирование, мониторинг, отчётные системы. - Не проверять сертификат только с хоста. Сервисы RX смотрят в хранилище своего контейнера.
- Не выдавать сертификат базы на имя, по которому никто не подключается.
Похожая картина у вас?
Разберём ваш контур за 30 минут бесплатного созвона: версия RX, состав сервисов, что болит сильнее всего.
Написать