PXC: TLS между нодами без даунтайма

9 сентября 2026 года в блоге Percona вышла заметка Juan Arruti: как включить TLS на трафике между нодами Percona XtraDB Cluster, не останавливая весь кластер. Это не новый пакет и не CVE. Это процедура для живого PXC, у которого шифрование репликации выключено, а аудит, compliance или сегмент сети вдруг потребовали его включить.

С PXC 8.0 переменная pxc-encrypt-cluster-traffic по умолчанию включена. Она закрывает SSL на group communication, State Snapshot Transfer (SST) и Incremental State Transfer (IST). На практике кластеры без TLS встречаются часто: ставили давно, дефолт тогда был другой, или кто-то выключил «чтобы завелось».

Переменная не динамическая. Нода с шифрованием слушает ssl://, соседи без него остаются на tcp://. Если просто включить флаг на одной ноде и перезапустить её, в логе Galera будет Failed to establish connection: wrong version number. Это обычная реакция OpenSSL, когда клиент шлёт TLS, а сервер ждёт открытый TCP.

Откуда это известно и что говорит документация

Первоисточник: пост Percona от 9 сентября 2026. Автор разбирает обходной путь, который появляется с PXC 8.0.28 и есть во всех PXC 8.4. Он опирается на опцию провайдера Galera socket.dynamic.

Документация PXC 8.4 по шифрованию трафика по-прежнему описывает штатный сценарий иначе: pxc-encrypt-cluster-traffic нельзя поменять на работающем кластере, смена требует остановить кластер, поправить конфиг и поднять ноды заново. В индексе wsrep_provider_options для 8.4 опции socket.dynamic нет. Имеет смысл не спорить с докой «на глаз», а идти по шагам из блога и сверять логи ssl:// / tcp://.

Что это меняет на VPS с WordPress

Типичный WordPress на одном VPS с nginx, PHP-FPM и одной MariaDB эту процедуру не запускает. PXC это не «просто MySQL» и не MariaDB Galera из репозитория Debian. Это кластер Percona Server на базе Galera, обычно три ноды. Если в переменных нет wsrep_* и pxc_encrypt_cluster_traffic, дальше можно не читать: новость про чужой контур.

Если база сайта реально сидит на PXC, TLS между нодами и HTTPS сайта это разные слои. Сертификат Let’s Encrypt на nginx сайт не шифрует порт 4567. Аудит, который просит «закрыть репликацию», смотрит на межнодовый трафик: SST, IST и write-set. Клиентский TLS (WordPress → MySQL) тоже отдельно: его документация PXC описывает через те же ssl-ca / ssl-cert / ssl-key, но пост Arruti про inter-node, не про wp-config.php.

На практике это означает: пока нода в rolling restart, кворум держат остальные. Если PHP ходит в конкретный хост из DB_HOST, рестарт этой ноды даст обрыв SQL на время подъёма. Если перед кластером балансировщик с живыми бэкендами, запрос уйдёт на соседнюю ноду. Kubernetes и «просто уйдите в облако» здесь ни при чём.

Что делает socket.dynamic

Опция позволяет ноде говорить с соседями и по TLS, и открытым TCP. Сначала попытка зашифрованного соединения. Если сосед отвечает не TLS, нода повторяет уже без шифрования и входит в кластер. Без socket.dynamic на первой же ошибке wrong version number она так и не присоединится.

В runtime это не переключается. Чтобы включить опцию, ноду всё равно нужно перезапустить. Даунтайм всего кластера при этом не обязателен: схема рассчитана на rolling restart трёх нод, и таких проходов будет два. Percona прямо пишет: полностью перевести кластер на TLS без двух проходов не получится.

Сертификаты и my.cnf до первого рестарта

На всех нодах одни и те же ключ и сертификат. Неважно, сгенерировал их MySQL сам или выпустили вы. Percona советует держать файлы вне datadir: joiner перед полным SST вычищает каталог данных, выживают только файлы по шаблону [sst] cpat. По умолчанию туда попадают *.pem. Файл .crt или .key, лежащий в datadir, будет удалён.

Документация 8.4 для того же сюжета предлагает каталог /etc/mysql/certs и те же имена: ca.pem, server-cert.pem, server-key.pem. В блоге каркас my.cnf такой:

[mysqld]
pxc-encrypt-cluster-traffic=ON
wsrep_provider_options="socket.dynamic=YES"
ssl-ca=/etc/mysql/certs/ca.pem
ssl-cert=/etc/mysql/certs/server-cert.pem
ssl-key=/etc/mysql/certs/server-key.pem
[sst]
encrypt=4
ssl-ca=/etc/mysql/certs/ca.pem
ssl-cert=/etc/mysql/certs/server-cert.pem
ssl-key=/etc/mysql/certs/server-key.pem

pxc-encrypt-cluster-traffic=ON включает TLS для group communication, IST и SST. socket.dynamic=YES нужен на время раскатки, чтобы нода умела оба протокола. Секция [sst] с encrypt=4 в блоге названа страховкой: флаг сам по себе требует SST на encrypt=4, а явная секция помогает донору и joiner, пока правка ещё не применена на всех нодах.

Побочный эффект, который легко пропустить: скрипт SST перечитывает my.cnf на каждой передаче. Как только в файле появились [sst] encrypt=4 и ssl-*, доноры начинают шифровать SST, даже если mysqld на них ещё не перезапускали. Конфиг лучше разложить на все ноды до первого рестарта, а не «по ходу». Перед правкой: копия my.cnf.

Два прохода rolling restart

Типовая схема в посте: три ноды.

Первый проход: TLS есть, plaintext ещё можно

Ноды 1 и 2 перезапускают по одной с конфигом выше. После каждой ждать, пока нода снова в кластере и в состоянии Synced. Перезапущенная нода пробует TLS и откатывается на plaintext.

Что настройки дошли до провайдера, в блоге проверяют так. wsrep_provider_options это длинная строка пар, из неё вытаскивают нужные ключи:

SELECT
  REGEXP_SUBSTR(@@wsrep_provider_options,'socket\\.dynamic = [^;]+') AS socket_dynamic,
  REGEXP_SUBSTR(@@wsrep_provider_options,'socket\\.ssl = [^;]+') AS socket_ssl,
  REGEXP_SUBSTR(@@wsrep_provider_options,'socket\\.ssl_ca = [^;]+') AS socket_ssl_ca,
  REGEXP_SUBSTR(@@wsrep_provider_options,'socket\\.ssl_cert = [^;]+') AS socket_ssl_cert,
  REGEXP_SUBSTR(@@wsrep_provider_options,'socket\\.ssl_key = [^;]+') AS socket_ssl_key\G

Ожидаемый кадр из разбора: socket.dynamic=YES, socket.ssl=YES, пути к pem совпадают с конфигом. socket.ssl и сертификаты провайдер получает от MySQL, потому что pxc-encrypt-cluster-traffic уже ON. Это ещё не значит, что репликация зашифрована. В промежуточном состоянии трафик может идти открытым текстом.

В статье это ловят tcpdump на ноде 1, порт 4567, пакеты с ноды 2. На ноде 2 создают учебную таблицу. DDL в PXC уходит statement’ом, и в дампе виден create table test.table_test:

tcpdump -i any -nn -A -s 0 "tcp port 4567 and host node2" -c 2000 | grep -a table_test

На проде такую таблицу на боевой базе лучше не плодить. Достаточно смотреть лог Galera: после первого прохода соединения ещё могут быть tcp://. Учебный CREATE TABLE оставлен как в источнике, не как обязательный шаг.

Второй проход: только TLS

Ноду 3 поднимают с socket.dynamic=NO. Ей динамический режим не нужен: две другие уже умеют и TLS, и plaintext. В my.cnf на этом шаге:

[mysqld]
wsrep_provider_options="socket.dynamic=NO"

После рестарта в логе должны появиться соединения ssl://, не tcp://. Затем то же на нодах 1 и 2: убрать socket.dynamic, перезапустить по одной. Когда все три говорят только TLS, тот же tcpdump на 4567 уже не находит DROP TABLE test.table_test: трафик зашифрован.

Снять TLS, если понадобится, можно тем же механизмом и тоже двумя rolling restart. Сначала вернуть socket.dynamic=YES на нодах 1 и 2, оставив pxc-encrypt-cluster-traffic=ON. Затем ноду 3 поднять с pxc-encrypt-cluster-traffic=OFF. Потом ноды 1 и 2 тоже выключить шифрование и убрать socket.dynamic. Секцию [sst] с encrypt и ssl-* чистят в конце, не в начале.

Что проверить руками

Сначала убедиться, что это PXC, а не одиночный mysqld или MariaDB. Команды ниже типовые, без выдуманных пакетов Ubuntu. Пользователь с правом чтения переменных, не обязательно root на всю машину.

mysql --version
mysql -e "SHOW VARIABLES LIKE 'pxc_encrypt_cluster_traffic'; SHOW VARIABLES LIKE 'wsrep_on'; SHOW STATUS LIKE 'wsrep_cluster_size'; SHOW STATUS LIKE 'wsrep_local_state_comment';"

Если pxc_encrypt_cluster_traffic нет в списке, вы не на PXC. Не копируйте wsrep_provider_options в MariaDB «на всякий случай». Версии 8.0.28 и 8.4 в посте относятся к Percona XtraDB Cluster, не к пакету mariadb-server из Debian.

После каждого рестарта ноды: тот же SHOW STATUS, плюс запрос с REGEXP_SUBSTR из блока выше. Пока socket.dynamic=YES, наличие socket.ssl=YES ещё не равно «соседи говорят только TLS».

Рестарт ноды кластера это не nginx -t и не reload. Две ноды сразу не трогать. Копия конфига до правки. Не предлагаю mysql_upgrade с потолка: к включению TLS он не относится.

Где обычно ломается

  • Разные сертификаты на нодах. Документация 8.4 об этом пишет отдельно: тогда нода просто не вступит.
  • Сертификаты оставили в datadir, потом случился SST. Joiner получил чистый каталог без .crt / .key.
  • Включили pxc-encrypt-cluster-traffic на одной ноде без socket.dynamic и без подготовки соседей. Получили wrong version number и узел вне кластера.
  • Поправили только [mysqld], забыли [sst]. Скрипт SST читает файл сразу, donor и joiner могут разъехаться по encrypt=4.
  • Перепутали HTTPS сайта и TLS Galera. certbot на 443 порт 4567 не закрывает.
  • Перепутали MariaDB на том же VPS и PXC. Ветки, пакеты и дефолты другие.
  • Рестарт всех трёх нод «чтобы наверняка». Кворум и сайт поедут вместе.

Не выключать AppArmor и файрвол «чтобы Galera завелась». Если 4567 и соседние порты репликации не ходят между нодами, сначала сеть, не шифрование. chmod 777 на каталог сертификатов тоже не нужен: в документации 8.4 для /etc/mysql/certs достаточно владельца mysql:mysql.

Если у вас один MySQL или MariaDB рядом с WordPress, эта новость про чужой контур. Если PXC уже стоит и TLS между нодами выключен, имеет смысл идти по схеме Percona, а не останавливать три ноды разом. Смотреть версию: обходной путь заявлен с 8.0.28 и во всей 8.4. Документация по-прежнему описывает полную остановку кластера как штатный способ сменить pxc-encrypt-cluster-traffic. Блог как раз про то, как этого избежать двумя rolling restart.

Источники и ссылки


Комментарии загружаются…