Научить Нгинкс обслуживать по ХТТПС сразу много доменов
Вопрос спецам по Нгинксу.
В конфигурации Нгинкса прописаны такие заклинания про ССЛ:
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;
Насколько я понял, так сделать нельзя: Нгинкс не поддерживает переменные в этих строках конфигурации, потому что они обрабатываются не во время обслуживания запроса, а при запуске самого Нгинкса. Если нужных файлов нет, Нгинкс просто не запустится, потому что конфигурация некорректна.
Но как-то же должна решаться задача? Мне нужно, чтобы именно в момент обращения к серверу Нгинкс смотрел, есть ли сертификат в нужной папке, и соответственно, чтобы изменение набора доменов не требовало изменения конфигурации и перезапуска сервера. Куда копать?
Нгинкс не нужно перезапускать, ему достаточно сделать reload
Создание конфигов тоже легко автоматизируемое действие
Обнови nginx — эта фича добавлена в версии 1.15.9
Переменная 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.
Тут может возникнуть проблема с дополнительными доменными именами: скажем, сертификат из файла 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;
...
}
Спасибо, так и настроили!