Перейти к содержанию

Постквантовый ssl_ecdh_curve, из-за которого NGINX не запускается

Идентификатор проверки Gixy: ssl_ecdh_curve

Каждая статья «добавьте постквантовый TLS в nginx» заканчивается одной и той же строкой:

ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1;

Скопируйте её на машину, где OpenSSL старше 3.5, и сайт упадёт. Не «потеряет поддержку постквантовой криптографии» — именно упадёт.

Почему это полный отказ, а не деградация

nginx передаёт значение прямо в OpenSSL:

/* src/event/ngx_event_openssl.c */
if (SSL_CTX_set1_curves_list(ssl->ctx, &conf->ecdh_curve...) == 0) {
    ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
                  "SSL_CTX_set1_curves_list(\"%V\") failed", &conf->ecdh_curve);
    return NGX_ERROR;
}

Здесь неудачно складываются два свойства:

  1. OpenSSL отвергает весь список, если хотя бы одно имя группы ему неизвестно. Частичного применения не существует — X25519MLKEM768:X25519:prime256v1 отклоняется целиком, хотя две из трёх групп поддерживаются повсеместно.
  2. nginx пишет об этом с уровнем NGX_LOG_EMERG и возвращает ошибку, так что мастер-процесс не завершает разбор конфигурации. Это отказ при запуске, а не откат на уровне отдельного соединения.

Проверено на OpenSSL 3.6.3:

$ openssl s_client -groups bogusgroup -connect 127.0.0.1:1
Call to SSL_CONF_cmd(-groups, bogusgroup) failed

$ openssl s_client -groups '?bogusgroup:X25519' -connect 127.0.0.1:1
connect:errno=61          # список принят, не удалось только TCP-соединение

X25519MLKEM768 и родственные группы появились в OpenSSL 3.5. Дистрибутивы, на которых работает большая часть продакшен-инсталляций nginx, поставляют более старые версии:

Дистрибутив OpenSSL X25519MLKEM768
Debian 12 (bookworm) 3.0 ✗ nginx не запустится
Ubuntu 24.04 LTS 3.0 ✗ nginx не запустится
RHEL / AlmaLinux / Rocky 9 3.2 ✗ nginx не запустится

Прежде чем называть любую постквантовую группу, проверьте, что у вас на самом деле установлено, командой openssl version; openssl list -tls-groups (OpenSSL 3.x) покажет точный набор, который примет слинкованная библиотека.

То же самое относится к достандартным именам X25519Kyber768Draft00, которые используют старые сборки на BoringSSL и руководства 2024 года.

Плохой пример

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    # Одно неизвестное имя — и мастер-процесс откажется стартовать
    ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1;
}

Хороший пример

Добавьте постквантовым группам префикс ?. Тогда OpenSSL пропускает имена, которые не распознаёт, вместо того чтобы отвергнуть весь список:

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    ssl_ecdh_curve ?X25519MLKEM768:X25519:prime256v1;
}

Машина с OpenSSL 3.5+ согласует X25519MLKEM768, а более старая незаметно откатится на X25519 и продолжит обслуживать запросы.

У префикса ? есть собственное ограничение по версии

? появился в OpenSSL 3.3. На OpenSSL 3.0 и 3.2 сама строка ?X25519MLKEM768 является неразбираемым именем группы и приводит ровно к тому же отказу. Поэтому:

  • OpenSSL 3.5+ — перечисляйте постквантовые группы напрямую или с ?.
  • OpenSSL 3.3 / 3.4 — используйте ?, чтобы конфигурация была совместима с будущими версиями.
  • OpenSSL 3.0 / 3.2 (Debian 12, Ubuntu 24.04, RHEL 9) — не указывайте постквантовые группы вообще. Оставьте ssl_ecdh_curve auto; (значение по умолчанию) или перечислите только классические кривые.

Если одна конфигурация раскатывается на смешанный парк серверов, включайте директиву на этапе сборки, а не рассчитывайте на то, что ? вас спасёт.

Что эта проверка не помечает

  • ssl_ecdh_curve auto; — значение nginx по умолчанию; список групп выбирает OpenSSL.
  • Только классические кривые (X25519:prime256v1:secp384r1).
  • Постквантовые группы, у которых уже есть префикс ?.