Научить Нгинкс обслуживать по ХТТПС сразу много доменов

Вопрос спецам по Нгинксу.

В конфигурации Нгинкса прописаны такие заклинания про ССЛ:

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/nginx/ssl/letsencrypt-chain.pem;

Только на месте example.com настоящий домен.

Сервер обслуживает много доменов, которые добавляются и удаляются. Каждому домену соответствует своя папка, в которой лежат эти файлы, нужные для ССЛа. Соответственно при добавлении домена приходится создавать очередной конфиг, прописывать туда пути к папкам, перезапускать Нгинкс.

Хочется этого избежать и просто прописать единый конфиг, который сразу покроет все домены, условно говоря, вот так:

ssl_certificate /etc/letsencrypt/live/$HTTP_HOST/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/$HTTP_HOST/privkey.pem;
ssl_trusted_certificate /etc/nginx/ssl/letsencrypt-chain.pem;

Насколько я понял, так сделать нельзя: Нгинкс не поддерживает переменные в этих строках конфигурации, потому что они обрабатываются не во время обслуживания запроса, а при запуске самого Нгинкса. Если нужных файлов нет, Нгинкс просто не запустится, потому что конфигурация некорректна.

Но как-то же должна решаться задача? Мне нужно, чтобы именно в момент обращения к серверу Нгинкс смотрел, есть ли сертификат в нужной папке, и соответственно, чтобы изменение набора доменов не требовало изменения конфигурации и перезапуска сервера. Куда копать?

Дальше
5 комментариев
Roman 🚀 ilin 2021

Нгинкс не нужно перезапускать, ему достаточно сделать reload

Roman 🚀 ilin 2021

Создание конфигов тоже легко автоматизируемое действие

pétiukh 2021

Обнови nginx — эта фича добавлена в версии 1.15.9

Konstantin Baryshnikov 2021

Переменная HTTP_HOST недоступна на этапе определения пути к сертификату, поскольку заголовок HOST (вместе со всем остальным HTTP-запросом) на этот момент еще не расшифрован, а для того, чтобы его расшифровать, нужен как раз тот самый сертификат.

Но как тогда работает механизм виртуальных хостов вместе с HTTPS? А долгое время никак и не работал, приходилось делать отдельные IP-адреса на каждый домен. Для решения этой проблемы в какой-то момент придумали SNI — это когда server name передается в открытом виде на уровне TLS.

Соответствующая переменная — это $ssl_server_name. В документации это вскользь упоминается:

https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate

Since version 1.15.9, variables can be used in the file name when using OpenSSL 1.0.2 or higher:

ssl_certificate $ssl_server_name.crt;
ssl_certificate_key $ssl_server_name.key;

Note that using variables implies that a certificate will be loaded for each SSL handshake, and this may have a negative impact on performance.

Konstantin Baryshnikov 2021

Тут может возникнуть проблема с дополнительными доменными именами: скажем, сертификат из файла ilyabirman.ru.crt надо использовать и для ilyabirman.ru, и для www.ilyabirman.ru.

Если все укладывается в паттерн «префикс www. или есть, или его нет», то можно использовать директиву map с регулярными выражениями, и ей определить другую переменную, по принципу «если ssl_server_name начинается с www., то берем хвост, в остальных случаях берем все целиком». В регулярках с map важно использовать именованные captures (как это по-русски?), поскольку позиционные, типа $1, могут оказаться в процессе затерты другими регулярками (location/rewrite).

Примерно так (не проверял):

map $ssl_server_name $ssl_certificate_filename {
«~^www\.(?<h>.*)$» $h;
default $ssl_server_name;
}

server {
...
ssl_certificate /etc/letsencrypt/live/$ssl_certificate_filename/fullchain.pem;
...
}

Илья Бирман 2021

Спасибо, так и настроили!

Мои книги