null

Проксируем HTTPS без «человека посередине»

Доброе утро!

Данная заметка, по сути своей, является продолжением статьи моего коллеги.

Обрисую ситуацию: есть 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, а потом уже отсылаем на веб-сервер «по-умолчанию».