Шифрование трафика до базы данных — базовая гигиена безопасности, которая часто откладывается «на потом». Пока запросы ходят внутри одного дата-центра или private VPC, риски кажутся низкими. Но именно в таких сегментах встречаются прослойки, прокси, бэкапные агенты и админские ноутбуки, создающие поверхность атаки: ARP-спуфинг, несанкционированное прослушивание зеркалируемых портов, утечки дампов. TLS закрывает эти классы проблем и облегчает соответствие внутренним политикам и требованиям безопасности.
Модель и термины
Наша цель — включить TLS на стороне сервера базы данных и заставить клиентов проверять цепочку доверия (CA) и имя хоста из сертификата. В идеале — ограничить протоколы до TLS 1.2+ и современные шифросuites. Такой подход предотвращает «тихий» даунгрейд и MITM внутри сети. Дополнительно, при необходимости, можно включить взаимную аутентификацию (mTLS), когда клиент тоже предъявляет сертификат, а сервер проверяет его по CA.
TLS против mTLS
Обычный TLS защищает канал и подтверждает личность сервера: клиент проверяет, что сертификат выпущен доверенным CA и что имя хоста совпадает с SAN сертификата. mTLS добавляет проверку клиента, полезен в сервис-сервис коммуникациях, когда не хочется полагаться только на пароль/токен. Но mTLS требует дистрибуции и ротации клиентских сертификатов — это сложнее в эксплуатации.
Валидация CA и имени хоста
Просто «включить SSL» недостаточно. Клиент обязан проверять корневой/промежуточный CA и сопоставлять FQDN сервера с полями Subject Alternative Name (SAN) сертификата. Игнорирование проверки имени делает шифрование уязвимым для MITM. В современных клиентах MySQL это режим VERIFY_IDENTITY
, а в PostgreSQL — sslmode=verify-full
.
Где брать сертификаты
Внутренние БД чаще обслуживаются частным (внутренним) центром сертификации. Это удобно: вы контролируете выпуск, сроки годности и отзыв. Публичные CA уместны, если БД или прокси доступны из внешних сетей: в таком случае используйте проверенные SSL-сертификаты. При этом важно грамотно хранить корневой ключ, подписывать промежуточный и соблюдать процесс ротации.
Если вы только строите процесс, начните с собственного внутреннего CA и коротких сроков жизни серверных сертификатов (90–180 дней). Так проще привыкнуть к ротации и выработать автоматизацию.

Выпуск внутреннего CA и серверных сертификатов
Минимально жизнеспособная схема — корневой CA (оффлайн) и один промежуточный CA (онлайн), которым подписываются серверные и, при mTLS, клиентские сертификаты. Для простоты ниже показан один CA, но производственно лучше разделять.
Создание корневого CA (пример OpenSSL)
# 1) Генерируем ключ и самоподписанный сертификат CA
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -days 3650 -sha256 -subj "/CN=Fastfox Internal DB Root CA" -out ca.crt
Храните ca.key
в изолированном месте, с резервным копированием и строгими правами. Для продакшна сделайте оффлайн-CA и рабочий промежуточный CA, чтобы ежедневные операции не затрагивали корневой ключ.
Конфиг SAN и выпуск серверного сертификата
# 2) Конфиг для SAN (server-openssl.cnf)
[ req ]
distinguished_name = dn
req_extensions = v3_req
prompt = no
[ dn ]
CN = db01.internal.local
[ v3_req ]
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = db01.internal.local
DNS.2 = db-read.internal.local
IP.1 = 10.10.10.21
# 3) Ключ и CSR
openssl genrsa -out db01.key 2048
openssl req -new -key db01.key -out db01.csr -config server-openssl.cnf
# 4) Подпись CSR CA
openssl x509 -req -in db01.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out db01.crt -days 180 -sha256 -extfile server-openssl.cnf -extensions v3_req
В SAN обязательно укажите реальный FQDN, по которому клиенты подключаются. Если клиент использует IP, добавьте его в SAN как IP
. Не полагайтесь на устаревший CN без SAN.
MySQL/MariaDB: включаем TLS и проверку
Современный MySQL 8.0 и MariaDB умеют TLS 1.2/1.3 «из коробки». Мы включим TLS, ограничим протоколы и потребуем защищённый транспорт для любых подключений.
Серверная конфигурация
# /etc/my.cnf или /etc/mysql/my.cnf
[mysqld]
ssl_ca = /etc/mysql/tls/ca.crt
ssl_cert = /etc/mysql/tls/db01.crt
ssl_key = /etc/mysql/tls/db01.key
require_secure_transport = ON
# Ограничим версии протоколов
# Для MySQL 8.0: список через запятую
tls_version = TLSv1.2,TLSv1.3
# Опционально указать шифросьюты TLS 1.3
tls_ciphersuites = TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384
Ключевые моменты эксплуатации: права на db01.key
— 600, владелец системный пользователь MySQL. Каталог с сертификатами должен быть доступен только службе базы данных. После внесения изменений перезапустите или выполните безостановочный reload/refresh, если поддерживается вашей сборкой. В MariaDB для обновления цепочки можно использовать FLUSH SSL
, в MySQL — перезапуск либо мягкая перезагрузка службы.
Проверка со стороны сервера
# Проверим статус шифрования сессии
mysql -u root -p -e "SHOW SESSION STATUS LIKE 'Ssl_cipher';"
mysql -u root -p -e "SHOW VARIABLES LIKE 'tls_version';"
Если Ssl_cipher
пусто — ваше подключение без TLS. Проверьте require_secure_transport
и что клиент действительно запрашивает TLS.
Клиент MySQL CLI с проверкой CA и имени
mysql --host=db01.internal.local --user=app --password --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/mysql/tls/ca.crt
VERIFY_IDENTITY
включает проверку имени хоста по SAN. Без корректного SAN подключение упадёт с ошибкой несовпадения имени — и это правильное поведение. Если вы используете промежуточный CA, удостоверьтесь, что в --ssl-ca
лежит файл с полной цепочкой, которую понимает клиент.
Интеграция в приложения
PHP (PDO MySQL): укажите режим проверки и путь к CA. Важно: не отключайте проверку имени ради «быстро починить» — лучше выпустите корректный сертификат.
// PHP PDO (mysql)
$pdo = new PDO(
'mysql:host=db01.internal.local;dbname=app;charset=utf8mb4',
'app',
'secret',
[
PDO::MYSQL_ATTR_SSL_CA => '/etc/mysql/tls/ca.crt',
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
]
);
Java (JDBC Connector/J): добавьте параметры в URL, импортируйте CA в truststore, если требуется корпоративной политикой. Node.js (mysql2) принимает объект ssl
с ca
.
PostgreSQL: TLS с проверкой CA
PostgreSQL включает TLS через OpenSSL. Мы активируем TLS, ограничим протоколы, пропишем CA и убедимся, что клиенты используют verify-full
. При необходимости зафиксируем политику в pg_hba.conf
.
Серверная конфигурация
# postgresql.conf
ssl = on
ssl_cert_file = '/etc/postgresql/tls/db01.crt'
ssl_key_file = '/etc/postgresql/tls/db01.key'
ssl_ca_file = '/etc/postgresql/tls/ca.crt'
ssl_min_protocol_version = 'TLSv1.2'
# Дополнительно можно сузить шифросьюты для версий ниже TLS 1.3
ssl_ciphers = 'HIGH:!aNULL:!MD5'
Права на db01.key
— только у пользователя postgres
, режим 600. Для принудительного TLS используйте pg_hba.conf
с записями hostssl
. Например, запретите незашифрованные маршруты, оставив только hostssl
для нужных подсетей.
# pg_hba.conf
# Разрешаем TLS-подключения из приватной подсети, без клиентских сертификатов
hostssl all all 10.10.10.0/24 scram-sha-256
# При mTLS (опционально):
# hostssl all all 10.10.10.0/24 cert clientcert=verify-full
Примените изменения через перезагрузку конфигурации. Сертификаты и CRL в PostgreSQL можно перечитать сигналом SIGHUP либо pg_ctl reload
. Если меняется структура ключа или его парольная фраза, может потребоваться рестарт.
Проверка со стороны сервера
# Показать активные TLS-сессии
SELECT pid, usename, client_addr, ssl, version, cipher
FROM pg_stat_ssl JOIN pg_stat_activity USING (pid)
WHERE ssl;
Если вы видите ssl = t
и заполненные version
/cipher
— соединение защищено. Убедитесь, что незашифрованные маршруты отсутствуют или запрещены политиками в pg_hba.conf
.
Клиент PostgreSQL с verify-full
# psql
psql "host=db01.internal.local dbname=app user=app sslmode=verify-full sslrootcert=/etc/postgresql/tls/ca.crt"
verify-full
одновременно требует защищённого канала, валидного CA и соответствия имени хоста SAN. Если DNS-имя отличается от того, что в сертификате, подключение будет отклонено.
Нужен ли mTLS к БД
mTLS уместен в Zero Trust-сегментах и для межсервисных взаимодействий, где нельзя доверять только паролям. Он позволяет ограничить доступ на уровне сертификатов и отзывом быстро «выключать» компрометированные инстансы. Но дистрибуция и ротация клиентских сертификатов повышают цену владения. Для классических веб-приложений достаточно TLS с проверкой CA и имени, плюс сильная аутентификация (SCRAM для PostgreSQL, учетные данные с ротацией для MySQL).
Если всё же решитесь: выпустите клиентский сертификат с extendedKeyUsage = clientAuth
, настройте pg_hba.conf
с clientcert=verify-full
или в MySQL используйте плагин X509 и соответствующие GRANT-правила для субъектов сертификата.
Хранение ключей и права
Приватные ключи серверов — самый чувствительный артефакт. Положите их в выделенный каталог, доступный только пользователю СУБД. На Linux это обычно /etc/mysql/tls
и /etc/postgresql/tls
с правами каталога 700 и файла ключа 600. Не используйте парольные фразы на серверных ключах, если это приведёт к интерактивному вводу при перезапуске демона. Следите за бэкапами: ключи не должны утекать в объёмные бэкапы, которыми часто делятся команды. Для инфраструктуры на VDS дополнительно проверьте шифрование дисков и политику доступа к снапшотам.
Ротация и оперативное обновление
Сертификаты с коротким сроком жизни снижают боль отзывов, но требуют автоматизации. Выберите процесс: конфигурационный менеджмент, Secret-хранилище, пайплайн CI для выпусков и выкладки. Важно обеспечить перекрытие сроков действия: разложите новые сертификаты заранее, перезагрузите службы, проверьте, что клиенты принимают новую цепочку CA, и лишь затем отзывайте старую.
MySQL: обновление сертификатов зачастую требует перезапуска службы, хотя часть сборок поддерживает мягкую перезагрузку цепочки. MariaDB умеет FLUSH SSL
. PostgreSQL перечитывает сертификаты по SIGHUP; при смене ключа или пароля ключа возможен рестарт. Планируйте окна обслуживания и убедитесь, что пулы соединений переносят обрывы. Для надёжной стратегии восстановления пригодится наше практическое руководство по offsite-бэкапам в S3 — см. статью «S3-бэкапы с restic/borg» по ссылке S3-бэкапы с restic/borg.
Типовые ошибки и их диагностика
Несовпадение имени хоста. Клиент жалуется на hostname mismatch — проверьте, что FQDN клиента совпадает с SAN сертификата. Добавьте все используемые DNS-алиасы, а также IP
, если клиенты подключаются по адресу.
Неполная цепочка CA. Если серверный сертификат подписан промежуточным CA, клиенты должны иметь полный путь доверия. Для MySQL указывайте файл ssl_ca
на стороне сервера и соответствующий --ssl-ca
на стороне клиента. В PostgreSQL используйте ssl_ca_file
и клиентский sslrootcert
. Убедитесь, что файл содержит правильный набор сертификатов в PEM-последовательности.
Старые протоколы/шифры. При жёстких настройках сервера клиенты со старыми библиотеками OpenSSL могут не установиться. Проверьте версии драйверов (Connector/J, libpq, mysqlclient) и минимальную версию TLS. Лучше обновить клиентов, чем ослаблять серверную политику.
Без TLS из-за режима по умолчанию. Для MySQL явно включайте --ssl-mode=VERIFY_IDENTITY
. Для PostgreSQL используйте sslmode=verify-full
. Режимы prefer
или require
без проверки имени не обеспечивают сопротивление MITM.
Форматы ключей и прав. Неверные права на ключи заставляют серверы отказываться от TLS. PostgreSQL придирчив: если ssl_key_file
доступен шире, чем 600, TLS не стартует. Смотрите логи запуска.
Производительность и пулы соединений
Нагрузка от TLS в типичной БД-переписке невысока: основная цена — в рукопожатии. Помогают лимитированные пулы соединений и keepalive. В PostgreSQL часто применяют PgBouncer: он может принимать TLS от приложений и держать внутренние незашифрованные коннекты к Postgres или тоже TLS — как позволяет политика. В MySQL роль пула берут ProxySQL и аналогичные решения. Неплохо замерить p95/p99 задержек до и после включения TLS, чтобы корректно подобрать лимиты пула. Для смежных оптимизаций Postgres полезно перечитать заметку «Тюнинг autovacuum и индексов» — рекомендации по autovacuum и индексам.
Сегментация сети и firewall
Шифрование не заменяет сегментацию. Ограничьте доступ к 3306/TCP (MySQL) и 5432/TCP (PostgreSQL) только необходимыми подсетями и сервисами. Исключите публикацию портов БД в Интернет. Внутри кластера используйте списки контроля доступа и статические маршруты, чтобы исключить «случайные» хопы и NAT, которые усложняют отладку TLS. Если БД крутится на изолированном VDS, настраивайте хостовый firewall и security groups как первый рубеж обороны.

CRL и отзыв сертификатов
Если вы используете внутренний CA, спланируйте процесс отзывов. PostgreSQL поддерживает ssl_crl_file
, MySQL — ssl_crl
и ssl_crlpath
. Не все клиенты валидируют OCSP, поэтому практика коротких сроков жизни и автоматической ротации в сочетании с CRL остаётся рабочим компромиссом.
Контейнеры и секреты
В Kubernetes и аналогичных оркестраторах сертификаты и ключи храните как секреты и монтируйте в только-читаемые тома внутри контейнера. Следите, чтобы обновления секретов триггерили перезапуск/перезагрузку подов. Не включайте приватные ключи в образ: это усложняет ротацию и повышает риск утечки.
Чеклист внедрения
- Выпустить внутренний CA и настроить безопасное хранение его ключей.
- Сгенерировать для каждого узла БД сертификат с корректным SAN.
- Включить TLS на сервере, ограничить протоколы до TLS 1.2+ и задать шифросьюты.
- Принудить защищённый транспорт:
require_secure_transport
в MySQL,hostssl
в PostgreSQL. - Настроить клиентов на строгую проверку:
VERIFY_IDENTITY
иsslmode=verify-full
. - Проверить, что все приложения ходят по TLS; мониторить метрики TLS-сессий.
- Автоматизировать ротацию сертификатов и CA, описать процедуру отзывов.
- Ограничить доступ по портам БД на уровне сети и хостовых firewall.
Резюмируя: шифрование канала до БД — задача одного-двух спринтов и нескольких аккуратных правок конфигураций. На выходе вы убираете целый класс рисков, упрощаете аудит и закладываете фундамент под дальнейшие практики Zero Trust и сервис-сервис аутентификацию.