Nginx: динамическое подключение TLS-сертификатов из переменной

Начиная с версии Nginx 1.15.9, появилась возможность использовать переменные в директивах ssl_certificate и ssl_certificate_key. Для этого обязательна также openssl версии не ниже 1.0.2

В моём случае возникла ситуация, когда на балансировщике должны слушать домены вида:

*.sub1.rmn-lux.ru
*.sub2.rmn-lux.ru
*.sub3.rmn-lux.ru

Для вышеописанных доменов 4 уровня были выписаны wildcard сертификаты, которые лежат по пути:

ls -1 /etc/nginx/certs

sub1.cer
sub1.key
sub2.cer
sub2.key
sub3.cer
sub3.key

Решил сделать не совсем стандартное и более правильное, как я думаю, решение, чтобы не плодить конфиги, отличие которых друг от друга будет лишь в пути до сертификата и ключа.

Основной итоговый конфиг будет следующим (с условными доменами):

map $ssl_server_name $cert {
        ~*sub1.rmn-lux.ru sub1;
        ~*sub1.rmn-lux.ru sub2;
        ~*sub1.rmn-lux.ru sub3;
}

server {
                listen 443 ssl;
                server_name     .*(sub1|sub2|sub3)\.rmn-lux\.ru;
                ssl_certificate "/etc/nginx/certs/$cert.cer";
                ssl_certificate_key "/etc/nginx/certs/$cert.key";
                ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
                ssl_prefer_server_ciphers on;
                ssl_ciphers EECDH:+AES256:-3DES:RSA+AES:RSA+3DES:!NULL:!RC4;
...
}

Пробежимся по основным моментам.

В конструкции map под регулярное выражение попадают все три записи:

*.sub1.rmn-lux.ru
*.sub2.rmn-lux.ru
*.sub3.rmn-lux.ru

и в соответствии от того, какая содержится в строенной переменной $ssl_server_name (по идее также можно юзать $host), заполняется моя пользовательская переменная $cert.

После этого в основном блоке Server прописывается регулярное выражение вида:

.*(sub1|sub2|sub3)\.rmn-lux\.ru

под которое попадает любое выражение на все три поддомена, к примеру, такие:

1.sub1.rmn-lux.ru
2.sub2.rmn-lux.ru
3.sub3.rmn-lux.ru

и соответственно подгружается правильный сертификат.

Очень удобно проверять regex на онлайновом ресурсе, вот один, вот второй (рекомендую второй).

Схема вышла несложная и простая для чтения и понимания, в отличие от той, если бы пришлось городить 3 таких конфига с минимальными отличиями.

Стоит иметь ввиду, что при использовании такого способа,во время каждой операции TLS-рукопожатия будет загружаться сертификат, что будет влиять на производительность.

Также при настройке у меня возникли некоторые нюансы, о них ниже.

Если в поле server_name прописано несколько доменов, например, вот так

server_name *.sub1.rmn-lux.ru *.sub2.rmn-lux.ru *.sub3.rmn-lux.ru;

то в таком случае даже если map отработает корректно, домен в server_name будет выбран тот, который стоит первый в списке (по умолчанию), и соответственно с сертификатом возникнет проблема. Так происходит из-за особенностей работы протокола TLS: сначала устанавливается TLS-соединение, а потом только прилетает http-запрос с именем сервера, а потому nginx не может знать имени сервера заранее и предлагает сертификат сервера по умолчанию. Именно поэтому была применена регулярка для server_name.

Ваш комментарий будет первым

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *