Доброе утро!
Данная заметка, по сути своей, является продолжением статьи моего коллеги.
Обрисую ситуацию: есть haproxy, балансирующий некоторый набор сервисов на одном порту, включая nginx. Примерно как и в случае по ссылке выше. Кроме того, внури моей сети внезапно оказался один из серверов товарища. И вдруг ему захотелось поднять на 443-ем порту веб-сайтик. Он выделил поддомен, озаботился с TLS-сертификатами, а дальше пришёл ко мне с посылом „я направлю на тебя свою A-запись, можешь сделать в своём nginx-е что-то вроде proxy_pass, только чтобы мои сертификаты остались?“. Я сказал „да не вопрос“, а потом задумался, как бы это сделать.
Первое, что приходит в голову — отдельный server{} в nginx. И сразу же уходит — для того, чтобы посмотреть заголовок Host нужно „забампить“ TLS-соединение, что условиям задачи не удовлетворяет. Что делать? Вспоминается такая штука, как SNI. Кроме того, учитываем тот факт, что первые байты при соединении и так прочитает haproxy. Почему бы не сделать это там?
В случае с конфигом из приведённой вначале текста статьи, правки тривиальны. Добавляем новый backend:
backend subdomain-example-com
mode tcp
timeout server 2h
server https-subdomain-example-com 192.168.38.10:443
Имена произвольные, IP сервера из внутренней сети, где слушает „чужой“ nginx.
Теперь нужно научить haproxy на этот backend кого-то посылать. При чём делать это по SNI, а не весь подряд https-трафик. И haproxy это умеет! В уже имеющийся frontend добавим следующее условие:
use_backend subdomain-example-com if { req_ssl_sni -i subdomain.example.com }
Здорово!
При таком подходе стоит учитывать, что данная строка должна стоять до use_backend https if { req.ssl_hello_type 1 }
, потому что в противном случае запрос клиента до неё просто не дойдёт. То есть, сначала проверяем по SNI, а потом уже отсылаем на веб-сервер «по-умолчанию».