У нас есть Nginx с сотнями прокси для нескольких доменов / поддоменов. Как это проводится годами, каждый новый домен, мы просто идем туда и создаем новый файл conf с серверным блоком, содержащим HTTP и HTTPS.
Обратите внимание на то, что многие из этих серверных блоков имеют одинаковые параметры прокси, только меняют бэкэнд, и они напоминают: «Хм, могу ли я попытаться поместить все это в одну пару серверных блоков с http и https?» и решаем попробовать :)
Моя идея заключалась в том, чтобы сопоставить переменные с адресом с серверной частью и с используемыми сертификатами, поскольку это единственная информация, которая изменяется в большинстве имеющихся у нас серверных блоков, таким образом, для новых доменов. просто добавил бы линии на карты.
HTTP работает нормально, никаких проблем, проблема - HTTPS.
У меня есть несколько доменов, например «example02.com, example01.com, ...», и я использую групповой сертификат для каждого из этих доменов, поэтому в зависимости от хоста я сопоставляю имя сертификата с переменной и использую его в ssl_certificate и ssl_certificate_key, как показано ниже, которые я видел только возможными, начиная с Nginx 1.15, выглядят так. (Я использую 1.18 прямо сейчас)
Проблема в том, что при том, как я создаю указанный ниже серверный блок, только www.example01.com будет работать с правильным сертификатом, когда вы попытаетесь войдите в www.example02.com , он все равно будет пытаться загрузить сертификат * .example01.com.
Можно просто создать серверный блок для каждого домена, чтобы обслуживать несколько поддоменов с разными сертификатами, но пока я просто пытаюсь выяснить, можно ли это сделать с помощью пары серверных блоков, и проверяю, делаю ли я что-то не так.
map $host $backendaddr {
default "127.0.0.1:4444";
www.example01.com "127.0.0.1:4445"
www.example02.com "127.0.0.1:4446"
}
map $host $certificate {
default "example.com";
www.example01.com example01.com;
www.example02.com example02.com;
}
server {
listen 80;
server_name www.example01.com www.example02.com;
access_log /var/log/nginx/access-http.log main;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 1800;
proxy_read_timeout 1800;
proxy_send_timeout 1800;
location / {
proxy_pass http://$backendaddr$request_uri;
}
}
server {
listen 443 ssl;
server_name www.example01.com www.example02.com;
access_log /var/log/nginx/access-https.log main;
ssl_certificate ssl/$certificate.pem;
ssl_certificate_key ssl/$certificate.key;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 1800;
proxy_read_timeout 1800;
proxy_send_timeout 1800;
location / {
proxy_pass https://$backendaddr$request_uri;
}
}
Проблема в том, что переменная $host
используется в map
для сертификатов. Поток запросов в TLS соединении выглядит примерно так:
SNI
, которое сообщает виртуальный хост, к которому пытается подключиться клиент. $host
переменная заполняется только из следующего:Host
заголовка запросаимя_сервера
, соответствующее запросуЕдинственной доступной информацией здесь является имя_сервера
, поэтому www.example01.com
используется как значение для $host
переменной.
Чтобы достичь поставленной цели, вы можете попробовать следующее:
server {
listen 443 ssl;
server_name www.example01.com www.example02.com;
ssl_preread on;
...
}
map $ssl_preread_server_name $certificate {
default example.com;
www.example01.com example01.com;
www.example02.com example02.com;
}
ssl_preread on
заставляет nginx назначать переменные на основе пакета TLS ClientHello. $ssl_preread_server_name
содержит значение поля SNI из пакета ClientHello, которое затем может быть использовано для выбора сертификата.