Бывают ситуации, когда на обратном прокси нужно балансировать сразу несколько разных доменов. На первый взгляд решение выглядит просто – засунуть все домены в один единственный сертификат. Этот вариант, тем не менее, не всегда бывает подходящим и в таком случае вам понадобится установить несколько сертификатов на Nginx. Как это сделать и обсудим в этой статье.
Если вам интересна тематика Debian и связанных с ним приложений, рекомендую обратиться к тегу Debian на моем блоге.
Содержание
Несколько сертификатов на Nginx
В качестве тестового обратного прокси возьмем конфигурацию, которую мы получили, разбирая материал предыдущих статей по Nginx, а именно:
Приступим!
Предлагаю ознакомиться с другими статьями про Nginx на моем блоге:
Приятного чтения!
Особенности работы SSL/TLS
Тут я не планирую рассказывать про асимметричные алгоритмы и принципы шифрования. Поговорим о том, как взаимодействуют TLS и HTTP.
Инкапсуляция
Когда вы вбиваете в браузере имя сайта или переходите по ссылке/закладке, в работу включается множество протоколов различных уровней. Между уровнями протоколы обычно никак не связаны и даже не знают о существовании друг друга. И это правильно. Представьте, если бы протоколы уровня Application (например веб-браузер) начали переживать что же происходит на транспортном уровне с TCP.
Озвученный выше момент очень важен для понимания принципа работы HTTPS в целом (и далеко не только его). Дело в том, что при обращении к какому-либо веб-ресурсу, сначала в ход пойдут протоколы всех нижестоящих уровней по очереди с самого низкого и только в самом конце начнут отправляться данные HTTP. Этот процесс называется инкапсуляцией и наиболее точно его механизм работы изображен на картинке ниже 1:
Хоть и в сетевой модели TCP/IP протоколы TLS и HTTP принадлежат одному уровню (Application), модель OSI/ISO размещает их на разных уровнях (HTTP – Application, TLS – Presentation).
Было
Когда клиент подключается к веб-ресурсу с использованием TLS, выполняется процесс согласования и установки защищенного соединения. Это называется SSL handshake. Коротко и в человекопонятном виде эта процедура изображена на картинке ниже 2:
Данные передаются в незащищенном виде, но это лишь служебная информация. Как можно заметить, адрес веб-сайта передается уже непосредственно внутри данных протокола HTTP. Таким образом, напрашивается интересный вывод – на сервере должен быть один сертификат, иначе для определенного доменного имени TLS может использовать неподходящий сертификат.
Такая проблема действительно существовала. Решений было два и оба в лоб:
- Засунуть все используемые доменные имена в один сертификат. Крайне неудобный вариант. И он тем больше неудобен, чем больше доменов второго уровня в нем бы использовалось;
- Для каждого доменного имени (или набора имен) использовать отдельный адрес. Если для IPv6 это был очень даже неплохой способ, то для IPv4 это уже не подходило в виду исчерпания пула адресов.
Очевидно, что оба варианта очень неудобны.
Стало
Решение проблемы было найдено очень удобное и вместе с тем простое – можно передавать имя веб-ресурса внутри протокола TLS. В этом случае веб-сервер заранее выберет нужный сертификат и установит защищенное соединение именно с его помощью. Механизм этот называется SNI (Server Name Indication – расширение протокола TLS). Процесс установки соединения TLS и начала передачи данных HTTP принял следующий вид:
Таким образом стало возможно размещать на одном адресе множество доменов с разными сертификатами.
Настройка Nginx
Из теоретического блока выше мы выяснили, что процесс передачи доменного имени веб-ресурса, с которым клиент хочет установить связь, выполняется на этапе установки защищенного сеанса TLS. Уже после этого идет передача полезных данных HTTP. Следовательно, нужно указывать связку домен(ы)-сертификат в одном блоке. Прослушиваемый адрес веб-ресурса мы указываем в секции server {}. Соответственно, там же и должен быть прописан нужный сертификат и его закрытый ключ.
В общем виде этот участок конфигурации может выглядеть так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
upstream blog { server 172.16.1.118:80; } server { listen 443 ssl; ssl_certificate /etc/ssl/certs/cert.crt; ssl_certificate_key /etc/ssl/private/private.key; server_name blog.domain.tld www.blog.domain.tld; location / { proxy_pass http://blog; } } upstream testlab { server 10.0.0.52:443; } server { listen 443 ssl; ssl_certificate /etc/ssl/certs/testlab.cer; ssl_certificate_key /etc/ssl/private/testlab.key; server_name testlab.tld www.testlab.tld; location / { proxy_pass http://testlab; } } |
Если же попытаться перенести опыт последних статей по Nginx (Обратный прокси на Nginx, Расширенная настройка обратного прокси на Nginx), в которых я рассматривал разные дополнительные опции, то получим более массивную конструкцию.
Но перед реальными изменениями сделайте бэкап конфига, чтобы быстро вернуться к рабочему состоянию, если что-то пойдет не так:
1 |
cp /etc/nginx/nginx.conf{,.backup$(date +"%d-%m-%y")} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
upstream blog { server 172.16.1.118:80; } server { listen 443 ssl; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_certificate /etc/ssl/certs/blog.crt; ssl_certificate_key /etc/ssl/private/blog.key; ssl_ciphers HIGH:!aNULL:!MD5; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header Host $host; server_name blog.domain.tld www.blog.domain.tld; location / { proxy_pass http://blog; } } upstream testlab { server 10.0.0.52:443; } server { listen 443 ssl; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_certificate /etc/ssl/certs/testlab.cer; ssl_certificate_key /etc/ssl/private/testlab.key; ssl_ciphers HIGH:!aNULL:!MD5; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_set_header Host $host; server_name testlab.tld www.testlab.tld; location / { proxy_pass http://testlab; } } |
После изменения конфигурации не забудьте рестартануть демон:
1 |
systemctl restart nginx |
Не забудьте проверить корректность работы сервисов со стороны пользователей. На этом все. Успехов!